Come cercare un elenco di tuple in Python


90

Quindi ho un elenco di tuple come questo:

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

Voglio questo elenco per una tupla il cui valore numerico è uguale a qualcosa.

In modo che se lo faccio search(53)restituirà il valore di indice di2

C'è un modo semplice per farlo?

Risposte:


94
[i for i, v in enumerate(L) if v[0] == 53]

68
Mi spieghi per favore?
schatten

17
Spiegato a parole: per ogni i, v in un elenco enumerato di L (che rende i la posizione dell'elemento nell'elenco enumerato ev la tupla originale) controlla se il primo elemento della tupla è 53, in tal caso, aggiungi il risultato del codice prima di "for" in un elenco appena creato, qui: i. Potrebbe anche essere my_function (i, v) o ancora un'altra comprensione della lista. Poiché la tua lista di tuple ha solo una tupla con 53 come primo valore, otterrai una lista con un elemento.
djangonaut

6
Vorrei solo aggiungere [i per i, v in enumerate (L) se v [0] == 53] .pop () per avere un valore int.
alemol


47

tl; dr

Un generatore di espressione è probabilmente la soluzione più performante e semplice al tuo problema:

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

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

Spiegazione

Ci sono diverse risposte che forniscono una semplice soluzione a questa domanda con la comprensione degli elenchi. Sebbene queste risposte siano perfettamente corrette, non sono ottimali. A seconda del tuo caso d'uso, potrebbero esserci vantaggi significativi apportando alcune semplici modifiche.

Il problema principale che vedo con l'utilizzo di una comprensione dell'elenco per questo caso d'uso è che l' intero elenco verrà elaborato, sebbene tu voglia trovare solo 1 elemento .

Python fornisce un semplice costrutto che è l'ideale qui. Si chiama espressione del generatore . Ecco un esempio:

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

Possiamo aspettarci che questo metodo abbia fondamentalmente lo stesso risultato delle comprensioni di lista nel nostro banale esempio, ma cosa succede se stiamo lavorando con un set di dati più grande? È qui che entra in gioco il vantaggio di utilizzare il metodo del generatore. Piuttosto che costruire un nuovo elenco, useremo l'elenco esistente come nostro iterabile e useremo next()per ottenere il primo elemento dal nostro generatore.

Vediamo come questi metodi si comportano in modo diverso su alcuni set di dati più grandi. Si tratta di elenchi di grandi dimensioni, composti da 10000000 + 1 elementi, con il nostro obiettivo all'inizio (migliore) o alla fine (peggiore). Possiamo verificare che entrambi questi elenchi funzioneranno allo stesso modo utilizzando la seguente comprensione dell'elenco:

Elenca le comprensioni

"Caso peggiore"

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}

"Caso migliore"

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}

Espressioni del generatore

Ecco la mia ipotesi per i generatori: vedremo che i generatori avranno prestazioni significativamente migliori nel migliore dei casi, ma allo stesso modo nel peggiore dei casi. Questo aumento delle prestazioni è dovuto principalmente al fatto che il generatore viene valutato pigramente, il che significa che calcolerà solo ciò che è necessario per produrre un valore.

Nel peggiore dei casi

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

Caso migliore

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}

CHE COSA?! Il caso migliore spazza via le comprensioni dell'elenco, ma non mi aspettavo che il nostro caso peggiore superasse a tal punto le comprensioni dell'elenco. Com'è? Francamente, potevo solo speculare senza ulteriori ricerche.

Prendi tutto questo con le pinze, non ho eseguito alcun profilo robusto qui, solo alcuni test di base. Ciò dovrebbe essere sufficiente per comprendere che un'espressione del generatore è più performante per questo tipo di ricerca nell'elenco.

Nota che questo è tutto Python di base integrato. Non è necessario importare nulla o utilizzare alcuna libreria.

Ho visto per la prima volta questa tecnica per la ricerca nel corso Udacity cs212 con Peter Norvig.


2
interessante, ho provato e ho trovato molto veloce
Grijesh Chauhan

3
Questa dovrebbe essere la risposta accettata. Le espressioni del generatore non materializzano l'intera sequenza di output quando vengono eseguite, ma valutano piuttosto un iteratore che produce un elemento alla volta dall'espressione.
BoltzmannBrain

2
Questo è fantastico, molto più veloce di una comprensione di elenchi nel mio caso, grazie!
mindm49907

29

Le tue tuple sono fondamentalmente coppie chiave-valore - un python dict- quindi:

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

Modifica - aha, dici che vuoi il valore di indice di (53, "xuxa"). Se questo è davvero quello che vuoi, dovrai scorrere l'elenco originale, o forse creare un dizionario più complicato:

d = dict((n,i) for (i,n) in enumerate(e[0] for e in l))
idx = d[53]

2
Se ignoriamo ciò che l'OP ha effettivamente chiesto, penso che la tua risposta iniziale sia la migliore risposta a "Come cercare un elenco di tuple in Python"
Rick Westera

La tua prima risposta è stata utile per i miei scopi. Forse è meglio usare .get (), nel caso in cui l'elemento non sia nel dict. l = [(1,"juca"),(22,"james"),(53,"xuxa"),(44,"delicia")] val = dict(l).get(53)
user1503941

12

Hmm ... beh, il modo più semplice che mi viene in mente è convertirlo in un dict

d = dict(thelist)

e l'accesso d[53].

EDIT : Oops, ho letto male la tua domanda la prima volta. Sembra che tu voglia effettivamente ottenere l'indice in cui è memorizzato un dato numero. In tal caso, prova

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

invece di una semplice vecchia dictconversione. Allora d[53]sarebbe 2.


6

Supponendo che l'elenco possa essere lungo e che i numeri possano ripetersi, prendi in considerazione l'utilizzo del tipo SortedList dal modulo Sortedcontainers di Python . Il tipo SortedList manterrà automaticamente le tuple in ordine per numero e consentirà una ricerca rapida.

Per esempio:

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]

Questo funzionerà molto più velocemente del suggerimento di comprensione dell'elenco eseguendo una ricerca binaria. Il suggerimento del dizionario sarà ancora più veloce ma non funzionerà se potrebbero esserci numeri duplicati con stringhe diverse.

Se sono presenti numeri duplicati con stringhe diverse, è necessario eseguire un ulteriore passaggio:

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

results = sl[index:end]

Bisettando per 54, troveremo l'indice finale per la nostra fetta. Questo sarà significativamente più veloce su elenchi lunghi rispetto alla risposta accettata.


1

Solo un altro modo.

zip(*a)[0].index(53)

-1

[k per k, v in l se v == ' delicia ']

ecco la lista delle tuple - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")]

E invece di convertirlo in un dict, stiamo usando la comprensione di llist.

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


Si certo. Grazie @cosmoonot.
Mantej Singh

ecco l'elenco delle tuple - [(1, "juca"), (22, "james"), (53, "xuxa"), (44, "delicia")] E invece di convertirlo in un dict, stiamo usando la comprensione di llist. " Key in Key, Value in list, where value = delicia "
Mantej Singh
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.