Pandas条件分组:基于谓词的分区聚合实现方案问询
基于列值条件的窗口聚合(替代循环方案)
你需要的**按当前行days值做条件聚合(≤、≥、等于)**可以通过Pandas的分组+累计求和实现,完全替代循环,效率提升明显。以下是具体实现:
一、基础实现(全局范围的条件聚合)
先明确需求:对每一行,计算所有days≤当前行days的amount总和(同理处理≥的情况)。
步骤1:计算每个days对应的amount总和
先把相同days的amount合并,得到每个days的总金额:
import pandas as pd df = pd.DataFrame(data={ 'id': ['0001', '0001', '0001', '0002', '0002', '0003', '0003', '0004'], 'days': [2, 5, 6, 3, 5, 4, 6, 8], 'amount': [100, 150, 150, 200, 100, 300, 250, 200] }) # 按days分组,计算每个days对应的amount总和 day_total = df.groupby('days')['amount'].sum()
步骤2:计算≤当前days的累计总和
对day_total按days升序排序后做累计求和,得到每个days对应的「所有≤该days的amount总和」,再映射回原DataFrame:
# 升序累计求和 → days≤当前值的总和 cum_le = day_total.sort_index().cumsum() df['run_sum_le'] = df['days'].map(cum_le)
步骤3:计算≥当前days的累计总和
同理,按days降序排序后做累计求和,再映射回原DataFrame:
# 降序累计求和后重新按days排序 → days≥当前值的总和 cum_ge = day_total.sort_index(ascending=False).cumsum().sort_index() df['run_sum_ge'] = df['days'].map(cum_ge)
步骤4:等于当前days的求和(你已实现的方法)
直接用map映射分组后的结果即可:
df['run_sum_eq'] = df['days'].map(day_total)
最终结果:
id days amount run_sum_le run_sum_ge run_sum_eq 0 0001 2 100 100 1450 100 1 0001 5 150 850 850 250 2 0001 6 150 1250 600 400 3 0002 3 200 300 1350 200 4 0002 5 100 850 850 250 5 0003 4 300 600 1150 300 6 0003 6 250 1250 600 400 7 0004 8 200 1450 200 200
二、按id分组的条件聚合(如果需要在每个id内计算)
如果需求是每个id范围内,days≤当前行days的amount总和,可以调整为按id分组后做累计:
# 按id+days分组求和 id_day_total = df.groupby(['id', 'days'])['amount'].sum().reset_index() # 按id分组,对days升序排序后计算累计和 id_day_total['run_sum_le'] = id_day_total.groupby('id').apply( lambda x: x.sort_values('days')['amount'].cumsum() ).reset_index(drop=True) # 合并回原DataFrame df = df.merge(id_day_total[['id', 'days', 'run_sum_le']], on=['id', 'days'], how='left')
三、为什么不用窗口函数直接实现?
Pandas的窗口函数(如rolling、expanding)主要基于行的顺序而非列值的条件匹配,无法直接实现「依赖当前行列值的全局条件聚合」。而分组+累计求和的方式,本质是先按列值聚合相同条件的行,再通过累计得到全局范围的条件总和,是更高效的方案。
内容的提问来源于stack exchange,提问作者Domenico Spidy Tamburro
相关产品推荐
相关产品推荐

