¿Cómo hacer una lista plana a partir de una lista de listas?

Me pregunto si hay un atajo para hacer una lista simple a partir de la lista de listas en Python.

Puedo hacer eso en un for bucle, pero tal vez haya algo interesante "en una sola línea"? Lo probé con reduce(), pero obtengo un error.

Code

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'

preguntado el 04 de junio de 09 a las 17:06

Hay una discusión en profundidad de esto aquí: rightfootin.blogspot.com/2006/09/more-on-python-flatten.html, discutiendo varios métodos de aplanar listas de listas anidadas arbitrariamente. ¡Una lectura interesante! -

Algunas otras respuestas son mejores, pero la razón por la que la suya falla es que el método 'extender' siempre devuelve None. Para una lista con longitud 2, funcionará pero devolverá Ninguno. Para una lista más larga, consumirá los primeros 2 argumentos, lo que devuelve Ninguno. Luego continúa con None.extend ( ), que causa este error -

La solución @ shawn-chin es la más pitónica aquí, pero si necesita conservar el tipo de secuencia, digamos que tiene una tupla de tuplas en lugar de una lista de listas, entonces debería usar reduce (operator.concat, tuple_of_tuples). El uso de operator.concat con tuplas parece funcionar más rápido que chain.from_iterables con list. -

stackoverflow.com/questions/50259290/… (este artículo explica la diferencia entre un np.flatten () y un tf.flatten () usa (estático vs dinámico) ndarray. -

30 Respuestas

Dada una lista de listas t,

flat_list = [item for sublist in t for item in sublist]

lo que significa:

flat_list = []
for sublist in t:
    for item in sublist:
        flat_list.append(item)

es más rápido que los atajos publicados hasta ahora. (t es la lista para aplanar.)

Aquí está la función correspondiente:

flatten = lambda t: [item for sublist in t for item in sublist]

Como prueba, puede utilizar el timeit módulo en la biblioteca estándar:

$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' '[item for sublist in t for item in sublist]'
10000 loops, best of 3: 143 usec per loop
$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'sum(t, [])'
1000 loops, best of 3: 969 usec per loop
$ python -mtimeit -s't=[[1,2,3],[4,5,6], [7], [8,9]]*99' 'reduce(lambda x,y: x+y,t)'
1000 loops, best of 3: 1.1 msec per loop

Explicación: los atajos basados ​​en + (incluido el uso implícito en sum) son, por necesidad, O(T**2) cuando hay T sublistas, dado que la lista de resultados intermedios sigue alargándose, en cada paso se asigna un nuevo objeto de lista de resultados intermedios, y todos los elementos del resultado intermedio anterior deben copiarse (así como algunos nuevos agregados al final). Entonces, por simplicidad y sin pérdida real de generalidad, digamos que tiene T sublistas de k elementos cada una: los primeros k elementos se copian una y otra vez T-1 veces, los segundos k elementos T-2 veces, y así sucesivamente; el número total de copias es k multiplicado por la suma de x para x de 1 a T excluido, es decir, k * (T**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.

Respondido el 06 de enero de 21 a las 23:01

Probé una prueba con los mismos datos, usando 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))'. Se ejecuta 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í. - intuido

Encontré la sintaxis difícil de entender hasta que me di cuenta de que puede pensar en ella exactamente como bucles for anidados. para sublista en l: para artículo en sublista: artículo de rendimiento - Rob Crowell

@BorisChervenkov: Observe que envié la llamada en list() para convertir el iterador en una lista. - intuido

[hoja por árbol en bosque por hoja en árbol] podría ser más fácil de comprender y aplicar. - Juan Mee

@Joel, de hecho hoy en día list(itertools.chain.from_iterable(l)) es mejor, como se notó en otros comentarios y en la respuesta de Shawn. - alex martelli

Puedes usar itertools.chain():

import itertools

