如何基于条件使用pandas的shift函数处理缺货日需求平滑?
解决缺货日回溯取首个非缺货订单量的问题
问题描述
我有一个包含日期(calendar_date)、缺货标识(stock_out)和订单量(orders)的销售DataFrame。需要平滑缺货日的需求,计算逻辑是取昨日订单量、12日前订单量以及3日移动平均值的均值,但有个关键规则:如果昨日或12日前当天处于缺货状态,需要持续回溯直至找到首个非缺货日的订单量。
示例数据如下:
import pandas as pd calendar_date = pd.date_range("2020-01-01", "2020-01-31") stock_out_flag = [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1.] orders = [45., 51., 48., 52., 51., 47., 49., 48., 51., 51., 49., 49., 52., 47., 51., 55., 51., 48., 49., 53., 50., 52., 51., 48., 51., 53., 49., 56., 0., 0., 0.] df = pd.DataFrame.from_dict({'calendar_date':calendar_date, 'stock_out':stock_out_flag, 'orders':orders})
比如示例中当月最后三天缺货,1月30日、31日的昨日订单值应该取首个非缺货日的56。平时习惯用pandas的shift函数取滞后值,请问怎么实现这种带条件的回溯取值?
解决方案
核心思路
直接使用shift无法处理回溯逻辑,需要先构建一个非缺货订单量的填充序列:将缺货日的订单量替换为最近的非缺货日值,之后再用shift取滞后值就能满足需求。
具体步骤
1. 生成非缺货订单的填充列
先筛选出所有非缺货日的订单量,缺货日设为NaN,再通过向前填充(ffill())用最近的非缺货值覆盖缺货日:
# 创建填充列:非缺货日保留原订单量,缺货日设为NaN df['filled_orders'] = df['orders'].where(df['stock_out'] == 0) # 向前填充,用最近的非缺货值填充缺货日 df['filled_orders'] = df['filled_orders'].ffill()
执行后,示例中最后三天的filled_orders都会被填充为1月28日的56。
2. 用shift获取滞后值
基于填充后的列,直接用shift取昨日(滞后1天)和12日前(滞后12天)的订单量:
# 昨日首个非缺货订单量 df['prev_day_order'] = df['filled_orders'].shift(1) # 12日前首个非缺货订单量 df['prev_12day_order'] = df['filled_orders'].shift(12)
3. 计算3日移动平均值
移动平均同样基于填充后的列,计算3日窗口的均值(min_periods=1确保窗口不足3天时仍能计算):
# 3日移动平均值(窗口包含当前及前2天) df['3d_ma'] = df['filled_orders'].rolling(window=3, min_periods=1).mean()
4. 计算最终平滑需求
取三个指标的均值作为缺货日的平滑需求:
# 计算平滑需求 df['smoothed_demand'] = df[['prev_day_order', 'prev_12day_order', '3d_ma']].mean(axis=1)
验证结果
查看最后三天的数据:
print(df.tail(3)[['calendar_date', 'stock_out', 'orders', 'filled_orders', 'prev_day_order', 'smoothed_demand']])
输出会显示prev_day_order均为56,符合预期的回溯取值逻辑。
补充说明
- 若存在连续多个缺货日,
ffill()会自动持续回溯到最近的非缺货日,完全满足需求; - 若需处理最早日期之前的缺货情况(比如前12天无有效数据),可结合
bfill()或指定默认值补充; - 所有计算基于填充后的
filled_orders,确保逻辑统一。
内容的提问来源于stack exchange,提问作者Jordan Howell
相关产品推荐
相关产品推荐

