Me pregunto si hay un atajo para hacer una lista simple de una lista de listas en Python.
Puedo hacer eso en un for
bucle, pero ¿tal vez hay algo genial "one-liner"? Lo intenté con reduce()
, pero me sale un error.
Código
l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
reduce(lambda x, y: x.extend(y), l)
Mensaje de error
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 1, in <lambda>
AttributeError: 'NoneType' object has no attribute 'extend'
Respuestas:
Dada una lista de listas
l
,flat_list = [item for sublist in l for item in sublist]
lo que significa:
es más rápido que los accesos directos publicados hasta ahora. (
l
es la lista para aplanar)Aquí está la función correspondiente:
Como evidencia, puede usar el
timeit
módulo en la biblioteca estándar:Explicación: los accesos directos basados en
+
(incluido el uso implícito ensum
) son, necesariamente,O(L**2)
cuando hay sublistas L, ya que la lista de resultados intermedios se alarga, en cada paso se asigna un nuevo objeto de lista de resultados intermedios y todos los elementos en el resultado intermedio anterior debe copiarse (así como algunos nuevos agregados al final). Entonces, por simplicidad y sin pérdida real de generalidad, digamos que tiene L sublistas de ítems I cada uno: los primeros ítems I se copian una y otra vez L-1 veces, el segundo I ítems L-2 veces, y así sucesivamente; número total de copias es I veces la suma de x para x desde 1 a L excluidos, es decir,I * (L**2)/2
.La comprensión de la lista solo genera una lista, una vez, y copia cada elemento (desde su lugar de residencia original a la lista de resultados) también exactamente una vez.
fuente
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))'
. Funciona un poco más del doble de rápido que la comprensión de la lista anidada que es la más rápida de las alternativas que se muestran aquí.list()
para realizar el iterador en una lista.list(itertools.chain.from_iterable(l))
es mejor, como se observó en otros comentarios y la respuesta de Shawn.Puedes usar
itertools.chain()
:O puede usar
itertools.chain.from_iterable()
lo que no requiere desempaquetar la lista con el*
operador :fuente
*
es lo complicado que hace que seachain
menos sencillo que la comprensión de la lista. Debe saber que la cadena solo une los iterables pasados como parámetros, y el * hace que la lista de nivel superior se expanda en parámetros, por lo quechain
une todos esos iterables, pero no desciende más. Creo que esto hace que la comprensión sea más legible que el uso de la cadena en este caso.for
bucle queappend
es más obvio en repetidas ocasiones .list = [["abc","bcd"],["cde","def"],"efg"]
dará como resultado una salida de["abc", "bcd", "cde", "def", "e", "f", "g"].
*
operador no se puede usar en python2Nota del autor : Esto es ineficiente. Pero divertido, porque los monoides son increíbles. No es apropiado para la producción de código Python.
Esto solo suma los elementos de iterable pasados en el primer argumento, tratando el segundo argumento como el valor inicial de la suma (si no se da,
0
se usa en su lugar y este caso le dará un error).Debido a que estás sumando listas anidadas, en realidad obtienes
[1,3]+[2,4]
como resultado desum([[1,3],[2,4]],[])
, que es igual a[1,3,2,4]
.Tenga en cuenta que solo funciona en listas de listas. Para las listas de listas de listas, necesitará otra solución.
fuente
Monoid
, que es una de las abstracciones más convenientes para pensar en una+
operación en un sentido general (no se limita solo a números). Así que esta respuesta merece un +1 de mi parte para el tratamiento (correcto) de las listas como un monoide. Sin embargo, el rendimiento es preocupante ...Probé la mayoría de las soluciones sugeridas con perfplot (un proyecto mío, esencialmente un envoltorio
timeit
), y encontrépara ser la solución más rápida, tanto cuando se concatenan muchas listas pequeñas y pocas listas largas. (
operator.iadd
es igualmente rápido)Código para reproducir la trama:
fuente
El
extend()
método en su ejemplo modifica enx
lugar de devolver un valor útil (quereduce()
espera).Una forma más rápida de hacer la
reduce
versión seríafuente
reduce(operator.add, l)
sería la forma correcta de hacer lareduce
versión. Los empotrados son más rápidos que las 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
. Pero, ¿y si la lista contienestring
?operator.add
función funciona igualmente bien tanto para listas de enteros como para listas de cadenas.No reinvente la rueda si usa Django :
... pandas :
... Itertools :
... Matplotlib
... Unipath :
... Herramientas de configuración :
fuente
flatten = itertools.chain.from_iterable
debería ser la respuesta correctalist_of_menuitems = [1, 2, [3, [4, 5, [6]]]]
, que se traducirá en:[1, 2, 3, 4, 5, 6]
. Lo que extraño es el nivel de aplanamiento.Aquí hay un enfoque general que se aplica a números , cadenas , listas anidadas y contenedores mixtos .
Código
Notas :
yield from flatten(x)
puede reemplazarfor sub_x in flatten(x): yield sub_x
collection.abc
eltyping
módulo.Manifestación
Referencia
fuente
more_itertools
entre otros discutidos en esta publicación. Salud.traverse
también podría ser un buen nombre para esta forma de árbol, mientras que lo mantendría menos universal para esta respuesta al apegarme a las listas anidadas.if hasattr(x, '__iter__')
lugar de importar / verificarIterable
y eso excluirá las cadenas también.Si desea aplanar una estructura de datos donde no sabe qué tan profundo está anidado, puede usar 1
iteration_utilities.deepflatten
Es un generador, por lo que debe emitir el resultado
list
o iterarlo explícitamente.Para aplanar solo un nivel y si cada uno de los elementos es iterable en sí mismo, también puede usar el
iteration_utilities.flatten
cual es solo un envoltorio delgadoitertools.chain.from_iterable
:Solo para agregar algunos tiempos (basados en la respuesta de Nico Schlömer que no incluyó la función presentada en esta respuesta):
Es un diagrama log-log para acomodar la gran variedad de valores abarcados. Para razonamiento cualitativo: más bajo es mejor.
Los resultados muestran que si el iterable contiene solo unos pocos iterables internos, entonces
sum
será más rápido, sin embargo, para iterables largos solo elitertools.chain.from_iterable
,iteration_utilities.deepflatten
o la comprensión anidada tienen un rendimiento razonable alitertools.chain.from_iterable
ser el más rápido (como ya lo notó Nico Schlömer).1 Descargo de responsabilidad: soy el autor de esa biblioteca
fuente
sum
ya no funciona en secuencias arbitrarias, ya que comienza con0
, por lo quefunctools.reduce(operator.add, sequences)
la sustitución (no estamos contentos de que se retiraranreduce
de órdenes internas?). Cuando se conocen los tipos, puede ser más rápido de usartype.__add__
.sum
. ¿Sabes en qué versiones de Python dejó de funcionar?0
es solo el valor inicial predeterminado, por lo que funciona si uno usa el argumento de inicio para comenzar con una lista vacía ... pero sigue siendo una cadena de casos especiales y me dice que use join. Se está implementando enfoldl
lugar defoldl1
. El mismo problema aparece en 2.7.Retiro mi declaración. La suma no es el ganador. Aunque es más rápido cuando la lista es pequeña. Pero el rendimiento se degrada significativamente con listas más grandes.
¡La versión de suma todavía se está ejecutando durante más de un minuto y aún no se ha procesado!
Para listas medianas:
Usando pequeñas listas y timeit: number = 1000000
fuente
Parece que hay una confusión con
operator.add
! Cuando agrega dos listas juntas, el término correcto para eso esconcat
no agregar.operator.concat
es lo que necesitas usar.Si está pensando en funcional, es tan fácil como esto ::
Verá reducir respeta el tipo de secuencia, por lo que cuando proporciona una tupla, obtiene una tupla. Probemos con una lista ::
Ajá, recuperas una lista.
¿Qué hay de rendimiento ::
from_iterable
es bastante rapido! Pero no es una comparación para reducir conconcat
.fuente
list(chain.from_iterable(...))
y 2.5 segundos parareduce(concat, ...)
. El problema es quereduce(concat, ...)
tiene tiempo de ejecución cuadrático, mientras quechain
es lineal.¿Por qué usas extender?
Esto debería funcionar bien.
fuente
from functools import reduce
Considere instalar el
more_itertools
paquete.Se envía con una implementación para
flatten
( fuente , de las recetas de itertools ):A partir de la versión 2.4, puede aplanar iterables anidados más complicados con
more_itertools.collapse
( fuente , contribuido por abarnet).fuente
La razón por la que su función no funcionó es porque la extensión extiende una matriz en el lugar y no la devuelve. Todavía puede devolver x de lambda, usando algo como esto:
Nota: extender es más eficiente que + en las listas.
fuente
extend
se utiliza mejor comonewlist = []
,extend = newlist.extend
,for sublist in l: extend(l)
ya que evita la (bastante grande) por encima de lalambda
, la búsqueda atributo enx
, y laor
.from functools import reduce
fuente
def flatten(l, a=None): if a is None: a = []
[...]Versión recursiva
fuente
matplotlib.cbook.flatten()
funcionará para listas anidadas incluso si anidan más profundamente que en el ejemplo.Resultado:
Esto es 18 veces más rápido que el subrayado ._. Flatten:
fuente
La respuesta aceptada no funcionó para mí cuando trataba con listas basadas en texto de longitudes variables. Aquí hay un enfoque alternativo que funcionó para mí.
Respuesta aceptada que no funcionó:
Nueva solución que propone hizo el trabajo para mí:
fuente
Una mala característica de la función anterior de Anil es que requiere que el usuario siempre especifique manualmente el segundo argumento para que sea una lista vacía
[]
. Esto debería ser un valor predeterminado. Debido a la forma en que funcionan los objetos de Python, estos deben establecerse dentro de la función, no en los argumentos.Aquí hay una función de trabajo:
Pruebas:
fuente
Lo siguiente me parece más simple:
fuente
También se puede usar el piso de NumPy :
Edición 11/02/2016: solo funciona cuando las sublistas tienen dimensiones idénticas.
fuente
Puedes usar numpy:
flat_list = list(np.concatenate(list_of_list))
fuente
[1, 2, [3], [[4]], [5, [6]]]
Si está dispuesto a renunciar a una pequeña cantidad de velocidad para una apariencia más limpia, entonces podría usar
numpy.concatenate().tolist()
onumpy.concatenate().ravel().tolist()
:Puede encontrar más información aquí en los documentos numpy.concatenate y numpy.ravel
fuente
[1, 2, [3], [[4]], [5, [6]]]
La solución más rápida que he encontrado (para una lista grande de todos modos):
¡Hecho! Por supuesto, puede volver a convertirlo en una lista ejecutando list (l)
fuente
Código simple para
underscore.py
ventilador de paqueteResuelve todos los problemas de aplanamiento (ninguno de los elementos de la lista o anidamiento complejo)
Se puede instalar
underscore.py
con pipfuente
fuente
[[1, 2, 3], [4, 5, 6], [7], [8, 9]]
Nota : A continuación se aplica a Python 3.3+ porque lo usa
yield_from
.six
También es un paquete de terceros, aunque es estable. Alternativamente, podrías usarsys.version
.En el caso de
obj = [[1, 2,], [3, 4], [5, 6]]
, todas las soluciones aquí son buenas, incluida la comprensión de listas yitertools.chain.from_iterable
.Sin embargo, considere este caso un poco más complejo:
Hay varios problemas aquí:
6
es solo un escalar; no es iterable, por lo que las rutas anteriores fallarán aquí.'abc'
, es técnicamente iterable (todosstr
s son). Sin embargo, al leer un poco entre líneas, no desea tratarlo como tal, desea tratarlo como un solo elemento.[8, [9, 10]]
es en sí mismo un iterable anidado. Lista de comprensión básica ychain.from_iterable
solo extrae "1 nivel abajo".Puede remediar esto de la siguiente manera:
Aquí, verifica que el subelemento (1) sea iterable con
Iterable
un ABC deitertools
, pero también quiere asegurarse de que (2) el elemento no sea "similar a una cadena".fuente
yield from
a unfor
bucle, por ejemplofor x in flatten(i): yield x
Este código también funciona bien, ya que solo extiende la lista por completo. Aunque es muy similar, solo tiene uno para el bucle. Por lo tanto, tiene menos complejidad que agregar 2 para bucles.
fuente
La ventaja de esta solución sobre la mayoría de las demás aquí es que si tiene una lista como:
mientras que la mayoría de las otras soluciones arrojan un error, esta solución las maneja.
fuente
Puede que esta no sea la forma más eficiente, pero pensé en poner una línea (en realidad una de dos líneas). Ambas versiones funcionarán en listas anidadas de jerarquía arbitraria y explotarán las características del lenguaje (Python3.5) y la recursividad.
La salida es
Esto funciona en profundidad de primera manera. La recursividad disminuye hasta que encuentra un elemento que no es de la lista, luego extiende la variable local
flist
y luego la revierte al padre. Cada vez queflist
se devuelve, se extiende a los padresflist
en la comprensión de la lista. Por lo tanto, en la raíz, se devuelve una lista plana.El anterior crea varias listas locales y las devuelve, que se utilizan para ampliar la lista de los padres. Creo que la forma de evitar esto puede ser crear un gloabl
flist
, como a continuación.La salida es de nuevo
Aunque no estoy seguro en este momento sobre la eficiencia.
fuente
Otro enfoque inusual que funciona para listas heterogéneas y homogéneas de enteros:
fuente
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(',')]
. Pero sugeriría seguir con la propuesta de Deleet para casos de uso real. No contiene transformaciones de tipo hacky, es más rápido y más versátil porque, naturalmente, también maneja listas con tipos mixtos.