Comment rechercher une liste de tuples en Python

90

J'ai donc une liste de tuples comme celle-ci:

[(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

Je veux cette liste pour un tuple dont la valeur numérique est égale à quelque chose.

Si je le fais search(53), la valeur d'index de2

Y a-t-il un moyen facile de faire ceci?

hdx
la source

Réponses:

94
[i for i, v in enumerate(L) if v[0] == 53]
Ignacio Vazquez-Abrams
la source
68
Pourriez-vous expliquer s'il vous plaît?
schatten
17
Expliqué en mots: Pour chaque i, v dans une liste énumérée de L (qui fait de i la position de l'élément dans la liste énumérée et v le tuple d'origine), vérifiez si le premier élément du tuple est 53, si oui, ajoutez le résultat du code avant «pour» dans une liste nouvellement créée, ici: i. Cela pourrait aussi être ma_fonction (i, v) ou encore une autre compréhension de liste. Comme votre liste de tuples n'a qu'un seul tuple avec 53 comme première valeur, vous obtiendrez une liste avec un élément.
djangonaut
6
J'ajouterais simplement [i pour i, v dans enumerate (L) si v [0] == 53] .pop () pour avoir une valeur int.
alemol
49

Vous pouvez utiliser une compréhension de liste :

>>> a = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
>>> [x[0] for x in a]
[1, 22, 53, 44]
>>> [x[0] for x in a].index(53)
2
Greg Hewgill
la source
47

tl; dr

Une expression de générateur est probablement la solution la plus performante et la plus simple à votre problème:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

result = next((i for i, v in enumerate(l) if v[0] == 53), None)
# 2

Explication

Il existe plusieurs réponses qui fournissent une solution simple à cette question avec des compréhensions de liste. Bien que ces réponses soient parfaitement correctes, elles ne sont pas optimales. Selon votre cas d'utilisation, il peut y avoir des avantages importants à apporter quelques modifications simples.

Le principal problème que je vois avec l'utilisation d'une compréhension de liste pour ce cas d'utilisation est que la liste entière sera traitée, bien que vous ne souhaitiez trouver qu'un élément .

Python fournit une construction simple qui est idéale ici. Cela s'appelle l' expression du générateur . Voici un exemple:

# Our input list, same as before
l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]

# Call next on our generator expression.
next((i for i, v in enumerate(l) if v[0] == 53), None)

Nous pouvons nous attendre à ce que cette méthode fonctionne essentiellement de la même manière que les compréhensions de liste dans notre exemple trivial, mais que se passe-t-il si nous travaillons avec un ensemble de données plus grand? C'est là que l'avantage d'utiliser la méthode du générateur entre en jeu. Plutôt que de construire une nouvelle liste, nous utiliserons votre liste existante comme notre itérable, et utiliseronsnext() pour obtenir le premier élément de notre générateur.

Voyons comment ces méthodes fonctionnent différemment sur certains ensembles de données plus volumineux. Ce sont de grandes listes, faites de 10000000 + 1 éléments, avec notre cible au début (meilleur) ou à la fin (pire). Nous pouvons vérifier que ces deux listes fonctionneront de la même manière en utilisant la compréhension de liste suivante:

Compréhensions de listes

"Pire cas"

worst_case = ([(False, 'F')] * 10000000) + [(True, 'T')]
print [i for i, v in enumerate(worst_case) if v[0] is True]

# [10000000]
#          2 function calls in 3.885 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.885    3.885    3.885    3.885 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

"Meilleur cas"

best_case = [(True, 'T')] + ([(False, 'F')] * 10000000)
print [i for i, v in enumerate(best_case) if v[0] is True]

# [0]
#          2 function calls in 3.864 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    3.864    3.864    3.864    3.864 so_lc.py:1(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

Expressions de générateur

Voici mon hypothèse pour les générateurs: nous verrons que les générateurs fonctionneront nettement mieux dans le meilleur des cas, mais de même dans le pire des cas. Ce gain de performance est principalement dû au fait que le générateur est évalué paresseusement, ce qui signifie qu'il ne calculera que ce qui est nécessaire pour produire une valeur.

Pire cas

# 10000000
#          5 function calls in 1.733 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         2    1.455    0.727    1.455    0.727 so_lc.py:10(<genexpr>)
#         1    0.278    0.278    1.733    1.733 so_lc.py:9(<module>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    1.455    1.455 {next}

Meilleur cas

best_case  = [(True, 'T')] + ([(False, 'F')] * 10000000)
print next((i for i, v in enumerate(best_case) if v[0] == True), None)

# 0
#          5 function calls in 0.316 seconds
#
#    Ordered by: standard name
#
#    ncalls  tottime  percall  cumtime  percall filename:lineno(function)
#         1    0.316    0.316    0.316    0.316 so_lc.py:6(<module>)
#         2    0.000    0.000    0.000    0.000 so_lc.py:7(<genexpr>)
#         1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
#         1    0.000    0.000    0.000    0.000 {next}

QUELLE?! Le meilleur cas emporte les compréhensions de liste, mais je ne m'attendais pas à ce que notre pire cas surpasse les compréhensions de liste à un tel point. Comment c'est? Franchement, je ne pouvais que spéculer sans autre recherche.

Prenez tout cela avec un grain de sel, je n'ai pas exécuté de profilage robuste ici, juste des tests très basiques. Cela devrait être suffisant pour apprécier qu'une expression de générateur est plus performante pour ce type de recherche de liste.

Notez que tout cela est du python intégré de base. Nous n'avons pas besoin d'importer quoi que ce soit ou d'utiliser des bibliothèques.

J'ai vu cette technique pour la première fois dans le cours Udacity cs212 avec Peter Norvig.

Jon Surrell
la source
2
intéressant, j'ai testé et trouvé son très rapide
Grijesh Chauhan
3
Cela devrait être la réponse acceptée. Les expressions génératrices ne matérialisent pas toute la séquence de sortie lorsqu'elles sont exécutées, mais elles s'évaluent plutôt en un itérateur qui produit un élément à la fois à partir de l'expression.
BoltzmannBrain
2
C'est génial, beaucoup plus rapide qu'une compréhension de liste dans mon cas, merci!
mindm49907
29

Vos tuples sont essentiellement des paires clé-valeur - un python - dictdonc:

l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")]
val = dict(l)[53]

Edit - aha, vous dites que vous voulez la valeur d'index de (53, "xuxa"). Si c'est vraiment ce que vous voulez, vous devrez parcourir la liste d'origine, ou peut-être créer un dictionnaire plus compliqué:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]
Andrew Jaffe
la source
2
Si nous ignorons ce que l'OP a réellement demandé, je pense que votre réponse initiale est la meilleure réponse à "Comment rechercher une liste de tuples en Python"
Rick Westera
Votre première réponse a été utile à mes fins. Il vaut peut-être mieux utiliser .get (), au cas où l'élément ne serait pas dans le dict. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941
12

Hmm ... eh bien, le moyen simple qui me vient à l'esprit est de le convertir en dict

d = dict(thelist)

et l'accès d[53].

EDIT : Oups, vous avez mal lu votre question la première fois. Il semble que vous vouliez réellement obtenir l'index où un nombre donné est stocké. Dans ce cas, essayez

dict((t[0], i) for i, t in enumerate(thelist))

au lieu d'un simple vieux dict conversion . Ce d[53]serait alors 2.

David Z
la source
6

En supposant la liste peut être longue et le nombre peut répéter, envisager d' utiliser le SortedList type à partir du module Python . Le type SortedList maintiendra automatiquement les tuples dans l'ordre par numéro et permettra une recherche rapide.

Par exemple:

from sortedcontainers import SortedList
sl = SortedList([(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")])

# Get the index of 53:

index = sl.bisect((53,))

# With the index, get the tuple:

tup = sl[index]

Cela fonctionnera beaucoup plus rapidement que la suggestion de compréhension de liste en effectuant une recherche binaire. La suggestion de dictionnaire sera encore plus rapide mais ne fonctionnera pas s'il peut y avoir des nombres en double avec des chaînes différentes.

S'il y a des numéros en double avec des chaînes différentes, vous devez faire une étape de plus:

end = sl.bisect((53 + 1,))

results = sl[index:end]

En divisant 54 en deux, nous trouverons l'index de fin de notre tranche. Ce sera nettement plus rapide sur les longues listes par rapport à la réponse acceptée.

GrantJ
la source
1

Juste une autre façon.

zip(*a)[0].index(53)
RussW
la source
-1

[k pour k, v dans l si v == ' delicia ']

voici la liste des tuples - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

Et au lieu de le convertir en dict, nous utilisons la compréhension de liste.

*Key* in Key,Value in list, where value = **delicia**

Mantej Singh
la source
Oui bien sûr. Merci @cosmoonot.
Mantej Singh
voici la liste des tuples - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] Et au lieu de le convertir en dict, nous utilisons la compréhension de liste. ` Key Key, la valeur dans la liste, où la valeur = Delicia `
Mantej Singh