Méthode pythonique pour renvoyer la liste de chaque nième élément dans une liste plus grande

171

Disons que nous avons une liste de nombres de 0 à 1000. Existe-t-il un moyen pythonique / efficace de produire une liste du premier élément et de chaque 10e élément suivant, c'est [0, 10, 20, 30, ... ]-à- dire ?

Oui, je peux le faire en utilisant une boucle for, mais je me demande s'il existe un moyen plus propre de le faire, peut-être même en une seule ligne?

p. marron
la source

Réponses:

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]

Notez que c'est environ 100 fois plus rapide que de boucler et de vérifier un module pour chaque élément:

$ 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
la source
4
Bien sûr, les compréhensions de listes sont plus puissantes en général. OTOH, la question pose une liste existante et, dans ce cas, une tranche fonctionne très bien.
Ned Deily
J'ai commenté cela ci-dessous dans les réponses de compréhension de la liste. Soyez prudent avec "si x% 10 == 0". Cela ne fonctionne qu'avec cet exemple de liste particulier, mais si la liste d'entrée est par exemple l = range (0,1000,2), elle ne sortira pas tous les 10 éléments.
Andre Miller
12
@Andre: très vrai. Voici donc un exemple d'une modeste fonctionnalité de langage, l'opérateur de tranche, qui s'avère dans ce cas (1) pour faciliter l'obtention des résultats corrects; (2) donne une expression plus concise; et (3) se trouve être 2 ordres de grandeur plus rapide. (1) est de loin la préoccupation la plus importante, bien sûr, mais, grâce à la conception et à la mise en œuvre soignées du langage, vous obtenez les trois pour le prix de 1. Belle question et réponses.
Ned Deily
2
Le 0est redondant dans l[0::10]. l[::10]est plus lisible, moins déroutant.
Konstantin Schubert
Je suis surpris par la comparaison des performances de 0,5 seconde pour la compréhension de la liste et de 0,4 s pour la tranche de liste. Cela semble très lent, pourquoi le découpage de la liste a besoin de 100 000 boucles pour une liste de 1 000!?
Damo le
58
  1. source_list[::10] est le plus évident, mais cela ne fonctionne pour aucun itérable et n'est pas efficace en mémoire pour les grandes listes.
  2. itertools.islice(source_sequence, 0, None, 10) fonctionne pour tout itérable et est efficace en mémoire, mais ce n'est probablement pas la solution la plus rapide pour les grandes listes et les grandes étapes.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
la source
1
+1 Meilleure réponse, OMI. Les trois propositions sont des solutions générales (c'est-à-dire prendre la liste des sources comme une donnée). La solution du générateur (3.) est agréable car elle filtre sur l'index de la liste source. Il est probablement aussi efficace en mémoire que 2. Les indices et la liste de résultats sont des générateurs et sont donc construits paresseusement, ce qui est probablement aussi le plus rapide si vous n'avez pas besoin de la liste de résultats en un seul morceau. Ce n'est que si la liste source pouvait être un générateur que j'irais avec l'idiome «item, i» de Paul dans enumerate (l) », car il n'y a pas de len () d'un générateur. BTW, quel type d'itérable ne fonctionnerait pas avec 1.? Générateurs?!
ThomasH
Iterable = objet avec la méthode __iter __ () retournant l'itérateur (objet avec la méthode next ())
Denis Otkidach
24

Vous pouvez utiliser l'opérateur de tranche comme ceci:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick Dandoulakis
la source
comment obtenir un article sur 2 à partir du 3?
user1993
4
@ user1993L[2::2]
Nick Dandoulakis
19

À partir du manuel: 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
la source
13
newlist = oldlist[::10]

Cela sélectionne tous les 10 éléments de la liste.

David Z
la source
4

Pourquoi ne pas simplement utiliser un paramètre d' étape de la fonction de plage pour obtenir:

l = range(0, 1000, 10)

A titre de comparaison, sur ma machine:

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
van
la source
3
@SilentGhost: C'est vrai, mais comme il s'agit d'une question pour débutant, la fonction de plage pourrait être ce qu'ils veulent vraiment faire, donc je pense que c'est une réponse valable. (Bien que la limite supérieure devrait être 1001, pas 1000)
Scott Griffiths
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

la source
1
Pourquoi avez-vous la clause if alors que range (0, 1001, 10) ne prend déjà que tous les 10 éléments?
Autoplectic
4
Même commentaire ici, cela ne résout pas le problème plus général de la "manière pythonique de renvoyer la liste de chaque nième élément dans une liste plus grande" votre solution dépend du fait que les valeurs de la liste d'exemple vont de 0 à 1000 et ne tire que les éléments hors de la liste qui a une valeur divisible par 10 au lieu de chaque 10 élément.
Andre Miller
1
Eh bien, l'OP écrit: "nous avons une liste de nombres de zéro à 1000". Il n'a donc pas besoin d'une solution générale.
1
Il écrit «Disons que nous avons…», ce qui implique que ce n'est qu'un exemple. S'il voulait vraiment chaque 10e nombre sur une liste de zéro à 1000, la réponse serait la plage (0,1001,10) ou quelque chose de similaire.
Andre Miller
1

Voici une meilleure implémentation d'une compréhension de liste «tous les 10 éléments», qui n'utilise pas le contenu de la liste dans le cadre du test d'adhésion:

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

Mais c'est encore beaucoup plus lent que d'utiliser simplement le découpage de liste.

PaulMcG
la source
-9

Les compréhensions de listes sont exactement faites pour cela:

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

Vous pouvez obtenir plus d'informations à leur sujet dans la documentation officielle de python: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
la source
La limite supérieure doit être 1 000 et non 10 000. Votre solution n'inclut pas la limite supérieure de 1 000 puisque la plage s'arrête à 999. +1 pour le lien vers la compréhension de la liste.
19
En fait, cela ne sort pas tous les 10 éléments, mais tous les éléments dont la valeur est divisible par 10. Dans cet exemple particulier, c'est la même chose, mais ce n'est peut-être pas le cas.
Andre Miller