¿Cómo obtener el primer elemento en una lista de tuplas?

178

Tengo una lista como la siguiente, donde el primer elemento es la identificación y el otro es una cadena:

[(1, u'abc'), (2, u'def')]

Quiero crear una lista de identificadores solo a partir de esta lista de tuplas de la siguiente manera:

[1,2]

Usaré esta lista, por __inlo que debe ser una lista de valores enteros.

wasimbhalli
fuente

Respuestas:

245
>>> a = [(1, u'abc'), (2, u'def')]
>>> [i[0] for i in a]
[1, 2]
Rakesh
fuente
68

Use la función zip para desacoplar elementos:

>>> inpt = [(1, u'abc'), (2, u'def')]
>>> unzipped = zip(*inpt)
>>> print unzipped
[(1, 2), (u'abc', u'def')]
>>> print list(unzipped[0])
[1, 2]

Editar (@BradSolomon): lo anterior funciona para Python 2.x, donde zipdevuelve una lista.

En Python 3.x, zipdevuelve un iterador y lo siguiente es equivalente a lo anterior:

>>> print(list(list(zip(*inpt))[0]))
[1, 2]
WayneSan
fuente
¿Esto necesita una importación por separado?
JuliandotNut
2
@JuliandotNut No, es una función incorporada. (en Python 2.x)
WayneSan
22

¿Te refieres a algo como esto?

new_list = [ seq[0] for seq in yourlist ]

Lo que realmente tiene es una lista de tupleobjetos, no una lista de conjuntos (como su pregunta original implicaba). Si en realidad es una lista de conjuntos, entonces no hay un primer elemento porque los conjuntos no tienen orden.

Aquí he creado una lista plana porque generalmente eso parece más útil que crear una lista de tuplas de 1 elemento. Sin embargo, puede crear fácilmente una lista de tuplas de 1 elemento simplemente reemplazándolas seq[0]por (seq[0],).

mgilson
fuente
Lo intenté. Da este error:int() argument must be a string or a number, not 'QuerySet'
wasimbhalli
44
@wasimbhalli - int()no está en ninguna parte de mi solución, por lo que la excepción que está viendo debe venir más adelante en el código.
mgilson
He actualizado la pregunta, necesito usar esta lista más adelante __inpara filtrar datos
wasimbhalli
lo que es __in? - Según el ejemplo de entrada que ha proporcionado, esto creará una lista de enteros. Sin embargo, si su lista de tuplas no comienza con números enteros, entonces no obtendrá números enteros y deberá convertirlos en números enteros int, o tratar de descubrir por qué su primer elemento no puede convertirse en un número entero.
mgilson
Funciona new_list = [ seq[0] for seq in yourlist if type(seq[0]) == int]?
pR0Ps
11

Puede usar "desempaquetar tuplas":

>>> my_list = [(1, u'abc'), (2, u'def')]
>>> my_ids = [idx for idx, val in my_list]
>>> my_ids
[1, 2]

En el momento de la iteración, cada tupla se desempaqueta y sus valores se establecen en las variables idxy val.

>>> x = (1, u'abc')
>>> idx, val = x
>>> idx
1
>>> val
u'abc'
ssoler
fuente
8

Esto es para lo que operator.itemgettersirve.

>>> a = [(1, u'abc'), (2, u'def')]
>>> import operator
>>> b = map(operator.itemgetter(0), a)
>>> b
[1, 2]

La itemgetterinstrucción devuelve una función que devuelve el índice del elemento que especifique. Es exactamente lo mismo que escribir

>>> b = map(lambda x: x[0], a)

Pero creo que itemgetteres más claro y más explícito .

Esto es útil para hacer declaraciones de ordenación compactas. Por ejemplo,

>>> c = sorted(a, key=operator.itemgetter(0), reverse=True)
>>> c
[(2, u'def'), (1, u'abc')]
bcattle
fuente
7

Desde el punto de vista del rendimiento, en python3.X

  • [i[0] for i in a]y list(zip(*a))[0]son equivalentes
  • son mas rapidos que list(map(operator.itemgetter(0), a))

Código

import timeit


iterations = 100000
init_time = timeit.timeit('''a = [(i, u'abc') for i in range(1000)]''', number=iterations)/iterations
print(timeit.timeit('''a = [(i, u'abc') for i in range(1000)]\nb = [i[0] for i in a]''', number=iterations)/iterations - init_time)
print(timeit.timeit('''a = [(i, u'abc') for i in range(1000)]\nb = list(zip(*a))[0]''', number=iterations)/iterations - init_time)

salida

3.491014136001468e-05

