Zastanawiam się, czy jest skrót, aby wykonać prostą listę z listy list w Pythonie.

Mogę to zrobić w pętli for, ale może jest trochę fajnego "one-liner"? Próbowałem go z reduce(), ale otrzymuję błąd.

Kod

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

komunikat o błędzie

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
3847
Emma 5 czerwiec 2009, 00:30

27 odpowiedzi

Najlepsza odpowiedź

Biorąc pod uwagę listę list {X0}},

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

Co znaczy:

flat_list = []
for sublist in l:
    for item in sublist:
        flat_list.append(item)

Jest szybszy niż skróty do tej pory. (l to lista spłaszczona.)

Oto odpowiednia funkcja:

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

Jako dowód, możesz użyć modułu timeit w standardowej bibliotece:

$ 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

Objaśnienie: Skróty na bazie + (w tym dorozumianego użycia w sum) są, z konieczności, O(L**2), gdy są l subblists - ponieważ lista wyników pośrednich staje się dłuższa Każdy krok, który zostanie przydzielony nowy obiekt listy wyników pośrednich, a wszystkie elementy w poprzednim wyniku pośredniego muszą być kopiowane (jak również kilka nowych dodanych na końcu). Tak więc, aby uzyskać prostotę i bez rzeczywistej utraty ogólności, powiedz, że masz l Subsists of I Items każdy: Pierwszy I przedmioty są kopiowane tam iz powrotem L-1 razy, drugi I przedmioty L-2 razy i tak dalej; Całkowita liczba kopii jest czasami suma X dla X od 1 do L wyłączonych, tj., {x3}}.

Rozumienie listy właśnie generuje jedną listę, raz i kopiuje każdy element (z pierwotnego miejsca zamieszkania do listy wyników) również dokładnie raz.

5463
Richard 6 styczeń 2021, 23:17

Kolejny zabawny sposób na to:

from functools import reduce
from operator import add

li=[[1,2],[3,4]]
x= reduce(add, li)
5
sudeepgupta90 28 sierpień 2019, 08:09

Możesz użyć itertools.chain():

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

Możesz też używać itertools.chain.from_iterable() Wymagaj rozpakowania listy za pomocą operatora {x1}}:

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

Takie podejście jest prawdopodobnie bardziej czytelne niż [item for sublist in l for item in sublist] i wydaje się być szybsza:

$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;import itertools' 'list(itertools.chain.from_iterable(l))'
20000 loops, best of 5: 10.8 usec per loop
$ python3 -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 5: 21.7 usec per loop
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(l, [])'
1000 loops, best of 5: 258 usec per loop
$ python3 -mtimeit -s'l=[[1,2,3],[4,5,6], [7], [8,9]]*99;from functools import reduce' 'reduce(lambda x,y: x+y,l)'
1000 loops, best of 5: 292 usec per loop
$ python3 --version
Python 3.7.5rc1
1745
Boris 24 marzec 2020, 18:56

Uwaga od autora : Jest to nieefektywne. Ale zabawny, ponieważ Monoids są niesamowite. Nie jest odpowiedni do kodu Pythona produkcyjnego.

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

To podsumowuje elementy iterable przekazane w pierwszym argumencie, traktując drugą argument jako wartość początkową suma (jeśli nie podana, 0 jest używana, a ta sprawa daje błąd).

Ponieważ podsumowujesz zagnieżdżone listy, faktycznie otrzymujesz [1,3]+[2,4] w wyniku sum([[1,3],[2,4]],[]), który jest równy [1,3,2,4].

Należy pamiętać, że działa tylko na listach list. Dla list list list, potrzebujesz innego rozwiązania.

995
jorijnsmit 29 grudzień 2018, 16:00
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]

Metoda extend() w przykładzie modyfikuje {X1}} zamiast zwrócić przydatną wartość (która oczekuje reduce()).

Szybszy sposób wykonania wersji {x0}} byłoby

>>> 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]
214
Sanjay Verma 2 styczeń 2018, 05:02

Nie naświetnaj koła, jeśli używasz Django :

>>> from django.contrib.admin.utils import flatten
>>> l = [[1,2,3], [4,5], [6]]
>>> flatten(l)
>>> [1, 2, 3, 4, 5, 6]

... pandas :

>>> from pandas.core.common import flatten
>>> list(flatten(l))

... iTOtools :

