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.

wasimbhalli
la source

Réponses:

245
>>> a = [(1, u'abc'), (2, u'def')]
>>> [i[0] for i in a]
[1, 2]
Rakesh
la source
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]
WayneSan
la source
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],).

mgilson
la source
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'
ssoler
la source
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')]
bovins
la source
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

audacieux
la source
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']
>>> 
Jiri Semmler
la source
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)

James
la source
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()
Kederrac
la source
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,)]
Lanaru
la source
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]
Kederrac
la source
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]
CodePrinz
la source