list2d = [[1,2,3], [4,5,6], [7], [8,9]]
merged = list(itertools.chain(*list2d))

O puede utilizar itertools.chain.from_iterable() que no requiere desempacar la lista con el * operador:

merged = list(itertools.chain.from_iterable(list2d))

respondido 11 mar '21, 11:03

EL * es lo complicado que hace chain 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 que chain 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. - Tim Dierks

@TimDierks: No estoy seguro de que "esto requiere que entiendas la sintaxis de Python" sea un argumento en contra del uso de una técnica determinada en Python. Claro, el uso complejo puede confundir, pero el operador "splat" es generalmente útil en muchas circunstancias, y esto no lo usa de una manera particularmente oscura; Rechazar todas las características del lenguaje que no son necesariamente obvias para los usuarios principiantes significa que está atando una mano a la espalda. También puede descartar listas por comprensión mientras lo hace; los usuarios de otros orígenes encontrarían un for bucle que repetidamente appendes más obvio. - ShadowRanger

Esta respuesta, y otras respuestas aquí, dan un resultado incorrecto si el nivel superior también contiene un valor. por ejemplo, list = [["abc","bcd"],["cde","def"],"efg"] resultará en una salida de ["abc", "bcd", "cde", "def", "e", "f", "g"]. - gouravkr

Parece * operador no se puede utilizar en python2 - semana

@gouravkr eso es correcto, pero aquí, la cadena es mucho menos propuesta de que es un iterable. La pregunta es sobre listas de listas, por lo que, de hecho, no se garantiza que funcione cualquier otro iterable en la lista principal. Las listas (como estructuras de datos homogéneas) suelen contener elementos del mismo tipo de todos modos, por lo que quizás las tuplas sean más adecuadas en su caso de uso. - Alex Povel

Nota del autor: Esto es ineficiente. Pero divertido, porque monoides son impresionantes. No es apropiado para el código Python de producción.

>>> sum(l, [])
[1, 2, 3, 4, 5, 6, 7, 8, 9]

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 utiliza en su lugar y este caso le dará un error).

Debido a que está sumando listas anidadas, en realidad obtiene [1,3]+[2,4] como resultado de sum([[1,3],[2,4]],[]), que es igual a [1,3,2,4].

Tenga en cuenta que solo funciona en listas de listas. Para listas de listas de listas, necesitará otra solución.

Respondido el 29 de diciembre de 18 a las 16:12

eso es bastante ordenado e inteligente, pero no lo usaría porque es confuso de leer. - andrewrk

Este es un algoritmo de Shlemiel el pintor joelonsoftware.com/articles/fog0000000319.html - innecesariamente ineficaz así como innecesariamente feo. - mike graham

La operación de agregar en listas forma una Monoid, que es una de las abstracciones más convenientes para pensar en un + operación en un sentido general (no limitado a números solamente). Entonces, esta respuesta merece un +1 de mi parte para el tratamiento (correcto) de las listas como un monoide. Sin embargo, la actuación es preocupante ... - ulidtko

@andrewrk Bueno, algunas personas piensan que esta es la forma más limpia de hacerlo: youtube.com/watch?v=IOiZatlZtGU los que no entienden por qué esto es genial solo necesitan esperar unas décadas hasta que todos lo hagan de esta manera :) usemos lenguajes de programación (y abstracciones) que se descubren y no se inventan, se descubre Monoid. - jhegedus

esta es una forma muy ineficiente debido al aspecto cuadrático de la suma. - Jean-François Fabre ♦

Probé la mayoría de las soluciones sugeridas con diagrama de rendimiento (un proyecto favorito mío, esencialmente un envoltorio timeit), y encontrado

import functools
import operator
functools.reduce(operator.iconcat, a, [])

para ser la solución más rápida, tanto cuando se concatenan muchas listas pequeñas como pocas listas largas. (operator.iadd es igualmente rápido.)

enter image description here

enter image description here


