Skip to main content
 首页 » 编程设计

python之np.vectorize 和 np.apply_along_axis 两次将相同的参数传递给映射函数

2025年05月04日31shangdawei

我想将一个函数 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.vectorize 或 np.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) 

只有打印的函数返回None。 vectorized 调用它一次以确定返回数据类型 - 在本例中它推导出 object。

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

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 采用接受一维数组的函数。它遍历所有其他维度,将一组一维切片传递给您的函数。对于像您的 a 这样的一维数组,这没有任何用处。即使找到了 a,也无济于事。您的 fv 不需要一维输入。

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

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

了解 apply_along 如何影响计算顺序。

或者对整个行(或列)做一些事情:

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