Ich frage mich, ob es eine Verknüpfung gibt, um aus einer Liste von Listen in Python eine einfache Liste zu erstellen.
Ich kann das in einer for
Schleife machen, aber vielleicht gibt es einen coolen "Einzeiler"? Ich habe es mit versucht reduce()
, aber ich bekomme eine Fehlermeldung.
Code
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Fehlermeldung
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Antworten:
Angesichts einer Liste von Listen
l
,flat_list = [item for sublist in l for item in sublist]
was bedeutet:
ist schneller als die bisher veröffentlichten Verknüpfungen. (
l
Ist die Liste zu reduzieren.)Hier ist die entsprechende Funktion:
Als Beweis können Sie das
timeit
Modul in der Standardbibliothek verwenden:Erläuterung: Die Verknüpfungen, die auf
+
(einschließlich der impliziten Verwendung insum
) basieren, sind notwendigerweise erforderlich,O(L**2)
wenn L Unterlisten vorhanden sind. Da die Zwischenergebnisliste immer länger wird, wird bei jedem Schritt ein neues Zwischenergebnislistenobjekt und alle Elemente zugewiesen im vorherigen Zwischenergebnis muss kopiert werden (sowie einige neue, die am Ende hinzugefügt wurden). Nehmen wir zur Vereinfachung und ohne tatsächlichen Verlust der Allgemeinheit an, Sie haben L Unterlisten von jeweils I Elementen: Die ersten I Elemente werden L-1-mal hin und her kopiert, die zweiten I Elemente L-2 Mal und so weiter. Die Gesamtzahl der Kopien ist das I-fache der Summe von x für x von 1 bis L ausgeschlossen, dI * (L**2)/2
. h .Das Listenverständnis generiert nur einmal eine Liste und kopiert jedes Element (von seinem ursprünglichen Wohnort in die Ergebnisliste) auch genau einmal.
quelle
itertools.chain.from_iterable
:$ python -mtimeit -s'from itertools import chain; l=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'list(chain.from_iterable(l))'
. Es läuft etwas mehr als doppelt so schnell wie das verschachtelte Listenverständnis, das die schnellste der hier gezeigten Alternativen ist.list()
, um den Iterator in eine Liste zu integrieren.list(itertools.chain.from_iterable(l))
am besten - wie in anderen Kommentaren und Shawns Antwort bemerkt.Sie können verwenden
itertools.chain()
:Oder Sie können
itertools.chain.from_iterable()
Folgendes verwenden, ohne die Liste mit dem*
Bediener entpacken zu müssen :quelle
*
ist die schwierige Sache, diechain
weniger einfach macht als das Listenverständnis. Sie müssen wissen, dass die Kette nur die als Parameter übergebenen Iterables zusammenfügt und das * bewirkt, dass die Liste der obersten Ebene zu Parametern erweitert wird, sodasschain
alle diese Iterables zusammengefügt werden, aber nicht weiter absteigen. Ich denke, dies macht das Verständnis in diesem Fall lesbarer als die Verwendung von Ketten.for
Schleife finden, die immerappend
offensichtlicher wird.list = [["abc","bcd"],["cde","def"],"efg"]
wird in einem Ausgang zur Folge["abc", "bcd", "cde", "def", "e", "f", "g"].
Anmerkung des Autors : Dies ist ineffizient. Aber Spaß, denn Monoide sind großartig. Es ist nicht für die Produktion von Python-Code geeignet.
Dies summiert nur die Elemente von iterable, die im ersten Argument übergeben wurden, und behandelt das zweite Argument als den Anfangswert der Summe (falls nicht angegeben,
0
wird stattdessen verwendet und dieser Fall gibt Ihnen einen Fehler).Da Sie verschachtelte Listen summieren, erhalten Sie tatsächlich
[1,3]+[2,4]
als Ergebnis vonsum([[1,3],[2,4]],[])
, was gleich ist[1,3,2,4]
.Beachten Sie, dass dies nur für Listen von Listen funktioniert. Für Listen mit Listen von Listen benötigen Sie eine andere Lösung.
quelle
Monoid
, eine der bequemsten Abstraktionen, um eine+
Operation im allgemeinen Sinne zu betrachten (nicht nur auf Zahlen beschränkt). Diese Antwort verdient also eine +1 von mir für die (korrekte) Behandlung von Listen als Monoid. Die Leistung ist jedochIch habe die meisten Lösungsvorschläge mit perfplot (einem meiner Lieblingsprojekte , im Wesentlichen einem Wrapper
timeit
) getestet und gefundenDies ist die schnellste Lösung, wenn viele kleine und wenige lange Listen verkettet sind. (
operator.iadd
ist gleich schnell.)Code zur Reproduktion der Handlung:
quelle
Die
extend()
Methode in Ihrem Beispiel ändert sich,x
anstatt einen nützlichen Wert zurückzugeben (derreduce()
erwartet).Ein schnellerer Weg, um die
reduce
Version zu machen, wärequelle
reduce(operator.add, l)
wäre der richtige Weg, um diereduce
Version zu machen. Eingebaute sind schneller als Lambdas.timeit.timeit('reduce(operator.add, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)
0.017956018447875977 *timeit.timeit('reduce(lambda x, y: x+y, l)', 'import operator; l=[[1, 2, 3], [4, 5, 6, 7, 8], [1, 2, 3, 4, 5, 6, 7]]', number=10000)
0.025218963623046875integers
. Aber was ist, wenn die Liste enthältstring
?operator.add
Funktion funktioniert sowohl für Ganzzahllisten als auch für Zeichenfolgenlisten gleich gut.Erfinden Sie das Rad nicht neu, wenn Sie Django verwenden :
... Pandas :
... Itertools :
... Matplotlib
... Unipath :
... Setuptools :
quelle
flatten = itertools.chain.from_iterable
sollte die richtige Antwort seinlist_of_menuitems = [1, 2, [3, [4, 5, [6]]]]
, ergibt sich Folgendes :[1, 2, 3, 4, 5, 6]
. Was ich vermisse, ist die Abflachung.Hier ist ein allgemeiner Ansatz, der für Zahlen , Zeichenfolgen , verschachtelte Listen und gemischte Container gilt.
Code
Anmerkungen :
yield from flatten(x)
kann ersetzenfor sub_x in flatten(x): yield sub_x
collection.abc
zumtyping
Modul.Demo
Referenz
quelle
more_itertools
unter anderem in diesem Beitrag diskutiert. Prost.traverse
könnte auch ein guter Name für diese Art eines Baumes sein, während ich ihn für diese Antwort weniger universell halten würde, indem ich mich an verschachtelte Listen halte .if hasattr(x, '__iter__')
anstatt zu importieren / zu prüfen,Iterable
und dies schließt auch Zeichenfolgen aus.Wenn Sie eine Datenstruktur reduzieren möchten, bei der Sie nicht wissen, wie tief sie verschachtelt ist, können Sie 1 verwenden
iteration_utilities.deepflatten
list
Da es sich um einen Generator handelt, müssen Sie das Ergebnis in a umwandeln oder explizit darüber iterieren.Um nur eine Ebene zu reduzieren und wenn jedes der Elemente selbst iterierbar ist, können Sie auch verwenden,
iteration_utilities.flatten
was selbst nur eine dünne Hülle istitertools.chain.from_iterable
:Nur um einige Zeitangaben hinzuzufügen (basierend auf der Antwort von Nico Schlömer, die die in dieser Antwort dargestellte Funktion nicht enthielt):
Es ist ein Log-Log-Plot, um den großen Wertebereich zu berücksichtigen. Zum qualitativen Denken: Niedriger ist besser.
Die Ergebnisse zeigen , dass , wenn die iterable nur wenige innere Iterables enthalten dann
sum
am schnellsten sein, aber für langen Iterables nur denitertools.chain.from_iterable
,iteration_utilities.deepflatten
oder das verschachtelten Verständnis haben angemessene Leistung mititertools.chain.from_iterable
der schnellsten zu sein (wie schon bemerkt , von Nico Schlömer).1 Haftungsausschluss: Ich bin der Autor dieser Bibliothek
quelle
sum
funktioniert nicht mehr mit beliebigen Sequenzen, wie es anfängt0
, und machtfunctools.reduce(operator.add, sequences)
den Ersatz (sind wir nicht froh, dass siereduce
aus den integrierten Elementen entfernt wurden?). Wenn die Typen bekannt sind, ist die Verwendung möglicherweise schnellertype.__add__
.sum
. Wissen Sie zufällig, bei welchen Python-Versionen es nicht mehr funktioniert?0
ist nur der Standardstartwert, also funktioniert es, wenn man das Startargument verwendet , um mit einer leeren Liste zu beginnen ... aber es werden immer noch Sonderzeichenfolgen verwendet und ich werde angewiesen, join zu verwenden. Es implementiertfoldl
stattfoldl1
. Das gleiche Problem taucht in 2.7 auf.Ich nehme meine Aussage zurück. Summe ist nicht der Gewinner. Obwohl es schneller ist, wenn die Liste klein ist. Bei größeren Listen nimmt die Leistung jedoch erheblich ab.
Die Summenversion läuft noch länger als eine Minute und wurde noch nicht verarbeitet!
Für mittlere Listen:
Mit kleinen Listen und Zeitangaben: Nummer = 1000000
quelle
Es scheint eine Verwechslung mit zu geben
operator.add
! Wenn Sie zwei Listen zusammenfügen, lautet der richtige Begriff dafürconcat
nicht hinzufügen.operator.concat
ist das, was Sie verwenden müssen.Wenn Sie funktional denken, ist es so einfach:
Sie sehen, dass der Sequenztyp reduziert wird. Wenn Sie also ein Tupel angeben, erhalten Sie ein Tupel zurück. Versuchen wir es mit einer Liste ::
Aha, du bekommst eine Liste zurück.
Wie wäre es mit Leistung ::
from_iterable
ist ziemlich schnell! Aber es ist kein Vergleich, mit dem man reduzieren kannconcat
.quelle
list(chain.from_iterable(...))
und 2,5 Sekunden fürreduce(concat, ...)
. Das Problem ist, dass esreduce(concat, ...)
eine quadratische Laufzeit hat, währendchain
es linear ist.Warum verwenden Sie verlängern?
Dies sollte gut funktionieren.
quelle
from functools import reduce
Erwägen Sie die Installation des
more_itertools
Pakets.Es wird mit einer Implementierung für
flatten
( Quelle , aus den itertools-Rezepten ) geliefert :Ab Version 2.4 können Sie kompliziertere, verschachtelte Iterables mit
more_itertools.collapse
( Quelle , beigesteuert von abarnet) reduzieren.quelle
Der Grund, warum Ihre Funktion nicht funktioniert hat, ist, dass die Erweiterung ein Array an Ort und Stelle erweitert und nicht zurückgibt. Sie können immer noch x von Lambda zurückgeben, indem Sie Folgendes verwenden:
Hinweis: Erweitern ist in Listen effizienter als +.
quelle
extend
besser als verwendet wirdnewlist = []
,extend = newlist.extend
,for sublist in l: extend(l)
wie es die (recht großen) Overhead von der vermeidetlambda
, das Attribut - Lookupx
und deror
.from functools import reduce
quelle
def flatten(l, a=None): if a is None: a = []
[...]Rekursive Version
quelle
matplotlib.cbook.flatten()
funktioniert für verschachtelte Listen, auch wenn sie tiefer als das Beispiel verschachtelt sind.Ergebnis:
Dies ist 18x schneller als der Unterstrich ._. Abflachen:
quelle
Die akzeptierte Antwort funktionierte bei mir nicht, wenn es um textbasierte Listen variabler Länge ging. Hier ist ein alternativer Ansatz, der für mich funktioniert hat.
Akzeptierte Antwort, die nicht funktioniert hat:
Neue vorgeschlagene Lösung, die für mich funktioniert hat:
quelle
Ein schlechtes Merkmal der obigen Funktion von Anil ist, dass der Benutzer das zweite Argument immer manuell als leere Liste angeben muss
[]
. Dies sollte stattdessen eine Standardeinstellung sein. Aufgrund der Funktionsweise von Python-Objekten sollten diese innerhalb der Funktion und nicht in den Argumenten festgelegt werden.Hier ist eine Arbeitsfunktion:
Testen:
quelle
Folgendes scheint mir am einfachsten zu sein:
quelle
Man kann auch NumPys Wohnung benutzen :
Edit 11/02/2016: Funktioniert nur, wenn Unterlisten identische Dimensionen haben.
quelle
Sie können numpy verwenden:
flat_list = list(np.concatenate(list_of_list))
quelle
[1, 2, [3], [[4]], [5, [6]]]
Wenn Sie bereit sind , eine winzige Menge an Geschwindigkeit für eine sauberere Aussehen zu geben, dann könnten Sie
numpy.concatenate().tolist()
odernumpy.concatenate().ravel().tolist()
:Weitere Informationen finden Sie hier in den Dokumenten numpy.concatenate und numpy.ravel
quelle
[1, 2, [3], [[4]], [5, [6]]]
Schnellste Lösung, die ich gefunden habe (für große Liste sowieso):
Erledigt! Sie können es natürlich wieder in eine Liste verwandeln, indem Sie list (l) ausführen.
quelle
Einfacher Code für
underscore.py
PaketlüfterEs löst alle Abflachungsprobleme (kein Listenelement oder komplexe Verschachtelung).
Sie können
underscore.py
mit pip installierenquelle
quelle
[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Hinweis : Das Folgende gilt für Python 3.3+, da es verwendet wird
yield_from
.six
ist auch ein Paket von Drittanbietern, obwohl es stabil ist. Alternativ könnten Sie verwendensys.version
.Im Fall von
obj = [[1, 2,], [3, 4], [5, 6]]
sind alle Lösungen hier gut, einschließlich Listenverständnis unditertools.chain.from_iterable
.Betrachten Sie jedoch diesen etwas komplexeren Fall:
Hier gibt es mehrere Probleme:
6
ist nur ein Skalar; Es ist nicht iterierbar, daher schlagen die oben genannten Routen hier fehl.'abc'
, ist technisch iterable (allestr
s). Wenn Sie jedoch ein wenig zwischen den Zeilen lesen, möchten Sie es nicht als solches behandeln - Sie möchten es als ein einzelnes Element behandeln.[8, [9, 10]]
ist selbst eine verschachtelte Iteration. Grundlegendes Listenverständnis undchain.from_iterable
nur "1 Ebene tiefer" extrahieren.Sie können dies wie folgt beheben:
Hier überprüfen Sie, ob das Unterelement (1) mit
Iterable
einem ABC von iterierbar ist ,itertools
möchten aber auch sicherstellen, dass (2) das Element nicht "stringartig" ist.quelle
yield from
zu einerfor
Schleife, z. B.for x in flatten(i): yield x
Dieser Code funktioniert auch gut, da er die Liste nur vollständig erweitert. Es ist zwar sehr ähnlich, hat aber nur eine for-Schleife. Es ist also weniger komplex als das Hinzufügen von 2 for-Schleifen.
quelle
Der Vorteil dieser Lösung gegenüber den meisten anderen hier ist, dass Sie eine Liste haben wie:
Während die meisten anderen Lösungen einen Fehler auslösen, werden sie von dieser Lösung behandelt.
quelle
Dies ist vielleicht nicht der effizienteste Weg, aber ich dachte, ich würde einen Einzeiler (eigentlich einen Zweiliner) einsetzen. Beide Versionen arbeiten mit verschachtelten Listen mit beliebiger Hierarchie und nutzen Sprachfunktionen (Python3.5) und Rekursion.
Die Ausgabe ist
Dies funktioniert in erster Linie in der Tiefe. Die Rekursion wird fortgesetzt, bis ein Nicht-Listenelement gefunden wird. Anschließend wird die lokale Variable erweitert
flist
und auf das übergeordnete Element zurückgesetzt. Immer wennflist
es zurückgegeben wird, wird esflist
im Listenverständnis auf das der Eltern erweitert . Daher wird im Stammverzeichnis eine flache Liste zurückgegeben.Die obige Liste erstellt mehrere lokale Listen und gibt diese zurück, die zum Erweitern der Liste der Eltern verwendet werden. Ich denke, der Weg dahin könnte darin bestehen, ein Gloabl zu schaffen
flist
, wie unten.Die Ausgabe ist wieder
Obwohl ich mir derzeit nicht sicher bin, wie effizient es ist.
quelle
Ein weiterer ungewöhnlicher Ansatz, der für hetero- und homogene Listen von ganzen Zahlen funktioniert:
quelle
wierd_list = [[1, 2, 3], [4, 5, 6], [7], [8, 9], 10]
>>nice_list=[1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 0]
flat_list = [int(e.replace('[','').replace(']','')) for e in str(deep_list).split(',')]
[int(e.strip('[ ]')) for e in str(deep_list).split(',')]
. Aber ich würde vorschlagen, bei Deleets Vorschlag für echte Anwendungsfälle zu bleiben. Es enthält keine Hacky-Typ-Transformationen, ist schneller und vielseitiger, da es natürlich auch Listen mit gemischten Typen verarbeitet.