Código para reproducir la trama:

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,
    # setup=lambda n: [list(range(n))] * 10,
    kernels=[
        forfor,
        sum_brackets,
        functools_reduce,
        functools_reduce_iconcat,
        itertools_chain,
        numpy_flat,
        numpy_concatenate,
    ],
    n_range=[2 ** k for k in range(16)],
    xlabel="num lists (of length 10)",
    # xlabel="len lists (10 lists total)"
)

respondido 05 nov., 20:01

Para listas anidadas enormes, 'list (numpy.array (a) .flat)' es la más rápida entre todas las funciones anteriores. - Sara

Intenté usar expresiones regulares: 'list (map (int, re.findall (r "[\ w] +", str (a))))'. La velocidad es un poco más lenta que numpy_concatenate - Justas

¿Hay alguna manera de hacer un perfplot en 3-d? número de matrices por tamaño medio de matriz? - Leo

Amo tu solución. Breve, simple y eficiente :-) - Chadée Fouad

@Sara, ¿puedes definir "enorme", por favor? - Boris

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]

EL extend() método en su ejemplo modifica x en lugar de devolver un valor útil (que reduce() espera).

Una forma más rápida de hacer reduce la versión sería

>>> 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]

Respondido el 02 de enero de 18 a las 05:01

reduce(operator.add, l) sería la forma correcta de hacer el reduce versión. Las incorporadas son más rápidas que las lambdas. - agf

@agf aquí es cómo: * 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.025218963623046875 - lukmdo

Este es un algoritmo de Shlemiel el pintor joelonsoftware.com/articles/fog0000000319.html - mike graham

esto solo se puede usar para integers. Pero, ¿y si la lista contiene string? - Freddy

@ Freddy: El operator.add La función funciona igualmente bien para listas de enteros y listas de cadenas. - greg hewgill

No reinventes la rueda

Si utiliza -

...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))

...unitrayecto:

>>> from unipath.path import flatten
>>> list(flatten(l))

...Herramientas de configuración:

>>> from setuptools.namespaces import flatten
>>> list(flatten(l))

respondido 06 mar '21, 13:03

flatten = itertools.chain.from_iterable debería ser la respuesta correcta - gecos

¡gran respuesta! también funciona para l = [[[1, 2, 3], [4, 5]], 5] en el caso de los pandas - Markus holandés

Me gusta la solución Pandas. Si tienes algo como: list_of_menuitems = [1, 2, [3, [4, 5, [6]]]], resultará en: [1, 2, 3, 4, 5, 6]. Lo que echo de menos es el nivel de aplanar. - imjoseangel

Todas estas grandes bibliotecas reinventaron la rueda, ¿por qué no debería hacerlo yo? - nathan chappell

Aquí hay un enfoque general que se aplica a números, instrumentos de cuerda, anidado listas y mezclado contenedores

Code

#from typing import Iterable 
from collections import Iterable                            # < py38


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

Notas:

  • En Python 3, yield from flatten(x) puede sustituir for sub_x in flatten(x): yield sub_x
  • En Python 3.8, clases base abstractas Fuck Cancer. emocionado a partir de octubre XNUMX collection.abc En el correo electrónico “Su Cuenta de Usuario en su Nuevo Sistema XNUMXCX”. typing módulo.

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']

Referencia

  • Esta solución se modifica a partir de una receta en Beazley, D. y B. Jones. Receta 4.14, Python Cookbook 3rd Ed., O'Reilly Media Inc. Sebastopol, CA: 2013.
  • Encontré una anterior SO publicar, posiblemente la demostración original.

Respondido el 13 de junio de 19 a las 00:06

Simplemente escribí más o menos lo mismo, porque no vi su solución ... esto es lo que busqué "aplanar recursivamente listas múltiples completas" ... (+1) - Martín Thoma

