list教學 python 如何從列表列表中製作一個平面列表?




15 Answers

你可以使用itertools.chain()

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))

或者,在Python> = 2.6上,使用itertools.chain.from_iterable() ,它不需要解壓縮列表:

>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))

這種方法可以說比[item for sublist in l for item in sublist]更具可讀性[item for sublist in l for item in sublist]並且看起來更快:

[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
10000 loops, best of 3: 24.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in l for item in sublist]'
10000 loops, best of 3: 45.2 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 488 usec per loop
[me@home]$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 3: 522 usec per loop
[me@home]$ python --version
Python 2.7.3
python list multidimensional-array flatten

我想知道是否有一條快捷方式可以在Python列表中列出一個簡單的列表。

我可以在for循環中做到這一點,但也許有一些很酷的“單行”? 我用reduce嘗試了,但是我收到了一個錯誤。

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)

錯誤信息

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'



我用perfplot測試了大多數建議的解決方案(我的一個寵物項目,基本上是timeit的包裝),並且發現了

list(itertools.chain.from_iterable(a))

成為最快的解決方案(如果連接的列表超過10個)。

重現情節的代碼:

import functools
import itertools
import numpy
import operator
import perfplot


def forfor(a):
    return [item for sublist in a for item in sublist]


def sum_brackets(a):
    return sum(a, [])


def functools_reduce(a):
    return functools.reduce(operator.concat, a)


def itertools_chain(a):
    return list(itertools.chain.from_iterable(a))


def numpy_flat(a):
    return list(numpy.array(a).flat)


def numpy_concatenate(a):
    return list(numpy.concatenate(a))


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        forfor, sum_brackets, functools_reduce, itertools_chain, numpy_flat,
        numpy_concatenate
        ],
    n_range=[2**k for k in range(16)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )



以下是適用於數字字符串嵌套列表和混合容器的一般方法。

from collections import Iterable


def flatten(items):
    """Yield items from any nested iterable; see Reference."""
    for x in items:
        if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
            for sub_x in flatten(x):
                yield sub_x
        else:
            yield x

注意:在Python 3中, yield from flatten(x)可以替換for sub_x in flatten(x): yield sub_x

演示

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(flatten(lst))                                         # nested lists
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

mixed = [[1, [2]], (3, 4, {5, 6}, 7), 8, "9"]              # numbers, strs, nested & mixed
list(flatten(mixed))
# [1, 2, 3, 4, 5, 6, 7, 8, '9']

參考

  • 該解決方案是根據Beazley,D。和B. Jones的配方修改的 食譜4.14,Python Cookbook 3rd Ed。,O'Reilly Media Inc. Sebastopol,CA:2013。
  • 找到了早期的SO帖子 ,可能是最初的演示。



為什麼使用extend?

reduce(lambda x, y: x+y, l)

這應該工作正常。




似乎與operator.add混淆了! 當您將兩個列表一起添加時,正確的術語是concat ,而不是添加。 operator.concat是您需要使用的。

如果您正在考慮功能,它就像這樣簡單::

>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)

你看到reduce尊重序列類型,所以當你提供一個元組時,你會得到一個元組。 讓我們嘗試一下清單::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

啊哈,你得到一份清單。

性能怎麼樣::

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> %timeit list(itertools.chain.from_iterable(list2d))
1000000 loops, best of 3: 1.36 µs per loop

from_iterable非常快! 但是用concat減少它是沒有可比性的。

>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop



考慮安裝more_itertools包。

> pip install more_itertools

它附帶了flattensource ,來自itertools配方 )的實現:

import more_itertools


lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.flatten(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

從版本2.4開始,您可以使用more_itertools.collapsesource ,由abarnet提供)來展平更複雜的嵌套迭代。

lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(more_itertools.collapse(lst)) 
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

lst = [[1, 2, 3], [[4, 5, 6]], [[[7]]], 8, 9]              # complex nesting
list(more_itertools.collapse(lst))
# [1, 2, 3, 4, 5, 6, 7, 8, 9]



def flatten(l, a):
    for i in l:
        if isinstance(i, list):
            flatten(i, a)
        else:
            a.append(i)
    return a

print(flatten([[[1, [1,1, [3, [4,5,]]]], 2, 3], [4, 5],6], []))

# [1, 1, 1, 3, 4, 5, 2, 3, 4, 5, 6]



也可以使用NumPy的flat

import numpy as np
list(np.array(l).flat)

