You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 15:02:00