Python数组间基于优先级数组的映射:非一对一映射处理疑问
NumPy非一对一索引映射的赋值规则
好问题!我刚好对NumPy里这种索引赋值的行为研究过,咱们来拆解一下非一对一映射时的具体处理逻辑:
核心规则:最后一次赋值覆盖所有前置值
当多个源元素通过索引数组映射到目标数组的同一个位置时,NumPy会按索引数组的顺序依次执行赋值,最终目标位置保留的是最后一次写入的值。这是因为普通的索引赋值操作是逐元素顺序执行的,NumPy不会额外检测重复索引并做聚合处理——这么设计主要是为了保证赋值操作的性能。
直观示例
咱们用一个小数组来验证:
import numpy as np # 构造小的源数组 src = np.array([[1, 2], [3, 4]]) # 构造索引数组:让src的4个元素都映射到dst的(0,0)和(1,1)位置(各两次) idx = np.array([[0, 0, 1, 1], [0, 0, 1, 1]]) # 初始化目标数组 dst = np.empty_like(src) # 执行赋值 dst[tuple(idx)] = src.ravel() print(dst)
输出结果会是:
[[2 0] [0 4]]
解释一下:src.ravel()是[1,2,3,4],按索引顺序依次赋值:
- 把1写入dst[0,0]
- 把2写入dst[0,0](覆盖了之前的1)
- 把3写入dst[1,1]
- 把4写入dst[1,1](覆盖了之前的3)
最终就得到了上面的结果。
如果你需要聚合操作(而非覆盖)
如果你的需求不是覆盖,而是把所有映射到同一位置的源元素做聚合(比如累加、求平均、取最大值),那普通的赋值就满足不了了,得用NumPy专门的原地聚合函数:
- 累加:用
np.add.at()dst = np.zeros_like(src) np.add.at(dst, tuple(idx), src.ravel()) print(dst) # 输出 [[3 0], [0 7]],也就是1+2和3+4的结果 - 求最大值/最小值:可以用
np.maximum.at()/np.minimum.at()实现原地更新最值 - 求平均:需要先统计每个索引出现的次数,再用累加结果除以次数
补充说明
这种“覆盖式”的行为是NumPy索引赋值的默认逻辑,官方文档里虽然没有专门针对非一对一场景做长篇幅说明,但这是顺序赋值的必然结果——毕竟NumPy的设计优先考虑数值计算的性能,额外的重复索引检测会带来不小的性能开销,所以把聚合的选择权交给了开发者。
内容的提问来源于stack exchange,提问作者ASML
相关产品推荐
相关产品推荐

