Je me demande s'il existe un raccourci pour faire une simple liste à partir d'une liste de listes en Python.
Je peux le faire en for
boucle, mais peut-être qu'il y a du "one-liner" cool? Je l'ai essayé avec reduce()
, mais j'obtiens une erreur.
Code
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Message d'erreur
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Réponses:
Étant donné une liste de listes
l
,flat_list = [item for sublist in l for item in sublist]
ce qui signifie:
est plus rapide que les raccourcis affichés jusqu'à présent. (
l
est la liste à aplatir.)Voici la fonction correspondante:
Pour preuve, vous pouvez utiliser le
timeit
module dans la bibliothèque standard:Explication: les raccourcis basés sur
+
(y compris l'utilisation implicite danssum
) sont, par nécessité,O(L**2)
lorsqu'il y a des sous-listes L - car la liste de résultats intermédiaire s'allonge, à chaque étape un nouvel objet de liste de résultats intermédiaire est alloué, et tous les éléments dans le résultat intermédiaire précédent doit être copié (ainsi que quelques nouveaux ajoutés à la fin). Donc, pour plus de simplicité et sans perte réelle de généralité, disons que vous avez chacun L sous-listes de I éléments: les premiers I éléments sont copiés d'avant en arrière L-1 fois, les seconds I éléments L-2 fois, et ainsi de suite; le nombre total de copies est I fois la somme de x pour x de 1 à L exclu, c'est-à-direI * (L**2)/2
.La compréhension de la liste ne génère qu'une seule liste, une fois, et copie chaque élément (de son lieu de résidence d'origine à la liste des résultats) également exactement une fois.
la source
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))'
. Il fonctionne un peu plus de deux fois plus vite que la compréhension de liste imbriquée qui est la plus rapide des alternatives présentées ici.list()
pour réaliser l'itérateur dans une liste.list(itertools.chain.from_iterable(l))
est le meilleur - comme remarqué dans d'autres commentaires et la réponse de Shawn.Vous pouvez utiliser
itertools.chain()
:Ou vous pouvez utiliser
itertools.chain.from_iterable()
ce qui ne nécessite pas de déballer la liste avec l'*
opérateur :la source
*
la chose délicate qui rendchain
moins simple que la compréhension de la liste. Vous devez savoir que la chaîne réunit uniquement les itérables passés en tant que paramètres, et le * entraîne l'extension de la liste de niveau supérieur en paramètres, doncchain
réunit tous ces itérables, mais ne descend pas plus loin. Je pense que cela rend la compréhension plus lisible que l'utilisation de la chaîne dans ce cas.for
boucle qui est à plusieurs reprisesappend
plus évidente.list = [["abc","bcd"],["cde","def"],"efg"]
entraînera une sortie de["abc", "bcd", "cde", "def", "e", "f", "g"].
*
opérateur ne puisse pas être utilisé en python2Note de l'auteur : c'est inefficace. Mais amusant, car les monoïdes sont géniaux. Ce n'est pas approprié pour le code de production Python.
Cela résume simplement les éléments d'itérable passés dans le premier argument, traitant le second argument comme la valeur initiale de la somme (s'il n'est pas donné,
0
est utilisé à la place et ce cas vous donnera une erreur).Parce que vous additionnez des listes imbriquées, vous obtenez en fait
[1,3]+[2,4]
à la suite desum([[1,3],[2,4]],[])
, qui est égal à[1,3,2,4]
.Notez que cela ne fonctionne que sur les listes de listes. Pour les listes de listes de listes, vous aurez besoin d'une autre solution.
la source
Monoid
, qui est l'une des abstractions les plus pratiques pour penser à une+
opération dans un sens général (non limité aux seuls chiffres). Cette réponse mérite donc un +1 de ma part pour le traitement (correct) des listes en tant que monoïde. La performance est cependant préoccupante ...J'ai testé la plupart des solutions suggérées avec perfplot (un de mes projets pour animaux de compagnie, essentiellement un emballage autour
timeit
), et j'ai trouvépour être la solution la plus rapide, à la fois lorsque de nombreuses petites listes et quelques longues listes sont concaténées. (
operator.iadd
est tout aussi rapide.)Code pour reproduire l'intrigue:
la source
La
extend()
méthode de votre exemple modifiex
au lieu de renvoyer une valeur utile (quireduce()
attend).Un moyen plus rapide de faire la
reduce
version seraitla source
reduce(operator.add, l)
serait la bonne façon de faire lareduce
version. Les fonctions intégrées sont plus rapides que les 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
. Mais que faire si la liste contientstring
?operator.add
fonction fonctionne aussi bien pour les listes d'entiers que pour les listes de chaînes.Ne réinventez pas la roue si vous utilisez Django :
... Pandas :
... Itertools :
... Matplotlib
... Unipath :
... Setuptools :
la source
flatten = itertools.chain.from_iterable
devrait être la bonne réponselist_of_menuitems = [1, 2, [3, [4, 5, [6]]]]
, il en résultera sur:[1, 2, 3, 4, 5, 6]
. Ce qui me manque, c'est le niveau aplati.Voici une approche générale qui s'applique aux nombres , aux chaînes , aux listes imbriquées et aux conteneurs mixtes .
Code
Remarques :
yield from flatten(x)
peut remplacerfor sub_x in flatten(x): yield sub_x
collection.abc
latyping
module.Démo
Référence
la source
more_itertools
entre autres discuté dans ce post. À votre santé.traverse
- être pourrait également être un bon nom pour cette façon d'un arbre, alors que je le garderais moins universel pour cette réponse en s'en tenant aux listes imbriquées.if hasattr(x, '__iter__')
au lieu d'importer / vérifierIterable
et cela exclura également les chaînes.Si vous voulez aplatir une structure de données où vous ne savez pas à quelle profondeur elle est imbriquée, vous pouvez utiliser 1
iteration_utilities.deepflatten
C'est un générateur, vous devez donc convertir le résultat en un
list
ou itérer explicitement dessus.Pour aplatir un seul niveau et si chacun des éléments est lui-même itérable, vous pouvez également utiliser
iteration_utilities.flatten
ce qui n'est lui-même qu'une mince enveloppe autouritertools.chain.from_iterable
: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 tracé de journal de bord pour s'adapter à la vaste gamme de valeurs réparties. Pour le raisonnement qualitatif: plus c'est bas, mieux c'est.
Les résultats montrent que si le itérables ne contient que quelques iterables internes alors
sum
sera le plus rapide, mais pour de longues iterables que lesitertools.chain.from_iterable
,iteration_utilities.deepflatten
ou la compréhension imbriquée ont des performances raisonnableitertools.chain.from_iterable
étant le plus rapide (comme déjà remarqué par Nico Schlömer).1 Avertissement: je suis l'auteur de cette bibliothèque
la source
sum
ne fonctionne plus sur les séquences arbitraires au début0
, ce qui rendfunctools.reduce(operator.add, sequences)
le remplacement (ne sommes-nous pas heureux qu'ils aient été supprimésreduce
des prédéfinis?). Lorsque les types sont connus, leur utilisation peut être plus rapidetype.__add__
.sum
. Savez-vous par hasard sur quelles versions de Python il a cessé de fonctionner?0
est juste la valeur de départ par défaut, donc ça marche si on utilise l' argument start pour commencer avec une liste vide ... mais ça casse toujours des chaînes spéciales et me dit d'utiliser join. Il implémentefoldl
au lieu defoldl1
. Le même problème apparaît dans 2.7.Je reprends ma déclaration. la somme n'est pas gagnante. Bien qu'il soit plus rapide lorsque la liste est petite. Mais les performances se dégradent considérablement avec de plus grandes listes.
La version sum fonctionne toujours depuis plus d'une minute et n'a pas encore été traitée!
Pour les listes moyennes:
Utilisation de petites listes et de timeit: nombre = 1000000
la source
Il semble y avoir une confusion avec
operator.add
! Lorsque vous ajoutez deux listes ensemble, le terme correct pour cela n'estconcat
pas ajouter.operator.concat
est ce que vous devez utiliser.Si vous pensez fonctionnel, c'est aussi simple que cela ::
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 ::
Aha, vous récupérez une liste.
Que diriez-vous de la performance ::
from_iterable
est assez rapide! Mais ce n'est pas une comparaison avec laquelle réduireconcat
.la source
list(chain.from_iterable(...))
et 2,5 secondes pourreduce(concat, ...)
. Le problème est qu'ilreduce(concat, ...)
a un temps d'exécution quadratique, alors qu'ilchain
est linéaire.Pourquoi utilisez-vous extend?
Cela devrait bien fonctionner.
la source
from functools import reduce
Pensez à installer le
more_itertools
package.Il est livré avec une implémentation pour
flatten
( source , à partir des recettes itertools ):Depuis la version 2.4, vous pouvez aplatir des itérables plus compliqués et imbriqués avec
more_itertools.collapse
( source , apportée par abarnet).la source
La raison pour laquelle votre fonction n'a pas fonctionné est que l' extension étend un tableau en place et ne le renvoie pas. Vous pouvez toujours renvoyer x de lambda, en utilisant quelque chose comme ceci:
Remarque: extend est plus efficace que + sur les listes.
la source
extend
est mieux utilisé commenewlist = []
,extend = newlist.extend
,for sublist in l: extend(l)
car il évite les frais généraux de la (plutôt grande)lambda
la recherche d'attribut,x
et leor
.from functools import reduce
la source
def flatten(l, a=None): if a is None: a = []
[...]Version récursive
la source
matplotlib.cbook.flatten()
fonctionnera pour les listes imbriquées même si elles s'imbriquent plus profondément que l'exemple.Résultat:
C'est 18 fois plus rapide que le soulignement ._. Aplatir:
la source
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.
Réponse acceptée qui n'a pas fonctionné:
Nouvelle solution proposée qui a fonctionné pour moi:
la source
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 étant 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:
Essai:
la source
Les éléments suivants me semblent les plus simples:
la source
On peut aussi utiliser l' appartement de NumPy :
Edit 11/02/2016: ne fonctionne que lorsque les sous-listes ont des dimensions identiques.
la source
Vous pouvez utiliser numpy:
flat_list = list(np.concatenate(list_of_list))
la source
[1, 2, [3], [[4]], [5, [6]]]
Si vous êtes prêt à abandonner une petite quantité de vitesse pour un look plus propre, vous pouvez utiliser
numpy.concatenate().tolist()
ounumpy.concatenate().ravel().tolist()
:Vous pouvez en savoir plus ici dans les documents numpy.concatenate et numpy.ravel
la source
[1, 2, [3], [[4]], [5, [6]]]
La solution la plus rapide que j'ai trouvée (pour une grande liste de toute façon):
Terminé! Vous pouvez bien sûr le reconvertir en liste en exécutant list (l)
la source
Code simple pour le
underscore.py
ventilateur de packageIl résout tous les problèmes d'aplatissement (aucun élément de liste ou imbrication complexe)
Vous pouvez installer
underscore.py
avec pipla source
la source
[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Remarque : ci-dessous s'applique à Python 3.3+ car il utilise
yield_from
.six
est également un package tiers, bien qu'il soit stable. Alternativement, vous pouvez utilisersys.version
.Dans le cas de
obj = [[1, 2,], [3, 4], [5, 6]]
, toutes les solutions ici sont bonnes, y compris la compréhension de liste etitertools.chain.from_iterable
.Cependant, considérons ce cas légèrement plus complexe:
Il y a plusieurs problèmes ici:
6
est juste un scalaire; ce n'est pas itérable, donc les itinéraires ci-dessus échoueront ici.'abc'
, est techniquement itérables (tousstr
s sont). Cependant, en lisant un peu entre les lignes, vous ne voulez pas le traiter comme tel - vous voulez le traiter comme un élément unique.[8, [9, 10]]
est lui-même un itérable imbriqué. Compréhension de liste de base etchain.from_iterable
extraire uniquement "1 niveau vers le bas".Vous pouvez y remédier comme suit:
Ici, vous vérifiez que le sous-élément (1) est itérable avec
Iterable
un ABC deitertools
, mais vous voulez également vous assurer que (2) l'élément n'est pas "semblable à une chaîne".la source
yield from
for
for x in flatten(i): yield x
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.
la source
L'avantage de cette solution sur la plupart des autres ici est que si vous avez une liste comme:
tandis que la plupart des autres solutions génèrent une erreur, cette solution les gère.
la source
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é.
La sortie est
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
et puis la restaure au parent. Chaque fois qu'ilflist
est retourné, il est étendu aux parentsflist
dans la liste de compréhension. 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 gloabl
flist
, comme ci-dessous.La sortie est à nouveau
Bien que je ne sois pas sûr pour le moment de l'efficacité.
la source
Une autre approche inhabituelle qui fonctionne pour des listes hétéro et homogènes d'entiers:
la source
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(',')]
. Mais je suggérerais de m'en tenir à la proposition de Deleet pour des cas d'utilisation réels. Il ne contient pas de transformations de type hacky, il est plus rapide et plus polyvalent car il gère également naturellement les listes avec des types mixtes.