¿Incluir la comprensión en una lista anidada?

275

Tengo esta lista anidada:

l = [['40', '20', '10', '30'], ['20', '20', '20', '20', '20', '30', '20'], ['30', '20', '30', '50', '10', '30', '20', '20', '20'], ['100', '100'], ['100', '100', '100', '100', '100'], ['100', '100', '100', '100']]

Ahora, lo que quiero hacer es convertir cada elemento de una lista en flotante. Mi solución es esta:

newList = []
for x in l:
  for y in x:
    newList.append(float(y))

Pero, ¿se puede hacer esto usando la comprensión de listas anidadas, verdad?

lo que he hecho es:

[float(y) for y in x for x in l]

Pero entonces el resultado es un montón de cientos con la suma de 2400.

cualquier solución, se agradecería mucho una explicación. ¡Gracias!

2
  • 19
    ¿ También quieres aplanar tu lista? 6 de agosto de 2013 a las 6:05
  • @GregHewgill: OP no respondió, pero según la respuesta que aceptaron, parece que querían mantener el anidamiento como está.
    smci
    2 feb 2018 a las 20:39
386

Así es como haría esto con una comprensión de lista anidada:

[[float(y) for y in x] for x in l]

Esto le daría una lista de listas, similar a la que comenzó, excepto con flotantes en lugar de cadenas. Si desea una lista plana, la usaría [float(y) for x in l for y in x].

0
272

A continuación, se explica cómo convertir un bucle for anidado en una lista de comprensión anidada:

ingrese la descripción de la imagen aquí

Así es como funciona la comprensión de listas anidadas:

            l a b c d e f
            ↓ ↓ ↓ ↓ ↓ ↓ ↓
In [1]: l = [ [ [ [ [ [ 1 ] ] ] ] ] ]
In [2]: for a in l:
   ...:     for b in a:
   ...:         for c in b:
   ...:             for d in c:
   ...:                 for e in d:
   ...:                     for f in e:
   ...:                         print(float(f))
   ...:                         
1.0

In [3]: [float(f)
         for a in l
   ...:     for b in a
   ...:         for c in b
   ...:             for d in c
   ...:                 for e in d
   ...:                     for f in e]
Out[3]: [1.0]

Para tu caso, será algo así.

In [4]: new_list = [float(y) for x in l for y in x]
6
  • 29
    ¡Súper útil! Deja en claro que los bucles (de arriba a abajo) están ordenados de izquierda a derecha en el generador. Esto no es obvio ya que en algunos (f(x) for x in l)lugares la segunda línea del equivalente de bucle for a la izquierda. 11/01/18 a las 18:11
  • 3
    Esta parece ser la única explicación que me está afectando, ¡gracias! 20/03/20 a las 2:33
  • 3
    Como novato en Python y proveniente de Java, luché durante horas para comprender la comprensión anidada y busqué en varios lugares. Pero hiciste mi velada. ¡¡¡Gracias una tonelada!!!
    vv88
    23/11/20 a las 19:32
  • @ user48956 sí, no creo que sea muy intuitivo tener una comprensión de lista anidada como una línea. tal uso sería un antipatrón en mi opinión.
    dtc
    14 abr a las 23:19
  • esa es una explicación bonita y clara. ¡Gracias! 12 de mayo a las 8:21
54
>>> l = [['40', '20', '10', '30'], ['20', '20', '20', '20', '20', '30', '20'], ['30', '20', '30', '50', '10', '30', '20', '20', '20'], ['100', '100'], ['100', '100', '100', '100', '100'], ['100', '100', '100', '100']]
>>> new_list = [float(x) for xs in l for x in xs]
>>> new_list
[40.0, 20.0, 10.0, 30.0, 20.0, 20.0, 20.0, 20.0, 20.0, 30.0, 20.0, 30.0, 20.0, 30.0, 50.0, 10.0, 30.0, 20.0, 20.0, 20.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0, 100.0]
0
49