>>> import itertools
>>> flatten = itertools.chain.from_iterable
>>> list(flatten(l))

... matplotlib

>>> from matplotlib.cbook import flatten
>>> list(flatten(l))

... unipath :

>>> from unipath.path import flatten
>>> list(flatten(l))

... Ustawienia :

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))
200
Max Malysh 1 kwiecień 2020, 14:10

Jeśli chcesz spłaszczyć strukturę danych, w której nie wiesz, jak głęboko zagnieżdżone możesz użyć 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]

To generator, więc musisz rzucić wynik do list lub wyraźnie iterować nad nim.


Aby spłaszczyć tylko jeden poziom, a jeśli każda z przedmiotów jest sama, że możesz również użyć iteration_utilities.flatten, który sama jest tylko cienką owijką wokół 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]

Aby dodać pewne czasy (na podstawie odpowiedzi Nico Schlömer, która nie zawierała funkcji przedstawionej w tej odpowiedzi):

enter image description here

To działka dziennika, aby pomieścić ogromny zakres wartościowych wartości. Do rozumowania jakościowego: niższy jest lepszy.

Wyniki pokazują, że jeśli iterable zawiera tylko kilka wewnętrznych iterables, a następnie sum będzie najszybszy, jednak dla długich iterables tylko {x2}}, {x2}} lub zagnieżdżone rozsądne osiągi z {{{ X3}} Bycie najszybszym (jak już zauważył Nico Schlö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 Zastrzeżenie: Jestem autorem tej biblioteki

60
MSeifert 18 kwiecień 2018, 21:27

Wracam do mojego oświadczenia. suma nie jest zwycięzcą. Chociaż jest szybszy, gdy lista jest mała. Ale wydajność znacznie rozkłada się z większymi listami.

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

Wersja Sum nadal działa dłużej niż minutę i jeszcze nie zrobiła przetwarzania!

Dla listy średnich:

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

Używanie małych list i czasu: Numer = 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
44
devsaw 23 grudzień 2013, 09:14

Wydaje się, że jest zamieszanie z operator.add! Po dodaniu dwóch list razem, właściwy termin dla tego concat, nie dodaj. operator.concat jest tym, czego potrzebujesz do użycia.

Jeśli myślisz funkcjonalny, jest tak łatwy jak to ::

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

Widzisz, że respektuje typ sekwencji, więc po dostarczeniu krotki, wrócisz do krotki. Spróbujmy z listą ::

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

Aha, odzyskasz listę.

Co powiesz na wydajność ::

>>> 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 jest dość szybki! Ale nie ma porównania, aby zmniejszyć z 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
43
TrebledJ 9 maj 2019, 04:33

Dlaczego używasz rozszerzenia?

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

To powinno działać dobrze.

38
MERose 31 grudzień 2014, 12:07

Rozważ instalację {x0}} pakiet.

> pip install more_itertools

IT dostarcza wdrożenie dla flatten (Źródło z Przepisy ITOtools):

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]

Od wersji 2.4, możesz spłaszczyć bardziej skomplikowane, zagnieżdżone iterables z {,4 {X0}} ( Źródło , przyczynione przez 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]
36
pylang 8 wrzesień 2020, 19:44

Powodem, dla którego Twoja funkcja nie działa, jest dlatego, że rozszerza rozszerza tablicę i nie zwraca go. Nadal możesz wrócić X z Lambda, używając czegoś takiego:

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

Uwaga: Rozszerzenie jest bardziej wydajne niż + na listach.

27
Shannon Kelly 19 listopad 2019, 16:26
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]
22
Guillaume Jacquenot 28 listopad 2016, 08:48

Wersja rekurencyjna

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

def flatten_list(k):
    result = list()
    for i in k:
        if isinstance(i,list):

            #The isinstance() function checks if the object (first argument) is an 
            #instance or subclass of classinfo class (second argument)

            result.extend(flatten_list(i)) #Recursive call
        else:
            result.append(i)
    return result

flatten_list(x)
#result = [1,2,3,4,5,6,7,8,9,10]
21
Saurabh Singh 14 grudzień 2018, 10:51

Akceptowana odpowiedź nie działała dla mnie, gdy zajmuje się listami tekstowymi o długościach zmiennych. Oto alternatywne podejście, które działało dla mnie.

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

Zaakceptowana odpowiedź, która nie nie praca:

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']

Nowe proponowane rozwiązanie, które zrobiłem

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']
20
user9074332 12 październik 2018, 01:32

