¿Cuál es la forma más eficiente de asignar una función a una matriz numpy? La forma en que lo he estado haciendo en mi proyecto actual es la siguiente:
import numpy as np
x = np.array([1, 2, 3, 4, 5])
# Obtain array of square of each element in x
squarer = lambda t: t ** 2
squares = np.array([squarer(xi) for xi in x])
Sin embargo, esto parece ser probablemente muy ineficiente, ya que estoy usando una comprensión de lista para construir la nueva matriz como una lista de Python antes de convertirla nuevamente en una matriz numpy.
¿Podemos hacerlo mejor?
python
performance
numpy
Ryan
fuente
fuente
squarer(x)
?x = np.array([1, 2, 3, 4, 5]); x**2
funcionaRespuestas:
He probado todos los métodos sugeridos más
np.array(map(f, x))
conperfplot
(un pequeño proyecto mío).Si la función que está tratando de vectorizar ya está vectorizada (como el
x**2
ejemplo en la publicación original), su uso es mucho más rápido que cualquier otra cosa (tenga en cuenta la escala de registro):Si realmente necesita vectorización, realmente no importa mucho qué variante use.
Código para reproducir las tramas:
fuente
f(x)
fuera de tu trama. Puede que no sea aplicable para todosf
, pero es aplicable aquí, y es fácilmente la solución más rápida cuando corresponde.vf = np.vectorize(f); y = vf(x)
gana por entradas cortas.pip install -U perfplot
), veo el mensaje:AttributeError: 'module' object has no attribute 'save'
al pegar el código de ejemplo.¿Qué hay de usar
numpy.vectorize
.fuente
The vectorize function is provided primarily for convenience, not for performance. The implementation is essentially a for loop.
en otras preguntas descubrí quevectorize
podría duplicar la velocidad de iteración del usuario. Pero la aceleración real es connumpy
operaciones de matriz real .squarer(x)
ya funcionaría para matrices no 1d.vectorize
solo tiene alguna ventaja sobre la comprensión de una lista (como la de la pregunta), no terminasquarer(x)
.TL; DR
Como señaló @ user2357112 , un método "directo" para aplicar la función es siempre la forma más rápida y sencilla de mapear una función sobre matrices Numpy:
En general
np.vectorize
, evítelo , ya que no funciona bien y tiene (o tuvo) varios problemas . Si está manejando otros tipos de datos, es posible que desee investigar los otros métodos que se muestran a continuación.Comparación de métodos
Aquí hay algunas pruebas simples para comparar tres métodos para mapear una función, este ejemplo con Python 3.6 y NumPy 1.15.4. Primero, las funciones de configuración para probar:
Prueba con cinco elementos (ordenados del más rápido al más lento):
Con cientos de elementos:
Y con miles de elementos de matriz o más:
Las diferentes versiones de Python / NumPy y la optimización del compilador tendrán resultados diferentes, así que haga una prueba similar para su entorno.
fuente
count
argumento y una expresión generadora, entoncesnp.fromiter
es significativamente más rápido.'np.fromiter((f(xi) for xi in x), x.dtype, count=len(x))'
f(x)
, que supera todo lo demás en un orden de magnitud .f
tiene 2 variables y la matriz es 2D?Hay numexpr , numba y cython , el objetivo de esta respuesta es tener en cuenta estas posibilidades.
Pero primero expongamos lo obvio: no importa cómo mapees una función de Python en una matriz numpy, sigue siendo una función de Python, eso significa para cada evaluación:
Float
).Entonces, qué maquinaria se usa para recorrer el conjunto no juega un papel importante debido a la sobrecarga mencionada anteriormente: se mantiene mucho más lenta que el uso de la funcionalidad incorporada de numpy.
Echemos un vistazo al siguiente ejemplo:
np.vectorize
se selecciona como representante de la clase de enfoques de la función de python puro. Usandoperfplot
(vea el código en el apéndice de esta respuesta) obtenemos los siguientes tiempos de ejecución:Podemos ver que el enfoque numpy es 10x-100x más rápido que la versión pura de Python. La disminución del rendimiento para tamaños de matriz más grandes probablemente se deba a que los datos ya no se ajustan al caché.
También vale la pena mencionar que
vectorize
también usa mucha memoria, por lo que a menudo el uso de la memoria es el cuello de botella (consulte la pregunta SO relacionada ). También tenga en cuenta que la documentación de Numpynp.vectorize
dice que "se proporciona principalmente por conveniencia, no por desempeño".Deben usarse otras herramientas, cuando se desea rendimiento, además de escribir una extensión C desde cero, existen las siguientes posibilidades:
A menudo se escucha que el rendimiento de numpy es tan bueno como es posible, porque es puro C debajo del capó. ¡Sin embargo, hay mucho margen de mejora!
La versión numpy vectorizada utiliza mucha memoria adicional y accesos a la memoria. Numexp-library intenta enlosar las matrices numpy y así obtener una mejor utilización de la caché:
Lleva a la siguiente comparación:
No puedo explicar todo en el diagrama anterior: podemos ver una sobrecarga mayor para numexpr-library al principio, pero debido a que utiliza mejor el caché, ¡es aproximadamente 10 veces más rápido para matrices más grandes!
Otro enfoque es compilar jit la función y así obtener un UFunc puro en C real. Este es el enfoque de numba:
Es 10 veces más rápido que el enfoque numpy original:
Sin embargo, la tarea es vergonzosamente paralelizable, por lo que también podríamos usarla
prange
para calcular el ciclo en paralelo:Como se esperaba, la función paralela es más lenta para entradas más pequeñas, pero más rápida (casi factor 2) para tamaños más grandes:
Mientras que numba se especializa en optimizar operaciones con matrices numpy, Cython es una herramienta más general. Es más complicado extraer el mismo rendimiento que con numba: a menudo se reduce a llvm (numba) frente al compilador local (gcc / MSVC):
Cython resulta en funciones algo más lentas:
Conclusión
Obviamente, probar solo una función no prueba nada. También se debe tener en cuenta que, para el ejemplo de función elegido, el ancho de banda de la memoria era el cuello de botella para tamaños mayores de 10 ^ 5 elementos, por lo que tuvimos el mismo rendimiento para numba, numexpr y cython en esta región.
Al final, la respuesta definitiva depende del tipo de función, hardware, distribución de Python y otros factores. Por ejemplo Anaconda-de distribución utiliza VML de Intel para funciones de numpy y por lo tanto supera a numba (a menos que utiliza SVML, ver este SO-post ) fácilmente para funciones trascendentales como
exp
,sin
,cos
y similares - véase, por ejemplo la siguiente SO-post .Sin embargo, a partir de esta investigación y de mi experiencia hasta el momento, afirmaría que la numba parece ser la herramienta más fácil con el mejor rendimiento siempre que no se involucren funciones trascendentales.
Trazado de tiempos de ejecución con perfplot -package :
fuente
Las operaciones aritméticas en matrices se aplican automáticamente por elementos, con bucles eficientes de nivel C que evitan toda la sobrecarga del intérprete que se aplicaría a un bucle o comprensión de nivel Python.
La mayoría de las funciones que desearía aplicar a una matriz NumPy por elementos simplemente funcionarán, aunque algunas pueden necesitar cambios. Por ejemplo,
if
no funciona por elementos. Desea convertirlos para usar construcciones comonumpy.where
:se convierte
fuente
En muchos casos, numpy.apply_along_axis será la mejor opción. Aumenta el rendimiento en aproximadamente 100 veces en comparación con los otros enfoques, y no solo para funciones de prueba triviales, sino también para composiciones de funciones más complejas de numpy y scipy.
Cuando agrego el método:
al código del diagrama de trama, obtengo los siguientes resultados:
fuente
Creo que en la versión más nueva (uso 1.13) de numpy, simplemente puede llamar a la función pasando la matriz numpy a la función que escribió para el tipo escalar, aplicará automáticamente la llamada de función a cada elemento sobre la matriz numpy y le devolverá otra matriz numpy
fuente
**
operador el que aplica el cálculo a cada elemento t det
. Eso es ordinario numpy. Envolverlo en ellambda
no hace nada extra.Parece que nadie ha mencionado un método de fábrica incorporado para producir
ufunc
en un paquete numpy:np.frompyfunc
que he probado nuevamentenp.vectorize
y lo he superado en un 20 ~ 30%. Por supuesto, funcionará bien según el código C prescrito o inclusonumba
(que no he probado), pero puede ser una mejor alternativa quenp.vectorize
También he probado muestras más grandes, y la mejora es proporcional. Vea la documentación también aquí.
fuente
Como se menciona en esta publicación , solo use expresiones generadoras de esta manera:
fuente
Todas las respuestas anteriores se comparan bien, pero si necesita usar una función personalizada para el mapeo, y tiene
numpy.ndarray
, y necesita conservar la forma de la matriz.Comparé solo dos, pero conservará la forma de
ndarray
. He usado la matriz con 1 millón de entradas para comparar. Aquí utilizo la función cuadrada, que también está incorporada en numpy y tiene un gran aumento de rendimiento, ya que si era necesario algo, puede usar la función que prefiera.Salida
aquí puede ver claramente que
numpy.fromiter
funciona muy bien teniendo en cuenta un enfoque simple, y si la función incorporada está disponible, úsela.fuente
Utilizar
numpy.fromfunction(function, shape, **kwargs)
Consulte " https://docs.scipy.org/doc/numpy/reference/generated/numpy.fromfunction.html "
fuente