¿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.

Respuestas:


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

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.


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 .


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.


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.



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]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.