@MartinThoma Muy apreciado. Para su información, si acoplar iterables anidados es una práctica común para usted, existen algunos paquetes de terceros que lo manejan bien. Esto puede evitar reinventar la rueda. He mencionado more_itertools entre otros comentados en este post. Salud. - Pylang

Tal vez 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 ceñirse a listas anidadas. - Lobo

Puedes comprobar if hasattr(x, '__iter__') en lugar de importar / comparar Iterable y eso también excluirá las cadenas. - Ryan Allen

el código anterior no parece funcionar si una de las listas anidadas tiene una lista de cadenas. [1, 2, [3, 4], [4], [], 9, 9.5, 'ssssss', ['str', 'sss', 'ss'], [3, 4, 5]] salida: - [1, 2, 3, 4, 4, 9, 9.5, 'ssssss', 3, 4, 5] - soleadoX

Si desea aplanar una estructura de datos en la que no sabe qué tan profundo está anidado, puede usar iteration_utilities.deepflatten1

>>> 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 un generador, por lo que debe enviar el resultado a un list o iterar explícitamente sobre él.


Para aplanar solo un nivel y si cada uno de los elementos es iterable en sí mismo, también puede usar iteration_utilities.flatten que en sí mismo es solo una fina envoltura alrededor 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]

Solo para agregar algunos tiempos (según la respuesta de Nico Schlömer que no incluyó la función presentada en esta respuesta):

enter image description here

Es una gráfica logarítmica para adaptarse a la amplia gama de valores abarcados. Para el razonamiento cualitativo: cuanto más bajo, 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 el itertools.chain.from_iterable, iteration_utilities.deepflatten o la comprensión anidada tienen un rendimiento razonable con itertools.chain.from_iterable siendo el más rápido (como ya lo notó 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 Descargo de responsabilidad: soy el autor de esa biblioteca

Respondido 18 Abr '18, 22:04

sum ya no funciona en secuencias arbitrarias, ya que comienza con 0, haciendo functools.reduce(operator.add, sequences) el reemplazo (¿no nos alegra que lo hayan eliminado? reduce de incorporados?). Cuando se conocen los tipos, puede ser más rápido de usar type.__add__. - Yann Vernier

@YannVernier Gracias por la información. Pensé que ejecuté estos puntos de referencia en Python 3.6 y funcionó con sum. ¿Sabe en qué versiones de Python dejó de funcionar? - MSeifert

Estaba algo equivocado. 0 es solo el valor inicial predeterminado, por lo que funciona si se usa el Argumento de inicio para comenzar con una lista vacía ... pero sigue siendo cadenas de casos especiales y me dice que use join. Esta implementando foldl en lugar de foldl1. El mismo problema aparece en 2.7. - Yann Vernier

Retiro mi declaración. 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.

>>> 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

¡La versión de suma todavía se está ejecutando durante más de un minuto y aún no ha terminado de procesarse!

Para listas medianas:

>>> 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

Usando listas pequeñas y timeit: number = 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

Respondido el 23 de diciembre de 13 a las 09:12

para una lista verdaderamente minúscula, por ejemplo, una con 3 sublistas, tal vez, pero dado que el rendimiento de la suma va con O (N ** 2) mientras que la comprensión de la lista va con O (N), solo aumentar un poco la lista de entrada revertirá las cosas - - de hecho, la LC será "infinitamente más rápida" que la suma en el límite a medida que N crece. Fui responsable de diseñar la suma y hacer su primera implementación en el tiempo de ejecución de Python, y todavía me gustaría haber encontrado una manera de restringirlo de manera efectiva a sumar números (en lo que es realmente bueno) y bloquear la "molestia atractiva" que ofrece a las personas. que quieren "sumar" listas ;-). - alex martelli

Parece haber una confusión con operator.add! Cuando agrega dos listas juntas, el término correcto para eso es concat, no agregar. operator.concat es lo que necesitas usar.

Si estás pensando en funcional, es tan fácil como esto:

