如何基于Pandas DataFrame现有列计算新列并引用前一行数据
解决思路
这类存在行依赖的有状态计算,无法直接用普通pandas矢量化操作或者逐行apply实现:矢量化操作默认基于计算前的原始值批量运算,不会动态读取前面行刚更新的计算结果;而df.apply逐行运行时,默认读取的也是计算前DataFrame的原始值,因此无法获取动态更新的前一行状态。有两种常用的实现方案:
方案1:手动迭代行(易理解,适合10万行以内的数据集)
直接遍历每一行,手动维护上一行的计算结果作为状态变量,示例代码如下:
import pandas as pd # 初始DataFrame df = pd.DataFrame({ 'item_tolerance': [230, 115, 155], 'item_intake': [250,100,100], 'open_items_previous_day': 0, 'total_items_to_process': 0, 'sla_relevant': 0, 'items_shipped': [230, 115, 50], 'items_over_under_sla': 0 }) # 处理第一行(初始状态) df.loc[0, 'total_items_to_process'] = df.loc[0, 'item_intake'] + df.loc[0, 'open_items_previous_day'] df.loc[0, 'sla_relevant'] = min(df.loc[0, 'open_items_previous_day'] + df.loc[0, 'item_intake'], df.loc[0, 'item_tolerance']) df.loc[0, 'items_over_under_sla'] = df.loc[0, 'items_shipped'] - df.loc[0, 'sla_relevant'] # 从第二行开始迭代计算 for i in range(1, len(df)): # 先算当前行的前日未结项:用上一行的计算结果 prev_row = df.loc[i-1] df.loc[i, 'open_items_previous_day'] = prev_row['item_intake'] + prev_row['open_items_previous_day'] - prev_row['items_shipped'] + prev_row['items_over_under_sla'] # 计算当前行剩余衍生列 df.loc[i, 'total_items_to_process'] = df.loc[i, 'item_intake'] + df.loc[i, 'open_items_previous_day'] df.loc[i, 'sla_relevant'] = min(df.loc[i, 'open_items_previous_day'] + df.loc[i, 'item_intake'], df.loc[i, 'item_tolerance']) df.loc[i, 'items_over_under_sla'] = df.loc[i, 'items_shipped'] - df.loc[i, 'sla_relevant']
计算完成后的结果如下:
| item_tolerance | item_intake | open_items_previous_day | total_items_to_process | sla_relevant | items_shipped | items_over_under_sla | |
|---|---|---|---|---|---|---|---|
| 0 | 230 | 250 | 0 | 250 | 230 | 230 | 0 |
| 1 | 115 | 100 | 20 | 120 | 115 | 115 | 0 |
| 2 | 155 | 100 | 5 | 105 | 105 | 50 | -55 |
方案2:numba加速(适合超大数据集)
如果你的数据集行数超过10万,循环性能会有瓶颈,可以用numba的JIT编译把循环逻辑编译成机器码,速度和矢量化操作接近,示例逻辑如下:
import numba import numpy as np @numba.njit def calc_cols(item_tolerance, item_intake, items_shipped): n = len(item_tolerance) # 初始化输出数组 open_prev = np.zeros(n, dtype=np.int64) total_process = np.zeros(n, dtype=np.int64) sla_rel = np.zeros(n, dtype=np.int64) over_under = np.zeros(n, dtype=np.int64) # 第一行 total_process[0] = item_intake[0] + open_prev[0] sla_rel[0] = min(open_prev[0] + item_intake[0], item_tolerance[0]) over_under[0] = items_shipped[0] - sla_rel[0] # 迭代剩余行 for i in range(1, n): open_prev[i] = item_intake[i-1] + open_prev[i-1] - items_shipped[i-1] + over_under[i-1] total_process[i] = item_intake[i] + open_prev[i] sla_rel[i] = min(open_prev[i] + item_intake[i], item_tolerance[i]) over_under[i] = items_shipped[i] - sla_rel[i] return open_prev, total_process, sla_rel, over_under # 调用函数赋值回DataFrame df['open_items_previous_day'], df['total_items_to_process'], df['sla_relevant'], df['items_over_under_sla'] = calc_cols( df['item_tolerance'].values, df['item_intake'].values, df['items_shipped'].values )
内容的提问来源于stack exchange,提问作者Ciszko
相关产品推荐
相关产品推荐

