[英]Using Numpy Vectorize on Functions that Return Vectors
numpy.vectorize
接受一个函数 f:a->b 并将其转换为 g:a[]->b[]。
当a
和b
是标量时,这很好用,但我想不出为什么它不能将 b 作为ndarray
或列表工作,即 f:a->b[] 和 g:a[]-> b[][]
例如:
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))
这产生:
array([[ 0. 0. 0. 0. 0.],
[ 1. 1. 1. 1. 1.],
[ 2. 2. 2. 2. 2.],
[ 3. 3. 3. 3. 3.]], dtype=object)
好的,这样就给出了正确的值,但给出了错误的 dtype。 更糟糕的是:
g(a).shape
产量:
(4,)
所以这个数组几乎没用。 我知道我可以转换它:
np.array(map(list, a), dtype=np.float32)
给我我想要的:
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
但这既不高效也不pythonic。 你们中的任何人都可以找到更清洁的方法吗?
提前致谢!
np.vectorize
只是一个方便的功能。 它实际上并没有使代码运行得更快。 如果使用np.vectorize
,只需编写您自己的函数即可。
np.vectorize
的目的是将不支持 numpy 的函数(例如,将浮点数作为输入并返回浮点数作为输出)转换为可以对(并返回)numpy 数组进行操作的函数。
您的函数f
已经是 numpy 感知的——它在其定义中使用了一个 numpy 数组并返回一个 numpy 数组。 所以np.vectorize
不适合您的用例。
因此,解决方案只是滚动您自己的函数f
以按您希望的方式工作。
1.12.0 中的新参数signature
完全符合您的要求。
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
g = np.vectorize(f, signature='()->(n)')
然后g(np.arange(4)).shape
将给出(4L, 5L)
。
这里指定了f
的签名。 (n)
是返回值的形状, ()
是标量参数的形状。 参数也可以是数组。 有关更复杂的签名,请参阅通用通用函数 API 。
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)
这应该可以解决问题,并且无论您的输入大小如何,它都会起作用。 “地图”仅适用于一维输入。 使用“.tolist()”并创建一个新的 ndarray 可以更完整、更好地解决问题(我相信)。 希望这可以帮助。
您想对函数进行矢量化
import numpy as np
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
假设您想要获得单个np.float32
数组作为结果,您必须将其指定为otype
。 但是,在您的问题中,您指定了otypes=[np.ndarray]
这意味着您希望每个元素都是np.ndarray
。 因此,您正确地得到了dtype=object
的结果。
正确的调用是
np.vectorize(f, signature='()->(n)', otypes=[np.float32])
然而,对于这样一个简单的函数,最好利用numpy
的 ufunctions; np.vectorize
只是循环它。 所以在你的情况下,只需将你的函数重写为
def f(x):
return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))
这更快并且产生更少的模糊错误(但是请注意,如果您传递复数或四精度数,结果dtype
将取决于x
,结果也是如此)。
我写了一个函数,它似乎符合你的需要。
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)
让我们试试
def f(x):
return x * np.array([1,1,1,1,1], dtype=np.float32)
amap(f, np.arange(4))
输出
array([[ 0., 0., 0., 0., 0.],
[ 1., 1., 1., 1., 1.],
[ 2., 2., 2., 2., 2.],
[ 3., 3., 3., 3., 3.]], dtype=float32)
为方便起见,您也可以用 lambda 或 partial 包装它
g = lambda x:amap(f, x)
g(np.arange(4))
注意vectorize
的文档字符串说
提供
vectorize
功能主要是为了方便,而不是为了性能。 该实现本质上是一个 for 循环。
因此,我们希望这里的amap
具有与vectorize
相似的性能。 我没有检查它,欢迎任何性能测试。
如果性能真的很重要,你应该考虑其他的东西,例如直接数组计算与reshape
和broadcast
以避免纯 python 中的循环( vectorize
和amap
都是后一种情况)。
解决这个问题的最佳方法是使用二维 NumPy 数组(在本例中为列数组)作为原始函数的输入,然后生成一个二维输出,其结果是我相信您所期望的。
以下是它在代码中的样子:
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)
这是一种更简单且不易出错的方式来完成操作。 此方法不是尝试使用 numpy.vectorize 转换函数,而是依赖于 NumPy 广播数组的自然能力。 诀窍是确保至少一个维度在数组之间具有相等的长度。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.