2009-07-20 22 views
40

Ho un elenco di elenchi di due elementi e devo cercare le cose in esso.Ricerca Python negli elenchi di elenchi

Se l'elenco è:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 

posso cercare un paio semplice eseguendo

['a','b'] in list 

Ora, c'è un modo per vedere se ho un paio in cui una stringa è presente solo nella seconda posizione? Posso fare questo:

for i in range (0, len(list)): 
    if list[i][1]==search: 
     found=1 

ma c'è un (migliore) modo senza il ciclo for? Non ho bisogno di sapere i o di tenere il ciclo in corso dopo che è stato trovato.

risposta

31

Hai sempre un ciclo - qualcuno potrebbe venire con un intelligente one-liner che nasconde il ciclo all'interno di una chiamata a map() o simile, ma sarà sempre lì.

La mia preferenza sarebbe sempre avere un codice semplice e pulito, a meno che le prestazioni non siano un fattore importante.

Ecco forse una versione più Pythonic del codice:

data = [['a','b'], ['a','c'], ['b','d']] 
search = 'c' 
for sublist in data: 
    if sublist[1] == search: 
     print "Found it!", sublist 
     break 
# Prints: Found it! ['a', 'c'] 

Rompe fuori dal giro, non appena trova una corrispondenza.

(Si dispone di un errore di battitura, tra l'altro, in ['b''d'].)

+0

A questo punto della mia "carriera" in pitone, preferisco questo approccio poiché è molto facile da leggere. Potrei tornare a provare gli altri per le prestazioni, però. La mia lista diventa abbastanza grande ad un certo punto. C'è un posto dove posso confrontare le prestazioni dei diversi approcci? – greye

+1

Usa il modulo 'timeit' per il test delle prestazioni di questo genere di cose: http://docs.python.org/library/timeit.html – RichieHindle

+0

E se non sappiamo se il personaggio che stiamo cercando sia in la posizione [0] th, [1] st, [2] nd ecc. all'interno dell'elenco annidato? per esempio. stiamo cercando 'b', usando questo metodo restituirebbe solo ['a', 'b'] piuttosto che ['b', 'd']. –

7
>>> my_list =[ ['a', 'b'], ['a', 'c'], ['b', 'd'] ] 
>>> 'd' in (x[1] for x in my_list) 
True 

Modifica per aggiungere:

Sia la risposta di David utilizzando qualsiasi e la mia usando in finirà quando trova una corrispondenza poiché utilizziamo espressioni generatrici. Ecco una prova utilizzando un generatore infinito di dimostrare che:

def mygen(): 
    ''' Infinite generator ''' 
    while True: 
     yield 'xxx' # Just to include a non-match in the generator 
     yield 'd' 

print 'd' in (x for x in mygen())  # True 
print any('d' == x for x in mygen()) # True 
# print 'q' in (x for x in mygen())  # Never ends if uncommented 
# print any('q' == x for x in mygen()) # Never ends if uncommented 

mi piace semplicemente usando in invece sia == e qualsiasi.

+0

Questo è quello che dovrebbe fare. –

+0

Penso che any() sia più chiaro, ma suppongo che sia solo una preferenza personale. +1 poi ... –

53

Ecco il modo Pythonic per farlo:

data = [['a','b'], ['a','c'], ['b','d']] 
search = 'c' 
any(e[1] == search for e in data) 

Oppure ... beh, non ho intenzione di rivendicare questo è il "unico vero modo Pythonic" per farlo perché a un certo punto diventa un po 'soggettivo ciò che è Pythonic e ciò che non lo è, o quale metodo è più Pythonic di un altro. Ma usare lo stile any() è decisamente più tipico di Python di un ciclo for come ad es. RichieHindle's answer,

Ovviamente esiste un ciclo nascosto nell'implementazione di any, sebbene si interrompa non appena trova una corrispondenza.


Poiché ero annoiato feci uno script di temporizzazione per confrontare le prestazioni delle diverse proposte, modificando alcuni come necessario per rendere l'API stesso.Ora, dovremmo tenere a mente che il più veloce non è sempre il migliore, e essere veloci non è sicuramente la stessa cosa di essere Pythonic. Detto questo, i risultati sono ... strani. A quanto pare i loop for sono molto veloci, il che non è quello che mi aspettavo, quindi li prenderei con un pizzico di sale senza capire perché sono venuti fuori come loro.

Ad ogni modo, quando ho usato l'elenco definito nella questione con tre liste parziali di due elementi ciascuno, dal più veloce al più lento ottengo questi risultati:

  1. RichieHindle's answer con il ciclo for, un clock di 0,22 ms
  2. Terence Honles' first suggestion che crea un elenco, a 0,36 ms
  3. Pierre-Luc Bedard's answer (last code block), a 0,43 ms
  4. essenzialmente legati tra Markus's answer e for ciclo da the original question, a 0,48 ms
  5. Coady's answer usando operator.itemgetter(), a 0,53 ms
  6. Abbastanza vicino a contare come un legame tra Alex Martelli's answer con ifilter() e Anon's answer, a 0.67 ms (Alex è costantemente circa la metà di un microsecondo più veloce)
  7. Un altro vicino -Basta legame tra jojo's answer, miniera, Brandon E Taylor's (che è identico al mio), e Terence Honles' second suggestion utilizzando any(), tutti provenienti in alle 0.81-0.82 ms
  8. e poi user27221's answer utilizzando comprehensions elenco nidificati, a 0,95 ms

Ovviamente i tempi effettivi non sono significativi sull'hardware di nessun altro, ma le differenze tra di loro dovrebbero dare un'idea di quanto siano vicini i diversi metodi.

Quando uso una lista più lunga, le cose cambiano un po '. Ho iniziato con la lista nella domanda, con tre sottoliste, e ho aggiunto altre 197 sottoliste, per un totale di 200 sottoliste ciascuna di lunghezza due. Usando questa lista più, ecco i risultati:

  1. RichieHindle's answer, alle stesse 0,22 ms come con l'elenco più breve
  2. Coady's answer utilizzando operator.itemgetter(), di nuovo a 0,53 ms
  3. Terence Honles' first suggestion che crea un elenco, a 0.36 ms
  4. un altro legame virtuale tra Alex Martelli's answer con ifilter() e Anon's answer, a 0.67 ms
  5. nuovo una cravatta stretta-sufficiente tra la mia risposta, Brandon E Taylor's metodo identico, e Terence Honles' second suggestion utilizzando any(), tutti provenienti in alle 0.81-0.82 ms

Questi sono quelli che mantengono la loro temporizzazione originale quando l'elenco viene esteso. Il resto, che non lo fanno, sono

  1. Il for ciclo da the original question, a 1.24 ms
  2. Terence Honles' first suggestion che crea un elenco, a 7,49 ms
  3. Pierre-Luc Bedard's answer (last code block), alle 8 .12 ms
  4. Markus's answer, a 10.27 ms
  5. jojo's answer, a 19.87 ms
  6. E infine user27221's answer utilizzando list comprehension nidificate, a 60,59 ms
+0

breve e più pythonic, quindi mi piace ;-) –

