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

发布于 2024-10-08 17:42:38 字数 797 浏览 8 评论 0 原文

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 可以在不进行向量化的情况下工作很好,但我想知道如何(通常)对函数进行向量化其参数采用列表而不是标量。

The numpy vectorize function is useful, but it doesn't behave well when the function arguments are lists rather then scalars. As an example:

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)

Gives:

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

I understand that the function f works just fine without vectorizeing it, but I'd like to know how to (in general) vectorize a function whose arguments take in lists rather than a scalar.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦明 2024-10-15 17:42:38

您的问题并没有明确说明您希望从向量化函数中看到什么输出,但我假设您希望将相同的列表 (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

prints:

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]

和 returns:

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

2.柯里化

由于您将相同的列表传递给数组中每个项目的函数调用,因此您可以在应用矢量化之前通过柯里化直接使用函数存储列表:

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)

prints:

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]

和 returns:

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

PS 您可能还希望拥有看一下 np.frompyfunc ——它与 vectorize() 类似,但工作级别稍低。

Your question doesn't make clear precisely what output you would like to see from the vectorized function, but I'm going to assume you would like the same list (A) applied as an argument to every invocation of f() (ie once for each element in the X array)

The vectorized version of a function ensures all arguments are arrays, and then applies numpy's broadcasting rules to determine how these arguments should be combined.

As with np.array's wrapping of np.ndarray, the coercion of the arguments to arrays tries to be helpful, by automatically converting a list to an array containing the same elements, rather than making an array with dtype=object that contains the list as its sole element. Most of the time this is what we want, but in your case, this "smart" behaviour is coming back to bite you.

While there may be a way to instruct numpy to only treat certain inputs as vectors, there are two straightforward ways to get the behaviour you're after:

  1. Manually create an array with dtype=object to work within the broadcasting rules
  2. Curry the value prior to vectorizing the function

1. dtype=object

Numpy arrays derive their efficiency from only storing one type of item, but they can still contain arbitrary python objects by specifying that the stored data type be python objects:

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

prints:

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]

and returns:

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

2. Currying

Since you are passing the same list to the function call for each item in the array, you can store the list directly with the function by currying before applying vectorization:

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)

prints:

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]

and returns:

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

P.S. you may also wish to have a look at np.frompyfunc -- it is similar to vectorize(), but works at a slightly lower level.

赏烟花じ飞满天 2024-10-15 17:42:38

这是我当前用来向量化一个以一维数组作为第一个参数的函数的递归装饰器的示例:

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 来完成这项工作更容易。

This is an example of a recursive decorator I'm currently using to vectorize a function that takes a 1D array as the first argument:

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

I guess it does pretty much the same as np.vectorize, but it turned out easier for me to use this than try to adapt vectorize/frompyfunc for the job.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文