So durchsuchen Sie eine Liste von Tupeln in Python

89

Ich habe also eine Liste solcher Tupel:

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

Ich möchte diese Liste für ein Tupel, dessen Zahlenwert gleich etwas ist.

Wenn ich das tue search(53), wird der Indexwert von zurückgegeben2

Gibt es eine einfache Möglichkeit, dies zu tun?

hdx
quelle

Antworten:

94
[i for i, v in enumerate(L) if v[0] == 53]
Ignacio Vazquez-Abrams
quelle
68
Würden Sie bitte erklären?
schatten
17
In Worten erklärt: Überprüfen Sie für jedes i, v in einer Aufzählungsliste von L (das i zur Position des Elements in der Aufzählungsliste und v zum ursprünglichen Tupel macht), ob das erste Element des Tupels 53 ist. Wenn ja, fügen Sie das Ergebnis des Codes hinzu vor 'für' zu einer neu erstellten Liste, hier: i. Es könnte auch my_function (i, v) oder noch ein anderes Listenverständnis sein. Da Ihre Tupelliste nur ein Tupel mit 53 als erstem Wert enthält, erhalten Sie eine Liste mit einem Element.
Djangonaut
6
Ich würde nur [i für i, v in Aufzählung (L) hinzufügen, wenn v [0] == 53] .pop () einen int-Wert haben würde.
Alemol
49

Sie können ein Listenverständnis verwenden :

>>> 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
quelle
47

tl; dr

Ein Generatorausdruck ist wahrscheinlich die performanteste und einfachste Lösung für Ihr Problem:

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

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

Erläuterung

Es gibt mehrere Antworten, die eine einfache Lösung für diese Frage mit Listenverständnis bieten. Diese Antworten sind zwar vollkommen richtig, aber nicht optimal. Abhängig von Ihrem Anwendungsfall können einige einfache Änderungen erhebliche Vorteile haben.

Das Hauptproblem bei der Verwendung eines Listenverständnisses für diesen Anwendungsfall besteht darin, dass die gesamte Liste verarbeitet wird, obwohl Sie nur 1 Element finden möchten .

Python bietet ein einfaches Konstrukt, das hier ideal ist. Es wird der Generatorausdruck genannt . Hier ist ein Beispiel:

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

Wir können erwarten, dass diese Methode im Wesentlichen die gleiche Leistung wie das Listenverständnis in unserem trivialen Beispiel erbringt. Was ist jedoch, wenn wir mit einem größeren Datensatz arbeiten? Hier kommt der Vorteil der Generatormethode ins Spiel. Anstatt eine neue Liste zu erstellen, verwenden wir Ihre vorhandene Liste als iterable und verwenden sienext() , um das erste Element von unserem Generator abzurufen.

Schauen wir uns an, wie sich diese Methoden bei einigen größeren Datenmengen unterschiedlich verhalten. Dies sind große Listen mit 10000000 + 1 Elementen, wobei unser Ziel am Anfang (am besten) oder am Ende (am schlechtesten) liegt. Wir können anhand des folgenden Listenverständnisses überprüfen, ob beide Listen gleich gut funktionieren:

Listenverständnisse

"Schlimmsten Fall"

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}

"I'm besten fall"

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}

Generatorausdrücke

Hier ist meine Hypothese für Generatoren: Wir werden sehen, dass Generatoren im besten Fall eine deutlich bessere Leistung erbringen, im schlimmsten Fall jedoch ähnlich. Dieser Leistungsgewinn ist hauptsächlich auf die Tatsache zurückzuführen, dass der Generator träge ausgewertet wird, was bedeutet, dass nur berechnet wird, was erforderlich ist, um einen Wert zu erhalten.

Schlimmsten Fall

# 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}

I'm besten fall

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}

WAS?! Der beste Fall bläst weg das Listenverständnis zum Erliegen, aber ich hatte nicht erwartet, dass unser schlechtester Fall das Listenverständnis in einem solchen Ausmaß übertrifft. Wie ist das? Ehrlich gesagt konnte ich nur ohne weitere Forschung spekulieren.

