在返回向量的函数上使用Numpy Vectorize

时间:2010-07-31 18:20:10

标签: python arrays numpy vectorization

numpy.vectorize采用函数f:a-> b并将其转换为g:a [] - > b []。

ab是标量符号时,此方法正常,但我无法想到为什么它不能作为ndarray或列表使用b,即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。你们中的任何人都能找到更清洁的方法吗?

提前致谢!

6 个答案:

答案 0 :(得分:33)

np.vectorize只是一个便利功能。它实际上并不是make code run any faster。如果使用np.vectorize不方便,只需编写自己喜欢的函数即可。

np.vectorize的目的是转换不是numpy-aware的函数(例如将float作为输入并将float作为输出返回)到可以操作(并返回)numpy数组的函数中。

你的函数f已经是numpy-aware - 它在其定义中使用了一个numpy数组并返回一个numpy数组。所以np.vectorize不适合您的用例。

因此,解决方案就是按照您希望的方式滚动您自己的函数f

答案 1 :(得分:5)

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)是返回值的形状,()是参数的形状,是标量。参数也可以是数组。有关更复杂的签名,请参阅Generalized Universal Function API

答案 2 :(得分:3)

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可以更完整,更好地解决问题(我相信)。希望这会有所帮助。

答案 3 :(得分:1)

我已经写了一个函数,它似乎符合你的需要。

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或部分包装

g = lambda x:amap(f, x)
g(np.arange(4))

请注意vectorize的文档字符串

  

提供vectorize功能主要是为了方便,而非提供   性能。实现基本上是for循环。

因此,我们希望此处的amap具有与vectorize类似的效果。我没有检查过,欢迎任何性能测试。

如果表现非常重要,您应该考虑其他因素,例如:使用reshapebroadcast进行直接数组计算以避免纯python中的循环(vectorizeamap都是后一种情况。)

答案 4 :(得分:1)

您要向量化功能

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的ufunction。 np.vectorize只是在其上循环。因此,在您的情况下,只需将您的函数重写为

def f(x):
    return np.multiply.outer(x, np.array([1,1,1,1,1], dtype=np.float32))

这更快,并且产生的模糊错误更少(但是,请注意,如果您传递的是复数或四精度数,则结果dtype将取决于x,结果也将如此)。

答案 5 :(得分:0)

解决此问题的最佳方法是使用2-D NumPy数组(在本例中为列数组)作为原始函数的输入,然后生成2-D输出结果我相信你期待。

以下是代码中的内容:

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广播数组的自然能力。诀窍是确保至少一个维度在数组之间具有相等的长度。