>>> 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)

Verá que reduce respeta el tipo de secuencia, por lo que cuando proporciona una tupla, obtiene una tupla. Probemos con una lista:

>>> list2d = [[1, 2, 3],[4, 5, 6], [7], [8, 9]]
>>> reduce(operator.concat, list2d)
[1, 2, 3, 4, 5, 6, 7, 8, 9]

Ajá, recuperas una lista.

¿Qué tal el rendimiento?

>>> 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 es bastante rápido! Pero no hay comparación para reducir con 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

contestado el 09 de mayo de 19 a las 05:05

Hmm para ser justos, el segundo ejemplo debería ser la lista también (¿o la primera tupla?) - Sr_y_Sra_D

El uso de insumos tan pequeños no es una comparación justa. Para 1000 secuencias de longitud 1000, obtengo 0.037 segundos para list(chain.from_iterable(...)) y 2.5 segundos para reduce(concat, ...). El problema es ese reduce(concat, ...) tiene tiempo de ejecución cuadrático, mientras que chain es lineal. - kayaxnumx

¿Por qué usas extender?

reduce(lambda x, y: x+y, l)

Esto debería funcionar bien.

Respondido el 31 de diciembre de 14 a las 12:12

para python3 from functools import reduce - Andorov

Lo siento, es muy lento ver el resto de respuestas - Sr_y_Sra_D

Esta es, con mucho, la solución más fácil de entender pero corta que funciona en Python 2 y 3. Me doy cuenta de que muchas personas de Python están en el procesamiento de datos donde hay grandes cantidades de datos para procesar y, por lo tanto, se preocupan mucho por la velocidad, pero está escribiendo un script de shell y solo tiene unas pocas docenas de elementos en algunas sublistas, entonces esto es perfecto. - Asfa y Qazi

Considere instalar el more_itertools paquete.

> pip install more_itertools

Se envía con una implementación para flatten (fuente, A partir de la recetas de herramientas):

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]

Nota: como se menciona en el documentos, flatten requiere una lista de listas. Vea a continuación cómo aplanar entradas más irregulares.


A partir de la versión 2.4, puede aplanar iterables anidados más complicados con more_itertools.collapse (fuente, aportado por abarnet).

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]

Respondido el 08 de Septiembre de 20 a las 20:09

Si puede permitirse agregar un paquete a su proyecto, esta respuesta es la mejor. viddik13

falla cuando todos los elementos no están en la lista. (por ejemplo, lst = [1, [2,3]]). por supuesto, el número entero no es iterable. - Sajad.sni

Además, tenga en cuenta que la lista de cadenas se acoplará a una lista de caracteres: viddik13

La razón por la que su función no funcionó es porque el ampliar extiende una matriz en el lugar y no la devuelve. Todavía puede devolver x de lambda, usando algo como esto:

reduce(lambda x,y: x.extend(y) or x, l)

Nota: extender es más eficiente que + en listas.

respondido 19 nov., 19:16

extend se usa mejor como newlist = [], extend = newlist.extend, for sublist in l: extend(l) ya que evita la sobrecarga (bastante grande) de la lambda, la búsqueda de atributos en x, y la or. - agf

para python 3 agregar from functools import reduce - Markus holandés

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]

respondido 28 nov., 16:08

def flatten(l, a=None): if a is None: a = [] [...] - Poike

Versión recursiva

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]

Respondido el 14 de diciembre de 18 a las 10:12

agradable, no se necesitan importaciones y está claro lo que está haciendo ... aplanar una lista, punto :) - goran b

simplemente brillante! - Sachin Sharma

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 []. En su lugar, 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:

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

Pruebas:

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]

Respondido el 10 de junio de 20 a las 18:06

en lugar de una declaración = Ninguno y si podría usar una = [] - Nikita cohete

La respuesta aceptada no funcionó para mí cuando se trataba de listas de texto de longitudes variables. Aquí hay un enfoque alternativo que funcionó para mí.

