Py学习  »  Python

Python的zip可以用来重构更深层次的嵌套列表吗?

Petteri Nevavuori • 5 年前 • 1803 次点击  

假设我有以下包含列表的列表列表:

samples = [
    # First sample
    [
        # Think 'x' as in input variable in ML
        [
            ['A','E'], # Data
            ['B','F']  # Metadata
        ],
        # Think 'y' as in target variable in ML
        [
            ['C','G'], # Data
            ['D','H'], # Metadata
        ]
    ],
    # Second sample
    [
        [
            ['1'],
            ['2']
        ],
        [
            ['3'],
            ['4']
        ]
    ]
]

我要的输出如下所示:

>>> samples
[
    ['A','E','1'], # x.data
    ['B','F','2'], # x.metadata
    ['C','G','3'], # y.data
    ['D','H','4']  # y.metadata
]

我的问题是 是否存在一种利用Python的方法 zip 函数和一些列表理解来实现这一点?

我已经寻找了一些解决办法,但是例如 this this 处理使用 拉链 处理不同的列表,而不是内部列表。

实现这一点的一种方法很可能是对样本进行简单的迭代,如下所示:

x,x_len,y,y_len=[],[],[],[]

for sample in samples:
    x.append(sample[0][0])
    x_len.append(sample[0][1])
    y.append(sample[1][0])
    y_len.append(sample[1][1])

samples = [
    x,
    x_len,
    y,
    y_len
]

我仍然好奇是否有一种方法可以利用。 拉链 结束 for 循环样本及其嵌套列表。

请注意 data metadata 可以在不同的样本长度上变化。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/52049
 
1803 次点击  
文章 [ 4 ]  |  最新文章 5 年前
Georgy
Reply   •   1 楼
Georgy    6 年前

这不是最舒适的数据结构。我建议重构代码并选择3倍嵌套列表之外的其他内容来保存数据,但如果当前不可能,我建议使用以下方法:

import itertools


def flatten(iterable):
    yield from itertools.chain.from_iterable(iterable)


result = []
for elements in zip(*map(flatten, samples)):
    result.append(list(flatten(elements)))

举例来说,它给出了:

[['A', 'E', '1'], 
 ['B', 'F', '2'], 
 ['C', 'G', '3'], 
 ['D', 'H', '4']]

测试2个以上的样品:

samples = [[[['A', 'E'], ['B', 'F']],
            [['C', 'G'], ['D', 'H']]],
           [[['1'], ['2']], 
            [['3'], ['4']]], 
           [[['5'], ['6']],
            [['7'], ['8']]]]

给予:

[['A', 'E', '1', '5'],
 ['B', 'F', '2', '6'],
 ['C', 'G', '3', '7'],
 ['D', 'H', '4', '8']]

说明:

这个 flatten generator function 只需展平嵌套iterable的1个级别。它基于 itertools.chain.from_iterable 功能。在 map(flatten, samples) 我们将此函数应用于 samples :

>>> map(flatten, samples)
<map at 0x3c6685fef0>  # <-- map object returned, to see result wrap it in `list`:

>>> list(map(flatten, samples))
[<generator object flatten at 0x0000003C67A2F9A8>,  # <-- will flatten the 1st sample
 <generator object flatten at 0x0000003C67A2FA98>,  # <-- ... the 2nd
 <generator object flatten at 0x0000003C67A2FB10>]  # <-- ... the 3rd and so on if there are more

# We can see what each generator will give by applying `list` on each one of them
>>> list(map(list, map(flatten, samples)))
[[['A', 'E'], ['B', 'F'], ['C', 'G'], ['D', 'H']],
 [['1'], ['2'], ['3'], ['4']],
 [['5'], ['6'], ['7'], ['8']]]

接下来,我们可以使用 zip 在平坦的样本上迭代。请注意,我们不能将其应用于 map 直接对象:

>>> list(zip(map(flatten, samples)))
[(<generator object flatten at 0x0000003C66944138>,),
 (<generator object flatten at 0x0000003C669441B0>,),
 (<generator object flatten at 0x0000003C66944228>,)]

我们应该 unpack 首先:

>>> list(zip(*map(flatten, samples)))
[(['A', 'E'], ['1'], ['5']),
 (['B', 'F'], ['2'], ['6']),
 (['C', 'G'], ['3'], ['7']),
 (['D', 'H'], ['4'], ['8'])]

# or in a for loop:
>>> for elements in zip(*map(flatten, samples)):
...     print(elements)
(['A', 'E'], ['1'], ['5'])
(['B', 'F'], ['2'], ['6'])
(['C', 'G'], ['3'], ['7'])
(['D', 'H'], ['4'], ['8'])

最后,我们只需要加入每个 elements 把元组放在一起。我们可以用同样的 扁平化 功能:

>>> for elements in zip(*map(flatten, samples)):
...     print(list(flatten(elements)))
['A', 'E', '1', '5']
['B', 'F', '2', '6']
['C', 'G', '3', '7']
['D', 'H', '4', '8']

您只需将其全部放回一个列表中,如第一个代码示例所示。

Tomerikoo
Reply   •   2 楼
Tomerikoo    6 年前

你可以:

res = [[y for l in x for y in l] for x in zip(*([x for var in sample for x in var] for sample in samples))]

print([list(i) for i in res])

举例说明:

[['A', 'E', '1'], ['B', 'F', '2'], ['C', 'G', '3'], ['D', 'H', '4']]

这基本上是将每个“样本”展平到一个列表中,并将其打包到一个大列表中,然后将其解压缩到 zip 然后将每个压缩元素打包到一个列表中。

L3viathan
Reply   •   3 楼
L3viathan    6 年前

这是另一个解决方案。很难看,但它确实有用 zip ,甚至两次!

>>> sum(map(lambda y: list(map(lambda x: sum(x, []), zip(*y))), zip(*samples)), [])
[['A', '1'], ['B', '2'], ['C', '3'], ['D', '4']]

看看它是如何工作的很有意思,但请不要实际使用它;它既难阅读,又算法不好。

pault
Reply   •   4 楼
pault    6 年前

IIUC,一种方法是 itertools.chain 使…的结果变平 zip(samples) :

from itertools import chain

new_samples = [
    list(chain.from_iterable(y)) for y in zip(
        *((chain.from_iterable(*x)) for x in zip(samples))
    )
]

print(new_samples)
#[['A', 'E', '1'], ['B', 'F', '2'], ['C', 'G', '3'], ['D', 'H', '4']]

循序渐进的解释

1) 第一次通话 zip samples :

print(list(zip(samples)))
#[([[['A', 'E'], ['B', 'F']], [['C', 'G'], ['D', 'H']]],),
# ([[['1'], ['2']], [['3'], ['4']]],)]

注意,在上面输出的两行中,如果元素被展平,您将拥有 拉链 为了得到你的最终结果。

2) 使用 itertools.chain to flatten (这将是很多 more efficient than using sum ).

print([list(chain.from_iterable(*x)) for x in zip(samples)])
#[[['A', 'E'], ['B', 'F'], ['C', 'G'], ['D', 'H']],
# [['1'], ['2'], ['3'], ['4']]]

3) 现在打电话 拉链 再一次:

print(list(zip(*((chain.from_iterable(*x)) for x in zip(samples)))))
#[(['A', 'E'], ['1']),
# (['B', 'F'], ['2']),
# (['C', 'G'], ['3']),
# (['D', 'H'], ['4'])]

4) 现在你基本上得到了你想要的,除了列表是嵌套的。所以使用 itertools.chain 再次压平最终列表。

print(
    [
        list(chain.from_iterable(y)) for y in zip(
            *((chain.from_iterable(*x)) for x in zip(samples))
        )
    ]
)
#[['A', 'E', '1'], ['B', 'F', '2'], ['C', 'G', '3'], ['D', 'H', '4']]