從Python列表中列出一個扁平列表


Answers

flat_list = [item for sublist in l for item in sublist]

意思是:

for sublist in l:
    for item in sublist:
        flat_list.append(item)

比迄今發布的捷徑要快。 ( l是要變平的列表。)

這是一個相應的功能:

flatten = lambda l: [item for sublist in l for item in sublist]

作為證據,一如既往,您可以在標準庫中使用timeit模塊:

$ 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: 143 usec per loop
$ python -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 3: 969 usec per loop
$ 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: 1.1 msec per loop

說明:當存在L個子列表時,基於+ (包括sum的隱含用法)的快捷方式必然是O(L**2) - 隨著中間結果列表不斷變長,每一步都有一個新的中間結果列表對像被分配,並且之前中間結果中的所有項目都必須被複製(以及最後添加的幾個新項目)。 因此(為了簡單起見,並沒有實際的通用性喪失),假設你有每個I項的L子列表:第一個I項被來回複製L-1次,第二個I項L-2次,等等; 拷貝總數是I的倍數,x是從1到L排除的x,即I * (L**2)/2

列表理解只會生成一個列表,並且每​​個項目(從其原始的居住地點到結果列表)也只是一次。

Question

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

我可以在for循環中做到這一點,但也許有一些很酷的“單線程”? 我嘗試減少 ,但我得到一個錯誤。

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'



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



根據我將所有字符串列表放到一個列表中的要求,以下方法正在工作:

list(itertools.chain(*listoflists))

與數字數據,其他方法運作良好。 如果是字符串,你可以試試這個。




以下對我來說似乎最簡單:

>>> import numpy as np
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> print (np.concatenate(l))
[1 2 3 4 5 6 7 8 9]



我用perfplot測試了大多數建議的解決方案(我的一個寵物項目,本質上是時間上的包裝),並找到了

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))


out = perfplot.bench(
    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(12)],
    logx=True,
    logy=True,
    xlabel='num lists'
    )
out.show()



我發現的最快解決方案(無論如何都是大列表):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

完成! 您當然可以通過執行列表(l)將其重新轉換為列表




作者註意 :這是低效的。 但很有趣,因為monad很棒。 它不適合生產Python代碼。

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

這只是對第一個參數傳遞的iterable的元素進行求和,將第二個參數作為sum的初始值(如果沒有給出,則使用0代替,這種情況會給出錯誤)。

由於sum([[1,3],[2,4]],[])的結果實際上得到了[1,3]+[2,4] ,這等於[1,3,2,4]

請注意,只適用於列表清單。 對於列表清單列表,您需要另一個解決方案。




這是一個適用於列表,數字,字符串和其他混合容器類型的嵌套列表的一般方法。

from collections import Iterable


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

list(flatten(l))                               # list of lists
#[1, 2, 3, 4, 5, 6, 7, 8, 9]

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

該解決方案採用了Python 3的關鍵字yield from功能,可以從子生成器中提取項目。 請注意,此解決方案不適用於字符串。 更新:現在支持字符串。

REF:由Beazley,D.和B. Jones修改的解決方案 Recipe 4.14,Python Cookbook 3rd Ed。,O'Reilly Media Inc. Sebastopol,CA:2013。




注意 :以下適用於Python 3.3+,因為它使用yield_from

obj = [[1, 2,], [3, 4], [5, 6]] ,這裡所有的解都是很好的,包括列表理解和itertools.chain.from_iterable

但是,請考慮這個稍微複雜的情況:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

這裡有幾個問題:

  • 一個元素, 6 ,只是一個標量; 它不是可迭代的,所以上面的路線在這裡將失敗。
  • 一個元素'abc' 技術上可迭代的(全部都是)。 但是,在一行之間閱讀,我們不希望把它看作是這樣 - 我們想把它看作一個單一的元素。
  • 最後一個元素[8, [9, 10]]本身就是一個嵌套迭代器。 Basic list comprehension and chain.from_iterable only extract "1 level down."

We can remedy this as follows:

>>> import sys
>>> from collections import Iterator