3.422205176000717e-05

negrita
fuente
6

si las tuplas son únicas, entonces esto puede funcionar

>>> a = [(1, u'abc'), (2, u'def')]
>>> a
[(1, u'abc'), (2, u'def')]
>>> dict(a).keys()
[1, 2]
>>> dict(a).values()
[u'abc', u'def']
>>> 
Jiri Semmler
fuente
44
Esto perderá el pedido. Sin ordereddictembargo, puede funcionar .
Tim Tisdall
si 2 o más tuplas tienen el mismo primer elemento que su solución no funcionará
kederrac
3

cuando corrí (como se sugirió anteriormente):

>>> a = [(1, u'abc'), (2, u'def')]
>>> import operator
>>> b = map(operator.itemgetter(0), a)
>>> b

en lugar de regresar:

[1, 2]

Recibí esto como la devolución:

<map at 0xb387eb8>

Descubrí que tenía que usar list ():

>>> b = list(map(operator.itemgetter(0), a))

para devolver con éxito una lista usando esta sugerencia. Dicho esto, estoy contento con esta solución, gracias. (probado / ejecutado usando Spyder, consola iPython, Python v3.6)

James
fuente
3

Estaba pensando que podría ser útil comparar los tiempos de ejecución de los diferentes enfoques, así que hice un punto de referencia (usando la biblioteca simple_benchmark )

I) Benchmark con tuplas con 2 elementos. ingrese la descripción de la imagen aquí

Como puede esperar seleccionar el primer elemento de las tuplas por índice, 0muestra que es la solución más rápida muy cercana a la solución de desempaquetado esperando exactamente 2 valores

import operator
import random

from simple_benchmark import BenchmarkBuilder

b = BenchmarkBuilder()



@b.add_function()
def rakesh_by_index(l):
    return [i[0] for i in l]


@b.add_function()
def wayneSan_zip(l):
    return list(list(zip(*l))[0])


@b.add_function()
def bcattle_itemgetter(l):
     return list(map(operator.itemgetter(0), l))


@b.add_function()
def ssoler_upacking(l):
    return [idx for idx, val in l]

@b.add_function()
def kederrack_unpacking(l):
    return [f for f, *_ in l]



@b.add_arguments('Number of tuples')
def argument_provider():
    for exp in range(2, 21):
        size = 2**exp
        yield size, [(random.choice(range(100)), random.choice(range(100))) for _ in range(size)]


r = b.run()
r.plot()

II) Punto de referencia que tiene tuplas con 2 o más elementos ingrese la descripción de la imagen aquí

import operator
import random

from simple_benchmark import BenchmarkBuilder

b = BenchmarkBuilder()

@b.add_function()
def kederrack_unpacking(l):
    return [f for f, *_ in l]


@b.add_function()
def rakesh_by_index(l):
    return [i[0] for i in l]


@b.add_function()
def wayneSan_zip(l):
    return list(list(zip(*l))[0])


@b.add_function()
def bcattle_itemgetter(l):
     return list(map(operator.itemgetter(0), l))


@b.add_arguments('Number of tuples')
def argument_provider():
    for exp in range(2, 21):
        size = 2**exp
        yield size, [tuple(random.choice(range(100)) for _
                     in range(random.choice(range(2, 100)))) for _ in range(size)]

from pylab import rcParams
rcParams['figure.figsize'] = 12, 7

r = b.run()
r.plot()
Kederrac
fuente
0

Esas son tuplas, no conjuntos. Puedes hacerlo:

l1 = [(1, u'abc'), (2, u'def')]
l2 = [(tup[0],) for tup in l1]
l2
>>> [(1,), (2,)]
Lanaru
fuente
2
Realmente no es lo que se está preguntando
Mad Physicist
0

puedes desempacar tus tuplas y obtener solo el primer elemento usando una lista de comprensión:

l = [(1, u'abc'), (2, u'def')]
[f for f, *_ in l]

salida:

[1, 2]

esto funcionará sin importar cuántos elementos tenga en una tupla:

l = [(1, u'abc'), (2, u'def', 2, 4, 5, 6, 7)]
[f for f, *_ in l]

salida:

[1, 2]
Kederrac
fuente
0

Me preguntaba por qué nadie sugirió usar numpy, pero ahora, después de comprobarlo, entiendo. Quizás no sea el mejor para matrices de tipo mixto.

Esta sería una solución en numpy:

>>> import numpy as np

>>> a = np.asarray([(1, u'abc'), (2, u'def')])
>>> a[:, 0].astype(int).tolist()
[1, 2]
CodePrinz
fuente