54

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。你们中的任何人都可以找到更清洁的方法吗?

提前致谢!

4

6 回答 6

61

np.vectorize只是一个便利功能。它实际上并没有使代码运行得更快。如果使用起来不方便np.vectorize,只需编写自己的功能即可。

的目的np.vectorize是将不支持 numpy 的函数(例如,将浮点数作为输入并返回浮点数作为输出)转换为可以对(并返回)numpy 数组进行操作的函数。

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

因此,解决方案就是推出您自己的功能f,以您想要的方式工作。

于 2010-07-31T19:25:37.890 回答
21

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

于 2017-10-21T05:11:57.860 回答
6
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 可以更完整、更好地解决问题(我相信)。希望这可以帮助。

于 2014-07-24T05:07:47.330 回答
2

您想对函数进行矢量化

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])

然而,对于这样一个简单的函数,最好利用numpyufunctions;np.vectorize只是循环它。所以在你的情况下,只需将你的函数重写为

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

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

于 2020-02-01T17:50:51.280 回答
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 或 partial 包装它

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

注意vectorize说的文档字符串

提供该vectorize功能主要是为了方便,而不是为了性能。该实现本质上是一个 for 循环。

因此,我们希望amap这里有与 类似的性能vectorize。我没有检查它,欢迎任何性能测试。

如果性能真的很重要,你应该考虑别的,例如直接数组计算reshapebroadcast避免纯python中的循环(两者vectorize都是amap后一种情况)。

于 2016-08-02T01:34:21.060 回答
0

解决这个问题的最佳方法是使用二维 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 广播数组的自然能力。诀窍是确保至少一个维度在数组之间具有相等的长度。

于 2016-08-02T01:23:59.863 回答