Wie extrahiere ich die n-ten Elemente aus einer Liste von Tupeln?

112

Ich versuche, die n-ten Elemente aus einer Liste von Tupeln zu erhalten.

Ich habe so etwas wie:

elements = [(1,1,1),(2,3,7),(3,5,10)]

Ich möchte nur die zweiten Elemente jedes Tupels in eine Liste extrahieren:

seconds = [1, 3, 5]

Ich weiß, dass es mit einer forSchleife gemacht werden kann, aber ich wollte wissen, ob es einen anderen Weg gibt, da ich Tausende von Tupeln habe.

erfreut nicht dazu
quelle

Antworten:

185
n = 1 # N. . .
[x[n] for x in elements]
luc
quelle
34

Das funktioniert auch:

zip(*elements)[1]

(Ich poste dies hauptsächlich, um mir selbst zu beweisen, dass ich gescheitert bin zip...)

Sehen Sie es in Aktion:

>>> help(zip)

Hilfe auf eingebaute Funktion zip in Modul builtin :

Postleitzahl(...)

zip (seq1 [, seq2 [...]]) -> [(seq1 [0], seq2 [0] ...), (...)]

Gibt eine Liste von Tupeln zurück, wobei jedes Tupel das i-te Element aus jeder der Argumentsequenzen enthält. Die zurückgegebene Liste wird auf die Länge der kürzesten Argumentsequenz gekürzt.

>>> elements = [(1,1,1),(2,3,7),(3,5,10)]
>>> zip(*elements)
[(1, 2, 3), (1, 3, 5), (1, 7, 10)]
>>> zip(*elements)[1]
(1, 3, 5)
>>>

Ordentlich, was ich heute gelernt habe: Verwenden Sie *listArgumente, um eine Parameterliste für eine Funktion zu erstellen ...

Hinweis : Gibt in Python3 zipeinen Iterator zurück. Verwenden Sie ihn stattdessen list(zip(*elements)), um eine Liste von Tupeln zurückzugeben.

Daren Thomas
quelle
2
und verwenden **dict, um Schlüsselwortargumente zu erstellen: def test(foo=3, bar=3): return foo*bardannd = {'bar': 9, 'foo'=12}; print test(**d)
Wayne Werner
@ Wayne Werner: Ja. Dieses Zeug war alles nur passives Wissen (ich benutze es nicht oft) - aber es ist gut, ab und zu daran erinnert zu werden, damit Sie wissen, wo / wonach Sie suchen müssen ...
Daren Thomas
1
Eine wahre Geschichte - ich finde , dass in etwas verwende ich oft genug (Python, vim), neige ich dazu, Notwendigkeit Erinnerungen an ordentlich / kühlen Funktionen , die ich vergessen habe , weil ich sie nicht verwenden , dass oft.
Wayne Werner
Die * Listensyntax ist ziemlich nützlich. Gibt es eine Idee, wo dies in der offiziellen Python-Dokumentation beschrieben ist?
user1748155
Ich habe es nur im Tutorial gefunden: docs.python.org/2/tutorial/…
Daren Thomas
30

Ich weiß, dass es mit einem FOR gemacht werden kann, aber ich wollte wissen, ob es einen anderen Weg gibt

Es geht auch anders. Sie können dies auch mit map und itemgetter tun :

>>> from operator import itemgetter
>>> map(itemgetter(1), elements)

Dies führt jedoch intern immer noch eine Schleife durch und ist etwas langsamer als das Listenverständnis:

setup = 'elements = [(1,1,1) for _ in range(100000)];from operator import itemgetter'
method1 = '[x[1] for x in elements]'
method2 = 'map(itemgetter(1), elements)'

