python - array - Usando Numex Vectorize en funciones que devuelven vectores
numpy vectorize (5)
La mejor manera de resolver esto sería usar una matriz NumPy 2-D (en este caso una matriz de columnas) como una entrada a la función original , que luego generará una salida 2-D con los resultados que creo que esperaba.
Esto es lo que podría verse en el código:
import numpy as np
def f(x):
return x*np.array([1, 1, 1, 1, 1], dtype=np.float32)
a = np.arange(4).reshape((4, 1))
b = f(a)
# b is a 2-D array with shape (4, 5)
print(b)
Esta es una forma mucho más simple y menos propensa a errores para completar la operación. En lugar de tratar de transformar la función con numpy.vectorize, este método se basa en la capacidad natural de NumPy para transmitir matrices. El truco es asegurarse de que al menos una dimensión tenga la misma longitud entre las matrices.
numpy.vectorize
toma una función f: a-> b y la convierte en g: a [] -> b [].
Esto funciona bien cuando a
y b
son escalares, pero no puedo pensar en una razón por la cual no funcione con b como una ndarray
o lista, es decir, f: a-> b [] yg: a [] -> segundo[][]
Por ejemplo:
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
print(g(a))
Esto produce:
array([[ 0. 0. 0. 0. 0.],
[ 1. 1. 1. 1. 1.],
[ 2. 2. 2. 2. 2.],
[ 3. 3. 3. 3. 3.]], dtype=object)
Ok, eso da los valores correctos, pero el tipo incorrecto. Y aún peor:
g(a).shape
rendimientos:
(4,)
Entonces esta matriz es bastante inútil. Sé que puedo convertirlo haciendo:
np.array(map(list, a), dtype=np.float32)
para darme lo que quiero:
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
pero eso no es ni eficiente ni pitónico. ¿Alguno de ustedes puede encontrar una manera más limpia de hacer esto?
¡Gracias por adelantado!
np.vectorize
es solo una función de conveniencia. En realidad, no hace que el código se ejecute más rápido . Si no es conveniente usar np.vectorize
, simplemente escriba su propia función que funcione como desee.
El objetivo de np.vectorize
es transformar las funciones que no son numpy-aware (por ejemplo, tomar flotantes como entrada y devolver flotantes como salida) en funciones que pueden operar en matrices numpy (y regresar).
Su función f
ya es numpy-aware - usa una matriz numpy en su definición y devuelve una matriz numpy. Entonces np.vectorize
no es una buena np.vectorize
para su caso de uso.
La solución, por lo tanto, es simplemente desplegar su propia función f
que funcione de la manera que desee.
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, otypes=[np.ndarray])
a = np.arange(4)
b = g(a)
b = np.array(b.tolist())
print(b)#b.shape = (4,5)
c = np.ones((2,3,4))
d = g(c)
d = np.array(d.tolist())
print(d)#d.shape = (2,3,4,5)
Esto debería solucionar el problema y funcionará independientemente del tamaño de su entrada. "mapa" solo funciona para una entrada dimensional. Usar ".tolist ()" y crear un nuevo ndarray resuelve el problema de forma más completa y agradable (creo). Espero que esto ayude.
He escrito una función, parece ajustarse a tu necesidad.
def amap(func, *args):
''''''array version of build-in map
amap(function, sequence[, sequence, ...]) -> array
Examples
--------
>>> amap(lambda x: x**2, 1)
array(1)
>>> amap(lambda x: x**2, [1, 2])
array([1, 4])
>>> amap(lambda x,y: y**2 + x**2, 1, [1, 2])
array([2, 5])
>>> amap(lambda x: (x, x), 1)
array([1, 1])
>>> amap(lambda x,y: [x**2, y**2], [1,2], [3,4])
array([[1, 9], [4, 16]])
''''''
args = np.broadcast(None, *args)
res = np.array([func(*arg[1:]) for arg in args])
shape = args.shape + res.shape[1:]
return res.reshape(shape)
Dejar que intente
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
amap(f, np.arange(4))
Salidas
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
También puede envolverlo con lambda o parcial para mayor comodidad
g = lambda x:amap(f, x)
g(np.arange(4))
Tenga en cuenta que el docstring de vectorize
dice
La función
vectorize
se proporciona principalmente por conveniencia, no por rendimiento. La implementación es esencialmente un ciclo for.
Por lo tanto, esperaríamos que el amap
aquí tenga un rendimiento similar al de vectorize
. No lo revisé, cualquier prueba de rendimiento es bienvenida.
Si el rendimiento es realmente importante, debe considerar otra cosa, por ejemplo, el cálculo de matriz directa con reshape
y broadcast
para evitar el bucle en python puro (tanto vectorize
como amap
son el caso posterior).
Una nueva signature
parámetro en 1.12.0 hace exactamente lo que tú.
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, signature=''()->(n)'')
Entonces la forma g(np.arange(4)).shape
Dará (4L, 5L)
.
Aquí se especifica la firma de f
. El (n)
es la forma del valor de retorno, y el ()
es la forma del parámetro que es escalar. Y los parámetros también pueden ser matrices. Para firmas más complejas, vea la API de función universal generalizada .