Numpy矢量化,使用列表作为参数

9
numpy中的vectorize函数非常有用,但在函数参数是列表而不是标量时,它的表现不佳。以一个例子来说明:
import numpy as np

def f(x, A):
    print "type(A)=%s, A=%s"%(type(A),A)
    return sum(A)/x

X = np.linspace(1,2,10)
P = [1,2,3]

f2 = np.vectorize(f)

f(X,P)
f2(X,P)

提供:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'numpy.int64'>, A=1

Traceback (most recent call last):
  File "vectorize.py", line 14, in <module>
    f2(X,P)
  File "/usr/local/lib/python2.6/dist-packages/numpy/lib/function_base.py", line 1824, in __call__
    theout = self.thefunc(*newargs)
  File "vectorize.py", line 5, in f
    return sum(A)/x
TypeError: 'numpy.int64' object is not iterable

我知道函数f在没有使用vectorize的情况下可以正常工作,但我想知道如何(通常情况下)将参数接受列表的函数向量化。

2个回答

12

你的问题并没有明确说明你想从向量化函数中看到什么输出,但我假设你希望将同一个列表A作为参数应用于每次调用f()(也就是对X数组中的每个元素都调用一次)。

向量化函数的版本确保所有参数都是数组,并应用NumPy广播规则来确定如何组合这些参数。

与np.array包装np.ndarray类似,将参数强制转换为数组尝试提供便利,它会自动将列表转换为包含相同元素的数组,而不是创建包含该列表作为唯一元素的dtype=object数组。大多数情况下,这正是我们想要的,但在你的情况下,这种“智能”的行为却让你感到困扰。

虽然可能有一种方法可以指示NumPy仅将某些输入视为向量,但有两种简单的方法可以获得你想要的行为:

  1. 手动创建一个dtype=object的数组以符合广播规则
  2. 在向量化函数之前进行柯里化

1. dtype=object

Numpy数组从只存储一种类型的项中获得效率,但它们仍然可以通过指定存储的数据类型为Python对象来包含任意Python对象:

list_obj_array = np.ndarray((1,), dtype=object)
list_obj_array[0] = [1,2,3]
f2(X,list_obj_array)  # using your definition from above

打印:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]

并返回:

array([ 6.        ,  5.4       ,  4.90909091,  4.5       ,  4.15384615,
        3.85714286,  3.6       ,  3.375     ,  3.17647059,  3.        ])

2.柯里化(Currying)

由于你正在将相同的列表传递给数组中的每个项的函数调用,所以你可以通过在应用矢量化之前对函数进行柯里化来直接存储列表:

def curry_f(A):
    def f_curried(x):
        return f(x, A)  # using your definition from above
    return f_curried

f2 = np.vectorize(curry_f(P))
f2(X)

输出:

type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]
type(A)=<type 'list'>, A=[1, 2, 3]

并返回:

array([ 6.        ,  5.4       ,  4.90909091,  4.5       ,  4.15384615,
        3.85714286,  3.6       ,  3.375     ,  3.17647059,  3.        ])

顺便提一句,您也可以看看np.frompyfunc -- 它类似于vectorize(),但在稍微低一级的层面上运作。


1

这是一个递归装饰器的示例,我目前正在使用它来向量化一个以1D数组作为第一个参数的函数:

def broadcast(fvec):
    def inner(vec, *args, **kwargs):
        if len(vec.shape) > 1:
            return np.array([inner(row, *args, **kwargs) for row in vec])
        else:
            return fvec(vec, *args, **kwargs)
    return inner

我猜它和np.vectorize差不多,但对我来说使用这个比尝试适应vectorize/frompyfunc更容易。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接