import timeit
t = timeit.Timer(method1, setup)
print('Method 1: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup)
print('Method 2: ' + str(t.timeit(100)))

Ergebnisse:

Methode 1: 1.25699996948
Methode 2: 1.46600008011

Wenn Sie eine Liste durchlaufen müssen, ist die Verwendung von a forin Ordnung.

Mark Byers
quelle
2
Ein kleiner Zusatz: In Python-3.x zeigt der Benchmark, dass die Karte nur einen Bruchteil einer Millisekunde dauert. Das liegt daran, dass ein Iterator zurückgegeben wird. method2 = 'list (map (itemgetter (1), elements))' rendert das alte Verhalten.
Maik Beckmann
12

Ich habe dies gefunden, als ich nach dem Weg gesucht habe, auf dem das zweite Element einer 2-Tupel-Liste am schnellsten abgerufen werden kann. Nicht das, was ich wollte, aber ich habe den gleichen Test wie mit einer dritten Methode durchgeführt und die Zip-Methode getestet

setup = 'elements = [(1,1) for _ in range(100000)];from operator import itemgetter'
method1 = '[x[1] for x in elements]'
method2 = 'map(itemgetter(1), elements)'
method3 = 'dict(elements).values()'
method4 = 'zip(*elements)[1]'

import timeit
t = timeit.Timer(method1, setup)
print('Method 1: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup)
print('Method 2: ' + str(t.timeit(100)))
t = timeit.Timer(method3, setup)
print('Method 3: ' + str(t.timeit(100)))
t = timeit.Timer(method4, setup)
print('Method 4: ' + str(t.timeit(100)))

Method 1: 0.618785858154
Method 2: 0.711684942245
Method 3: 0.298138141632
Method 4: 1.32586884499

Also mehr als doppelt so schnell, wenn Sie ein 2-Tupel-Paar haben, um es einfach in ein Diktat umzuwandeln und die Werte zu übernehmen.

Graeme Gellatly
quelle
Dies ist wahrscheinlich offensichtlich, aber ich würde erwähnen, dict(elements).values()dass dies zu einem Diktat mit einem Element führen wird, im Gegensatz zu Listenverständnis oder Karte. Dies ist genau das, was ich wollte (ich war an einzigartigen Toupples interessiert) (+1 und großes Dankeschön für das Posten), aber andere fragen sich vielleicht, warum das Diktat schneller ist - es ordnet keinen Speicher zu, sondern prüft nur das vorhandene Element.
Greg0ry
6

Timings für Python 3.6 zum Extrahieren des zweiten Elements aus einer 2-Tupel-Liste.

Außerdem wurde eine numpyArray-Methode hinzugefügt , die einfacher zu lesen ist (aber wohl einfacher als das Listenverständnis).

from operator import itemgetter
elements = [(1,1) for _ in range(100000)]

%timeit second = [x[1] for x in elements]
%timeit second = list(map(itemgetter(1), elements))
%timeit second = dict(elements).values()
%timeit second = list(zip(*elements))[1]
%timeit second = np.array(elements)[:,1]

und die Zeiten:

list comprehension:  4.73 ms ± 206 µs per loop
list(map):           5.3 ms ± 167 µs per loop
dict:                2.25 ms ± 103 µs per loop
list(zip)            5.2 ms ± 252 µs per loop
numpy array:        28.7 ms ± 1.88 ms per loop

Beachten Sie dies map()und zip()geben Sie keine Liste mehr zurück, daher die explizite Konvertierung.

Oleg
quelle
3
map (lambda x:(x[1]),elements)
Thras
quelle
9
Erwägen Sie, eine Erklärung hinzuzufügen.
Fedorqui 'SO hör auf zu schaden'
1

Verwenden von isliceund chain.from_iterable:

>>> from itertools import chain, islice
>>> elements = [(1,1,1),(2,3,7),(3,5,10)]
>>> list(chain.from_iterable(islice(item, 1, 2) for item in elements))
[1, 3, 5]

Dies kann nützlich sein, wenn Sie mehr als ein Element benötigen:

>>> elements = [(0, 1, 2, 3, 4, 5), 
                (10, 11, 12, 13, 14, 15), 
                (20, 21, 22, 23, 24, 25)]
>>> list(chain.from_iterable(islice(tuple_, 2, 5) for tuple_ in elements))
[2, 3, 4, 12, 13, 14, 22, 23, 24]
Georgy
quelle