¿Comprensión en iterables anidados?

11 minutos de lectura

Avatar de usuario de Boy Pasmo
chico pasmo

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

Quiero convertir cada elemento en l a float. tengo este codigo:

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

¿Cómo puedo resolver el problema con una comprensión de lista anidada?


Ver también: ¿Cómo puedo obtener un resultado plano de una lista de comprensión en lugar de una lista anidada?

  • además ¿Quieres aplanar tu lista?

    – Greg Hewgill

    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 de febrero de 2018 a las 20:39


  • Ver también: treyhunner.com/2015/12/python-list-comprehensions-now-in-color

    – Karl Knechtel

    18 de febrero a las 21:32

  • [float(y) for y in x for x in l] no funciona solo. Solo funciona debido a la definición existente de x sobrante del código anterior.

    – Karl Knechtel

    2 ago a las 21:43

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, entonces usaría [float(y) for x in l for y in x].

Avatar de usuario de Rahul
Raúl

Aquí se explica cómo convertir un bucle for anidado a una comprensión de lista 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 como esto.

In [4]: new_list = [float(y) for x in l for y in x]

  • ¡Súper útil! Deja en claro que los bucles (de arriba a abajo) se ordenan de izquierda a derecha en el generador. Esto no es obvio ya que en (f(x) for x in l) coloca la segunda línea del equivalente del bucle for a la izquierda.

    – usuario48956

    11 de enero de 2018 a las 18:11

  • @ user48956 sí, no creo que sea muy intuitivo tener una comprensión de lista anidada como una sola línea. tal uso sería un imo antipatrón

    – dtc

    14 abr 2021 a las 23:19

  • Impresionante truco hombre!

    – Muhammad Umar Amanat

    17 abr a las 17:43

  • ¡Una de las mejores respuestas que he visto en StackOverflow!

    – forzagreen

    15 de septiembre a las 13:33

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. Así que 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.

  • esta debería ser la respuesta, ya que algunas veces no queremos poner entre corchetes la iteratool

    – zinking

    19 de abril de 2017 a las 1:48

  • esta puede no ser la respuesta correcta ya que genera una lista no anidada, pero es lo que estaba buscando, especialmente el principio. ¡Gracias!

    – Rodrigo E. Príncipe

    29 de septiembre de 2017 a las 10:52

  • Esto no es correcto: debe tener soporte alrededor [float(y)]

    – Proyectos de la costa oeste

    5 de marzo de 2021 a las 15:36

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

Avatar de usuario de Aaditya Ura
Aaditya Ura

Quería compartir cómo funciona realmente la comprensión de listas, especialmente para las comprensiones de listas anidadas:

new_list= [float(x) for x in l]

en realidad es lo mismo que:

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

Y ahora para la comprensión de la lista anidada:

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

es lo mismo 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]]

Avatar de usuario de Sohaib Farooqi
Sohaib Farooqui

Tuve un problema similar para 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 iteran sobre las listas internas. Uno de ellos usa incorporado mapa, mientras que otros utilizan la comprensión de listas. La función de mapa tiene una ligera ventaja de rendimiento en comparación con su comprensión de lista equivalente si no requiere el uso de lambdas. Así que en el contexto de esta pregunta map debería funcionar un poco mejor que la comprensión de listas.

Hagamos un benchmark de rendimiento para ver si realmente es 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 para ser 10 y variar el número de listas de 10-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 próximo conjunto de pruebas, me gustaría aumentar el número de elementos por lista para 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 sean 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 estas pruebas podemos concluir que map tiene un beneficio de rendimiento sobre la comprensión de listas en este caso. Esto también es aplicable si está intentando lanzar a cualquiera int o str. Para un pequeño número 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 map en lugar de la comprensión de la lista, pero depende totalmente de las necesidades de la aplicación.

Sin embargo, personalmente encuentro 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.

Avatar de usuario de Remi Guan
Remi Guan

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

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

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

    – píxel perfecto

    17 de marzo de 2017 a las 15:49


  • @pixelperfect eso se debe a (mal informado ..) cambiar en python3 para volver generadores de comprensiones.

    – Proyectos de la costa oeste

    4 de marzo de 2018 a las 4:56


¿Ha sido útil esta solución?