Pandas:如何高效实现分组滚动去重求和?
问题描述
我们有一个以time为索引的DataFrame,包含标识符id_col、分组列group_col以及值列value_col="value"。value_col会按id_col的不同频率随机更新,需求是对组内每个id_col的最后一次更新值求和。
初始实现代码如下:
df["duplicates_rolling_sum"] = ( df.groupby([id_col, group_col])[value_col] .rolling("30d") .apply(lambda x: x.iloc[:-1].sum()) .fillna(0)) df["rolling_deduplicate_sum"] = ( df.groupby(group_col)["value_col"].rolling("30d").sum() - df.groupby(group_col)["duplicated_rolling_sum"].rolling("30d").sum() )
但处理超700万行的大数据集且分组较多时,该方法耗时极长,瓶颈出在.iloc[:-1].sum()这一步的自定义lambda应用上。
高效解决方案
核心是用Pandas内置的向量化滚动聚合替代自定义lambda,内置操作经过C级优化,性能远高于逐行执行的lambda函数。
1. 预处理:确保时间索引有序
滚动窗口依赖有序的时间索引,先排序既避免计算错误,也能提升滚动操作的效率:
df = df.sort_index()
2. 替换低效的apply计算
原代码中lambda x: x.iloc[:-1].sum()等价于窗口总和减去当前行的值,直接用内置滚动求和后做减法即可:
# 计算每个(id_col, group_col)组内的30天滚动总和 df['id_group_rolling_sum'] = df.groupby([id_col, group_col])[value_col].rolling("30d").sum().values # 生成原逻辑中的duplicates_rolling_sum df["duplicates_rolling_sum"] = (df['id_group_rolling_sum'] - df[value_col]).fillna(0)
3. 计算最终去重滚动求和
继续用内置滚动聚合完成后续计算,全程无自定义lambda:
# 计算group_col层面的value_col滚动总和 df['group_value_rolling_sum'] = df.groupby(group_col)[value_col].rolling("30d").sum().values # 计算group_col层面的duplicates_rolling_sum滚动总和 df['group_dup_rolling_sum'] = df.groupby(group_col)['duplicates_rolling_sum'].rolling("30d").sum().values # 最终结果:组内每个id最后一次更新值的总和 df["rolling_deduplicate_sum"] = df['group_value_rolling_sum'] - df['group_dup_rolling_sum']
可选优化:减少中间列
如果不需要保留中间计算列,可以链式调用减少内存占用:
df = df.sort_index() # 计算duplicates_rolling_sum的临时值 dup_sum = ( df.groupby([id_col, group_col])[value_col].rolling("30d").sum().values - df[value_col] ).fillna(0) # 直接计算最终结果 df["rolling_deduplicate_sum"] = ( df.groupby(group_col)[value_col].rolling("30d").sum().values - df.groupby(group_col)[dup_sum].rolling("30d").sum().values )
这种实现方式能将性能提升几个数量级,完全适配700万行的大数据集。
内容的提问来源于stack exchange,提问作者flT
相关产品推荐
相关产品推荐

