Pythonische Methode, um die Liste aller n-ten Elemente in einer größeren Liste zurückzugeben

170

Angenommen, wir haben eine Liste mit Zahlen von 0 bis 1000. Gibt es eine pythonische / effiziente Möglichkeit, eine Liste des ersten und jedes nachfolgenden 10. Elements zu erstellen, d [0, 10, 20, 30, ... ]. H.

Ja, ich kann dies mit einer for-Schleife tun, aber ich frage mich, ob es einen besseren Weg gibt, dies zu tun, vielleicht sogar in einer Zeile?

p.brown
quelle

Antworten:

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

Beachten Sie, dass dies etwa 100-mal schneller ist als das Schleifen und Überprüfen eines Moduls für jedes Element:

$ 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
quelle
4
Sicher, Listenverständnisse sind im Allgemeinen leistungsfähiger. OTOH, die Frage setzt eine vorhandene Liste und in diesem Fall funktioniert ein Slice einwandfrei.
Ned Deily
Ich habe dies unten in den Antworten zum Listenverständnis kommentiert. Seien Sie vorsichtig mit "if x% 10 == 0". Es funktioniert nur mit diesem speziellen Listenbeispiel, aber wenn die Eingabeliste zum Beispiel l = Bereich (0,1000,2) ist, wird nicht jedes 10. Element herausgezogen.
Andre Miller
12
@ Andrew: sehr wahr. Dies ist also ein Beispiel für eine bescheidene Sprachfunktion, den Slice-Operator, der sich in diesem Fall herausstellt (1), um das Erreichen der richtigen Ergebnisse zu erleichtern. (2) führt zu einem prägnanteren Ausdruck; und (3) zufällig 2 Größenordnungen schneller ist. (1) ist natürlich bei weitem das wichtigste Anliegen, aber dank der sorgfältigen Gestaltung und Implementierung der Sprache erhalten Sie alle drei zum Preis von 1. Schöne Fragen und Antworten.
Ned Deily
2
Das 0ist überflüssig in l[0::10]. l[::10]ist besser lesbar, weniger verwirrend.
Konstantin Schubert
Ich bin überrascht vom Leistungsvergleich 0,5 Sekunden für das Listenverständnis und 0,4 Sekunden für das Listen-Slice. Scheint sehr langsam zu sein, warum das Schneiden von Listen 100.000 Schleifen für eine Liste der Größe 1 Tausend benötigt!?
Damo
57
  1. source_list[::10] ist das offensichtlichste, aber dies funktioniert nicht für iterierbare und ist für große Listen nicht speichereffizient.
  2. itertools.islice(source_sequence, 0, None, 10) funktioniert für jedes iterable und ist speichereffizient, ist aber wahrscheinlich nicht die schnellste Lösung für große Listen und große Schritte.
  3. (source_list[i] for i in xrange(0, len(source_list), 10))
Denis Otkidach
quelle
1
+1 Beste Antwort, IMO. Alle drei Vorschläge stellen allgemeine Lösungen dar (dh nehmen Sie die Quellenliste als gegeben). Die Generatorlösung (3.) ist nett, da sie nach dem Index der Quellliste filtert. Es ist wahrscheinlich so speichereffizient wie 2. Sowohl die Indizes als auch die Ergebnisliste sind Generatoren und daher träge aufgebaut. Dies ist wahrscheinlich auch die schnellste, wenn Sie die Ergebnisliste nicht in einem einzelnen Block benötigen. Nur wenn die Quellenliste ein Generator sein könnte, würde ich mit Pauls "item, i in enumerate (l)" Idiom gehen, da es keine len () eines Generators gibt. Übrigens, welche Art von Iterable würde mit 1. nicht funktionieren? Generatoren?!
ThomasH
Iterable = Objekt mit __iter __ () -Methode, das Iterator zurückgibt (Objekt mit next () -Methode)
Denis Otkidach
24

Sie können den Slice-Operator folgendermaßen verwenden:

l = [1,2,3,4,5]
l2 = l[::2] # get subsequent 2nd item
Nick Dandoulakis
quelle
Wie bekomme ich jeden 2. Artikel ab dem 3.?
Benutzer1993
4
@ user1993L[2::2]
Nick Dandoulakis
18

Aus dem Handbuch: 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
quelle
13
newlist = oldlist[::10]

Dies wählt jedes 10. Element der Liste aus.

David Z.
quelle
4

Warum nicht auch einen Schrittparameter der Bereichsfunktion verwenden, um Folgendes zu erhalten:

l = range(0, 1000, 10)

Zum Vergleich auf meiner Maschine:

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
quelle
3
@SilentGhost: Das stimmt, aber da dies eine Anfängerfrage ist, könnte die Bereichsfunktion das sein, was sie wirklich wollen, also denke ich, dass es eine gültige Antwort ist. (Obwohl die Obergrenze 1001 sein sollte, nicht 1000)
Scott Griffiths
2
existing_list = range(0, 1001)
filtered_list = [i for i in existing_list if i % 10 == 0]

quelle
1
Warum haben Sie die if-Klausel, wenn der Bereich (0, 1001, 10) bereits nur jedes 10. Element akzeptiert?
Autoplektische
4
Gleicher Kommentar hier, dies löst nicht das allgemeinere Problem der "pythonischen Methode, die Liste aller n-ten Elemente in einer größeren Liste zurückzugeben". Ihre Lösung hängt von der Tatsache ab, dass die Werte der Beispielliste 0 bis 1000 sind und nur Elemente abrufen aus der Liste, deren Wert durch 10 anstelle jedes 10. Elements teilbar ist.
Andre Miller
1
Nun, das OP schreibt: "Wir haben eine Liste von Zahlen von Null bis 1000". Er braucht also keine allgemeine Lösung.
1
Er schreibt "Sag, wir haben ...", was impliziert, dass es nur ein Beispiel ist. Wenn er wirklich jede 10. Zahl aus einer Liste von Null bis 1000 haben wollte, wäre die Antwort Bereich (0,1001,10) oder ähnliches.
Andre Miller
1

Hier ist eine bessere Implementierung eines Listenverständnisses für "alle 10 Elemente", bei dem der Listeninhalt nicht als Teil des Mitgliedschaftstests verwendet wird:

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

Dies ist jedoch immer noch viel langsamer als nur das Aufteilen von Listen.

PaulMcG
quelle
-9

Listenverständnisse sind genau dafür gemacht:

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

Weitere Informationen dazu finden Sie in der offiziellen Python-Dokumentation: http://docs.python.org/tutorial/datastructures.html#list-comprehensions

Santi
quelle
Die Obergrenze sollte 1000 sein, nicht 10000. Ihre Lösung enthält nicht die Obergrenze von 1000, da der Bereich bei 999 endet. +1 für den Link zum Listenverständnis.
18
Dies zieht nicht jedes 10. Element heraus, sondern jedes Element, dessen Wert durch 10 teilbar ist. In diesem Beispiel ist es dasselbe, aber möglicherweise nicht.
Andre Miller