【问题标题】:List of tuples from heirarchical nested lists来自层次嵌套列表的元组列表
【发布时间】:2015-03-09 07:34:05
【问题描述】:

有一个内部元素的外部列表,每个内部元素是 平面/嵌套列表。每个所述内部列表具有与前面的外部单元格中的内部列表匹配的嵌套结构。这意味着列表中的每个原始值都对应于原始值或列表 - 在以下单元格列表中(递归应用)。因此,每个内部列表的深度等于或超过前一个单元格中元素深度的 1。

(请注意,第一个单元格元素可以作为任意深度的嵌套列表开始)。

以上示例:

[
    [[1, 2,      [3, 4]], 1           ],
    [[3, [4, 5], [6, 7]], [5, 4]      ],   
    [[5, [6, 7], [8, 9]], [7, [8, 6]] ],
]

希望将嵌套列表展开为元组列表,其中每个值与父值组合,或者如果父项是列表则与相应的列表元素组合(保持顺序)。所以对于上面的示例列表,输出应该是:

[
(1, 3, 5),
(2, 4, 6),
(2, 5, 7),
(3, 6, 8),
(4, 7, 9),
(1, 5, 7),
(1, 4, 8),
(1, 4, 6),
]

注意:这个问题是对上一个问题here 的扩展,但与链接问题不同的是,这里所需的元组是扁平的。

【问题讨论】:

  • 当数据结构复杂时,像这样,你应该修复源:(
  • 为什么是[(2,4,6),(2,5,7)] 而不是[(2,[4,5],[6,7])]
  • @khajvah:所需的输出是平面元组, - 这与链接的问题不同,我将编辑以澄清这一点。如果列表有父原语,则列表中的每个值都与父元素结合,如果列表有父列表,则对应的值结合。
  • @thefourtheye:你能指出问题吗?
  • 关于问题本身的注释。我认为,您的声誉可以解决问题。如果您是新人,您会看到很多反对票和 cmets,例如:“您尝试过什么吗?”、“我们不做作业”、“尝试一些事情然后问我们是否有问题。”.. .

标签: python list nested tuples


【解决方案1】:

好的,这个怎么样:

x = [
    [[1, 2,      [3, 4]], 1           ],
    [[3, [4, 5], [6, 7]], [5, 4]      ],   
    [[5, [6, 7], [8, 9]], [7, [8, 6]] ],
]

from collections import defaultdict

def g(x):
    paths = defaultdict(lambda: [])

    def calculate_paths(item, counts):
        if type(item) is list:
            for i, el in enumerate(item):
                calculate_paths(el, counts + (i,))
        else:
            paths[counts].append(item)

    def recreate_values(k, initial_len, desired_len):
        if len(paths[k]) + initial_len == desired_len:
            yield paths[k]
        else:
            for ks in keysets:
                if len(ks) > len(k) and ks[0:len(k)] == k:
                    for ks1 in recreate_values(ks, initial_len + len(paths[k]), desired_len):
                        yield paths[k] + ks1

    for lst in x:
        calculate_paths(lst, (0,))
    keysets = sorted(list(paths.keys()))
    for k in keysets:
        yield from recreate_values(k, 0, len(x))


>>> import pprint
>>> pprint.pprint(list(g(x)))
[[1, 3, 5],
 [2, 4, 6],
 [2, 5, 7],
 [3, 6, 8],
 [4, 7, 9],
 [1, 5, 7],
 [1, 4, 8],
 [1, 4, 6]]

通过为结构中的每个数字创建一个“路径”来工作,这是一个元组,用于标识它如何适合其特定行。


(原始尝试):

如果总是三个级别,那么是这样的吗?

def listify(lst):
    max_len = max(len(item) if type(item) is list else 1 for item in lst)
    yield from zip(*[item if type(item) is list else [item] * max_len for item in lst])


def f():
    for i in listify(x):
        for j in listify(i):
            for k in listify(j):
                yield k

>>> list(f())

【讨论】:

  • 并不总是三个级别。一个通用列表。
【解决方案2】:

这是一个需要解决的问题:-)

我确实设法按预期获得了不同级别的解决方案。但是,我做了一个假设:

  • 输入的最后一列是指向其他列的指针

如果没问题,下面的解决方案可以正常工作:-)

input = [
    [[1, 2,      [3, 4]], 1           ],
    [[3, [4, 5], [6, 7]], [5, 4]      ],
    [[5, [6, 7], [8, 9]], [7, [8, 6]] ],
]

def level_flatten(level):
    """
    This method compares the elements and their types of last column and
    makes changes to other columns accordingly
    """
    for k, l in level.items():
        size = len(l[-1]) if isinstance(l[-1], list) else 1
        # Mostly l[-1] is going to be a list; this is for just in case
        elements = l[-1]
        for i in range(-1, -len(l)-1, -1):
            elem = l[i]
            if isinstance(l[i], int):
                l[i] = [elem] * size
            else:
                for j in range(len(elem)):
                    if not isinstance(elem[j], type(elements[j])):
                        # For a list found in elements[j], there is a int at l[i][j]
                        elem[j] = [elem[j]] * len(elements[j])
    return level

level = {}

for i in range(len(input[0])):
    level[i] = []
    for j in input:
        level[i].append(j[i])

for k, l in level.items():
    for i in range(len(l[-1])):
        level = level_flatten(level)

    total_flat = []
    for item in l:
        row = []
        for x in item:
            if isinstance(x, list):
                row += x
            else:
                row.append(x)
        total_flat.append(row)
    level[k] = total_flat

output_list = []
for i in range(len(level)):# For maintaining the order
    output_list += zip(*level[i])

print output_list

我知道这不是一个很好的解决方案,可以进一步优化。我试图想出比这更好的算法。如果我有更好的解决方案,会更新:-)

【讨论】:

    【解决方案3】:

    我首先尝试使用 2d 矩阵来解决这个问题,但结果发现迭代最后一行划分它上面的列段更简单:

    def unfold(ldata):
        ''' 
        ldata: list of hierarchical lists.
        technique: repeatedly flatten bottom row one level at a time, unpacking lists or
        adding repeats in the column above at the same time. 
        convention: n=1 are primitives, n>=2 are lists.
        '''
    
        has_lists = True
        while has_lists:
            has_lists = False 
            for i, elm in enumerate(ldata[-1]):
                if type(elm) is list:
                    has_lists = True
                    ldata[-1][i:i+1] = ldata[-1][i] # unpack
                    for k in range(0, len(ldata)-1): # over corresponding items in above column
                        if type(ldata[k][i]) is list:
                            ldata[k][i:i+1] = ldata[k][i] # unpack
                        else:
                            ldata[k][i:i+1] = [ldata[k][i]]*len(elm) # add repeats
        return list(zip(*ldata))            
    
    x = [
        [[1, 2,      [3, 4]], 1           ],
        [[3, [4, 5], [6, 7]], [5, 4]      ],   
        [[5, [6, 7], [8, 9]], [7, [8, 6]] ],
    ]
    
    from pprint import pprint
    pprint(unfold(x))
    
    >>>
    [(1, 3, 5),
     (2, 4, 6),
     (2, 5, 7),
     (3, 6, 8),
     (4, 7, 9),
     (1, 5, 7),
     (1, 4, 8),
     (1, 4, 6)]
    

    【讨论】:

      猜你喜欢
      • 2020-12-03
      • 2019-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      • 2017-09-11
      • 1970-01-01
      相关资源
      最近更新 更多