Como obter o primeiro elemento em uma lista de tuplas?

178

Eu tenho uma lista como abaixo, onde o primeiro elemento é o id e o outro é uma string:

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

Quero criar uma lista de IDs apenas dessa lista de tuplas, conforme abaixo:

[1,2]

Usarei essa lista __inpara que ela precise ser uma lista de valores inteiros.

wasimbhalli
fonte

Respostas:

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

Use a função 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): O exemplo acima funciona para o Python 2.x, onde zipretorna uma lista.

No Python 3.x, zipretorna um iterador e o seguinte é equivalente ao acima:

>>> print(list(list(zip(*inpt))[0]))
[1, 2]
WayneSan
fonte
isso precisa de uma importação separada?
JuliandotNut
2
@JuliandotNut Não, é uma função interna. (Python 2.x)
WayneSan
22

você quer dizer algo assim?

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

O que você realmente tem é uma lista de tupleobjetos, não uma lista de conjuntos (como sua pergunta original implicava). Se, na verdade, é uma lista de conjuntos, não há primeiro elemento porque os conjuntos não têm ordem.

Aqui eu criei uma lista simples, porque geralmente isso parece mais útil do que criar uma lista de tuplas de 1 elemento. No entanto, você pode criar facilmente uma lista de tuplas de 1 elemento apenas substituindo seq[0]por (seq[0],).

mgilson
fonte
Eu tentei. Dá este erro:int() argument must be a string or a number, not 'QuerySet'
wasimbhalli
4
@wasimbhalli - int()não está em nenhum lugar da minha solução, então a exceção que você está vendo deve vir mais tarde no código.
mgilson
Eu atualizei a questão, eu preciso usar esta lista mais tarde __inpara a filtragem de dados
wasimbhalli
o que é __in? - Com base no exemplo de entrada que você deu, isso criará uma lista de números inteiros. No entanto, se sua lista de tuplas não começar com números inteiros, você não receberá números inteiros e precisará torná-los inteiros via int, ou tente descobrir por que seu primeiro elemento não pode ser convertido em um número inteiro.
mgilson
Funciona new_list = [ seq[0] for seq in yourlist if type(seq[0]) == int]?
pR0Ps 27/08/12
11

Você pode usar "tuple unpacking":

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

No momento da iteração, cada tupla é descompactada e seus valores são definidos para as variáveis idxe val.

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

É para isso que operator.itemgetterserve.

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

A itemgetterinstrução retorna uma função que retorna o índice do elemento que você especificar. É exatamente o mesmo que escrever

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

Mas acho que isso itemgetteré mais claro e explícito .

Isso é útil para fazer declarações de classificação compactas. Por exemplo,

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

Do ponto de vista do desempenho, em python3.X

  • [i[0] for i in a]e list(zip(*a))[0]são equivalentes
  • eles são mais rápidos 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)

resultado

3.491014136001468e-05

3.422205176000717e-05

negrito
fonte
6

se as tuplas são únicas, isso pode 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
fonte
4
Isso perderá o pedido. Pode funcionar com ordereddict, no entanto.
precisa saber é o seguinte
se duas ou mais tuplas tiverem o mesmo primeiro elemento que a sua solução não funcionará
kederrac em 18/02
3

quando eu corri (como sugerido acima):

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

em vez de retornar:

[1, 2]

Eu recebi isso como o retorno:

<map at 0xb387eb8>

Eu descobri que tinha que usar list ():

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

para retornar com êxito uma lista usando esta sugestão. Dito isto, estou feliz com esta solução, obrigado. (testado / executado usando Spyder, console iPython, Python v3.6)

James
fonte
3

Eu estava pensando que seria útil comparar os tempos de execução das diferentes abordagens, então fiz uma referência (usando simple_benchmark biblioteca )

I) Referência com tuplas com 2 elementos insira a descrição da imagem aqui

Como você pode selecionar o primeiro elemento das tuplas por índice 0, a solução é a mais rápida, muito próxima da solução de desempacotamento, esperando exatamente 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) Referência com tuplas com 2 ou mais elementos insira a descrição da imagem aqui

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
fonte
0

Essas são tuplas, não conjuntos. Você consegue fazer isso:

l1 = [(1, u'abc'), (2, u'def')]
l2 = [(tup[0],) for tup in l1]
l2
>>> [(1,), (2,)]
Lanaru
fonte
2
Não realmente o que está sendo solicitado
Mad Físico
0

você pode descompactar suas tuplas e obter apenas o primeiro elemento usando uma compreensão de lista:

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

resultado:

[1, 2]

isso funcionará, não importa quantos elementos você tenha em uma tupla:

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

resultado:

[1, 2]
kederrac
fonte
0

Gostaria de saber por que ninguém sugeriu usar numpy, mas agora depois de verificar eu entendo. Talvez não seja o melhor para matrizes de tipo misto.

Esta seria uma solução em numpy:

>>> import numpy as np

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