No estoy seguro de cuál es el resultado deseado, pero si está utilizando la comprensión de listas, el orden sigue el orden de los bucles anidados, que tiene al revés. Entonces obtuve lo que creo que quieres con:

[float(y) for x in l for y in x]

El principio es: use el mismo orden que usaría para escribirlo como bucles for anidados.

3
  • esta debería ser la respuesta, ya que algunas veces no queremos poner entre corchetes el iteratool
    zinking
    19/04/2017 a las 1:48
  • 2
    Puede que esta no sea la respuesta correcta, ya que genera una lista no anidada, pero es lo que estaba buscando, especialmente el principio . ¡Gracias! 29 de septiembre de 2017 a las 10:52
  • Esto no es correcto: debería tener un corchete alrededor [float(y)] 5 de marzo a las 15:36
7

Tenía un problema similar que resolver, así que me encontré con esta pregunta. Hice una comparación de rendimiento de la respuesta de Andrew Clark y Narayan que me gustaría compartir.

La principal diferencia entre dos respuestas es cómo se iteran sobre las listas internas. Uno de ellos usa un mapa incorporado , mientras que otro usa la comprensión de listas. La función de mapa tiene una ligera ventaja de rendimiento con respecto a su comprensión de lista equivalente si no requiere el uso de lambdas . Entonces, en el contexto de esta pregunta, mapdebería funcionar un poco mejor que la comprensión de listas.

Hagamos un punto de referencia de rendimiento para ver si es realmente cierto. Usé la versión 3.5.0 de Python para realizar todas estas pruebas. En el primer conjunto de pruebas, me gustaría mantener los elementos por lista en 10 y variar el número de listas de 10 a 100.000

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,10))]*10]"
>>> 100000 loops, best of 3: 15.2 usec per loop   
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,10))]*10]"
>>> 10000 loops, best of 3: 19.6 usec per loop 

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,10))]*100]"
>>> 100000 loops, best of 3: 15.2 usec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,10))]*100]"
>>> 10000 loops, best of 3: 19.6 usec per loop 

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,10))]*1000]"
>>> 1000 loops, best of 3: 1.43 msec per loop   
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,10))]*1000]"
>>> 100 loops, best of 3: 1.91 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,10))]*10000]"
>>> 100 loops, best of 3: 13.6 msec per loop   
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,10))]*10000]"
>>> 10 loops, best of 3: 19.1 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,10))]*100000]"
>>> 10 loops, best of 3: 164 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,10))]*100000]"
>>> 10 loops, best of 3: 216 msec per loop

ingrese la descripción de la imagen aquí

En el siguiente conjunto de pruebas, me gustaría aumentar el número de elementos por lista a 100 .

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,100))]*10]"
>>> 10000 loops, best of 3: 110 usec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,100))]*10]"
>>> 10000 loops, best of 3: 151 usec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,100))]*100]"
>>> 1000 loops, best of 3: 1.11 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,100))]*100]"
>>> 1000 loops, best of 3: 1.5 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,100))]*1000]"
>>> 100 loops, best of 3: 11.2 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,100))]*1000]"
>>> 100 loops, best of 3: 16.7 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,100))]*10000]"
>>> 10 loops, best of 3: 134 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,100))]*10000]"
>>> 10 loops, best of 3: 171 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,100))]*100000]"
>>> 10 loops, best of 3: 1.32 sec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,100))]*100000]"
>>> 10 loops, best of 3: 1.7 sec per loop

ingrese la descripción de la imagen aquí

Demos un paso valiente y modifiquemos el número de elementos en las listas para que sea 1000

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,1000))]*10]"
>>> 1000 loops, best of 3: 800 usec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,1000))]*10]"
>>> 1000 loops, best of 3: 1.16 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,1000))]*100]"
>>> 100 loops, best of 3: 8.26 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,1000))]*100]"
>>> 100 loops, best of 3: 11.7 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,1000))]*1000]"
>>> 10 loops, best of 3: 83.8 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,1000))]*1000]"
>>> 10 loops, best of 3: 118 msec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,1000))]*10000]"
>>> 10 loops, best of 3: 868 msec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,1000))]*10000]"
>>> 10 loops, best of 3: 1.23 sec per loop

