Comment obtenir le premier élément d'une liste de tuples?


178

J'ai une liste comme ci-dessous où le premier élément est l'identifiant et l'autre est une chaîne:

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

Je veux créer une liste d'identifiants uniquement à partir de cette liste de tuples comme ci-dessous:

[1,2]

J'utiliserai cette liste, __indonc il doit s'agir d'une liste de valeurs entières.

Réponses:


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

68

Utilisez la fonction zip pour découpler les éléments:

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

Edit (@BradSolomon): Ce qui précède fonctionne pour Python 2.x, où ziprenvoie une liste.

En Python 3.x, ziprenvoie un itérateur et ce qui suit est équivalent à ce qui précède:

>>> print(list(list(zip(*inpt))[0]))
[1, 2]

cela nécessite-t-il une importation séparée?
JuliandotNut

2
@JuliandotNut Non, c'est une fonction intégrée. (en Python 2.x)
WayneSan

22

Voulez-vous dire quelque chose comme ça?

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

Ce que vous avez en fait est une liste d' tupleobjets, pas une liste d'ensembles (comme votre question initiale l'impliquait). S'il s'agit en fait d'une liste d'ensembles, alors il n'y a pas de premier élément car les ensembles n'ont pas d'ordre.

Ici, j'ai créé une liste plate car cela semble généralement plus utile que de créer une liste de tuples à 1 élément. Cependant, vous pouvez facilement créer une liste de tuples à 1 élément en remplaçant simplement seq[0]par (seq[0],).


Je l'ai essayé. Il donne cette erreur:int() argument must be a string or a number, not 'QuerySet'
wasimbhalli

4
@wasimbhalli - int()n'est nulle part dans ma solution, donc l'exception que vous voyez doit venir plus tard dans le code.
mgilson

J'ai mis à jour la question, je dois utiliser cette liste plus tard __inpour filtrer les données
wasimbhalli

qu'est ce que c'est __in? - Sur la base de l'exemple d'entrée que vous avez donné, cela créera une liste d'entiers. Cependant, si votre liste de tuples ne commence pas par des entiers, vous n'obtiendrez pas d'entiers et vous devrez les rendre entiers via int, ou essayer de comprendre pourquoi votre premier élément ne peut pas être converti en entier.
mgilson

Ça new_list = [ seq[0] for seq in yourlist if type(seq[0]) == int]marche?
pR0Ps

11

Vous pouvez utiliser le "déballage de tuple":

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

Au moment de l'itération, chaque tuple est décompressé et ses valeurs sont définies sur les variables idxet val.

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

8

C'est à ça que ça operator.itemgettersert.

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

L' itemgetterinstruction renvoie une fonction qui renvoie l'index de l'élément que vous spécifiez. C'est exactement la même chose que d'écrire

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

Mais je trouve que itemgetterc'est plus clair et plus explicite .

Ceci est pratique pour créer des instructions de tri compactes. Par exemple,

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

7

Du point de vue des performances, dans python3.X

  • [i[0] for i in a]et list(zip(*a))[0]sont équivalents
  • ils sont plus rapides que list(map(operator.itemgetter(0), a))

Code

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)

production

3.491014136001468e-05

3.422205176000717e-05


6

si les tuples sont uniques, cela peut fonctionner

>>> 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
Cela perdra la commande. Cela peut fonctionner avec ordereddict, cependant.
Tim Tisdall

si 2 tuples ou plus ont le même premier élément que votre solution ne fonctionnera pas
kederrac

3

quand j'ai couru (comme suggéré ci-dessus):

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

au lieu de retourner:

[1, 2]

J'ai reçu ceci en retour:

<map at 0xb387eb8>

J'ai trouvé que je devais utiliser list ():

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

pour renvoyer avec succès une liste en utilisant cette suggestion. Cela dit, je suis satisfait de cette solution, merci. (testé / exécuté avec Spyder, console iPython, Python v3.6)


3

Je pensais qu'il pourrait être utile de comparer les temps d'exécution des différentes approches alors j'ai fait un benchmark (en utilisant la bibliothèque simple_benchmark )

I) Benchmark ayant des tuples avec 2 éléments entrez la description de l'image ici

Comme vous pouvez vous attendre à sélectionner le premier élément parmi les tuples par index, se 0révèle être la solution la plus rapide très proche de la solution de déballage en s'attendant à exactement 2 valeurs

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) Benchmark ayant des tuples avec 2 éléments ou plus entrez la description de l'image ici

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

Ce sont des tuples, pas des ensembles. Tu peux le faire:

l1 = [(1, u'abc'), (2, u'def')]
l2 = [(tup[0],) for tup in l1]
l2
>>> [(1,), (2,)]

2
Pas vraiment ce qui est demandé
Mad Physicist

0

vous pouvez décompresser vos tuples et obtenir uniquement le premier élément en utilisant une compréhension de liste:

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

production:

[1, 2]

cela fonctionnera quel que soit le nombre d'éléments que vous avez dans un tuple:

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

production:

[1, 2]

0

Je me suis demandé pourquoi personne n'avait suggéré d'utiliser numpy, mais maintenant, après avoir vérifié, je comprends. Ce n'est peut-être pas le meilleur pour les tableaux de type mixte.

Ce serait une solution dans numpy:

>>> import numpy as np

>>> a = np.asarray([(1, u'abc'), (2, u'def')])
>>> a[:, 0].astype(int).tolist()
[1, 2]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.