+0

molto pythonesque – 3kstc

+0

Per il confronto delle risposte questo merita un "Grazie!" "spam" :) –

15
>>> the_list =[ ['a','b'], ['a','c'], ['b''d'] ] 
>>> any('c' == x[1] for x in the_list) 
True 
1
>>> the_list =[ ['a','b'], ['a','c'], ['b','d'] ] 
>>> "b" in zip(*the_list)[1] 
True 

zip() prende un mazzo di liste e raggruppa elementi per indice, trasponendo efficacemente la matrice list-of-list. L'asterisco prende il contenuto di the_list e lo invia a zip come argomenti, quindi stai effettivamente passando i tre elenchi separatamente, che è ciò che vuole zip. Tutto ciò che rimane è controllare se "b" (o qualsiasi altra cosa) sia presente nell'elenco composto da elementi con l'indice che ti interessa.

4

Markus ha un modo per evitare di usare la parola for - eccone un'altra, che dovrebbe hanno prestazioni molto meglio per lunghi the_list s ...:

import itertools 
found = any(itertools.ifilter(lambda x:x[1]=='b', the_list) 
+0

Ah, bene, Alex è qui. ;-) Ovviamente, il gen exp usa la parola 'per' - ma se permettiamo che, interpretando l'obiettivo come evitare lo standard per la struttura del ciclo invece della parola 'per' stessa, come confrontare tutte le risposte date in termini di prestazione? – Anon

+0

@Anon, non ho tempo adesso per eseguire le solite cose -mtimeit (OSCON è acceso, e sono piuttosto impegnato con esso ;-), ma per esperienza precedente so che gli itertools tendono ad esibirsi come un fulmine unto. Tutte le risposte salvano la sosta di Markus alla prima partita, quindi sono tutti ugualmente veloci in questo senso. –

+0

NP affatto. Grazie. ;-) – Anon

2

Niente di sbagliato con l'utilizzo di un exp gen, ma se l'obiettivo è quello di inline il ciclo ...

>>> import itertools, operator 
>>> 'b' in itertools.imap(operator.itemgetter(1), the_list) 
True 

dovrebbe essere il più veloce anche.

12

l'soprattutto guardare bene

ma vuoi mantenere il risultato?

in caso affermativo ...

è possibile utilizzare il seguente

result = [element for element in data if element[1] == search] 

quindi un semplice

len(result) 

permette di sapere se è stato trovato nulla (e ora si può fare cose con i risultati)

ovviamente th non gestisce elementi di lunghezza inferiore a uno (che dovresti controllare se non sai che sono sempre maggiori di lunghezza 1, e in tal caso dovresti usare una tupla? (Tuple sono immutabili))

se si conosce tutti gli elementi sono una lunghezza prestabilita si può anche fare:

any(second == search for _, second in data) 

o per len (dati [0]) == 4:

any(second == search for _, second, _, _ in data) 

...e mi consiglia di utilizzare

for element in data: 
    ... 

invece di

for i in range(len(data)): 
    ... 

(per usi futuri, a meno che non si desidera salvare o utilizzare 'i', e solo così sai il '0' è non richiesto, è sufficiente utilizzare la sintassi completa se si avvia ad un valore diverso da zero)

+0

Grazie! Adoro la comprensione delle liste :)) !! – Mick

4

Che dire:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
search = 'b' 

filter(lambda x:x[1]==search,list) 

Ciò restituirà ogni lista nell'elenco di elenchi con il secondo elemento uguale alla ricerca.

-1

Dato qui di seguito è un modo semplice per trovare esattamente nella lista l'elemento.

for i in range (0,len(a)): 
sublist=a[i] 
for i in range(0,len(sublist)): 
    if search==sublist[i]: 
     print "found in sublist "+ "a"+str(i) 
2

k vecchio post, ma nessuno l'uso lista espressione di rispondere: P

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
Search = 'c' 

# return if it find in either item 0 or item 1 
print [x for x,y in list if x == Search or y == Search] 

# return if it find in item 1 
print [x for x,y in list if y == Search] 
0

Penso usando list comprehension nidificati è il modo più elegante per risolvere questo, perché il risultato intermedio è la posizione in cui l'elemento è. Un'implementazione sarebbe:

list =[ ['a','b'], ['a','c'], ['b','d'] ] 
search = 'c' 
any([ (list.index(x),x.index(y)) for x in list for y in x if y == search ])