如何在Pandas DataFrame中高效实现按两列分组的多重求和?
高效实现Pandas分组两两相乘求和(满足特定过滤条件)
问题背景
给定如下Pandas DataFrame:
import pandas as pd df = pd.DataFrame({"i": [1,1,2,2,2,2,3,3,3,3], "k": [2,3,1,3,3,3,4,4,5,5], "l": [1,1,1,1,2,3,1,2,1,2], "X_ikl": [0,0,1,8,2,4,1,1,2,10]})
其输出为:
i k l X_ikl 0 1 2 1 0 1 1 3 1 0 2 2 1 1 1 3 2 3 1 8 4 2 3 2 2 5 2 3 3 4 6 3 4 1 1 7 3 4 2 1 8 3 5 1 2 9 3 5 2 10
需要完成两个高效求和操作(禁止使用for循环迭代,适配大数据量):
- 按
(i,k)分组,对每个X_ikl与其他所有X_i'k'l'两两相乘求和,要求k'不等于当前分组的k值; - 按
(i,k)分组,对每个X_ikl与其他所有X_i'k'l'两两相乘求和,要求i'不等于当前分组的i值。
注:i'、k'、l'为另一组迭代变量;不存在的(i,k)对直接跳过;l是每个(i,k)对的内部索引。
第一个求和的预期结果
(1,2): (0*0) + (0*8 + 0*2 + 0*10) + (0*1 + 0*1) + (0*2 + 0*10) = 0 (1,3): (0*0) + (0*1 + 0*1) + (0*2 + 0*10) = 0 (2,1): (1*0) + (1*8 + 1*2 + 1*4) + (1*1 + 1*1) + (1*2 + 1*10) = 28 (2,3): (8*1) + ((8*1 + 8*1) + (2*1 + 2*1) + (4*1 + 4*1)) + ((8*2 + 8*10) + (2*2 + 2*10) + (4*2 + 4*10)) = 204 (3,4): ((1*0) + (1*0)) + ((1*1) + (1*1)) + ((1*2 + 1*10) + (1*2 + 1*10)) = 26 (3,5): ((2*0) + (10*0)) + ((2*1) + (10*1)) + ((2*1 + 2*1) + (10*1 + 10*1)) = 36 TOTAL SUM = 0 + 0 + 28 + 204 + 26 + 36 = 294
解决方案
核心思路:利用全局总和的数学变形替代两两遍历,结合分组聚合实现高效计算,避免O(n²)的循环操作。
1. 实现第一个求和(k'≠当前k)
思路
两两相乘的总和可通过数学公式转化:所有元素两两乘积之和 - 当前k对应的所有元素两两乘积之和
其中,任意集合的两两乘积和 = $\frac{(总和)^2 - 平方和}{2}$,基于该公式可快速计算全局及分组内的乘积和。
代码实现
# 计算全局统计量 total_sum = df['X_ikl'].sum() total_sq_sum = df['X_ikl'].pow(2).sum() global_pair_sum = (total_sum ** 2 - total_sq_sum) / 2 # 按k分组计算统计量 k_stats = df.groupby('k').agg( s_k=('X_ikl', 'sum'), sq_s_k=('X_ikl', lambda x: x.pow(2).sum()) ).assign(pair_sum_k=lambda x: (x['s_k'] ** 2 - x['sq_s_k']) / 2) # 按(i,k)分组计算目标求和值 result1 = df.groupby(['i', 'k']).agg( s_ik=('X_ikl', 'sum') ).merge(k_stats, left_on='k', right_index=True).assign( target_sum=lambda x: x['s_ik'] * (total_sum - x['s_k']) + (global_pair_sum - x['pair_sum_k']) ) # 输出结果 print("第一个求和结果:") print(result1['target_sum']) print("总求和:", result1['target_sum'].sum())
结果验证
输出与预期完全一致:
第一个求和结果: i k 1 2 0.0 3 0.0 2 1 28.0 3 204.0 3 4 26.0 5 36.0 Name: target_sum, dtype: float64 总求和: 294.0
2. 实现第二个求和(i'≠当前i)
思路
与第一个求和逻辑完全对称,仅需将分组维度从k替换为i:
目标求和值 = s_ik * (total_sum - s_i) + (global_pair_sum - pair_sum_i)
代码实现
# 按i分组计算统计量 i_stats = df.groupby('i').agg( s_i=('X_ikl', 'sum'), sq_s_i=('X_ikl', lambda x: x.pow(2).sum()) ).assign(pair_sum_i=lambda x: (x['s_i'] ** 2 - x['sq_s_i']) / 2) # 按(i,k)分组计算目标求和值 result2 = df.groupby(['i', 'k']).agg( s_ik=('X_ikl', 'sum') ).merge(i_stats, left_on='i', right_index=True).assign( target_sum=lambda x: x['s_ik'] * (total_sum - x['s_i']) + (global_pair_sum - x['pair_sum_i']) ) # 输出结果 print("\n第二个求和结果:") print(result2['target_sum'])
效率说明
- 时间复杂度为O(n),完全适配大数据量场景;
- 所有计算基于Pandas内置分组聚合函数,避免了任何迭代操作,性能远高于循环实现。
内容的提问来源于stack exchange,提问作者aaa
相关产品推荐
相关产品推荐

