Pandas按id分组创建依赖前序行值的新列并逐行更新的实现方法
Pandas分组逐行递推计算新列实现方案
问题背景
学习pandas过程中遇到如下结构的DataFrame:
index Random id diff pct 0 2018-01-01 31 1 3 1 1 2018-01-02 11 1 2 2 2 2018-01-03 21 1 4 0 3 2018-01-04 23 2 1 0 4 2018-01-05 43 2 6 3 5 2018-01-06 42 2 1 1 6 2018-01-07 51 3 2 5 7 2018-01-08 47 3 2 0 8 2018-01-09 49 3 3 2 9 2018-01-10 22 3 1 3
需求为基于判断条件创建recommend列与Random_new列,核心要求是Random_new的计算存在行级依赖:当前行计算得到的新值需要作为同组后续行的计算基准,且所有计算按id分组独立执行。
期望输出结构如下:
index Random id diff pct recommend Random_new 0 2018-01-01 31 1 3 1 Y 32 1 2018-01-02 31 1 2 2 N 32 2 2018-01-03 31 1 4 0 Y 36 3 2018-01-04 23 2 1 0 Y 24 4 2018-01-05 23 2 6 3 Y 27 5 2018-01-06 23 2 1 1 N 27 6 2018-01-07 51 3 2 5 N 51 7 2018-01-08 51 3 2 0 Y 53 8 2018-01-09 51 3 3 2 Y 56 9 2018-01-10 51 3 1 3 N 56
尝试过np.where实现,但该方法仅支持静态向量化计算,无法处理逐行依赖前序计算结果的递推逻辑;尝试编写for循环但未跑通。
计算规则
- 若当前行
pct < diff,则recommend取值为Y,Random_new = 当前行Random值 + 当前行diff值 - 若当前行
pct >= diff,则recommend取值为N,Random_new = 同组上一行的Random_new值 - 同组内计算得到的
Random_new值需要向后传递,作为后续行的计算基准 - 不同
id分组的计算完全独立,互不干扰
实现代码
这类带状态传递的递推计算无法通过普通向量化操作完成,按分组逐行迭代、维护计算状态即可实现,代码如下:
import pandas as pd # --------------- 初始化示例数据 --------------- df = pd.DataFrame({ 'index': pd.date_range(start='2018-01-01', periods=10), 'Random': [31,11,21,23,43,42,51,47,49,22], 'id': [1,1,1,2,2,2,3,3,3,3], 'diff': [3,2,4,1,6,1,2,2,3,1], 'pct': [1,2,0,0,3,1,5,0,2,3] }) # --------------- 核心计算逻辑 --------------- def process_group(group_df): # 存储单组计算结果 calc_result = [] # 初始化上一行计算值为None,标记组内第一行 last_random_new = None # 逐行遍历组内数据 for _, row in group_df.iterrows(): if row['pct'] < row['diff']: # 满足更新条件,计算新值 current_random_new = row['Random'] + row['diff'] current_rec = 'Y' else: # 不满足更新条件,取上一行值;组内第一行直接取当前行Random作为初始值 current_random_new = last_random_new if last_random_new is not None else row['Random'] current_rec = 'N' calc_result.append((current_rec, current_random_new)) # 更新状态,供下一行使用 last_random_new = current_random_new # 将结果赋值回分组DataFrame group_df[['recommend', 'Random_new']] = calc_result return group_df # 先按id和时间排序保证行顺序正确,再分组应用计算逻辑 df = df.sort_values(by=['id', 'index']).groupby('id', group_keys=False).apply(process_group)
注意事项
- 计算前必须保证DataFrame按
id和业务时间字段(本例中为index列)排序,否则逐行递推的顺序会出错,得到错误结果 - 如果数据量较大,
iterrows性能一般,可以换用itertuples迭代提升速度,逻辑完全一致 - 如果需要直接更新原
Random列而非新建Random_new,只需要把函数内赋值的列名改成Random即可
内容的提问来源于stack exchange,提问作者ash1
相关产品推荐
相关产品推荐

