Me pregunto si hay un atajo para hacer una lista simple de una lista de listas en Python.
Puedo hacer eso en un forbucle, 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. (
les la lista para aplanar)Aquí está la función correspondiente:
Como evidencia, puede usar el
timeitmó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 seachainmenos 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 quechainune 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.forbucle queappendes 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,
0se 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.iaddes igualmente rápido)Código para reproducir la trama:
fuente
El
extend()método en su ejemplo modifica enxlugar de devolver un valor útil (quereduce()espera).Una forma más rápida de hacer la
reduceversión seríafuente
reduce(operator.add, l)sería la forma correcta de hacer lareduceversió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.addfunció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_iterabledeberí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_xcollection.abceltypingmódulo.Manifestación
Referencia
fuente
more_itertoolsentre otros discutidos en esta publicación. Salud.traversetambié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 / verificarIterabley 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.deepflattenEs un generador, por lo que debe emitir el resultado
listo 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.flattencual 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
sumserá más rápido, sin embargo, para iterables largos solo elitertools.chain.from_iterable,iteration_utilities.deepflatteno la comprensión anidada tienen un rendimiento razonable alitertools.chain.from_iterableser el más rápido (como ya lo notó Nico Schlömer).1 Descargo de responsabilidad: soy el autor de esa biblioteca
fuente
sumya 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 retiraranreducede ó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?0es 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 enfoldllugar 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 esconcatno agregar.operator.concates 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_iterablees 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 quechaines lineal.¿Por qué usas extender?
Esto debería funcionar bien.
fuente
from functools import reduceConsidere instalar el
more_itertoolspaquete.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
extendse 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 reducefuente
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.pyventilador de paqueteResuelve todos los problemas de aplanamiento (ninguno de los elementos de la lista o anidamiento complejo)
Se puede instalar
underscore.pycon 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.sixTambié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í:
6es solo un escalar; no es iterable, por lo que las rutas anteriores fallarán aquí.'abc', es técnicamente iterable (todosstrs 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_iterablesolo extrae "1 nivel abajo".Puede remediar esto de la siguiente manera:
Aquí, verifica que el subelemento (1) sea iterable con
Iterableun ABC deitertools, pero también quiere asegurarse de que (2) el elemento no sea "similar a una cadena".fuente
yield froma unforbucle, por ejemplofor x in flatten(i): yield xEste 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
flisty luego la revierte al padre. Cada vez queflistse devuelve, se extiende a los padresflisten 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.