>>> py2 = sys.version_info[0] == 2

>>> if py2:
...     str_types = basestring, unicode, str
... else:
...    str_types = str, bytes


>>> def flatten(obj):
...     for i in obj:
...         if isinstance(i, Iterable) and not isinstance(i, str_types):
...             yield from flatten(i)
...         else:
...             yield i


>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]

Here, we check that the sub-element (1) is iterable with Iterable , an ABC from itertools , but also want to ensure that (2) the element is not "string-like." The first few lines ensure compatability between Python 2 & 3.

This competes with or is a hair faster than matplotlib.cbook.flatten :

>>> from matplotlib.cbook import flatten as m_flatten

%timeit flatten(obj*100)
1.39 µs ± 21.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

%timeit m_flatten(obj*100)
1.43 µs ± 17.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)



我回我的聲明。 總和不是贏家。 雖然列表很小,但速度更快。 但是,隨著更大的列表,性能會顯著降低。

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10000'
    ).timeit(100)
2.0440959930419922

總和版本仍然運行了一分多鐘,但尚未完成處理!

對於中型列表:

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
20.126545906066895
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
22.242258071899414
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]] * 10'
    ).timeit()
16.449732065200806

使用小列表和timeit:number = 1000000

>>> timeit.Timer(
        '[item for sublist in l for item in sublist]',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
2.4598159790039062
>>> timeit.Timer(
        'reduce(lambda x,y: x+y,l)',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.5289170742034912
>>> timeit.Timer(
        'sum(l, [])',
        'l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]'
    ).timeit()
1.0598428249359131



你可以使用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]更可讀,並且似乎也更快:

[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



這可以使用toolz.concatcytoolz.concat (cythonized版本,在某些情況下可能會更快)完成:

from cytoolz import concat
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
list(concat(l)) # or just `concat(l)` if one only wants to iterate over the items

在我的計算機上,在python 3.6中,這看起來幾乎和[item for sublist in l for item in sublist]一樣快(不包括導入時間):

In [611]: %timeit L = [item for sublist in l for item in sublist]
695 ns ± 2.75 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [612]: %timeit L = [item for sublist in l for item in sublist]
701 ns ± 5.5 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [613]: %timeit L = list(concat(l))
719 ns ± 12 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [614]: %timeit L = list(concat(l))
719 ns ± 22.9 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

toolz版本確實比較慢:

In [618]: from toolz import concat

In [619]: %timeit L = list(concat(l))
845 ns ± 29 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [620]: %timeit L = list(concat(l))
833 ns ± 8.73 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)



考慮安裝more_itertools包。

> pip install more_itertools

它附帶flattensource ,來自itertools食譜 )的實現:

import more_itertools

# Using flatten()
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提供)將更複雜,嵌套的迭代變平。

# Using collapse()
lst = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]       # given example 
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 unusual_flatten(some_list: list) -> list:
    cleaned_list = str(some_list).replace(("["), "").replace("]", "").split(",")
    return [int(item) for item in cleaned_list]

在示例列表中應用...

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

unusual_flatten(l)

結果:

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




似乎與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



如果你想扁平化一個你不知道嵌套有多深的數據結構,你可以使用iteration_utilities.deepflatten 1

>>> from iteration_utilities import deepflatten

>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(deepflatten(l, depth=1))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