>>> python -m timeit "[list(map(float,k)) for k in [list(range(0,1000))]*100000]"
>>> 10 loops, best of 3: 9.2 sec per loop
>>> python -m timeit "[[float(y) for y in x] for x in [list(range(0,1000))]*100000]"
>>> 10 loops, best of 3: 12.7 sec per loop

ingrese la descripción de la imagen aquí

De esta prueba podemos concluir que maptiene un beneficio de rendimiento sobre la comprensión de listas en este caso. Esto también es aplicable si está intentando lanzar a into str. Para una pequeña cantidad de listas con menos elementos por lista, la diferencia es insignificante. Para listas más grandes con más elementos por lista, uno podría querer usar en maplugar de la comprensión de la lista, pero depende totalmente de las necesidades de la aplicación.

Sin embargo, personalmente considero que la comprensión de listas es más legible e idiomática que map. Es un estándar de facto en Python. Por lo general, las personas son más competentes y cómodas (especialmente los principiantes) en el uso de la comprensión de listas que map.

7

Como llegué un poco tarde aquí, pero quería compartir cómo funciona realmente la comprensión de listas, especialmente la comprensión de listas anidadas:

New_list= [[float(y) for x in l]

es en realidad lo mismo que:

New_list=[]
for x in l:
    New_list.append(x)

Y ahora comprensión de lista anidada:

[[float(y) for y in x] for x in l]

es igual que;

new_list=[]
for x in l:
    sub_list=[]
    for y in x:
        sub_list.append(float(y))

    new_list.append(sub_list)

print(new_list)

producción:

[[40.0, 20.0, 10.0, 30.0], [20.0, 20.0, 20.0, 20.0, 20.0, 30.0, 20.0], [30.0, 20.0, 30.0, 50.0, 10.0, 30.0, 20.0, 20.0, 20.0], [100.0, 100.0], [100.0, 100.0, 100.0, 100.0, 100.0], [100.0, 100.0, 100.0, 100.0]]
3

Si no le gustan las comprensiones de listas anidadas, también puede hacer uso de la función de mapa ,

>>> from pprint import pprint

>>> l = l = [['40', '20', '10', '30'], ['20', '20', '20', '20', '20', '30', '20'], ['30', '20', '30', '50', '10', '30', '20', '20', '20'], ['100', '100'], ['100', '100', '100', '100', '100'], ['100', '100', '100', '100']] 

>>> pprint(l)
[['40', '20', '10', '30'],
['20', '20', '20', '20', '20', '30', '20'],
['30', '20', '30', '50', '10', '30', '20', '20', '20'],
['100', '100'],
['100', '100', '100', '100', '100'],
['100', '100', '100', '100']]

>>> float_l = [map(float, nested_list) for nested_list in l]

>>> pprint(float_l)
[[40.0, 20.0, 10.0, 30.0],
[20.0, 20.0, 20.0, 20.0, 20.0, 30.0, 20.0],
[30.0, 20.0, 30.0, 50.0, 10.0, 30.0, 20.0, 20.0, 20.0],
[100.0, 100.0],
[100.0, 100.0, 100.0, 100.0, 100.0],
[100.0, 100.0, 100.0, 100.0]]
2
  • Su código genera objetos de mapa en lugar de listas: >>> float_l = [map(float, nested_list) for nested_list in l] [[<map at 0x47be9b0>], [<map at 0x47be2e8>], [<map at 0x47be4a8>], [<map at 0x47beeb8>], [<map at 0x484b048>], [<map at 0x484b0b8>]] pero agregar una llamada adicional a la lista funciona como se esperaba: >>> float_l = [list(map(float, nested_list)) for nested_list in l] 17 de marzo de 2017 a las 15:49
  • @pixelperfect que se debe al cambio ( desinformado ..) python3para devolver los generadores fuera de comprensión. 4 de marzo de 2018 a las 4:56
3

Este problema se puede resolver sin utilizar el bucle for, para ello bastará con un código de una sola línea. El uso de Nested Map con la función lambda también funcionará aquí.

l = [['40', '20', '10', '30'], ['20', '20', '20', '20', '20', '30', '20'], ['30', '20', '30', '50', '10', '30', '20', '20', '20'], ['100', '100'], ['100', '100', '100', '100', '100'], ['100', '100', '100', '100']]

map(lambda x:map(lambda y:float(y),x),l)

Y la lista de salida sería la siguiente:

[[40.0, 20.0, 10.0, 30.0], [20.0, 20.0, 20.0, 20.0, 20.0, 30.0, 20.0], [30.0, 20.0, 30.0, 50.0, 10.0, 30.0, 20.0, 20.0, 20.0], [100.0, 100.0], [100.0, 100.0, 100.0, 100.0, 100.0], [100.0, 100.0, 100.0, 100.0]]
2
  • 1
    ¿Lambdas tiene algún beneficio de rendimiento sobre las soluciones de @Andrew Clark o Harry Binswanger (la comprensión de la lista más básica)? Como las lambdas parecen más difíciles de leer. 2 oct 2017 a las 13:45
  • En casi cualquier otro lenguaje de programación general, usaríamos encadenado map, en la línea de lo que muestra. Pero mire lo feo que es en Python, especialmente dado que el resultado anterior no es suficiente: es un generador, no una lista. Necesita agregar aún más texto estándar list(list( .. ) )para completar la imagen. Pruebe fluentpybibliotecas similares para escapar de esta trampa hasta cierto punto. 5 de marzo a las 15:31
2

Sí, puede hacerlo con dicho código:

l = [[float(y) for y in x] for x in l]
1
  • [float(y) for y in x for x in l] esto resultaría en un montón de cientos con una suma de 2400. 6 de agosto de 2013 a las 6:09
0

En mi opinión, la mejor manera de hacer esto es usar el itertoolspaquete de python .

>>>import itertools
>>>l1 = [1,2,3]
>>>l2 = [10,20,30]
>>>[l*2 for l in itertools.chain(*[l1,l2])]
[2, 4, 6, 20, 40, 60]
0

Sí, puede hacer lo siguiente.

[[float(y) for y in x] for x in l]
-2
    deck = [] 
    for rank in ranks:
        for suit in suits:
            deck.append(('%s%s')%(rank, suit))

Esto se puede lograr usando la comprensión de listas:

[deck.append((rank,suit)) for suit in suits for rank in ranks ]
4
  • 1
    Esto no parece abordar la pregunta de arriba en absoluto. Tenga en cuenta que todo lo que se publique como respuesta debe ser un intento de responder a la pregunta en la que se publica. 22/03/18 a las 11:46
  • Si bien este fragmento de código puede resolver la pregunta, incluir una explicación realmente ayuda a mejorar la calidad de su publicación. Recuerde que está respondiendo a la pregunta para los lectores en el futuro, y es posible que esas personas no conozcan los motivos de su sugerencia de código. Por favor, también trate de no llenar su código con comentarios explicativos, ¡esto reduce la legibilidad tanto del código como de las explicaciones!
    Filnor
    22/03/18 a las 11:46
  • Anidado para bucle usando comprensión de lista, 22/03/18 a las 11:56
  • 1
    Ok, aparentemente, este es un intento de responder la pregunta. Sin embargo, esto parece ser un escenario completamente diferente al de OP, ni siquiera maneja listas anidadas como entrada, e incluso si cambia esa sugerencia es más o menos lo que OP ya intentó. Además, no veo cómo un ejemplo sobre tarjetas ayude cuando la pregunta es sobre convertir cadenas en flotantes. 22/03/18 a las 12:02