Ich frage mich, ob es eine Verknüpfung gibt, um eine einfache Liste aus der Liste der Listen in Python zu erstellen.
Ich kann das in einer for-Schleife machen, aber vielleicht gibt es ein paar coole "Einzeiler"? Ich habe es mit verkleinern versucht, 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'
Gegeben eine Liste der Listen l
,
flat_list = [item for sublist in l for item in sublist]
was bedeutet:
for sublist in l:
for item in sublist:
flat_list.append(item)
ist schneller als die bisher veröffentlichten Verknüpfungen. (l
ist die Liste, die abgeflacht werden soll.)
Hier ist die entsprechende Funktion:
flatten = lambda l: [item for sublist in l for item in sublist]
Als Beweis können Sie das Modul timeit
in der Standardbibliothek verwenden:
$ 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
Erläuterung: Die auf +
basierenden Verknüpfungen (einschließlich der implizierten Verwendung in sum
) sind notwendigerweise O(L**2)
, wenn L Unterlisten vorhanden sind. Da die Zwischenergebnisliste immer länger wird, wird bei jedem Schritt ein neues Zwischenergebnislistenobjekt zugewiesen Alle Elemente des vorherigen Zwischenergebnisses müssen kopiert werden (sowie einige neue am Ende hinzugefügt). Zur Vereinfachung und ohne tatsächlichen Verlust der Allgemeinheit sagen Sie, dass Sie L Unterlisten von jeweils I-Elementen haben: Die ersten I-Elemente werden L-1-mal hin- und hergeschickt, die zweiten I-Elemente L-2-mal und so weiter. Die Gesamtzahl der Kopien ist I-mal die Summe von x für x von 1 bis L, d. h. I * (L**2)/2
.
Das Listenverständnis generiert nur einmal eine Liste und kopiert jedes Element (vom ursprünglichen Wohnort bis zur Ergebnisliste) ebenfalls genau einmal.
Sie können itertools.chain()
verwenden:
>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))
oder verwenden Sie bei Python> = 2.6 itertools.chain.from_iterable()
, um die Liste nicht auszupacken
>>> import itertools
>>> list2d = [[1,2,3],[4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))
Dieser Ansatz ist wahrscheinlich lesbarer als [item for sublist in l for item in sublist]
und scheint auch schneller zu sein:
[[email protected]]$ 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
[[email protected]]$ 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
[[email protected]]$ 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
[[email protected]]$ 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
[[email protected]]$ python --version
Python 2.7.3
Anmerkung des Autors: Dies ist ineffizient. Aber Spaß, weil Monoids sind fantastisch. Es ist nicht für die Produktion von Python-Code geeignet.
>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Dies summiert nur die Elemente von iterable, die im ersten Argument übergeben wurden, und behandelt das zweite Argument als Anfangswert der Summe (wenn nicht angegeben, wird stattdessen 0
verwendet und dieser Fall gibt einen Fehler aus).
Da Sie verschachtelte Listen summieren, erhalten Sie [1,3]+[2,4]
tatsächlich als Ergebnis von sum([[1,3],[2,4]],[])
, was gleich [1,3,2,4]
ist.
Beachten Sie, dass nur Listenlisten möglich sind. Für Listenlisten benötigen Sie eine andere Lösung.
Ich habe die meisten Lösungsvorschläge mit perfplot getestet (ein Haustierprojekt von mir, im Wesentlichen ein Wrapper um timeit
) und fand
functools.reduce(operator.iconcat, a, [])
die schnellste Lösung sein. (operator.iadd
ist gleich schnell.)
Code zum Reproduzieren des Diagramms:
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 functools_reduce_iconcat(a):
return functools.reduce(operator.iconcat, 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, functools_reduce_iconcat,
itertools_chain, numpy_flat, numpy_concatenate
],
n_range=[2**k for k in range(16)],
logx=True,
logy=True,
xlabel='num lists'
)
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]
Die extend()
-Methode in Ihrem Beispiel ändert x
, anstatt einen nützlichen Wert zurückzugeben (den reduce()
erwartet).
Eine schnellere Möglichkeit, die reduce
-Version auszuführen, wäre
>>> 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]
Hier ein allgemeiner Ansatz, der für numbers, strings, nested - Listen und mixed -Container gilt.
Code
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
Hinweis: In Python 3 kann yield from flatten(x)
for sub_x in flatten(x): yield sub_x
ersetzen.
Demo
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']
Referenz
Ich nehme meine Aussage zurück. Summe ist nicht der Gewinner. Obwohl es schneller ist, wenn die Liste klein ist. Bei größeren Listen verschlechtert sich die Leistung jedoch erheblich.
>>> 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
Die Summenversion läuft noch mehr als eine Minute und wurde noch nicht verarbeitet!
Für Medienlisten:
>>> 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
Verwenden von kleinen Listen und Zeit: Nummer = 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
Wenn Sie eine Datenstruktur reduzieren möchten, bei der Sie nicht wissen, wie tief sie verschachtelt ist, können Sie iteration_utilities.deepflatten
verwenden.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]
Es ist ein Generator, also müssen Sie das Ergebnis in eine list
umwandeln oder explizit darüber iterieren.
Um nur eine Ebene zu glätten, und wenn jedes Element selbst iterierbar ist, können Sie auch iteration_utilities.flatten
verwenden, das selbst nur ein dünner Wrapper um itertools.chain.from_iterable
ist:
>>> 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]
Um nur einige Timings hinzuzufügen (basierend auf der Antwort von Nico Schlömer, die die in dieser Antwort dargestellte Funktion nicht enthält):
Es handelt sich um eine Protokollprotokolldarstellung, die für die große Bandbreite an Werten geeignet ist. Aus qualitativen Gründen: Weniger ist besser.
Die Ergebnisse zeigen, dass, wenn das iterable nur wenige innere iterables enthält, sum
am schnellsten ist. Bei langen iterables haben jedoch nur itertools.chain.from_iterable
, iteration_utilities.deepflatten
oder das verschachtelte Verständnis eine angemessene Leistung, wobei itertools.chain.from_iterable
die schnellste ist (wie bereits von Nico Schlömer bemerkt).
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 Haftungsausschluss: Ich bin der Autor dieser Bibliothek
Warum nutzt du das?
reduce(lambda x, y: x+y, l)
Das sollte gut funktionieren.
Es scheint eine Verwechslung mit operator.add
zu geben! Wenn Sie zwei Listen zusammen hinzufügen, lautet der korrekte Begriff concat
und nicht add. operator.concat
ist das, was Sie verwenden müssen.
Wenn Sie funktional denken, ist es so einfach:
>>> list2d = ((1, 2, 3), (4, 5, 6), (7,), (8, 9))
>>> reduce(operator.concat, list2d)
(1, 2, 3, 4, 5, 6, 7, 8, 9)
Sie sehen, dass der Sequenztyp reduziert wird. Wenn Sie einen Tuple angeben, erhalten Sie einen Tuple zurück. Versuchen wir es mit einer Liste:
>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Aha, du bekommst eine Liste zurück.
Wie wäre es mit der Leistung:
>>> 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 ist ziemlich schnell! Es ist jedoch kein Vergleich mit Concat zu reduzieren.
>>> list2d = ((1, 2, 3),(4, 5, 6), (7,), (8, 9))
>>> %timeit reduce(operator.concat, list2d)
1000000 loops, best of 3: 492 ns per loop
Erwägen Sie die Installation des Pakets more_itertools
.
> pip install more_itertools
Im Lieferumfang ist eine Implementierung für flatten
( source , aus den itertools-Rezepten ) enthalten:
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]
Ab Version 2.4 können Sie mit more_itertools.collapse
( source , bereitgestellt von abarnet) kompliziertere, geschachtelte iterierbare Elemente abflachen.
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]
Der Grund, warum Ihre Funktion nicht funktioniert hat: Das extend erweitert das Array an Ort und Stelle und gibt es nicht zurück. Sie können immer noch x von Lambda zurückgeben, indem Sie einen Trick verwenden:
reduce(lambda x,y: x.extend(y) or x, l)
Hinweis: Erweitern ist effizienter als + in Listen.
>>> 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))
... Itertools :
>>> 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))
... Setuptools :
>>> from setuptools.namespaces import flatten
>>> list(flatten(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]
Eine schlechte Funktion von Anils Funktion ist, dass der Benutzer das zweite Argument immer manuell als leere Liste []
angeben muss. Dies sollte stattdessen ein Standard sein. Aufgrund der Funktionsweise von Python-Objekten sollten diese in der Funktion und nicht in den Argumenten festgelegt werden.
Hier ist eine Arbeitsfunktion:
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
Testen:
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]
Folgendes scheint mir am einfachsten:
>>> 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]
matplotlib.cbook.flatten()
funktioniert für verschachtelte Listen, auch wenn sie tiefer schachteln als im Beispiel.
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))
Ergebnis:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Dies ist 18x schneller als der Unterstrich ._.
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
Die akzeptierte Antwort funktionierte für mich nicht, wenn es sich um textbasierte Listen variabler Längen handelte. Hier ist ein alternativer Ansatz, der für mich funktioniert hat.
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']
Man kann auch NumPy's flat verwenden:
import numpy as np
list(np.array(l).flat)
Bearbeiten 11/02/2016: Funktioniert nur, wenn Unterlisten identische Abmessungen haben.
Sie können numpy verwenden:flat_list = list(np.concatenate(list_of_list))
Einfacher Code für underscore.py
Paketlüfter
from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Es löst alle Abflachungsprobleme (kein Listenelement oder komplexe Verschachtelung)
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]
Sie können underscore.py
mit pip installieren
pip install underscore.py
def flatten(alist):
if alist == []:
return []
Elif type(alist) is not list:
return [alist]
else:
return flatten(alist[0]) + flatten(alist[1:])
Ein weiterer ungewöhnlicher Ansatz, der für hetero- und homogene Ganzzahlenlisten funktioniert:
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(',')]
Schnellste Lösung, die ich gefunden habe (für große Liste trotzdem):
import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()
Erledigt! Sie können es natürlich wieder in eine Liste umwandeln, indem Sie list (l) ausführen.
flat_list = []
for i in list_of_list:
flat_list+=i
Dieser Code funktioniert auch gut, da er nur die Liste 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 für Schleifen.
Wenn Sie bereit sind, ein wenig Geschwindigkeit für einen saubereren Look aufzugeben, können Sie numpy.concatenate().tolist()
oder numpy.concatenate().ravel().tolist()
verwenden:
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
Mehr erfahren Sie hier in den docs numpy.concatenate und numpy.ravel
Eine einfache rekursive Methode, die reduce
aus functools
und den Operator add
für Listen verwendet:
>>> 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]
Die Funktion flatten
nimmt lst
als Parameter an. Es durchläuft alle Elemente von lst
, bis ganze Zahlen erreicht werden (kann auch int
in float
, str
für andere Datentypen ändern), die zum Rückgabewert der äußersten Rekursion addiert werden.
Rekursion ist im Gegensatz zu Methoden wie for
-Schleifen und Monaden eine allgemeine Lösung, die nicht durch die Listentiefe begrenzt ist. Eine Liste mit einer Tiefe von 5 kann beispielsweise wie l
reduziert werden:
>>> 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]
Ich bin vor kurzem auf eine Situation gestoßen, in der ich eine Mischung aus Strings und numerischen Daten in Unterlisten wie
test = ['591212948',
['special', 'assoc', 'of', 'Chicago', 'Jon', 'Doe'],
['Jon'],
['Doe'],
['fl'],
92001,
555555555,
'hello',
['hello2', 'a'],
'b',
['hello33', ['z', 'w'], 'b']]
wo Methoden wie flat_list = [item for sublist in test for item in sublist]
nicht gearbeitet haben. Ich habe also die folgende Lösung für die Unterliste von 1+ Ebene gefunden
def concatList(data):
results = []
for rec in data:
if type(rec) == list:
results += rec
results = concatList(results)
else:
results.append(rec)
return results
Und das Ergebnis
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']
Dies ist vielleicht nicht der effizienteste Weg, aber ich dachte mir, ein Einliner (eigentlich ein Zweiliner) einzusetzen. Beide Versionen arbeiten mit verschachtelten Listen beliebiger Hierarchien und nutzen Sprachfunktionen (Python3.5) und 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)
Die Ausgabe ist
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Dies funktioniert zunächst in der Tiefe. Die Rekursion wird unterbrochen, bis ein Element gefunden wird, das kein Listenelement ist. Dann wird die lokale Variable flist
erweitert und anschließend zum übergeordneten Element zurückgesetzt. Immer, wenn flist
zurückgegeben wird, wird es im Listenverständnis um die flist
des übergeordneten Objekts erweitert. Daher wird im Stammverzeichnis eine flache Liste zurückgegeben.
Die obige Liste erstellt mehrere lokale Listen und gibt diese zurück, um die Liste der übergeordneten Elemente zu erweitern. Ich denke, der Weg dahin könnte eine gloabl flist
sein, wie unten beschrieben.
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)
Die Ausgabe ist wieder da
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Obwohl ich zu diesem Zeitpunkt nicht sicher bin, was die Effizienz angeht.
Note: Das Folgende gilt für Python 3.3+, da es yield_from
verwendet. six
ist auch ein Paket eines Drittanbieters, obwohl es stabil ist. Alternativ können Sie auch sys.version
verwenden.
Im Fall von obj = [[1, 2,], [3, 4], [5, 6]]
sind alle Lösungen hier gut, einschließlich Listenverständnis und itertools.chain.from_iterable
.
Betrachten Sie jedoch diesen etwas komplexeren Fall:
>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]
Hier gibt es mehrere Probleme:
6
, ist nur ein Skalar. Es ist nicht iterierbar, daher schlagen die obigen Routen hier fehl.'abc'
, ist ist technisch iterierbar (alle str
s sind). Wenn Sie jedoch zwischen den Zeilen lesen, möchten Sie sie nicht als solche behandeln, sondern als ein einzelnes Element.[8, [9, 10]]
, ist selbst eine verschachtelte Iteration. Grundlegendes Listenverständnis und chain.from_iterable
extrahieren nur "1 Ebene nach unten".Sie können dies wie folgt beheben:
>>> from collections import Iterable
>>> from six import string_types
>>> def flatten(obj):
... for i in obj:
... if isinstance(i, Iterable) and not isinstance(i, string_types):
... yield from flatten(i)
... else:
... yield i
>>> list(flatten(obj))
[1, 2, 3, 4, 5, 6, 'abc', 7, 8, 9, 10]
Hier überprüfen Sie, ob das Unterelement (1) mit Iterable
, einem ABC von itertools
, iterierbar ist, möchten jedoch auch sicherstellen, dass (2) das Element nicht "string-like ist. "
Sie können rekursive Aufrufe des Stacks mit einer tatsächlichen Stack-Datenstruktur ganz einfach vermeiden.
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)))
Rekursive Version
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]
Wirf meinen Hut in den Ring ...
B = [ [...], [...], ... ]
A = []
for i in B:
A.extend(i)
Dies kann mit toolz.concat
oder cytoolz.concat
(cythonized Version, die in einigen Fällen schneller sein könnte) gemacht werden:
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
Auf meinem Computer in Python 3.6 scheint dies fast so schnell wie [item for sublist in l for item in sublist]
zu sein (die Importzeit wird dabei nicht berücksichtigt):
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)
Die toolz
-Version ist in der Tat langsamer:
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)
Die Frage fragte nach einer coolen "Einzeiler" Antwort. Dann ist hier mein Beitrag in einfachem Python:
a = [[2,3,6],[False,'foo','bar','baz'],[3.1415],[],[0,0,'0']]
flat_a = [a[i][j] for i in range(len(a)) for j in range(len(a[i]))]
flat_a
[2, 3, 6, False, 'foo', 'bar', 'baz', 3.1415, 0, 0, '0']
Meine Lösung (intuitiv und kurz):
def unnest(lists):
unnested = []
for l in lists:
unnested = unnested + l
return unnested
Mit Dask können Sie eine Liste reduzieren/zusammenführen und dabei den zusätzlichen Vorteil der Speicherkonservierung erzielen. Dask verwendet einen verzögerten Speicher, der nur mit compute () aktiviert wird. Sie können also die Dask Bag-API in Ihrer Liste verwenden und sie am Ende berechnen (). Dokumentation: http://docs.dask.org/en/latest/bag-api.html
import dask.bag as db
my_list = [[1,2,3],[4,5,6],[7,8,9]]
my_list = db.from_sequence(my_list, npartitions = 1)
my_list = my_list.flatten().compute()
# [1,2,3,4,5,6,7,8,9]
Wenn Sie eine langsamere (Laufzeit-) Route einschlagen, sollten Sie weiter gehen, als mit der Zeichenfolge abzugleichen. Möglicherweise haben Sie eine verschachtelte Liste von Tupeln oder Tupel von Listen oder eine Sequenz eines benutzerdefinierten iterierbaren Typs.
Nimm einen Typ aus der Eingabesequenz und überprüfe diesen.
def iter_items(seq):
"""Yield items in a sequence."""
input_type = type(seq)
def items(subsequence):
if type(subsequence) != input_type:
yield subsequence
else:
for sub in subsequence:
yield from items(sub)
yield from items(seq)
>>> list(iter_items([(1, 2), [3, 4], "abc", [5, 6], [[[[[7]]]]]]))
[(1, 2), 3, 4, 'abc', 5, 6, 7]
@Deleet-Beispiel aufgeräumt
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))
Beispiel: https://repl.it/G8mb/0
sie können die Kettenklasse verwenden
chain.from_iterable(['ABC', 'DEF'])
ausgabe wäre: [A B C D E F]
Referenz: https://docs.python.org/2/library/itertools.html#itertools.chain
Dies funktioniert mit beliebig verschachtelten Listen. Es kann leicht erweitert werden, um mit anderen Arten von Iterables zu arbeiten.
def flatten(seq):
"""list -> list
return a flattend list from an abitrarily nested list
"""
if not seq:
return seq
if not isinstance(seq[0], list):
return [seq[0]] + flatten(seq[1:])
return flatten(seq[0]) + flatten(seq[1:])
Probelauf
>>> flatten([1, [2, 3], [[[4, 5, 6], 7], [[8]]], 9])
[1, 2, 3, 4, 5, 6, 7, 8, 9]
Hier ist eine Funktion mit Rekursion, die auf jeder beliebigen verschachtelten Liste funktioniert.
def flatten(nested_lst):
""" Return a list after transforming the inner lists
so that it's a 1-D list.
>>> flatten([[[],["a"],"a"],[["ab"],[],"abc"]])
['a', 'a', 'ab', 'abc']
"""
if not isinstance(nested_lst, list):
return(nested_lst)
res = []
for l in nested_lst:
if not isinstance(l, list):
res += [l]
else:
res += flatten(l)
return(res)
>>> flatten([[[],["a"],"a"],[["ab"],[],"abc"]])
['a', 'a', 'ab', 'abc']
ein weiterer lustiger Weg, dies zu tun:
from functools import reduce
from operator import add
li=[[1,2],[3,4]]
x= reduce(add, li)
Wir können dasselbe tun, indem wir die grundlegenden Konzepte von Python verwenden
nested_list=[10,20,[30,40,[50]],[80,[10,[20]],90],60]
flat_list=[]
def unpack(list1):
for item in list1:
try:
len(item)
unpack(item)
except:
flat_list.append(item)
unpack(nested_list)
print (flat_list)