¿Cómo puedo encontrar el índice de la primera aparición de un número en una matriz Numpy? La velocidad es importante para mi. No me interesan las siguientes respuestas porque escanean toda la matriz y no se detienen cuando encuentran la primera aparición:
itemindex = numpy.where(array==item)[0][0]
nonzero(array == item)[0][0]
Nota 1: ninguna de las respuestas de esa pregunta parece relevante ¿Existe una función Numpy para devolver el primer índice de algo en una matriz?
Nota 2: se prefiere utilizar un método compilado en C a un bucle de Python.
Aunque es demasiado tarde para usted, pero para referencia futura: usar numba ( 1 ) es la forma más fácil hasta que numpy lo implemente. Si usa la distribución anaconda python, ya debería estar instalada. El código se compilará para que sea rápido.
y entonces:
fuente
xrange
necesario cambiarlorange
.enumerate
, como enfor i, v in enumerate(vec):
;if v == item: return i
. (Esta no es una buena idea en Python <= 2.7, dondeenumerate
crea una lista en lugar de un iterador básico).Hice un punto de referencia para varios métodos:
argwhere
nonzero
como en la pregunta.tostring()
como en la respuesta de @Rob ReilinkEl código Python y Fortran están disponibles. Me salté los poco prometedores, como convertirlos en una lista.
Los resultados a escala logarítmica. El eje X es la posición de la aguja (se tarda más en encontrar si está más abajo en la matriz); El último valor es una aguja que no está en la matriz. El eje Y es el momento de encontrarlo.
La matriz tenía 1 millón de elementos y las pruebas se ejecutaron 100 veces. Los resultados aún fluctúan un poco, pero la tendencia cualitativa es clara: Python y f2py abandonan el primer elemento, por lo que escalan de manera diferente. Python se vuelve demasiado lento si la aguja no está en el primer 1%, mientras que
f2py
es rápido (pero debe compilarlo).En resumen, f2py es la solución más rápida , especialmente si la aguja aparece bastante pronto.
No está integrado, lo que es molesto, pero en realidad son solo 2 minutos de trabajo. Agregue esto a un archivo llamado
search.f90
:Si está buscando algo diferente a
integer
, simplemente cambie el tipo. Luego compila usando:después de lo cual puede hacer (desde Python):
fuente
f2py
más lento para 1 artículo que para 10?Puede convertir una matriz booleana en una cadena de Python usando
array.tostring()
y luego usando el método find ():Sin embargo, esto implica copiar los datos, ya que las cadenas de Python deben ser inmutables. Una ventaja es que también puede buscar, por ejemplo, un flanco ascendente al encontrar
\x00\x01
fuente
En caso de arreglos ordenados,
np.searchsorted
funciona.fuente
Creo que ha encontrado un problema en el que un método diferente y algo a priori conocimiento a de la matriz realmente ayudarían. El tipo de cosas en las que tienes una probabilidad X de encontrar tu respuesta en el primer Y por ciento de los datos. La división del problema con la esperanza de tener suerte y luego hacer esto en python con una lista de comprensión anidada o algo así.
Escribir una función C para hacer esta fuerza bruta tampoco es demasiado difícil con ctypes .
El código C que pirateé juntos (index.c):
y la pitón:
y obtengo 92.
Envuelva la pitón en una función adecuada y listo.
La versión C es mucho (~ 20x) más rápida para esta semilla (advirtiendo que no soy bueno con timeit)
fuente
@tal ya presentó una
numba
función para encontrar el primer índice, pero eso solo funciona para matrices 1D. Connp.ndenumerate
también puede encontrar el primer índice en una matriz dimensional arbitraria:Caso de muestra:
Los tiempos muestran que es similar en rendimiento a la solución tals :
fuente
array
antes de introducirlonp.ndenumerate
, de modo que su eje de interés sea lo primero.np.argwhere
) a 717ns (su solución), ambos para una matriz de formas(3000000, 12)
).Si su lista está ordenada , puede lograr una búsqueda de índice muy rápida con el paquete 'bisect'. Es O (log (n)) en lugar de O (n).
encuentra x en la matriz a, definitivamente más rápido en el caso ordenado que cualquier rutina C que pasa por todos los primeros elementos (para listas lo suficientemente largas).
A veces es bueno saberlo.
fuente
>>> cond = "import numpy as np;a = np.arange(40)"
timeit("np.searchsorted(a, 39)", cond)
Funciona durante 3.47867107391 segundos.timeit("bisect.bisect(a, 39)", cond2)
Funciona durante 7.0661458969116 segundos. Parece quenumpy.searchsorted
es mejor para matrices ordenadas (al menos para ints).Hasta donde yo sé, solo np.any y np.all en matrices booleanas están en cortocircuito.
En su caso, numpy tiene que pasar por toda la matriz dos veces, una para crear la condición booleana y una segunda para encontrar los índices.
Mi recomendación en este caso sería utilizar cython. Creo que debería ser fácil ajustar un ejemplo para este caso, especialmente si no necesita mucha flexibilidad para diferentes tipos y formas.
fuente
Necesitaba esto para mi trabajo, así que me enseñé a mí mismo la interfaz C de Python y Numpy y escribí la mía propia. http://pastebin.com/GtcXuLyd Es solo para matrices 1-D, pero funciona para la mayoría de los tipos de datos (int, float o strings) y las pruebas han demostrado que es nuevamente unas 20 veces más rápido que el enfoque esperado en Python puro. numpy.
fuente
Este problema se puede resolver de manera efectiva en números puros procesando la matriz en fragmentos:
La matriz se procesa en trozos de tamaño
step
. Cuantostep
más largo sea el paso, más rápido será el procesamiento de la matriz con cero (el peor de los casos). Cuanto más pequeño es, más rápido se procesa la matriz con un valor distinto de cero al principio. El truco consiste en empezar con una pequeña cantidadstep
y aumentarla exponencialmente. Además, no es necesario incrementarlo por encima de algún umbral debido a los beneficios limitados.He comparado la solución con la solución ndarary.nonzero y numba pura con 10 millones de arreglos flotantes.
Y resultados en mi máquina:
Pure
ndarray.nonzero
es definitivamente más suelto. La solución numba es alrededor de 5 veces más rápida en el mejor de los casos. Es aproximadamente 3 veces más rápido en el peor de los casos.fuente
Si está buscando el primer elemento distinto de cero, puede usar el siguiente truco:
Es muy rapido solución "pura y pura" , pero falla en algunos casos que se describen a continuación.
La solución aprovecha el hecho de que prácticamente todas las representaciones de cero para tipos numéricos constan de
0
bytes. También se aplica a Numpybool
. En versiones recientes de numpy, laargmax()
función usa lógica de cortocircuito al procesar elbool
tipo. La talla debool
es 1 byte.Entonces uno necesita:
bool
. No se crea ninguna copiaargmax()
para encontrar el primer byte distinto de cero mediante lógica de cortocircuito//
) del desplazamiento por un tamaño de un solo elemento expresado en bytes (x.itemsize
)x[idx]
realidad es distinto de cero para identificar el caso en el que no hay ningún distinto de ceroHice un punto de referencia contra la solución numba y lo construí
np.nonzero
.El resultado en mi máquina es:
La solución es un 33% más rápida que numba y es "numpy-pure".
Las desventajas:
object
float
odouble
cálculosfuente
x
antes de llamarnonzero()
. Es probable que sea más lento que numba, pero ** no ** buscará en toda la matriz mientras busca la primera entrada cero, por lo que puede ser lo suficientemente rápido para sus necesidades.Como usuario de matlab desde hace mucho tiempo, he estado buscando una solución eficiente a este problema durante bastante tiempo. Finalmente, motivado por discusiones y proposiciones en este hilo , he tratado de encontrar una solución que implemente una API similar a la que se sugirió aquí , admitiendo por el momento solo matrices 1D.
Lo usarías así
Los operadores de condición admitidos son: cmp_equal, cmp_not_equal, cmp_larger, cmp_smaller, cmp_larger_eq, cmp_smaller_eq. Por razones de eficiencia, la extensión está escrita en c.
Aquí encontrará la fuente, los puntos de referencia y otros detalles:
https://pypi.python.org/pypi?name=py_find_1st&:action=display
para el uso en nuestro equipo (anaconda en linux y macos) he hecho un instalador anaconda que simplifica la instalación, puede usarlo como se describe aquí
https://anaconda.org/roebel/py_find_1st
fuente
Solo tenga en cuenta que si está haciendo una secuencia de búsquedas, la ganancia de rendimiento al hacer algo inteligente como convertir a una cadena, podría perderse en el ciclo externo si la dimensión de búsqueda no es lo suficientemente grande. Vea cómo el rendimiento de iterar find1 que usa el truco de conversión de cadenas propuesto anteriormente y find2 que usa argmax a lo largo del eje interior (más un ajuste para garantizar que no coincida devuelve -1)
salidas
Dicho esto, un hallazgo escrito en C sería al menos un poco más rápido que cualquiera de estos enfoques
fuente
Qué tal esto
fuente
where(array==item)[0][0]
de la pregunta ...Puede convertir su matriz en a
list
y usar suindex()
método:Hasta donde yo sé, este es un método compilado en C.
fuente
timeit()
en una matriz de 10000 enteros - ¡convertir a una lista fue aproximadamente 100 veces más lento! Había olvidado que la estructura de datos subyacente para una matriz numpy es muy diferente de una lista ..