編輯11/02/2016:僅當子列表具有相同的尺寸時才有效。




underscore.py包風扇的簡單代碼

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

它解決了所有展平問題(無列表項或複雜嵌套)

from underscore import _
# 1 is none list item
# [2, [3]] is complex nesting
_.flatten([1, [2, [3]], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

您可以使用pip安裝underscore.py

pip install underscore.py



在處理基於文本的可變長度列表時,接受的答案對我不起作用。 這是一種替代方法,對我有用。

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

接受的答案不起作用:

flat_list = [item for sublist in l for item in sublist]
print(flat_list)
['a', 'a', 'a', 'b', 'b', 'c', 'c', 'c', 'c', 'c', 'c', 'xx', 'yyyyyyy']

新建議的解決方案對我有用:

flat_list = []
_ = [flat_list.extend(item) if isinstance(item, list) else flat_list.append(item) for item in l if item]
print(flat_list)
['aaa', 'bb', 'cccccc', 'xx', 'yyyyyyy']



如果你願意放棄一numpy.concatenate().tolist()速度以獲得更清晰的外觀,那麼你可以使用numpy.concatenate().tolist()numpy.concatenate().ravel().tolist()

import numpy

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]] * 99

%timeit numpy.concatenate(l).ravel().tolist()
1000 loops, best of 3: 313 µs per loop

%timeit numpy.concatenate(l).tolist()
1000 loops, best of 3: 312 µs per loop

%timeit [item for sublist in l for item in sublist]
1000 loops, best of 3: 31.5 µs per loop

您可以在docs numpy.concatenatenumpy.ravel找到更多numpy.ravel




另一種適用於異構和同類整數列表的不尋常方法:

from typing import List


def flatten(l: list) -> List[int]:
    """Flatten an arbitrary deep nested list of lists of integers.

    Examples:
        >>> flatten([1, 2, [1, [10]]])
        [1, 2, 1, 10]

    Args:
        l: Union[l, Union[int, List[int]]

    Returns:
        Flatted list of integer
    """
    return [int(i.strip('[ ]')) for i in str(l).split(',')]



我最近遇到過一種情況,即我在子列表中混合了字符串和數字數據

test = ['591212948',
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'],
['Jon'],
['Doe'],
['fl'],
92001,
555555555,
'hello',
['hello2', 'a'],
'b',
['hello33', ['z', 'w'], 'b']]

哪些方法flat_list = [item for sublist in test for item in sublist]沒有奏效。所以,我提出了以下1+級別子列表的解決方案

def concatList(data):
    results = []
    for rec in data:
        if type(rec) == list:
            results += rec
            results = concatList(results)
        else:
            results.append(rec)
    return results

結果

In [38]: concatList(test)
Out[38]:
 Out[60]:
['591212948',
'special',
'assoc',
'of',
'Chicago',
'Jon',
'Doe',
'Jon',
'Doe',
'fl',
92001,
555555555,
'hello',
'hello2',
'a',
'b',
'hello33',
'z',
'w',
'b']



您可以非常簡單地使用實際的堆棧數據結構來避免對堆棧的遞歸調用。

alist = [1,[1,2],[1,2,[4,5,6],3, "33"]]
newlist = []

while len(alist) > 0 :
  templist = alist.pop()
  if type(templist) == type(list()) :
    while len(templist) > 0 :
      temp = templist.pop()
      if type(temp) == type(list()) :
        for x in temp :
          templist.append(x)
      else :
        newlist.append(temp)
  else :
    newlist.append(templist)
print(list(reversed(newlist)))



使用reducefrom functoolsadd列表上的運算符的簡單遞歸方法:

>>> from functools import reduce
>>> from operator import add
>>> flatten = lambda lst: [lst] if type(lst) is int else reduce(add, [flatten(ele) for ele in lst])
>>> flatten(l)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

該功能flatten發生在lst作為參數。它循環所有元素lst直到達到整數(也可以更改intfloatstr等等,用於其他數據類型),這些元素被添加到最外層遞歸的返回值。

for循環和monad 這樣的方法不同,遞歸是一種不受列表深度限制的通用解決方案。例如,深度為5的列表可以採用與以下相同的方式展平l

>>> l2 = [[3, [1, 2], [[[6], 5], 4, 0], 7, [[8]], [9, 10]]]
>>> flatten(l2)
[3, 1, 2, 6, 5, 4, 0, 7, 8, 9, 10]



Related