Como extrair os n-ésimos elementos de uma lista de tuplas?

112

Estou tentando obter os n-ésimos elementos de uma lista de tuplas.

Eu tenho algo como:

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

Desejo extrair apenas os segundos elementos de cada tupla em uma lista:

seconds = [1, 3, 5]

Eu sei que isso poderia ser feito com um forloop, mas queria saber se existe outra maneira, já que tenho milhares de tuplas.

satisfeito por pertencer
fonte

Respostas:

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

Isso também funciona:

zip(*elements)[1]

(Estou postando isso principalmente, para provar a mim mesmo que groquei zip...)

Veja em ação:

>>> help(zip)

Ajuda na função integrada zip no módulo integrado :

fecho eclair(...)

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

Retorne uma lista de tuplas, onde cada tupla contém o i-ésimo elemento de cada uma das sequências de argumento. A lista retornada é truncada em comprimento para o comprimento da menor seqüência de argumentos.

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

Uma coisa legal que aprendi hoje: use *listem argumentos para criar uma lista de parâmetros para uma função ...

Nota : Em Python3, zipretorna um iterador, então use list(zip(*elements))para retornar uma lista de tuplas.

Daren Thomas
fonte
2
e use **dictpara criar argumentos de palavra-chave: def test(foo=3, bar=3): return foo*barthend = {'bar': 9, 'foo'=12}; print test(**d)
Wayne Werner
@Wayne Werner: Sim. Tudo isso era apenas conhecimento passivo (não costumo usá-lo) - mas é bom ser lembrado de vez em quando para que você saiba onde / o que procurar ...
Daren Thomas
1
Verdadeira história - Acho que em qualquer coisa que eu uso com freqüência suficiente (Python, vim), que tendem a lembretes necessidade de puro / cool características que eu esqueci, porque eu não usá-los de que muitas vezes.
Wayne Werner
a * sintaxe da lista é muito útil. alguma ideia de onde isso é descrito na documentação oficial do python?
user1748155
Eu só encontrei no tutorial: docs.python.org/2/tutorial/…
Daren Thomas
30

Eu sei que isso poderia ser feito com um FOR, mas queria saber se há outra maneira

Existe outra maneira. Você também pode fazer isso com map e itemgetter :

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

Isso ainda executa um loop internamente e é um pouco mais lento do que a compreensão da lista:

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

Resultados:

Método 1: 1.25699996948
Método 2: 1,46600008011

Se você precisar iterar em uma lista, não há problema em usar um for.

Mark Byers
fonte
2
Uma pequena adição: em python-3.x, o benchmark mostrará que o mapa leva apenas uma fração de milissegundo. Isso porque ele retornará um iterador. method2 = 'list (map (itemgetter (1), elements))' renderiza o comportamento antigo.
Maik Beckmann
12

Encontrei isso enquanto procurava a maneira mais rápida de extrair o segundo elemento de uma lista de 2 tuplas. Não é o que eu queria, mas executei o mesmo teste mostrado com um terceiro método mais teste o método 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

Portanto, duas vezes mais rápido se você tiver um par de 2 tuplas apenas para converter para um dicionário e obter os valores.

Graeme Gellatly
fonte
Isso provavelmente é óbvio, mas eu mencionaria dict(elements).values()que resultará em dict de um elemento em oposição a list comprahension ou map. Isso é exatamente o que eu queria (eu estava interessado em toques únicos) (+1 e muito obrigado por postar), mas outros podem se perguntar por que o dict é mais rápido - não é alocar memória, mas apenas verificar o elemento existente.
Greg0ry
6

Tempo para Python 3.6 para extrair o segundo elemento de uma lista de 2 tuplas.

Além disso, adicionou o numpymétodo de array, que é mais simples de ler (mas sem dúvida mais simples do que a compreensão de lista).

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]

e os horários:

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

Observe que map()e zip()não retorna mais uma lista, daí a conversão explícita.

Oleg
fonte
3
map (lambda x:(x[1]),elements)
Thras
fonte
9
Considere adicionar alguma explicação.
fedorqui 'SO pare de prejudicar'
1

Usando islicee 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]

Isso pode ser útil quando você precisa de mais de um elemento:

>>> 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]
Georgiano
fonte