Dada una matriz NumPy A , ¿cuál es la forma más rápida / eficiente de aplicar la misma función, f , a cada celda?
Supongamos que asignaremos a A (i, j) la f (A (i, j)) .
La función, f , no tiene una salida binaria, por lo que las operaciones de máscara (ing) no ayudarán.
¿Es la iteración doble "obvia" (a través de cada celda) la solución óptima?
Respuestas:
Puede simplemente vectorizar la función y luego aplicarla directamente a una matriz de Numpy cada vez que la necesite:
Probablemente sea mejor especificar un tipo de salida explícito directamente al vectorizar:
fuente
vectorize
descripción de la función: la función vectorizar se proporciona principalmente por conveniencia, no por rendimiento. La implementación es esencialmente un bucle for. Por lo tanto, es muy probable que esto no acelere el proceso en absoluto.vectorize
determina el tipo de retorno. Eso ha producido errores.frompyfunc
es un poco más rápido, pero devuelve una matriz de objetos dtype. Ambos alimentan escalares, no filas o columnas.np.vectorize
mi función (que utiliza RK45) me da una aceleración de un factor de ~ 20.Una pregunta similar es: mapear una matriz NumPy en su lugar . Si puede encontrar un ufunc para su f (), entonces debe usar el parámetro out.
fuente
Si está trabajando con números y
f(A(i,j)) = f(A(j,i))
, podría usar scipy.spatial.distance.cdist que define f como una distancia entreA(i)
yA(j)
.fuente
Creo que he encontrado una mejor solución. La idea de cambiar la función a la función universal de Python (ver documentación ), que puede ejercer un cálculo paralelo bajo el capó.
Uno puede escribir el suyo personalizado
ufunc
en C, que seguramente es más eficiente, o invocandonp.frompyfunc
, que es un método de fábrica incorporado. Después de la prueba, esto es más eficiente quenp.vectorize
:También he probado muestras más grandes, y la mejora es proporcional. Para comparar el rendimiento de otros métodos, vea esta publicación
fuente
Cuando el 2d-array (o nd-array) es contiguo en C o F, entonces esta tarea de mapear una función en un 2d-array es prácticamente la misma que la tarea de mapear una función en un 1d-array - simplemente tiene que verlo de esa manera, por ejemplo, a través de
np.ravel(A,'K')
.La posible solución para 1d-array se ha discutido, por ejemplo, aquí .
Sin embargo, cuando la memoria de la matriz 2d no es contigua, entonces la situación es un poco más complicada, porque a uno le gustaría evitar posibles errores de caché si el eje se maneja en el orden incorrecto.
Numpy ya cuenta con una maquinaria para procesar ejes en el mejor orden posible. Una posibilidad para usar esta maquinaria es
np.vectorize
. Sin embargo, la documentación de numpynp.vectorize
dice que "se proporciona principalmente por conveniencia, no por rendimiento": ¡una función lenta de Python sigue siendo una función lenta de Python con toda la sobrecarga asociada! Otro problema es su gran consumo de memoria; consulte, por ejemplo, esta publicación SO .Cuando se quiere tener el rendimiento de una función C pero usar la maquinaria de numpy, una buena solución es usar numba para la creación de ufuncs, por ejemplo:
Supera fácilmente,
np.vectorize
pero también cuando la misma función se realizaría como multiplicación / suma de matriz numpy, es decirVea el apéndice de esta respuesta para el código de medición de tiempo:
La versión de Numba (verde) es aproximadamente 100 veces más rápida que la función python (es decir
np.vectorize
), lo cual no es sorprendente. Pero también es aproximadamente 10 veces más rápido que la funcionalidad numpy, porque la versión numbas no necesita matrices intermedias y, por lo tanto, usa la caché de manera más eficiente.Si bien el enfoque ufunc de numba es una buena compensación entre usabilidad y rendimiento, todavía no es lo mejor que podemos hacer. Sin embargo, no hay una bala de plata o un enfoque mejor para cualquier tarea: uno tiene que entender cuáles son las limitaciones y cómo se pueden mitigar.
Por ejemplo, para las funciones trascendentales (p
exp
. Ej .sin
,cos
) Numba no ofrece ninguna ventaja sobre numpy'snp.exp
(no se crean matrices temporales, la fuente principal de la aceleración). Sin embargo, mi instalación de Anaconda utiliza el VML de Intel para vectores mayores que 8192 ; simplemente no puede hacerlo si la memoria no es contigua. Por lo tanto, podría ser mejor copiar los elementos en una memoria contigua para poder usar el VML de Intel:Para ser justos en la comparación, he desactivado la paralelización de VML (ver código en el apéndice):
Como se puede ver, una vez que se inicia VML, la sobrecarga de la copia está más que compensada. Sin embargo, una vez que los datos se vuelven demasiado grandes para el caché L3, la ventaja es mínima, ya que la tarea vuelve a estar vinculada al ancho de banda de memoria.
Por otro lado, numba también podría usar SVML de Intel, como se explica en esta publicación :
y usando VML con rendimientos de paralelización:
La versión de numba tiene menos sobrecarga, pero para algunos tamaños VML supera a SVML incluso a pesar de la sobrecarga de copia adicional, lo que no es una sorpresa ya que los ufuncs de numba no están paralelos.
Listados:
A. comparación de la función polinómica:
B. comparación de
exp
:fuente
Todas las respuestas anteriores se comparan bien, pero si necesita utilizar una función personalizada para el mapeo, y tiene
numpy.ndarray
, y necesita conservar la forma de la matriz.He comparado solo dos, pero conservará la forma de
ndarray
. He usado la matriz con 1 millón de entradas para comparar. Aquí uso la función cuadrada. Estoy presentando el caso general de la matriz n dimensional. Para dos dimensiones solo creaiter
2D.Salida
aquí puede ver claramente
numpy.fromiter
la función cuadrada del usuario, use cualquiera de su elección. Si su función depende de losi, j
índices de la matriz, repita el tamaño de la matrizfor ind in range(arr.size)
, usenumpy.unravel_index
para obtener eni, j, ..
función de su índice 1D y la forma de la matriz numpy.unravel_indexEsta respuesta está inspirada en mi respuesta a otra pregunta aquí
fuente