2009-07-20 22 views
40

Tengo una lista de listas de dos elementos y necesito buscar cosas en ella.Búsqueda de Python en listas de listas

Si la lista es:

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

puedo buscar un par fácilmente haciendo

['a','b'] in list 

Ahora, hay una manera para ver si tengo una pareja en la que una cadena es presente en solo la segunda posición? Puedo hacer esto:

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

Pero hay una manera (mejor) sin el bucle for? No necesito saber i o seguir el ciclo después de encontrarlo.

Respuesta

31

Siempre tendrá un bucle: alguien podría venir con un inteligente delineador que esconde el bucle dentro de una llamada al map() o similar, pero siempre estará ahí.

Mi preferencia siempre sería tener un código limpio y simple, a menos que el rendimiento sea un factor importante.

Aquí es tal vez una versión más Pythonic de su código:

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'] 

Se rompe fuera del circuito tan pronto como se encuentra una coincidencia.

(Usted tiene un error tipográfico, por cierto, en ['b''d'].)

+0

En este punto de mi "carrera" python estoy a favor de este enfoque, ya que es muy fácil de leer. Aunque tal vez regrese para probar los otros para el rendimiento. Mi lista se pone bastante grande en un punto. ¿Hay algún lugar donde pueda comparar el rendimiento de los diferentes enfoques? – greye

+1

Utilice el módulo 'timeit' para las pruebas de rendimiento de este tipo de cosas: http://docs.python.org/library/timeit.html – RichieHindle

+0

¿Qué pasa si no sabemos si el personaje que estamos buscando está en la posición [0] th, [1] st, [2] nd etc. dentro de la lista anidada? p.ej. estamos buscando 'b', con este método solo devolverá ['a', 'b'] en lugar de ['b', 'd']. –

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

edición a añadir:

Tanto la respuesta de David usando cualquier y la mía usando en terminará cuando encuentra una coincidencia ya que estamos usando expresiones de generador. Aquí está una prueba usando un generador infinito para demostrar que:

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 

que al igual que el simple uso de en lugar de tanto == y cualquier.

+0

Eso es lo que se supone que debe hacer. –

+0

Creo que cualquiera() es más claro, pero supongo que es solo una preferencia personal. +1 luego ... –

53

Esta es la manera de hacerlo Pythonic:

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

O ... bueno, no voy a reclamar este es el "único y verdadero camino Pythonic" de hacerlo porque en algún momento se hace un poco subjetivo, qué es Ptónico y qué no, o qué método es más Ptónico que otro. Pero el uso de any() es definitivamente el estilo de Python más típico que un loop for como en p. RichieHindle's answer,

Por supuesto, hay un bucle oculto en la implementación de any, aunque se salga del bucle tan pronto como encuentre una coincidencia.


Como yo estaba aburrido Hice un script de tiempo para comparar el rendimiento de las diferentes sugerencias, modificando algunos de ellos si es necesario para hacer que el API de la misma.Ahora, debemos tener en cuenta que lo más rápido no siempre es lo mejor, y ser rápido definitivamente no es lo mismo que ser pitónico. Dicho esto, los resultados son ... extraños. Aparentemente los bucles for son muy rápidos, que no es lo que esperaba, así que los tomaría con un grano de sal sin entender por qué salieron como lo hacen.

De todos modos, cuando utilicé la lista definida en la pregunta con tres sublistas de dos elementos cada uno, del más rápido al más lento consigo estos resultados:

  1. RichieHindle's answer con el bucle for, en la hora en 0,22 mu s
  2. Terence Honles' first suggestion que crea una lista, en 0.36 mu s
  3. Pierre-Luc Bedard's answer (last code block), en 0.43 mu s
  4. Esencialmente atada entre Markus's answer y la for bucle de the original question, a 0,48 mu s
  5. Coady's answer usando operator.itemgetter(), en 0.53 mu s
  6. suficientemente cerca para contar como un empate entre Alex Martelli's answer con ifilter() y Anon's answer, a 0.67 mu s (Alex es consistentemente alrededor de la mitad de un microsegundo más rápido)
  7. Otro estrecha lazo -suficiente entre jojo's answer, mina, Brandon E Taylor's (que es idéntica a la mía), y Terence Honles' second suggestion usando any(), todo llegando a 0.81-0.82 mu s
  8. y entonces user27221's answer usando listas por comprensión anidados, en 0,95 mu s

Obviamente, los tiempos reales no son significativos en el hardware de otra persona, pero las diferencias entre ellos deberían dar una idea de cuán cerca están los diferentes métodos.

