关于Numpy中多轴自定义函数应用的疑问与实现需求
嘿,我来帮你把这些numpy的问题掰扯清楚,一步步来~
第一个问题:为什么apply_over_axes返回(100,1,1),而原生sum返回(100,)?
这是因为np.apply_over_axes的工作逻辑是递归地在每个指定轴上依次应用函数,而且每次操作后都会保留被处理的轴(只是把轴的长度变成1)。具体到你的例子:
- 先在
axis=1上应用f,原数组(100,101,102)变成(100,1,102)(axis=1的长度从101缩成1) - 再在
axis=2上应用f,数组又变成(100,1,1)(axis=2的长度从102缩成1)
而原生的b.sum(axis=(1,2))是一次性对多个轴进行求和操作,直接把这两个轴从数组里“压缩”掉,所以得到的是一维数组(100,)。如果想让apply_over_axes的结果和原生sum形状一致,你可以用np.squeeze(c)去掉所有长度为1的轴,或者直接对结果做reshape(100,)。
第二个问题:为什么自定义函数f必须加axis参数?
因为apply_over_axes在调用你的函数时,会自动把当前要处理的轴作为参数传给函数。比如你指定axes=[1,2],它会先调用f(b, axis=1),然后把第一次的结果再传给f,调用f(result, axis=2)(这里的axis是相对于当前数组的轴索引)。
如果你的函数f没有定义axis参数,apply_over_axes硬要传这个参数给你,自然就会报参数不匹配的错误啦。
第三个问题:如何实现多轴的自定义函数(比如找第二大元素)?
像“找第二大元素”这种操作,没法通过递归单轴处理得到正确结果(比如先找axis=1的第二大,再找axis=2的,和直接在(1,2)轴构成的整个子空间里找第二大,结果完全不一样)。这时候推荐用合并多轴为单轴的思路,具体步骤如下:
方法:先reshape合并轴,再应用自定义函数
- 把需要操作的多个轴(比如这里的axis=1和2)合并成一个新的轴,这样就把多轴问题转化为单轴问题。
- 用向量化的方式实现自定义函数(尽量避免循环,提升效率)。
举个具体的代码例子:
import numpy as np def second_largest(arr): # 用np.partition比排序高效:直接把数组分成两部分,取倒数第二个元素就是第二大 return np.partition(arr, -2)[-2] # 生成测试数组 b = np.random.randint(6, size=(100, 101, 102)) # 把axis=1和2合并成一个轴,形状变成(100, 101*102) b_reshaped = b.reshape(b.shape[0], -1) # 在合并后的轴上应用自定义函数 result = np.apply_along_axis(second_largest, axis=1, arr=b_reshaped) print(result.shape) # 输出 (100,),正是你想要的形状
如果用排序的方式实现second_largest也可以,但np.partition的效率更高(尤其是处理大数组时),因为它不需要完全排序,只要找到第k大的元素就行。
额外提示
对于sum、mean这类可分解的操作,你可以选择apply_over_axes,但记得处理结果的形状;而对于像第二大、中位数这种需要全局统计的操作,一定要先合并多轴,再应用自定义函数,优先用numpy的向量化函数实现,别自己写循环(效率太低)。
备注:内容来源于stack exchange,提问作者magnolia93

