¿Primer índice de lista de Python mayor que x?


81

¿Cuál sería la forma más pitónica de encontrar el primer índice en una lista que sea mayor que x?

Por ejemplo, con

list = [0.5, 0.3, 0.9, 0.8]

La función

f(list, 0.7)

volvería

2.

57
no use 'lista' como nombre de variable ...
mshsayem

11
Quieres decir "pitónico". Según urbandictionary.com/define.php?term=Pythonesque , " pythonesque " significa "surrealista, absurdo", y no creo que eso sea lo que estás buscando: P
Roberto Bonvallet

1
La pregunta es ambigua. ¿La respuesta es 2porque 0.9 > 0.7o porque 0.8 > 0.7? En otras palabras, ¿está buscando secuencialmente o en el orden de valores crecientes?
Sergey Orshanskiy


Voté para cerrar esta pregunta como un duplicado en lugar de hacerlo al revés porque la pregunta más nueva es más genérica.
Cristian Ciupitu

Respuestas:


118
next(x[0] for x in enumerate(L) if x[1] > 0.7)

29
+1: aunque preferiría evitar los números mágicos: siguiente (idx para idx, valor en enumerar (L) si valor> 0,7)
truppo

38
+1 por simplicidad y next(), pero tal vez esto por legibilidad:next(i for i,v in enumerate(L) if v > 0.7)
Will Hardy

14
Si bien esto se ve bien, el caso en el que no haya resultado generará una StopIteration confusa.
Virgil Dupras

3
@Wim: Pero luego regresas a evaluar toda la secuencia. Úselo en itertools.chain()lugar de agregar listas como esta.
Ignacio Vazquez-Abrams

3
@Wim, no necesitas cadena () aquí. next () acepta el segundo argumento:next((i for i, x in enumerate(L) if x > value), -1)
jfs

35

si la lista está ordenada, entonces bisect.bisect_left(alist, value)es más rápido para una lista grande quenext(i for i, x in enumerate(alist) if x >= value) .


Buena respuesta: definitivamente fue de 5 a 6 veces más rápido para mí usando una pequeña lista ordenada de 4 elementos pero, (no estoy seguro si estoy cometiendo un error), pero cuando cronometro esto usando timeit con una larga lista de 10000 elementos numpy array Sin embargo, encuentro que es aproximadamente el doble de lento que la respuesta de comprensión de la lista anterior, lo que me sorprendió.
Adrian Tompkins

1
@AdrianTompkins: algo anda mal con su punto de referencia. bisect_leftes O (log n), mientras que listcomp es O (n), es decir, cuanto mayor sea, mayor será nla ventaja del bisect_left()lado. Intenté encontrar el índice de 500_000al range(10**6)usar bisect_left()-> 3.75 microsegundos y usar genexpr con next()-> 51.0 milisegundos [ 10_000veces] más lento como se esperaba.
jfs

16
filter(lambda x: x>.7, seq)[0]

4
-1: Si bien es técnicamente correcto, no use el filtro donde la comprensión de una lista es más legible y más
eficaz

filter (lambda x: x [1]> .7, enumerate (seq)) [0] [0] - búsqueda lineal simple
lowtech

4
El filtro @truppo en Python 3 devuelve un generador, por lo que no debería ser peor que una lista de comprensión. También encuentro esta forma más legible que la solución enumerada.
BubuIIC

Una cosa que no es agradable de este es que ingresa al manejo de excepciones si no hay ningún elemento en la secuencia mayor que .7.
Brian C.

La solución es técnicamente incorrecta. El autor de la pregunta preguntó cómo encontrar el índice del elemento en la lista. Pero esta solución devuelve un registro en su lugar. Eventmore en Python 3.8 es más lento que bisect_left()(el más rápido) y enumerate().
Sergey Nevmerzhitsky

16
>>> alist= [0.5, 0.3, 0.9, 0.8]
>>> [ n for n,i in enumerate(alist) if i>0.7 ][0]
2

2
fallará si 'x' es mayor que cualquier otro valor en la lista
mshsayem

2
@mshsayem: El problema está mal definido para este caso. El fracaso puede ser lo correcto.
S.Lott

@ S.Loot: Buen punto. En su defecto, si no en los resultados de lista en un error comprensible cuando se asigna esto a una variable: IndexError: list index out of range. El uso index = next[ n for n,i in enumerate(alist) if i>0.7 ]de error da: NameError: name 'index' is not defined. nextes ligeramente más rápido: la diferencia de tiempo es de 12,7 ns frente a 11,9 ns para 60 000 números.
Leo