l = ['aaa', 'bb', 'cccccc', ['xx', 'yyyyyyy']]

Respuesta aceptada que hizo no trabajo:

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']

Nueva propuesta de solución que sí logró trabaja para mi:

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']

Respondido 12 Oct 18, 02:10

matplotlib.cbook.flatten() funcionará para listas anidadas incluso si se anidan más profundamente que el ejemplo.

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))

Resultado:

[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

Esto es 18 veces más rápido que el subrayado ._. Aplanar:

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

Respondido 20 Jul 18, 19:07

Lo siguiente me parece lo más simple:

>>> 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]

Respondido 05 Jul 17, 06:07

No funciona para listas con diferentes dimensiones. -1 - Nurub

También se puede usar NumPy plano:

import numpy as np
list(np.array(l).flat)

Editar 11/02/2016: solo funciona cuando las sublistas tienen dimensiones idénticas.

respondido 02 nov., 16:09

¿Sería esa la solución óptima? - retrocódigo

Personalmente, me resulta difícil recordar todos los módulos que debían importarse. Por lo tanto, tiendo a usar un método simple, aunque no sé cómo se compara su rendimiento con otras respuestas.

Si solo desea aplanar listas anidadas, lo siguiente hará el trabajo:

def flatten(lst):
    for item in lst:
        if isinstance(item, list):
            yield from flatten(item)
        else:
            yield item

# test case:
a =[0, [], "fun", [1, 2, 3], [4, [5], 6], 3, [7], [8, 9]]
list(flatten(a))
# output 
# [0, 'fun', 1, 2, 3, 4, 5, 6, 3, 7, 8, 9]

Sin embargo, si desea aplanar una lista de iterables (lista y / o tuplas), también puede hacer el trabajo con una ligera modificación:

from collections.abc import Iterable
def flatten(lst):
    for item in lst:
        if isinstance(item,Iterable) and not isinstance(item,str):
            yield from flatten(item)
        else:
            yield item

# test case:
a =[0, [], "fun", (1, 2, 3), [4, [5], (6)], 3, [7], [8, 9]]
list(flatten(a))
# output: 
# [0, 'fun', 1, 2, 3, 4, 5, 6, 3, 7, 8, 9]

Respondido el 25 de diciembre de 20 a las 06:12

Me gusta este, funciona sin importar cuántas dimensiones tenga la lista. - Max

from nltk import flatten

l = [[1, 2, 3], [4, 5, 6], [7], [8, 9]]
flatten(l)

La ventaja de esta solución sobre la mayoría de las demás aquí es que si tiene una lista como:

l = [1, [2, 3], [4, 5, 6], [7], [8, 9]]

mientras que la mayoría de las otras soluciones arrojan un error, esta solución los maneja.

Respondido el 30 de Septiembre de 19 a las 11:09

La pregunta establece una "lista de listas", pero su lista de ejemplo incluye un elemento que no forma parte de la lista. La mayoría de las otras soluciones se apegan a la pregunta original. Su solución resuelve un problema más amplio, pero también requiere un paquete Python no base (nltk) que debe instalarse primero. - simonobo

Nota:: A continuación se aplica a Python 3.3+ porque usa yield_from. six también es un paquete de terceros, aunque es estable. Alternativamente, podrías usar sys.version.


En el caso de los obj = [[1, 2,], [3, 4], [5, 6]], todas las soluciones aquí son buenas, incluida la comprensión de listas y itertools.chain.from_iterable.

Sin embargo, considere este caso un poco más complejo:

>>> obj = [[1, 2, 3], [4, 5], 6, 'abc', [7], [8, [9, 10]]]

