numpy.amax () encontrará el valor máximo en una matriz, y numpy.amin () hace lo mismo con el valor mínimo. Si quiero encontrar tanto el máximo como el mínimo, tengo que llamar a ambas funciones, lo que requiere pasar dos veces la matriz (muy grande), lo que parece lento.
¿Existe una función en la API numpy que encuentre tanto el máximo como el mínimo con solo un paso a través de los datos?
amax
amin
minmax
a la biblioteca en cuestión ( github.com/numpy/numpy/issues/9836 ).Respuestas:
No. En el momento de escribir este artículo, no existe tal función. (Y sí, si no eran tal función, su rendimiento sería significativamente mejor que llamar
numpy.amin()
ynumpy.amax()
sucesivamente sobre una gran variedad.)fuente
No creo que pasar por encima de la matriz dos veces sea un problema.Considere el siguiente pseudocódigo:Si bien solo hay 1 bucle aquí, todavía hay 2 controles. (En lugar de tener 2 bucles con 1 cheque cada uno). Realmente, lo único que guarda es la sobrecarga de 1 bucle. Si las matrices son realmente grandes como dice, esa sobrecarga es pequeña en comparación con la carga de trabajo real del ciclo. (Tenga en cuenta que todo esto está implementado en C, por lo que los bucles son más o menos libres de todos modos).
EDITAR Lo siento por los 4 que votaron a favor y tuvieron fe en mí. Definitivamente puedes optimizar esto.
Aquí hay un código de fortran que se puede compilar en un módulo de Python a través de
f2py
(tal vez unCython
gurú pueda venir y comparar esto con una versión C optimizada ...):Compílelo a través de:
Y ahora estamos en un lugar donde podemos probarlo:
Los resultados son un poco asombrosos para mí:
Tengo que decir que no lo entiendo del todo. Comparar solo
np.min
versusminmax1
yminmax2
sigue siendo una batalla perdida, por lo que no es solo un problema de memoria ...notas : aumentar el tamaño en un factor de
10**a
y disminuir la repetición en un factor de10**a
(mantener constante el tamaño del problema) cambia el rendimiento, pero no de una manera aparentemente consistente, lo que muestra que hay alguna interacción entre el rendimiento de la memoria y la sobrecarga de llamadas de función en pitón. Incluso comparar unamin
implementación simple en fortran supera a la de numpy por un factor de aproximadamente 2 ...fuente
i < minval
es verdadero, entoncesi > maxval
siempre es falso, por lo que solo necesita hacer 1.5 verificaciones por iteración en promedio cuando el segundoif
se reemplaza por unelif
.f2py
simplemente envuelve Fortran codificado a mano para que Python lo pueda llamar. Una prueba "más justa" probablemente sea codificar manualmente C y luego usarf2py
(!) Para ajustarlo a Python. Si está permitiendo C ++, entonces Shed Skin puede ser el punto ideal para equilibrar la facilidad de codificación con el rendimiento.Hay una función para encontrar (max-min) llamada numpy.ptp si le resulta útil:
pero no creo que haya una manera de encontrar tanto el mínimo como el máximo con un recorrido.
EDITAR: ptp solo llama mínimo y máximo bajo el capó
fuente
Puede usar Numba , que es un compilador dinámico de Python compatible con NumPy que usa LLVM. La implementación resultante es bastante simple y clara:
También debería ser más rápido que la
min() & max()
implementación de Numpy . Y todo ello sin tener que escribir una sola línea de código C / Fortran.Haz tus propias pruebas de rendimiento, ya que siempre depende de tu arquitectura, tus datos, las versiones de tus paquetes ...
fuente
numba
función una vez antes del punto de referencia para asegurarse de que esté compilada con JIT? ?. Además, si usaipython
, por simplicidad, le sugiero que lo use%timeit whatever_code()
para medir el tiempo de ejecución.elif
permite que su mínimo sea mayor que su máximo. Por ejemplo, con una matriz de longitud 1, el máximo será el valor que sea, mientras que el mínimo es + infinito. No es un gran problema para un código único, pero no es bueno para lanzarlo profundamente en el vientre de una bestia de producción.En general, puede reducir la cantidad de comparaciones para un algoritmo minmax procesando dos elementos a la vez y solo comparando el más pequeño con el mínimo temporal y el más grande con el máximo temporal. En promedio, solo se necesitan 3/4 de las comparaciones que un enfoque ingenuo.
Esto podría implementarse en co fortran (o cualquier otro lenguaje de bajo nivel) y debería ser casi imbatible en términos de rendimiento. Estoy usandonumba para ilustrar el principio y obtener una implementación muy rápida, independiente de dtype:
Definitivamente es más rápido que el enfoque ingenuo que presentó Peque :
Como se esperaba, la nueva implementación de minmax solo toma aproximadamente 3/4 del tiempo que tomó la implementación ingenua (
2.1 / 2.75 = 0.7636363636363637
)fuente
Solo para obtener algunas ideas sobre los números que uno podría esperar, dados los siguientes enfoques:
(los
extrema_loop_*()
enfoques son similares a los que se proponen aquí , mientras que losextrema_while_*()
enfoques se basan en el código de aquí )Los siguientes horarios:
indican que
extrema_while_*()
son los más rápidos, con losextrema_while_nb()
más rápidos. En cualquier caso, también las solucionesextrema_loop_nb()
yextrema_loop_cy()
superan el enfoque de solo NumPy (usandonp.max()
y pornp.min()
separado).Finalmente, tenga en cuenta que ninguno de estos es tan flexible como
np.min()
/np.max()
(en términos de soporte n-dim,axis
parámetro, etc.).(el código completo está disponible aquí )
fuente
extrema_while_nb
Nadie mencionó numpy.percentile , así que pensé que lo haría. Si pregunta por
[0, 100]
percentiles, le dará una matriz de dos elementos, el mínimo (percentil 0) y el máximo (percentil 100).Sin embargo, no satisface el propósito del OP: no es más rápido que el mínimo y el máximo por separado. Eso probablemente se deba a alguna maquinaria que permitiría percentiles no extremos (un problema más difícil, que debería llevar más tiempo).
Una versión futura de Numpy podría incluir un caso especial para omitir el cálculo del percentil normal si solo
[0, 100]
se solicita. Sin agregar nada a la interfaz, hay una manera de pedirle a Numpy el mínimo y el máximo en una llamada (al contrario de lo que se dijo en la respuesta aceptada), pero la implementación estándar de la biblioteca no aprovecha este caso para hacerlo vale la pena.fuente
Este es un hilo antiguo, pero de todos modos, si alguien vuelve a mirar esto ...
Al buscar el mínimo y el máximo simultáneamente, es posible reducir el número de comparaciones. Si está comparando flotadores (lo que supongo), esto podría ahorrarle algo de tiempo, aunque no complejidad computacional.
En lugar de (código Python):
primero puede comparar dos valores adyacentes en la matriz, y luego solo comparar el más pequeño con el mínimo actual y el más grande con el máximo actual:
El código aquí está escrito en Python, claramente para la velocidad usarías C o Fortran o Cython, pero de esta manera haces 3 comparaciones por iteración, con len (ar) / 2 iteraciones, dando 3/2 * len (ar) comparaciones. A diferencia de eso, al hacer la comparación "de la manera obvia" se hacen dos comparaciones por iteración, lo que lleva a comparaciones 2 * len (ar). Le ahorra un 25% de tiempo de comparación.
Tal vez alguien algún día lo encuentre útil.
fuente
np.bincount
, vea aquí . No usa el truco que señala, porque resultó ser hasta 2 veces más lento que el enfoque ingenuo. Existe un vínculo desde el RP a algunos puntos de referencia completos de ambos métodos.A primera vista, parece hacer el truco:
numpy.histogram
... pero si nos fijamos en la fuente de esa función, simplemente se llama
a.min()
ya.max()
de forma independiente, y por lo tanto no puede evitar los problemas de rendimiento abordan en esta pregunta. :-(Del mismo modo,
scipy.ndimage.measurements.extrema
parece una posibilidad, pero también, simplemente llamaa.min()
y de formaa.max()
independiente.fuente
np.histogram
no siempre funciona para esto porque los(amin, amax)
valores devueltos son para los valores mínimo y máximo del contenedor. Si tengo, por ejemploa = np.zeros(10)
,np.histogram(a, bins=1)
devuelve(array([10]), array([-0.5, 0.5]))
. El usuario busca(amin, amax)
= (0, 0) en ese caso.De todos modos, valió la pena el esfuerzo para mí, así que propondré aquí la solución más difícil y menos elegante para quien pueda estar interesado. Mi solución es implementar un mínimo-máximo de subprocesos múltiples en un algoritmo de un paso en C ++, y usarlo para crear un módulo de extensión de Python. Este esfuerzo requiere un poco de sobrecarga para aprender a usar las API de Python y NumPy C / C ++, y aquí mostraré el código y daré algunas pequeñas explicaciones y referencias para quien desee seguir este camino.
Min / Max multiproceso
No hay nada demasiado interesante aquí. La matriz se divide en trozos de tamaño
length / workers
. El mínimo / máximo se calcula para cada fragmento en afuture
, que luego se escanea para el mínimo / máximo global.El módulo de extensión de Python
Aquí es donde las cosas empiezan a ponerse feas ... Una forma de usar el código C ++ en Python es implementar un módulo de extensión. Este módulo se puede construir e instalar utilizando el
distutils.core
módulo estándar. Una descripción completa de lo que esto implica se cubre en la documentación de Python: https://docs.python.org/3/extending/extending.html . NOTA: ciertamente hay otras formas de obtener resultados similares, por citar https://docs.python.org/3/extending/index.html#extending-index :Esencialmente, esta ruta es probablemente más académica que práctica. Habiendo dicho eso, lo que hice a continuación fue, manteniéndome bastante cerca del tutorial, crear un archivo de módulo. Esto es esencialmente un texto estándar para que los distutils sepan qué hacer con su código y creen un módulo de Python a partir de él. Antes de hacer algo de esto, probablemente sea aconsejable crear un entorno virtual de Python para no contaminar los paquetes de su sistema (consulte https://docs.python.org/3/library/venv.html#module-venv ).
Aquí está el archivo del módulo:
En este archivo hay un uso significativo tanto de Python como de la API de NumPy, para más información consultar: https://docs.python.org/3/c-api/arg.html#c.PyArg_ParseTuple , y para NumPy : https://docs.scipy.org/doc/numpy/reference/c-api.array.html .
Instalación del módulo
Lo siguiente que debe hacer es utilizar distutils para instalar el módulo. Esto requiere un archivo de instalación:
Para finalmente instalar el módulo, ejecute
python3 setup.py install
desde su entorno virtual.Prueba del módulo
Finalmente, podemos probar para ver si la implementación de C ++ realmente supera al uso ingenuo de NumPy. Para hacerlo, aquí hay un script de prueba simple:
Estos son los resultados que obtuve al hacer todo esto:
Estos son mucho menos alentadores de lo que los resultados indican anteriormente en el hilo, que indicaron en algún lugar una aceleración de alrededor de 3.5x, y no incorporaron subprocesos múltiples. Los resultados que obtuve son algo razonables, esperaría que la sobrecarga de subprocesos y dominaría el tiempo hasta que las matrices se volvieran muy grandes, momento en el que el aumento de rendimiento comenzaría a acercarse a
std::thread::hardware_concurrency
x aumento.Conclusión
Ciertamente, parece que hay espacio para optimizaciones específicas de aplicaciones para algunos códigos de NumPy, en particular con respecto al subproceso múltiple. No tengo claro si vale la pena el esfuerzo o no, pero ciertamente parece un buen ejercicio (o algo así). Creo que quizás aprender algunas de esas "herramientas de terceros" como Cython puede ser un mejor uso del tiempo, pero quién sabe.
fuente
v = min_max_it->get();
. Elget
método se bloquea hasta que el resultado está listo y lo devuelve. Dado que el ciclo pasa por cada futuro, no terminará hasta que todo esté terminado. future.get ()La forma más corta que he encontrado es esta:
Pero dado que ordena la matriz, no es la más eficiente.
Otra forma corta sería:
Esto debería ser más eficiente, pero el resultado se calcula y se devuelve un flotante.
fuente