如何用Numpy实现矩阵行的逐行排除自身权重的加权平均?
如何用NumPy无循环实现每行排除对角线元素的加权平均?
我有一个对称且对角线元素为0的NumPy二维矩阵,想要结合一个与矩阵行长度相同的权重数组,计算每行的加权平均值,但不想让每行的对角线元素(值为0)参与计算——也就是对第i行,要把权重数组的第i位设为0,其余权重保持不变。
请问能否不使用显式循环实现该需求?最优实现方式是什么?
代码示例
生成矩阵和权重
import numpy as np mat = np.array([[ 0, 2436, 2434, 2428, 2416], [ 2436, 0, 2454, 2446, 2435], [ 2434, 2454, 0, 2447, 2436], [ 2428, 2446, 2447, 0, 2428], [ 2416, 2435, 2436, 2428, 0]]) weights = np.array([262140, 196608, 196608, 196608, 196608])
当前错误实现
直接用np.average会把对角线元素(带权重)算进去,结果不符合预期:
weighted_avg = np.average(mat, axis=-1, weights=weights) print(weighted_avg) # 输出: # [1821.38194802 1984.31077694 1984.18578409 1979.68578982 1972.56080841]
循环实现(正确但低效)
手动遍历每行修改权重,结果正确但用了显式循环:
weighted_avg = [] for i in range(mat.shape[0]): curr_weights = weights.copy() curr_weights[i] = 0 weighted_avg.append(np.average(mat[i], axis=-1, weights=curr_weights)) weighted_avg = np.array(weighted_avg) print(weighted_avg) # 输出: # [2428.5 2442.23079848 2442.076961 2436.53850163 2427.76928603]
无循环的NumPy实现方案
方案1:构造权重掩码矩阵,手动计算加权平均
np.average其实就是加权和除以权重和,我们可以先构造一个每行对角线位置为0的权重矩阵,再直接计算:
# 把一维weights广播成和mat同形状的二维矩阵 weight_mat = np.tile(weights, (mat.shape[0], 1)) # 将矩阵的对角线元素全部置0 np.fill_diagonal(weight_mat, 0) # 计算每行的加权总和与有效权重总和,再相除得到结果 weighted_sum = (mat * weight_mat).sum(axis=1) weight_sum = weight_mat.sum(axis=1) weighted_avg = weighted_sum / weight_sum print(weighted_avg) # 输出: # [2428.5 2442.23079848 2442.076961 2436.53850163 2427.76928603]
方案2:利用矩阵特性优化,避免构造额外矩阵
因为原矩阵的对角线元素都是0,所以直接计算mat * weights的每行求和,结果和排除对角线后的加权和完全一致(0乘任何权重都是0,不会影响总和)。我们只需要调整权重和:每行的有效权重总和等于权重数组的总和减去当前行对角线对应的权重值,直接用这个值来除加权和即可:
# 计算原加权和(对角线元素为0,不影响结果) original_weighted_sum = (mat * weights).sum(axis=1) # 计算每行的有效权重和 effective_weight_sum = weights.sum() - weights weighted_avg = original_weighted_sum / effective_weight_sum print(weighted_avg) # 输出和方案1完全一致
这个方案更高效,不需要额外创建二维矩阵,完全用一维数组运算,速度更快、内存占用更低。
内容的提问来源于stack exchange,提问作者Tal Afek
相关产品推荐
相关产品推荐