>>> l = [[1, 2, 3], [4, [5, 6]], 7, [8, 9]]
>>> list(deepflatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

它是一個生成器,因此您需要將結果轉換為list或明確地迭代它。

為了只展平一個級別,並且如果每個項目本身都是可迭代的,您還可以使用iteration_utilities.flatten ,它本身就是itertools.chain.from_iterable一個簡單包裝:

>>> from iteration_utilities import flatten
>>> l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
>>> list(flatten(l))
[1, 2, 3, 4, 5, 6, 7, 8, 9]

只需添加一些時間(根據NicoSchlömer答案,不包括此答案中提供的功能):

這是一個對數對數圖,以適應跨越的大範圍值。 為了定性推理:越低越好。

結果表明,如果迭代器只包含少量內部迭代器,那麼sum將會是最快的,但是對於長期迭代器,只有itertools.chain.from_iterableiteration_utilities.deepflatten或嵌套理解器具有合理的性能, itertools.chain.from_iterable是最快的(正如NicoSchlömer已經註意到的那樣)。

from itertools import chain
from functools import reduce
from collections import Iterable  # or from collections.abc import Iterable
import operator
from iteration_utilities import deepflatten

def nested_list_comprehension(lsts):
    return [item for sublist in lsts for item in sublist]

def itertools_chain_from_iterable(lsts):
    return list(chain.from_iterable(lsts))

def pythons_sum(lsts):
    return sum(lsts, [])

def reduce_add(lsts):
    return reduce(lambda x, y: x + y, lsts)

def pylangs_flatten(lsts):
    return list(flatten(lsts))

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

def reduce_concat(lsts):
    return reduce(operator.concat, lsts)

def iteration_utilities_deepflatten(lsts):
    return list(deepflatten(lsts, depth=1))


from simple_benchmark import benchmark

b = benchmark(
    [nested_list_comprehension, itertools_chain_from_iterable, pythons_sum, reduce_add,
     pylangs_flatten, reduce_concat, iteration_utilities_deepflatten],
    arguments={2**i: [[0]*5]*(2**i) for i in range(1, 13)},
    argument_name='number of inner lists'
)

b.plot()

1免責聲明:我是該圖書館的作者




上面Anil函數的一個壞處是它需要用戶總是手動指定第二個參數為空列表[] 。 這應該是一個默認設置。 由於Python對象的工作方式,這些應該在函數中設置,而不是在參數中設置。

這是一個工作功能:

def list_flatten(l, a=None):
    #check a
    if a is None:
        #initialize with empty list
        a = []

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

測試:

In [2]: lst = [1, 2, [3], [[4]],[5,[6]]]

In [3]: lst
Out[3]: [1, 2, [3], [[4]], [5, [6]]]

In [11]: list_flatten(lst)
Out[11]: [1, 2, 3, 4, 5, 6]



一個有趣的筆記來補充,
這樣的任務不能通過“解包”

In [46]: [*i for i in l]
SyntaxError: iterable unpacking cannot be used in comprehension

應該應用嵌套的理解

In [47]: [i for j in l for i in j]
Out[47]: [1, 2, 3, 4, 5, 6, 7, 8, 9]



你也可以使用NumPy的flat

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

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




清理了@Deleet示例

from collections import Iterable

def flatten(l, a=[]):
    for i in l:
        if isinstance(i, Iterable):
            flatten(i, a)
        else:
            a.append(i)
    return a

daList = [[1,4],[5,6],[23,22,234,2],[2], [ [[1,2],[1,2]],[[11,2],[11,22]] ] ]

print(flatten(daList))

例如: https://repl.it/G8mb/0 : https://repl.it/G8mb/0




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

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)))



你的函數不起作用的原因是:extend在原地擴展了數組,並且不返回它。 你仍然可以從lambda返回x,使用一些技巧:

reduce(lambda x,y: x.extend(y) or x, l)

注意:擴展比列表上的+更有效。




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]



你為什麼使用擴展?

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

這應該很好。




如果你願意放棄一個微小的速度以獲得更清晰的外觀,那麼你可以使用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

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




from functools import reduce #python 3

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

示例中的extend()方法修改x而不是返回有用的值( reduce()期望的)。

一個更快的方式來執行reduce版本將是

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



你可以試試這個:

weird_list=[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
nice_list = [int(e) for e in str(a) if e not in "[] ,"]



matplotlib.cbook.flatten()將適用於嵌套列表,即使它們的嵌套深度比示例更深。

import matplotlib
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
print(list(matplotlib.cbook.flatten(l)))
l2 = [[1, 2, 3], [4, 5, 6], [7], [8, [9, 10, [11, 12, [13]]]]]
print list(matplotlib.cbook.flatten(l2))

結果:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]



def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])