np.vectorize和np.apply_along_axis将同一参数两次传递给映射函数



我想将函数f映射到字符串数组上。我构造了f的矢量化版本,并将其应用于我的数组。但是数组的第一个元素被传递了两次:

import numpy as np
def f(string):
print('called with', string)
a = np.array(['110', '012'])
fv = np.vectorize(f)
np.apply_along_axis(fv, axis=0, arr=a)

called with 110
called with 110
called with 012

为什么?我没想到110会被传给f两次,我不明白为什么会这样。

我对np.vectorizenp.apply_along_axis有什么误解?

In [145]: def f(string):
...:     print('called with', string)
...: 
...: a = np.array(['110', '012'])
...: 
...: fv = np.vectorize(f)
...: 
In [146]: fv(a)
called with 110
called with 110
called with 012
Out[146]: array([None, None], dtype=object)

只有一个打印的函数返回Nonevectorized调用它一次以确定返回的dtype——在这种情况下,它推导出object

如果我们指定像int一样的otypes,我们会得到一个错误:

In [147]: fv = np.vectorize(f, otypes=[int])
In [148]: fv(a)
called with 110
called with 012
---------------------------------------------------------------------------
...    
TypeError: int() argument must be a string, a bytes-like object or a number, not 'NoneType'

otypes与返回的对象不兼容

In [149]: fv = np.vectorize(f, otypes=[object])
In [150]: fv(a)
called with 110
called with 012
Out[150]: array([None, None], dtype=object)

一个更好、更有意义的功能:

In [151]: def f(string):
...:     print('called with', string)
...:     return len(string)
...: 
...: 
In [152]: fv = np.vectorize(f, otypes=[int])
In [153]: fv(a)
called with 110
called with 012
Out[153]: array([3, 3])

请记住,vectorize会将标量值传递给您的函数。实际上,它评估输入数组的每个元素,返回具有匹配形状的数组:

In [154]: fv(np.array([a,a,a]))
called with 110
called with 012
called with 110
called with 012
called with 110
called with 012
Out[154]: 
array([[3, 3],
[3, 3],
[3, 3]])

与普通迭代(例如np.array([f(i) for i in a]))相比,它更慢,但如果输入数组可以有多个维度,则会更方便,如果有几个数组需要相互广播,则会更好。

对于像a这样的简单单数组来说,np.vectorize太过了。


vectorize有另一个参数cache,它可以避免这种双重调用,同时仍然允许自动数据类型检测:

In [156]: fv = np.vectorize(f, cache=True)
In [157]: fv(a)
called with 110
called with 012
Out[157]: array([3, 3])

自动数据类型检测有时会导致错误。例如,如果试算返回不同的数据类型:

In [160]: def foo(var):
...:     if var<0:
...:         return -var
...:     elif var>0:
...:         return var
...:     else:
...:         return 0  
In [161]: np.vectorize(foo)([0,1.2, -1.2])
Out[161]: array([0, 1, 1])           # int dtype
In [162]: np.vectorize(foo)([0.1,1.2, -1.2])
Out[162]: array([0.1, 1.2, 1.2])     # float dtype

apply_along_axis采用一个接受1d数组的函数。它在所有其他维度上进行迭代,将一组1d切片传递给函数。对于像a这样的1d数组,这没有任何用处。即使你的a是nd,也不会有多大帮助。您的fv不需要1d输入。

它还进行了一次试算,以确定返回数组的形状和数据类型。它会自动缓存结果。

vectorize一样,apply_along_axis是一个方便工具,而不是性能工具。

比较

np.apply_along_axis(fv, axis=0, arr=[a,a,a])
np.apply_along_axis(fv, axis=1, arr=[a,a,a])

以了解CCD_ 25如何影响评估顺序。

或者用对整个row(或列)做一些事情

np.apply_along_axis(lambda x: fv(x).mean(), axis=0, arr=[a,a,a])

来自文档:

矢量化的输出的数据类型是通过使用输入的第一个元素调用函数来确定的。这可以通过指定类型参数来避免。

进行额外调用以确定输出数据类型。

最新更新