Forma pitónica de devolver la lista de cada enésimo elemento en una lista más grande

171

Digamos que tenemos una lista de números del 0 al 1000. ¿Existe una manera pitónica / eficiente de producir una lista del primer y cada 10º elemento, es decir [0, 10, 20, 30, ... ]?

Sí, puedo hacer esto usando un bucle for, pero me pregunto si hay una forma más ordenada de hacerlo, ¿tal vez incluso en una línea?

p.brown
fuente

Respuestas:

290
>>> lst = list(range(165))
>>> lst[0::10]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]

Tenga en cuenta que esto es aproximadamente 100 veces más rápido que hacer un bucle y verificar un módulo para cada elemento:

$ python -m timeit -s "lst = list(range(1000))" "lst1 = [x for x in lst if x % 10 == 0]"
1000 loops, best of 3: 525 usec per loop
$ python -m timeit -s "lst = list(range(1000))" "lst1 = lst[0::10]"
100000 loops, best of 3: 4.02 usec per loop
Ned Deily
fuente
44
Claro, las comprensiones de listas son más poderosas en general. OTOH, la pregunta plantea una lista existente y, en ese caso, un segmento funciona bien.
Ned Deily
Comenté sobre esto a continuación en la lista de respuestas de comprensión. Tenga cuidado con "if x% 10 == 0". Solo funciona con este ejemplo de lista en particular, pero si la lista de entrada es, por ejemplo, l = rango (0,1000,2), no se extraerá cada décimo elemento.
Andre Miller
12
@Andre: muy cierto. Entonces, este es un ejemplo de una función de lenguaje humilde, el operador de corte, que resulta en este caso (1) para que sea más fácil obtener los resultados correctos; (2) da como resultado una expresión más concisa; y (3) pasa a ser 2 órdenes de magnitud más rápido. (1) es, con mucho, la preocupación más importante, por supuesto, pero, gracias al cuidadoso diseño e implementación del lenguaje, obtienes los tres por el precio de 1. Buena pregunta y respuestas.
Ned Deily
2
El 0es redundante en l[0::10]. l[::10]Es más legible, menos confuso.
Konstantin Schubert
Estoy sorprendido por la comparación de rendimiento 0.5 segundos para la comprensión de la lista y 0.4 segundos para el corte de la lista. Parece muy lento, ¿por qué el corte de listas necesita 100 mil bucles para una lista de tamaño 1 mil?
Damo
58
  1. source_list[::10] es lo más obvio, pero esto no funciona para ningún iterable y no es eficiente en memoria para listas grandes.
  2. itertools.islice(source_sequence, 0, None, 10) funciona para cualquier iterable y es eficiente en memoria, pero probablemente no sea la solución más rápida para una lista grande y un gran paso.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
fuente
1
+1 Mejor respuesta, OMI. Las tres propuestas son soluciones generales (es decir, tomar la lista de fuentes como un hecho). La solución del generador (3.) es agradable, ya que se filtra en el índice de la lista fuente. Probablemente sea tan eficiente en memoria como 2. Tanto los índices como la lista de resultados son generadores y, por lo tanto, se construyen perezosamente, lo que también es probablemente el más rápido si no necesita la lista de resultados en un solo fragmento. Solo si la lista fuente pudiera ser un generador, iría con el lenguaje "item, i in enumerate (l)" de Paul, ya que no hay len () de un generador. Por cierto, ¿qué tipo de iterable no funcionaría con 1.? ¿Generadores?
ThomasH
Iterable = objeto con el método __iter __ () que devuelve el iterador (objeto con el método next ())
Denis Otkidach
24

Puede usar el operador de división de esta manera:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick Dandoulakis
fuente
¿Cómo obtener cada segundo elemento a partir del tercero?
user1993
44
@ user1993L[2::2]
Nick Dandoulakis
19

Del manual: s[i:j:k] slice of s from i to j with step k

li = range(100)
sub = li[0::10]

>>> sub
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
tuergeist
fuente
13
newlist = oldlist[::10]

Esto selecciona cada décimo elemento de la lista.

David Z
fuente
4

¿Por qué no usar también un parámetro de paso de la función de rango para obtener:

l = range(0, 1000, 10)

A modo de comparación, en mi máquina:

H:\>python -m timeit -s "l = range(1000)" "l1 = [x for x in l if x % 10 == 0]"
10000 loops, best of 3: 90.8 usec per loop
H:\>python -m timeit -s "l = range(1000)" "l1 = l[0::10]"
1000000 loops, best of 3: 0.861 usec per loop
H:\>python -m timeit -s "l = range(0, 1000, 10)"
100000000 loops, best of 3: 0.0172 usec per loop
camioneta
fuente
3
@SilentGhost: Eso es cierto, pero como esta es una pregunta para principiantes, la función de rango podría ser lo que realmente quieren hacer, así que creo que es una respuesta válida. (Aunque el límite superior debería ser 1001, no 1000)
Scott Griffiths
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

fuente
1
¿por qué tiene la cláusula if cuando el rango (0, 1001, 10) ya solo toma cada décimo elemento?
Autoplectic
44
El mismo comentario aquí, esto no resuelve el problema más general de "Forma pitónica de devolver la lista de cada enésimo elemento en una lista más grande", su solución depende del hecho de que los valores de la lista de ejemplo son de 0 a 1000 y solo extrae elementos fuera de la lista que tiene un valor divisible por 10 en lugar de cada décimo elemento.
Andre Miller
1
Bueno, el OP escribe: "tenemos una lista de números del cero al 1000". Entonces no tiene la necesidad de una solución general.
1
Él escribe 'Di que tenemos ...', lo que implica que es solo un ejemplo. Si realmente quisiera cada décimo número de una lista de cero a 1000, entonces la respuesta sería rango (0,1001,10) o algo similar.
Andre Miller
1

Aquí hay una mejor implementación de una comprensión de la lista "cada 10 elementos", que no utiliza el contenido de la lista como parte de la prueba de membresía:

>>> l = range(165)
>>> [ item for i,item in enumerate(l) if i%10==0 ]
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120, 130, 140, 150, 160]
>>> l = list("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
>>> [ item for i,item in enumerate(l) if i%10==0 ]
['A', 'K', 'U']

Pero esto sigue siendo mucho más lento que simplemente usar el corte de listas.

PaulMcG
fuente
-9

Las comprensiones de listas están hechas exactamente para eso:

smaller_list = [x for x in range(100001) if x % 10 == 0]

Puede obtener más información sobre ellos en la documentación oficial de Python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
fuente
El límite superior debe ser 1000, no 10000. Su solución no incluye el límite superior de 1000 ya que el rango se detiene en 999. +1 para el enlace a la comprensión de la lista.
19
En realidad, esto no saca cada décimo elemento, saca todos los elementos que tienen un valor divisible por 10. En este ejemplo en particular, es lo mismo, pero podría no serlo.
Andre Miller