11
for index, elem in enumerate(elements):
    if elem > reference:
        return index
raise ValueError("Nothing Found")


3

1) NUMPY ARGWHERE, listas generales

Si está contento de usar numpy, lo siguiente funcionará en listas generales (ordenadas o sin clasificar):

numpy.argwhere(np.array(searchlist)>x)[0]

o si necesita la respuesta en forma de lista:

numpy.argwhere(np.array(searchlist)>x).tolist()[0]

o si necesita la respuesta como un índice entero:

numpy.argwhere(np.array(searchlist)>x).tolist()[0][0]

2) NUMPY BÚSQUEDA CLASIFICADA, listas ordenadas (muy eficiente para buscar listas)

Sin embargo, si su lista de búsqueda está ordenada, es mucho más limpio y agradable usar la función np.searchsorted :

numpy.searchsorted(searchlist,x)

Lo bueno de usar esta función es que además de buscar un único valor x, x también puede ser una lista, es decir, también puede devolver una lista de índices para una lista de valores buscados [x1, x2, x3 .. xn ], ( y es muy eficiente en relación con la comprensión de una lista en este caso ).


2

Tuve un problema similar cuando mi lista era muy larga. la comprensión o las soluciones basadas en filtros pasarían por la lista completa. itertools.take while romperá el ciclo una vez que la condición se vuelva falsa la primera vez:

from itertools import takewhile

def f(l, b): return len([x for x in takewhile(lambda x: x[1] <= b, enumerate(l))])

l = [0.5, 0.3, 0.9, 0.8]
f(l, 0.7)

1
¿por qué no escribe def f (l, b): return len (list (take while (lambda x: x [1] <= b, enumerate (l))))?
Avo Asatryan

2

Sé que ya hay muchas respuestas, pero a veces siento que la palabra pythonic se traduce en 'una sola línea'.

Cuando creo que una mejor definición está más cerca de esta respuesta :

"Aprovechar las características del lenguaje Python para producir código claro, conciso y fácil de mantener".

Si bien algunas de las respuestas anteriores son concisas, no las encuentro claras y un programador novato tardaría un tiempo en comprenderlas, por lo que no las hace extremadamente fáciles de mantener para un equipo formado por muchos niveles de habilidad.

l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: break
    return l.index(i)


f(l,.7)

o

l = [0.5, 0.3, 0.9, 0.8]

def f(l, x):
    for i in l:
        if i >x: return l.index(i)



f(l,.7)

Creo que lo anterior es fácil de entender por un novato y aún es lo suficientemente conciso como para ser aceptado por cualquier programador de Python veterano.

Creo que escribir código tonto es positivo.


1
>>> f=lambda seq, m: [ii for ii in xrange(0, len(seq)) if seq[ii] > m][0]
>>> f([.5, .3, .9, .8], 0.7)
2

Eso se ve bastante hábil. Pero teóricamente recorrerá toda la lista y luego devolverá el primer resultado (mayor que x), ¿verdad? ¿Hay alguna forma de hacer uno que se detenga inmediatamente después de encontrar el primer resultado?
c00kiemonster

¿Qué hay de malo en recorrer la lista completa? si el primer valor mayor que 0,7 está cerca del final de la lista, no hay ninguna diferencia.
ghostdog74

3
Cierto. Pero en este caso particular, las listas en las que tengo la intención de usar la función son bastante largas, por lo que preferiría dejar de atravesar tan pronto como se encuentre una coincidencia ...
c00kiemonster

sea ​​largo o no, si el primer valor es el último segundo elemento de la lista, ¡aún tendrá que recorrer toda la lista para llegar allí!
ghostdog74

4
@ ghostdog74: Sí, pero esta no es una razón para querer que todos los casos sean los peores casos.
UncleBens


-1

Prueba este:

def Renumerate(l):
    return [(len(l) - x, y) for x,y in enumerate(l)]

código de ejemplo:

Renumerate(range(10))

salida:

(10, 0)
(9, 1)
(8, 2)
(7, 3)
(6, 4)
(5, 5)
(4, 6)
(3, 7)
(2, 8)
(1, 9)

1
La pregunta era " encontrar el primer índice en una lista que sea mayor que x ".
Gino Mempin
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.