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.

Respostas:


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

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]

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


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'

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

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


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

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)


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

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

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]

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]
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.