Cuando uso una lista más larga, las cosas cambian un poco. Comencé con la lista en la pregunta, con tres sublistas, y añadí otras 197 sublistas, para un total de 200 sublistas de dos de longitud. Utilizando esta lista más larga, aquí están los resultados:

  1. RichieHindle's answer, en las mismas 0.22 mu s como con la lista más corta
  2. Coady's answer utilizando operator.itemgetter(), de nuevo en 0,53 mu s
  3. Terence Honles' first suggestion que crea una lista, a 0,36 microsiemens
  4. Otro empate virtual entre Alex Martelli's answer con ifilter() y Anon's answer, a 0.67 mu s
  5. nuevo un empate de cerca lo suficiente entre mi respuesta, Brandon E Taylor's método idéntico, y Terence Honles' second suggestion usando any(), todos llegando a 0.81-0.82 mu s

Esos son los que mantienen su calendario original cuando la lista se amplía. El resto, que no lo hacen, son

  1. El for bucle de the original question, en el 1,24 mu s
  2. Terence Honles' first suggestion que crea una lista, en 7.49 mu s
  3. Pierre-Luc Bedard's answer (last code block), en 8 .12 mu s
  4. Markus's answer, a 10.27 mu s
  5. jojo's answer, a 19,87 mu s
  6. Y finalmente user27221's answer usando listas por comprensión anidados, a 60,59 mu s
+0

corto y más pitónico, así me gusta ;-) –

+0

muy pythonesque – 3kstc

+0

Para la comparación de respuestas, esto merece un "¡Gracias!" "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() lleva un montón de listas y agrupa elementos por índice, transponiendo efectivamente la matriz de lista de listas. El asterisco toma los contenidos de the_list y lo envía a zip como argumentos, por lo que efectivamente está pasando las tres listas por separado, que es lo que quiere zip. Lo único que queda es comprobar si "b" (o lo que sea) está en la lista compuesta de elementos con el índice que le interesa.

4

Markus tiene una forma de evitar el uso de la palabra for - aquí hay otra, que debería tiene un rendimiento mucho mejor durante largos the_list ... s:

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

Ah, bien, Alex está aquí. ;-) Obviamente, los gen exp usan la palabra 'para', pero si permitimos eso, interpretando el objetivo como evitar el estándar para la estructura de bucle en lugar de la palabra 'para' sí mismo, ¿cómo se comparan todas las respuestas dadas en términos de ¿actuación? – Anon

+0

@Anon, no tengo tiempo en este momento para ejecutar las habituales -mtimeit thingies (OSCON está activado, y estoy bastante ocupado con eso ;-), pero por experiencia previa sé que los itertools tienden a funcionar como un rayo engrasado. Todas las respuestas salvan la parada de Markus en el primer partido, por lo que todas son igualmente rápidas en este sentido. –

+0

NP en absoluto. Gracias. ;-) – Anon

2

No hay nada malo con el uso de un gen exp, pero si el objetivo es inline el bucle ...

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

debería ser el más rápido también.

12

el por encima de todo se ve bien

pero qué quiere mantener el resultado?

si es así ...

puede utilizar el siguiente

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

entonces un simple

len(result) 

le permite saber si se ha encontrado nada (y ahora se puede hacer cosas con los resultados)

por supuesto th no maneja elementos cuya longitud es inferior a (que debe verificar a menos que sepa que siempre son mayores que longitud 1, y en ese caso, ¿debería utilizar una tupla? (Tuplas son inmutables))

si usted sabe todos los artículos son una longitud establecida también se puede hacer:

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

o para Len (datos [0]) == 4:

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

...y yo recomendaría el uso de

for element in data: 
    ... 

en lugar de

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

(para usos futuros, a menos que desee guardar o utilizar 'i', y para que lo sé el '0' no es necesario, sólo es necesario utilizar la sintaxis completa si usted está comenzando en un valor distinto de cero)

+0

¡Gracias! Me encanta la comprensión de la lista :)) !! – Mick

4

¿Qué hay de:

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

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

Esto devolverá cada lista en la lista de listas con el segundo elemento igual a la búsqueda.

-1

A continuación se presenta una manera simple de encontrar exactamente en qué lugar de la lista se encuentra el artículo.

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 antiguo puesto, pero no hay una lista de un solo uso la expresión de responder: 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

Creo usando listas por comprensión anidada es la forma más elegante de resolver esto, porque el resultado intermedio es la posición donde el elemento es Una implementación sería:

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