百万级Pandas DataFrame高效计算分组历史日期DPD均值
高效计算分组日期前的DPD均值(百万行数据优化)
原始数据
import pandas as pd df = pd.DataFrame([['John', '1/1/2017','10'], ['John', '2/2/2017','15'], ['John', '2/2/2017','20'], ['John', '3/3/2017','30'], ['Sue', '1/1/2017','10'], ['Sue', '2/2/2017','15'], ['Sue', '3/2/2017','20'], ['Sue', '3/3/2017','7'], ['Sue', '4/4/2017','20']], columns=['Customer', 'Deposit_Date','DPD']) # 预处理:转换日期和数值类型 df['Deposit_Date'] = pd.to_datetime(df['Deposit_Date']) df['DPD'] = df['DPD'].astype(int)
需求说明
新增一列PreviousMean,表示每个Customer截至当前Deposit_Date**之前(不含当日)**的DPD均值,无历史记录时填充为NaN。期望输出如下:
Customer Deposit_Date DPD PreviousMean 0 John 2017-01-01 10 NaN 1 John 2017-02-02 15 10.0 2 John 2017-02-02 20 10.0 3 John 2017-03-03 30 15.0 4 Sue 2017-01-01 10 NaN 5 Sue 2017-02-02 15 10.0 6 Sue 2017-03-02 20 12.5 7 Sue 2017-03-03 7 15.0 8 Sue 2017-04-04 20 13.0
现有方案的问题
原方案使用apply逐行遍历计算,代码如下:
df['PreviousMean'] = df.apply( lambda x: df[(df.Customer == x.Customer) & (df.Deposit_Date < x.Deposit_Date)].DPD.mean(), axis=1)
该方法时间复杂度为O(n²),每一行都要重新过滤整个DataFrame,在百万行数据下运行速度极慢,完全无法满足性能要求。
高效实现方案
优化思路
利用Pandas的分组聚合、累计统计函数替代逐行计算,核心是先按客户和日期聚合,再计算截至上一日期的累计总和与计数,最后推导均值。
代码实现
# 1. 按客户+日期分组,计算每日DPD总和与记录数 daily_stats = df.groupby(['Customer', 'Deposit_Date']).agg( dpd_sum=('DPD', 'sum'), count=('DPD', 'size') ).reset_index() # 2. 按客户分组,计算截至上一日期的累计总和与累计计数 daily_stats['cum_sum'] = daily_stats.groupby('Customer')['dpd_sum'].cumsum().shift(1) daily_stats['cum_count'] = daily_stats.groupby('Customer')['count'].cumsum().shift(1) # 3. 计算均值,无历史记录时为NaN daily_stats['PreviousMean'] = daily_stats['cum_sum'] / daily_stats['cum_count'] # 4. 将结果合并回原DataFrame df = df.merge(daily_stats[['Customer', 'Deposit_Date', 'PreviousMean']], on=['Customer', 'Deposit_Date'], how='left')
代码说明
- 第一步将同一天同客户的记录合并,减少后续计算量;
- 第二步用
cumsum()计算累计值,shift(1)将累计值后移一行,得到当前日期之前的所有历史数据总和与数量; - 最后通过
merge将结果关联回原表,确保同一天的所有记录共享相同的PreviousMean。
性能优势
该方法时间复杂度为O(n log n)(主要来自分组排序),百万行数据下的运行速度比原apply方法快数十倍甚至上百倍,完全适配大数据量场景。
内容的提问来源于stack exchange,提问作者Ishigami
相关产品推荐
相关产品推荐

