Les éléments suivants me semblent les plus simples:
>>> 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]
Si vous êtes prêt à abandonner une petite quantité de vitesse pour un look plus net, vous pouvez utiliser numpy.concatenate().tolist()
ou 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
Vous pouvez en savoir plus ici dans la documentation numpy.concatenate et numpy.ravel
Il semble y avoir une confusion avec operator.add
! Lorsque vous ajoutez deux listes ensemble, le terme correct pour cela est concat
, pas ajouter. operator.concat
est ce que vous devez utiliser.
Si vous pensez fonctionnel, c'est aussi simple que cela ::
>>> 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)
Vous voyez réduire respecte le type de séquence, donc lorsque vous fournissez un tuple, vous récupérez un tuple. Essayons avec une 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, vous récupérez une liste.
Que diriez-vous de la performance ::
>>> 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
est assez rapide! Mais ce n'est pas une comparaison à réduire avec 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
from nltk import flatten
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flatten(l)
L'avantage de cette solution sur la plupart des autres ici est que si vous avez une liste comme:
l = [1, [2, 3], [4, 5, 6], [7], [8, 9]]
Tandis que la plupart des autres solutions génèrent une erreur, cette solution les gère.
Ce n'est peut-être pas le moyen le plus efficace mais j'ai pensé à mettre une doublure (en fait une doublure). Les deux versions fonctionneront sur des listes imbriquées de hiérarchie arbitraire et exploiteront les fonctionnalités du langage (Python3.5) et la récursivité.
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)
La sortie est
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Cela fonctionne en profondeur d'abord. La récursivité descend jusqu'à ce qu'il trouve un élément non-liste, puis étend la variable locale flist
, puis la restitue au parent. Chaque fois que flist
est renvoyé, il est étendu à flist
du parent dans la compréhension de la liste. Par conséquent, à la racine, une liste plate est renvoyée.
Celui ci-dessus crée plusieurs listes locales et les renvoie qui sont utilisées pour étendre la liste des parents. Je pense que le moyen de contourner cela peut être de créer un flist
gloabl, comme ci-dessous.
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)
La sortie est à nouveau
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
Bien que je ne sois pas sûr pour le moment de l'efficacité.
Une autre façon amusante de le faire:
from functools import reduce
from operator import add
li=[[1,2],[3,4]]
x= reduce(add, li)
def flatten(alist):
if alist == []:
return []
elif type(alist) is not list:
return [alist]
else:
return flatten(alist[0]) + flatten(alist[1:])
Vous pouvez utiliser numpy:
flat_list = list(np.concatenate(list_of_list))
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]
Vous pouvez utiliser itertools.chain()
:
>>> import itertools
>>> list2d = [[1,2,3], [4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain(*list2d))
Ou vous pouvez utiliser itertools.chain.from_iterable()
qui ne le fait pas nécessite de décompresser la liste avec l'opérateur *
:
>>> import itertools
>>> list2d = [[1,2,3], [4,5,6], [7], [8,9]]
>>> merged = list(itertools.chain.from_iterable(list2d))
Cette approche est sans doute plus lisible que [item for sublist in l for item in sublist]
et semble également plus rapide:
$ 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
La solution la plus rapide que j'ai trouvée (pour une grande liste de toute façon):
import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()
Terminé! Vous pouvez bien sûr le reconvertir en liste en exécutant list (l)
La réponse acceptée n'a pas fonctionné pour moi lorsqu'il s'agissait de listes textuelles de longueurs variables. Voici une approche alternative qui a fonctionné pour moi.
l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]
Réponse acceptée qui n'a pas fonctionné:
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']
Nouvelle solution proposée qui a fonctionné pour moi:
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']
Une mauvaise caractéristique de la fonction d'Anil ci-dessus est qu'elle oblige l'utilisateur à toujours spécifier manuellement le deuxième argument comme une liste vide []
. Ce devrait plutôt être une valeur par défaut. En raison de la façon dont les objets Python fonctionnent, ceux-ci doivent être définis dans la fonction, pas dans les arguments.
Voici une fonction de travail:
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
Essai:
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]
Ne réinventez pas la roue si vous utilisez 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))
... 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))
Si vous souhaitez aplatir une structure de données dont vous ne savez pas à quelle profondeur elle est imbriquée, vous pouvez utiliser 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]
Il s'agit d'un générateur, vous devez donc convertir le résultat en list
ou le répéter explicitement.
Pour aplatir un seul niveau et si chacun des éléments est lui-même itérable, vous pouvez également utiliser iteration_utilities.flatten
, qui n'est lui-même qu'une mince enveloppe autour de 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]
Juste pour ajouter quelques timings (basés sur la réponse de Nico Schlömer qui n'incluait pas la fonction présentée dans cette réponse):
Il s'agit d'un graphique journal-log pour s'adapter à la vaste gamme de valeurs réparties. Pour un raisonnement qualitatif: plus c'est bas, mieux c'est.
Les résultats montrent que si l'itérable ne contient que quelques itérables internes, alors sum
sera le plus rapide, mais pour de longues itérables, seuls les itertools.chain.from_iterable
, iteration_utilities.deepflatten
ou la compréhension imbriquée ont des performances raisonnables avec {{ X3}} étant le plus rapide (comme l'a déjà remarqué 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 Avertissement: je suis l'auteur de cette bibliothèque
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]
La méthode extend()
de votre exemple modifie x
au lieu de renvoyer une valeur utile (que reduce()
attend).
Un moyen plus rapide de faire la version reduce
serait
>>> 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]
Code simple pour underscore.py
fan de package
from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
Il résout tous les problèmes d'aplatissement (aucun élément de liste ou imbrication complexe)
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]
Vous pouvez installer underscore.py
avec pip
pip install underscore.py
flat_list = []
for i in list_of_list:
flat_list+=i
Ce code fonctionne également très bien car il étend simplement la liste tout le long. Bien qu'il soit très similaire mais n'en a qu'un pour la boucle. Il a donc moins de complexité que d'ajouter 2 pour les boucles.