Nehmen Sie all dies mit einem Körnchen Salz, ich habe hier keine robuste Profilierung durchgeführt, nur einige sehr grundlegende Tests. Dies sollte ausreichen, um zu erkennen, dass ein Generatorausdruck für diese Art der Listensuche leistungsfähiger ist.

Beachten Sie, dass dies alles grundlegende, integrierte Python ist. Wir müssen nichts importieren oder Bibliotheken verwenden.

Ich habe diese Technik zum ersten Mal für die Suche im Udacity cs212- Kurs mit Peter Norvig gesehen.

Jon Surrell
quelle
2
Interessant, ich habe getestet und fand es sehr schnell
Grijesh Chauhan
3
Dies sollte die akzeptierte Antwort sein. Generatorausdrücke materialisieren nicht die gesamte Ausgabesequenz, wenn sie ausgeführt werden, sondern werden zu einem Iterator ausgewertet, der jeweils ein Element aus dem Ausdruck ergibt.
BoltzmannBrain
2
Das ist großartig, viel schneller als ein Listenverständnis in meinem Fall, danke!
mindm49907
28

Ihre Tupel sind im Grunde Schlüssel-Wert-Paare - eine Python - dictalso:

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

Bearbeiten - aha, Sie sagen, Sie möchten den Indexwert von (53, "xuxa"). Wenn dies wirklich das ist, was Sie wollen, müssen Sie die ursprüngliche Liste durchlaufen oder vielleicht ein komplizierteres Wörterbuch erstellen:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]
Andrew Jaffe
quelle
2
Wenn wir ignorieren, wonach das OP tatsächlich gefragt hat, ist Ihre erste Antwort meiner Meinung nach die beste Antwort auf "So suchen Sie eine Liste von Tupeln in Python"
Rick Westera
Ihre erste Antwort war für meine Zwecke nützlich. Vielleicht ist es besser, .get () zu verwenden, falls das Element nicht im Diktat enthalten ist. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941
12

Hmm ... nun, der einfache Weg, der mir in den Sinn kommt, besteht darin, ihn in ein Diktat umzuwandeln

d = dict(thelist)

und Zugang d[53].

EDIT : Ups, falsch verstanden Ihre Frage das erste Mal. Es hört sich so an, als ob Sie tatsächlich den Index erhalten möchten, in dem eine bestimmte Nummer gespeichert ist. Versuchen Sie es in diesem Fall

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

anstelle einer einfachen alten dictBekehrung. Dann d[53]wäre 2.

David Z.
quelle
6

Angenommen, die Liste ist lang und die Zahlen wiederholen sich. Verwenden Sie möglicherweise den Typ SortedList aus dem Python-Sortiercontainer-Modul . Der Typ SortedList verwaltet die Tupel automatisch in der Reihenfolge ihrer Nummer und ermöglicht eine schnelle Suche.

Beispielsweise:

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]

Dies funktioniert durch eine binäre Suche viel schneller als der Vorschlag zum Listenverständnis. Der Wörterbuchvorschlag ist noch schneller, funktioniert aber nicht, wenn doppelte Zahlen mit unterschiedlichen Zeichenfolgen vorhanden sein könnten.

Wenn es doppelte Zahlen mit unterschiedlichen Zeichenfolgen gibt, müssen Sie einen weiteren Schritt ausführen:

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

results = sl[index:end]

Durch Halbieren für 54 finden wir den Endindex für unser Slice. Dies ist auf langen Listen im Vergleich zur akzeptierten Antwort erheblich schneller.

GrantJ
quelle
1

Nur ein anderer Weg.

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

[k für k, v in l wenn v == ' delicia ']

hier ist l die Liste der Tupel - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

Und anstatt es in ein Diktat umzuwandeln, verwenden wir das Listenverständnis.

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

Mantej Singh
quelle
Ja sicher. Vielen Dank, dass Sie @cosmoonot.
Mantej Singh
hier ist l die Liste der Tupel - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] Und anstatt sie in ein Diktat umzuwandeln, Wir verwenden das Listenverständnis. ` Key in Key, Wert in der Liste, wobei Wert = delicia `
Mantej Singh