matplotlib.cbook.flatten() będzie działać na zagnieżdżone listy, nawet jeśli gniazdują się bardziej głęboko niż przykład.

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

Wynik:

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

Jest to 18x szybciej niż podkreśla ._. Flatten:

Average time over 1000 trials of matplotlib.cbook.flatten: 2.55e-05 sec
Average time over 1000 trials of underscore._.flatten: 4.63e-04 sec
(time for underscore._)/(time for matplotlib.cbook) = 18.1233394636
17
EL_DON 20 lipiec 2018, 18:16

Poniżej wydaje mi się najprostsze:

>>> 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]
16
devil in the detail 5 lipiec 2017, 05:14

Można również użyć numpy's płaski:

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

Edytuj 11/02/2016: Działa tylko wtedy, gdy podpiękki mają identyczne wymiary.

13
mdh 2 listopad 2016, 09:59

Możesz użyć numpy:
flat_list = list(np.concatenate(list_of_list))

7
A. Attia 24 lipiec 2018, 09:11
from nltk import flatten

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

Zaletą tego rozwiązania powyżej większości innych jest to, że jeśli masz listę jak:

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

Podczas gdy większość innych rozwiązań rzuca błąd, ten rozwiązanie z obsługuje ich rozwiązanie.

7
Alijy 30 wrzesień 2019, 10:49

Jeśli chcesz zrezygnować z niewielkiej ilości prędkości, aby spojrzeć, możesz użyć numpy.concatenate().tolist() lub 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

Możesz dowiedzieć się więcej tutaj w dokumentach numpy.ConCateNate i numpy.ravel

6
mkultra 27 październik 2016, 03:31

W każdym razie najszybsze rozwiązanie (na dużą listę):

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

Gotowy! Możesz oczywiście odwrócić go do listy, wykonując listę (L)

6
Canuck 28 listopad 2016, 21:09

Prosty kod dla underscore.py wentylator pakietu

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

Rozwiązuje wszystkie problemy spłaszczone (brak elementu lub złożonego zagnieżdżania)

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]

Możesz zainstalować underscore.py z pipem

pip install underscore.py
6
Vu Anh 25 marzec 2017, 05:09
def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])
6
englealuze 8 sierpień 2017, 14:59

Może to nie być najbardziej skuteczny sposób, ale pomyślałem, że umieścił jedną liniową (w rzeczywistości dwupiniarską). Oba wersje będą działać na listach zagnieżdżonych hierarchii i wykorzystują funkcje językowe (Python3.5) i rekursion.

def make_list_flat (l):
    flist = []
    flist.extend ([l]) if (type (l) is not list) else [flist.extend (make_list_flat (e)) for e in l]
    return flist

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = make_list_flat(a)
print (flist)

Wynik jest

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Działa to w pierwszej głębokości. Rekursja spada, aż znajdzie elementu nie-listy, a następnie rozszerza zmienną lokalną flist, a następnie włącza go do rodzica. Za każdym razem, gdy flist jest zwracany, jest rozszerzony do rodzica flist w rozumieniu listy. Dlatego na root zwracany jest płaska lista.

Powyższy tworzy kilka lokalnych list i zwraca ich, które są używane do rozszerzenia listy rodzica. Myślę, że droga do tego może tworzyć Gloabl flist, jak poniżej.

a = [[1, 2], [[[[3, 4, 5], 6]]], 7, [8, [9, [10, 11], 12, [13, 14, [15, [[16, 17], 18]]]]]]
flist = []
def make_list_flat (l):
    flist.extend ([l]) if (type (l) is not list) else [make_list_flat (e) for e in l]

make_list_flat(a)
print (flist)

Wyjście jest ponownie

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]

Chociaż nie jestem tego pewien o wydajności.

6
phoxis 20 maj 2018, 08:47
flat_list = []
for i in list_of_list:
    flat_list+=i

Ten kod działa również w porządku, ponieważ po prostu rozszerza listę całą drogę. Chociaż jest dużo podobny, ale mieć tylko jeden do pętli. Więc ma mniej złożoności niż dodanie 2 dla pętli.

6
Deepak Yadav 20 czerwiec 2018, 11:12

Kolejne niezwykłe podejście, które pracuje dla hetero i jednorodnych list całkowitych:

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(',')]
4
tharndt 13 wrzesień 2018, 11:34