
本文介绍如何基于 pandas 对具有多层级时间结构(如 submissionmonth 和 forecastmonth)的销售/预测数据,按业务规则对缺失月份执行精准前向填充,确保每个 submissionmonth 覆盖其生效起始月到截止月(如当前月 dec-2023)的完整连续月度记录。
本文介绍如何基于 pandas 对具有多层级时间结构(如 submissionmonth 和 forecastmonth)的销售/预测数据,按业务规则对缺失月份执行精准前向填充,确保每个 submissionmonth 覆盖其生效起始月到截止月(如当前月 dec-2023)的完整连续月度记录。
在实际业务场景中(如供应链预测、月度成本申报),原始数据常以「提交批次 + 预测周期」形式存储——即某次提交(如 Apr-2023)会附带一组未来月份的预测值(Apr-2023 至 Jul-2023)。但当后续无新提交时,需将最新有效批次的数据前向延续至当前月份(如 Dec-2023),形成连续、可比的月度快照视图。这并非简单 .ffill() 可解决,而是需结合时间对齐、层级重组与智能广播。
核心思路是:将数据升维为「提交月 × 预测月」的二维矩阵 → 补全提交月时间轴 → 前向填充 → 降维还原。具体步骤如下:
✅ 步骤 1:标准化时间索引
首先将字符串型月份(如 "Jan-2023")转为 datetime,便于生成连续时间范围:
import pandas as pd
input_data = pd.DataFrame([
{"submissionmonth": "Jan-2023", "sku": "A1", "location": "sup1", "forecastmonth": "Jan-2023", "cost": 100},
# ...(其余输入数据)
])
# 解析为 datetime 并获取完整提交月范围
sub_dt = pd.to_datetime(input_data["submissionmonth"], format="%b-%Y")
full_sub_months = pd.date_range(sub_dt.min(), sub_dt.max(), freq="M")
# 生成目标提交月列表(格式还原为 "Jan-2023")
target_sub_months = full_sub_months.strftime("%b-%Y")
✅ 步骤 2:构建提交月 × 预测月矩阵并补全
使用 set_index + unstack 将数据重塑为宽表,使每行对应一个 submissionmonth,每列对应一个 forecastmonth:
# 设置双层索引并展开 forecastmonth 为列
wide_df = (input_data
.set_index(["submissionmonth", "forecastmonth"])
.unstack("forecastmonth") # → 列为 MultiIndex: (value_col, forecastmonth)
)
# 按目标提交月重新索引(自动引入 NaN 行)
wide_filled = wide_df.reindex(target_sub_months, axis=0)
# 关键:按行前向填充 —— 每个新 submissionmonth 继承上一个有效批次的所有 forecastmonth 数据
wide_ffilled = wide_filled.ffill()
✅ 步骤 3:还原为长表并校准字段顺序
通过 stack() 将列级 forecastmonth 压回行内,并重置索引:
# 压栈还原为长格式,同时恢复 submissionmonth 和 forecastmonth 列
output_data = (wide_ffilled
.stack("forecastmonth") # → (submissionmonth, forecastmonth) 为新索引
.reset_index(names=["submissionmonth", "forecastmonth"])
)
# 保持原始列顺序(可选但推荐)
output_data = output_data.reindex(columns=input_data.columns)
⚠️ 注意事项与增强建议
- 截止月控制:示例中 full_sub_months 仅覆盖现有提交月范围。若需延伸至固定截止月(如 Dec-2023),应显式定义 end_date = pd.Timestamp("2023-12-31") 并传入 pd.date_range(..., end=end_date)。
- 分组独立填充:若数据含多个 SKU/Location 组合(如 A1-sup1 与 A2-sup2),需先 groupby(["sku", "location"]),再对各组分别执行上述流程,避免跨组污染。
- 空值处理:.ffill() 默认不跨组填充。若某组首个 submissionmonth 之前存在空档,需结合 limit 参数或预填充首行。
- 性能优化:对大数据集,优先使用 pd.Grouper 或 resample 替代手动时间范围生成,减少内存占用。
该方法兼顾了业务语义(提交批次的时效性)与工程严谨性(时间连续性保证),是构建月度滚动预测基线、BI 时间序列报表的可靠范式。











