Comment extraire les n-ièmes éléments d'une liste de tuples?

112

J'essaye d'obtenir les n-ièmes éléments d'une liste de tuples.

J'ai quelque chose comme:

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

Je souhaite extraire uniquement les seconds éléments de chaque tuple dans une liste:

seconds = [1, 3, 5]

Je sais que cela pourrait être fait avec une forboucle mais je voulais savoir s'il y avait un autre moyen puisque j'ai des milliers de tuples.

heureux
la source

Réponses:

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

Cela fonctionne également:

zip(*elements)[1]

(Je poste principalement ceci, pour me prouver que j'ai groked zip...)

Voyez-le en action:

>>> help(zip)

Aide sur la fonction intégrée zip dans le module intégré :

Zip *: français(...)

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

Renvoie une liste de tuples, où chaque tuple contient le i-ème élément de chacune des séquences d'arguments. La liste renvoyée est tronquée en longueur à la longueur de la séquence d'arguments la plus courte.

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

Chose intéressante que j'ai apprise aujourd'hui: utiliser *listdans les arguments pour créer une liste de paramètres pour une fonction ...

Remarque : dans Python3, ziprenvoie un itérateur, utilisez-le list(zip(*elements))à la place pour renvoyer une liste de tuples.

Daren Thomas
la source
2
et utilisez **dictpour créer des arguments de mots-clés: def test(foo=3, bar=3): return foo*barpuisd = {'bar': 9, 'foo'=12}; print test(**d)
Wayne Werner
@Wayne Werner: Oui. Ce truc était juste une connaissance passive (je ne l'utilise pas souvent) - mais il est bon de se le rappeler de temps en temps afin que vous sachiez où / quoi chercher ...
Daren Thomas
1
Histoire vraie - je trouve que dans tout ce que je l' utilise assez souvent (Python, vim), je tends à des rappels de besoin de cool / soigné caractéristiques que je l' ai oublié parce que je ne les utilise que souvent.
Wayne Werner
la syntaxe * list est assez utile. une idée où cela est décrit dans la documentation officielle de python?
user1748155
Je ne l'ai trouvé que dans le tutoriel: docs.python.org/2/tutorial
Daren Thomas
30

Je sais que cela pourrait être fait avec un FOR mais je voulais savoir s'il y avait une autre façon

Il y a un autre moyen. Vous pouvez également le faire avec map et itemgetter :

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

Cela exécute toujours une boucle en interne et c'est légèrement plus lent que la compréhension de la liste:

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

Résultats:

Méthode 1: 1.25699996948
Méthode 2: 1.46600008011

Si vous avez besoin d'itérer sur une liste, l'utilisation de a forest très bien.

Mark Byers
la source
2
Un petit ajout: dans python-3.x, le benchmark montrera que la carte ne prend qu'une fraction de milliseconde. C'est parce qu'il renverra un itérateur. method2 = 'list (map (itemgetter (1), elements))' rend l'ancien comportement.
Maik Beckmann
12

J'ai trouvé ceci pendant que je cherchais la manière la plus rapide d'extraire le deuxième élément d'une liste de 2 tuples. Pas ce que je voulais, mais j'ai exécuté le même test que celui indiqué avec une troisième méthode et tester la méthode 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

Donc, deux fois plus vite si vous avez une paire de 2 tuples pour simplement convertir en un dict et prendre les valeurs.

Graeme Gellatly
la source
C'est probablement évident, mais je mentionnerais que dict(elements).values()cela entraînera un dict à un élément par opposition à la compilation de liste ou à la carte. C'est exactement ce que je voulais (j'étais intéressé par les touples uniques) (+1 et un grand merci pour la publication), mais d'autres pourraient se demander pourquoi dict est plus rapide - il n'alloue pas de mémoire mais vérifie uniquement par rapport à l'élément existant.
Greg0ry
6

Timings for Python 3.6 pour extraire le deuxième élément d'une liste à 2 tuples.

Ajout de la numpyméthode de tableau, qui est plus simple à lire (mais sans doute plus simple que la compréhension de liste).

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]

et les horaires:

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

Notez que map()et zip()ne retournez plus de liste, d'où la conversion explicite.

Oleg
la source
3
map (lambda x:(x[1]),elements)
Thras
la source
9
Pensez à ajouter quelques explications.
fedorqui 'SO arrêtez de nuire'
1

Utilisation isliceet 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]

Cela peut être utile lorsque vous avez besoin de plusieurs éléments:

>>> 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
la source