Jak wyodrębnić n-ty element z listy krotek?

112

Próbuję uzyskać n-ty element z listy krotek.

Mam coś takiego:

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

Chcę wyodrębnić tylko drugie elementy każdej krotki do listy:

seconds = [1, 3, 5]

Wiem, że można to zrobić za pomocą forpętli, ale chciałem wiedzieć, czy jest inny sposób, skoro mam tysiące krotek.

zadowolona
źródło

Odpowiedzi:

185
n = 1 # N. . .
[x[n] for x in elements]
luc
źródło
34

Działa to również:

zip(*elements)[1]

(Publikuję to głównie, aby udowodnić sobie, że żałowałem zip...)

Zobacz to w akcji:

>>> help(zip)

Pomoc dotycząca wbudowanej funkcji zip we wbudowanym module :

zamek błyskawiczny(...)

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

Zwraca listę krotek, gdzie każda krotka zawiera i-ty element z każdej sekwencji argumentów. Długość zwracanej listy jest obcinana do długości najkrótszej sekwencji argumentów.

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

Niezła rzecz, której się dzisiaj nauczyłem: użyj *listargumentów, aby utworzyć listę parametrów dla funkcji ...

Uwaga : w Python3 zipzwraca iterator, więc zamiast tego użyj, list(zip(*elements))aby zwrócić listę krotek.

Daren Thomas
źródło
2
i użyj **dictdo tworzenia argumentów słów kluczowych: def test(foo=3, bar=3): return foo*barnastępnied = {'bar': 9, 'foo'=12}; print test(**d)
Wayne Werner
@Wayne Werner: Tak. Te rzeczy były tylko pasywną wiedzą (nie często z niej korzystam) - ale dobrze jest być przypominanym od czasu do czasu, aby wiedzieć, gdzie / czego szukać ...
Daren Thomas.
1
Prawdziwa historia - stwierdzam, że we wszystkim, czego używam wystarczająco często (Python, vim), potrzebuję przypomnień o fajnych / fajnych funkcjach, o których zapomniałem, ponieważ nie używam ich tak często.
Wayne Werner,
składnia * list jest całkiem przydatna. jakiś pomysł, gdzie jest to opisane w oficjalnej dokumentacji Pythona?
user1748155
Znalazłem to tylko w tutorialu: docs.python.org/2/tutorial/ ...
Daren Thomas,
30

Wiem, że można to zrobić za pomocą FOR, ale chciałem wiedzieć, czy jest inny sposób

Jest inny sposób. Możesz to również zrobić za pomocą map i itemgetter :

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

To nadal wykonuje wewnętrzną pętlę i jest nieco wolniejsze niż rozumienie listy:

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

Wyniki:

Metoda 1: 1,25699996948
Metoda 2: 1.46600008011

Jeśli potrzebujesz iterować po liście, użycie a forjest w porządku.

Mark Byers
źródło
2
Mały dodatek: w Pythonie-3.x benchmark pokaże, że mapa zajmuje tylko ułamek milisekundy. To dlatego, że zwróci iterator. method2 = 'list (map (itemgetter (1), elements))' renderuje stare zachowanie.
Maik Beckmann
12

Znalazłem to, ponieważ szukałem, w jaki sposób najszybciej wyciągnę drugi element listy 2 krotek. Nie to, czego chciałem, ale przeprowadziłem ten sam test, jak pokazano, przy trzeciej metodzie oraz przetestowałem metodę zip

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

Więc ponad dwa razy szybciej, jeśli masz 2 pary krotek, aby po prostu przekonwertować na dyktę i pobrać wartości.

Graeme Gellatly
źródło
Jest to prawdopodobnie oczywiste, ale wspomnę, dict(elements).values()że spowoduje to jednoelementowe dyktowanie, w przeciwieństwie do kompresji listy lub mapy. To jest dokładnie to, czego chciałem (interesowały mnie unikalne poprawki) (+1 i wielkie dzięki za wysłanie), ale inni mogą się zastanawiać, dlaczego dykt jest szybszy - nie alokuje pamięci, a jedynie sprawdza istniejący element.
Greg0ry,
6

Czasy dla Pythona 3.6 do wyodrębniania drugiego elementu z listy 2 krotek.

Dodano również numpymetodę tablicową, która jest prostsza do odczytania (ale prawdopodobnie prostsza niż rozumienie listy).

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]

i terminy:

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

Zauważ, że map()i zip()nie zwracaj już listy, stąd jawna konwersja.

Oleg
źródło
3
map (lambda x:(x[1]),elements)
Thras
źródło
9
Rozważ dodanie jakiegoś wyjaśnienia.
fedorqui 'SO przestać szkodzić'
1

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

Może to być przydatne, gdy potrzebujesz więcej niż jednego elementu:

>>> 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
źródło