¿Cómo extraer los elementos n-ésimo de una lista de tuplas?

112

Estoy tratando de obtener los elementos n-ésimo de una lista de tuplas.

Tengo algo como:

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

Deseo extraer solo los segundos elementos de cada tupla en una lista:

seconds = [1, 3, 5]

Sé que se podría hacer con un forbucle, pero quería saber si hay otra forma, ya que tengo miles de tuplas.

complacido
fuente

Respuestas:

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

Esto también funciona:

zip(*elements)[1]

(Principalmente estoy publicando esto, para demostrarme a mí mismo que he asimilado zip...)

Véalo en acción:

>>> help(zip)

Ayuda sobre la función incorporada zip en el módulo incorporado :

Código Postal(...)

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

Devuelve una lista de tuplas, donde cada tupla contiene el elemento i-ésimo de cada una de las secuencias de argumentos. La lista devuelta se trunca a la longitud de la secuencia de argumentos más corta.

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

Lo bueno que aprendí hoy: usar *listen argumentos para crear una lista de parámetros para una función ...

Nota : En Python3, zipdevuelve un iterador, por lo que en su lugar utilícelo list(zip(*elements))para devolver una lista de tuplas.

Daren Thomas
fuente
2
y utilizar **dictpara crear argumentos de palabras clave: def test(foo=3, bar=3): return foo*barluegod = {'bar': 9, 'foo'=12}; print test(**d)
Wayne Werner
@Wayne Werner: Sí. Todo esto era conocimiento pasivo (no lo uso a menudo), pero es bueno que se lo recuerden de vez en cuando para saber dónde / qué buscar ...
Daren Thomas
1
La verdadera historia - Me parece que en todo lo que uso con bastante frecuencia (Python, vim), que tienden a recordatorios necesidad de / frío ordenada características que he olvidado porque no los uso que a menudo.
Wayne Werner
la sintaxis * list es bastante útil. ¿Alguna idea de dónde se describe esto en la documentación oficial de Python?
user1748155
Solo lo encontré en el tutorial: docs.python.org/2/tutorial/…
Daren Thomas
30

Sé que se podría hacer con un FOR pero quería saber si hay otra forma

Hay otra forma. También puedes hacerlo con map y itemgetter :

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

Sin embargo, esto todavía realiza un bucle internamente y es un poco más lento que la comprensión de la 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

Si necesita iterar sobre una lista, entonces forestá bien usar a .

Mark Byers
fuente
2
Una pequeña adición: en python-3.x, el punto de referencia mostrará que el mapa solo toma una fracción de milisegundo. Eso es porque devolverá un iterador. method2 = 'list (map (itemgetter (1), elements))' muestra el comportamiento anterior.
Maik Beckmann
12

Encontré esto mientras buscaba cuál es la forma más rápida de extraer el segundo elemento de una lista de 2 tuplas. No es lo que quería, pero ejecuté la misma prueba que se muestra con un tercer método más probar el 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

Entonces, más del doble de rápido si tiene un par de 2 tuplas para convertirlo en un dict y tomar los valores.

Graeme Gellatly
fuente
Esto es probablemente obvio, pero mencionaría dict(elements).values()que resultará en un dict de un elemento en lugar de una lista de comprahension o map. Esto es exactamente lo que quería (estaba interesado en parejas únicas) (+1 y muchas gracias por publicar), pero otros podrían preguntarse por qué dict es más rápido: no está asignando memoria sino solo verificando el elemento existente.
Greg0ry
6

Tiempos de Python 3.6 para extraer el segundo elemento de una lista de 2 tuplas.

Además, se agregó el numpymétodo de matriz, que es más simple de leer (pero posiblemente más simple que la comprensión de la 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]

y los tiempos:

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

Tenga en cuenta eso map()y zip()ya no devuelva una lista, de ahí la conversión explícita.

Oleg
fuente
3
map (lambda x:(x[1]),elements)
Thras
fuente
9
Considere agregar alguna explicación.
fedorqui 'SO deja de dañar'
1

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

Esto puede resultar útil cuando necesite más de un 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]
Georgy
fuente