Pandas按Type分组实现带日期差的自定义递推累积计算
pandas分组递推计算Movement字段实现方案
这类强依赖上一行计算结果的递推逻辑,无法直接通过pandas内置的cumsum/cumprod等原生累积函数实现,以下是两种可直接运行的实现方案,新手优先选第一种。
前置处理(必做)
计算前必须先做格式转换和排序,否则会出现计算错误:
import pandas as pd import numpy as np # 将Date列转换为标准日期格式 df['Date'] = pd.to_datetime(df['Date']) # 按分组字段+日期升序排序,保证组内记录按时间先后排列 df = df.sort_values(by=['Type', 'Date']).reset_index(drop=True)
注:你问题里写的系数0,0001是欧洲数字写法,Python里小数点用点表示,实际计算时要写为0.0001
方案1:分组逐行递推(逻辑直观,新手友好)
直接给每个分组写独立的递推逻辑,代码可读性高,方便排查错误,10万行以内数据量性能完全够用:
def calc_group_movement(group): movement_res = [] # 初始化上期值缓存 last_mov = None last_date = None for _, row in group.iterrows(): dep = row['deposit'] wd = row['withdrawal'] cur_date = row['Date'] if last_mov is None: # 组内首条记录计算规则 cur_mov = dep - wd else: # 非首条记录:先计算和上一条记录的间隔天数 day_gap = (cur_date - last_date).days cur_mov = last_mov * (1 + day_gap * 0.0001) + dep - wd movement_res.append(cur_mov) # 更新缓存值供下一行计算使用 last_mov = cur_mov last_date = cur_date group['Movement'] = movement_res return group # 按Type分组应用计算逻辑 df = df.groupby('Type', group_keys=False).apply(calc_group_movement)
运行完直接打印df即可看到和示例一致的计算结果。
方案2:Numba加速方案(适合百万级以上大数据量)
如果数据量很大,逐行循环速度慢,可以用Numba做JIT编译加速,计算逻辑和方案1完全一致,运行速度可以提升10~100倍:
# 先安装依赖:pip install numba from numba import jit @jit(nopython=True) def _fast_calc(dates_arr, dep_arr, wd_arr): res_len = len(dates_arr) mov_arr = np.empty(res_len, dtype=np.float64) # 首行计算 mov_arr[0] = dep_arr[0] - wd_arr[0] # 逐行递推 for i in range(1, res_len): day_gap = (dates_arr[i] - dates_arr[i-1]) / np.timedelta64(1, 'D') mov_arr[i] = mov_arr[i-1] * (1 + day_gap * 0.0001) + dep_arr[i] - wd_arr[i] return mov_arr # 分组批量计算 df['Movement'] = df.groupby('Type', group_keys=False).apply( lambda x: _fast_calc(x['Date'].values, x['deposit'].values, x['withdrawal'].values) ).explode().astype(np.float64)
注意事项
- 不要跳过排序步骤,如果组内日期顺序颠倒,取到的上期日期和上期Movement值都是错的
- 日期间隔必须取整数天参与计算,直接用两个datetime对象相减得到的Timedelta类型无法直接参与数值乘法
- 计算前先检查deposit、withdrawal字段的空值,空值参与运算会导致整列结果返回NaN
内容的提问来源于stack exchange,提问作者Fernando A.W.
相关产品推荐
相关产品推荐

