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

Respuestas:


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

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]

¿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],).


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'

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

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


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']
>>> 

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)


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

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

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]

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