Hay varios problemas aquí:

  • Un elemento, 6, es solo un escalar; no es iterable, por lo que las rutas anteriores fallarán aquí.
  • Un elemento, 'abc', is técnicamente iterable (todos strs son). Sin embargo, leyendo un poco entre líneas, no querrá tratarlo como tal, sino como un solo elemento.
  • El elemento final, [8, [9, 10]] es en sí mismo un iterable anidado. Comprensión básica de listas y chain.from_iterable sólo extraiga "1 nivel hacia abajo".

Puede remediar esto de la siguiente manera:

>>> 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]

Aquí, verifica que el subelemento (1) sea iterable con Iterable, un ABC de itertools, pero también quiero asegurarse de que (2) el elemento es no "como una cuerda".

Respondido el 19 de junio de 18 a las 20:06

Si todavía está interesado en la compatibilidad con Python 2, cambie yield from a una for bucle, p. ej. for x in flatten(i): yield x - Pylang

Puedes usar numpy:
flat_list = list(np.concatenate(list_of_list))

Respondido 24 Jul 18, 10:07

Esto también funciona para listas numéricas, de cadenas y mixtas: Nitin

Falla para datos anidados de manera desigual, como [1, 2, [3], [[4]], [5, [6]]] - EL_DON

puedes usar list extend método, muestra ser el más rápido:

flat_list = []
for sublist in l:
    flat_list.extend(sublist)

actuación:

import functools
import itertools
import numpy
import operator
import perfplot



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 extend(a):
    n = []

    list(map(n.extend, a))

    return n 


perfplot.show(
    setup=lambda n: [list(range(10))] * n,
    kernels=[
        functools_reduce_iconcat, extend,itertools_chain, numpy_flat
        ],
    n_range=[2**k for k in range(16)],
    xlabel='num lists',
    )

salida: enter image description here

Respondido el 28 de enero de 20 a las 13:01

Este es un juego con el código del cartel original. (No estaba lejos)

f = []
list(map(f.extend, l))

Respondido el 10 de enero de 21 a las 19:01

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() or 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

Puede obtener más información aquí en los documentos. numpy.concatenar y numpy.ravel

Respondido 27 Oct 16, 04:10

No funciona para listas anidadas de manera desigual como [1, 2, [3], [[4]], [5, [6]]] - EL_DON

La solución más rápida que he encontrado (para una lista grande de todos modos):

import numpy as np
#turn list into an array and flatten()
np.array(l).flatten()

¡Hecho! Por supuesto, puede convertirlo de nuevo en una lista ejecutando list (l)

respondido 28 nov., 16:21

Esto es incorrecto, aplanar reducirá las dimensiones de la nd matriz a uno, pero no concatenará las listas internas como una. - Ando Jurai

Código simple para underscore.py ventilador de paquete

from underscore import _
_.flatten([[1, 2, 3], [4, 5, 6], [7], [8, 9]])
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

Resuelve todos los problemas de aplanar (ningún elemento de lista o anidamiento complejo)

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]

Puede instalar underscore.py con pip

pip install underscore.py

respondido 25 mar '17, 05:03

Del mismo modo, puede utilizar Pydash. Encuentro que esta versión es mucho más legible que la comprensión de la lista o cualquier otra respuesta. - gliemezis

Esto es muy lento. - Nico Schlömer

¿Por qué tiene un módulo llamado _? Eso parece un mal nombre. Ver stackoverflow.com/a/5893946/6605826 - EL_DON

@EL_DON: De la página Léame de underscore.py "Underscore.py es un puerto de Python de una excelente biblioteca de JavaScript, underscore.js". Creo que es la razón de este nombre. Y sí, no es un buen nombre para Python. VuAnh

def flatten(alist):
    if alist == []:
        return []
    elif type(alist) is not list:
        return [alist]
    else:
        return flatten(alist[0]) + flatten(alist[1:])

Respondido 08 ago 17, 15:08

Falla para python2.7 para la lista anidada de ejemplo en la pregunta: [[1, 2, 3], [4, 5, 6], [7], [8, 9]] - EL_DON

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.