
本文介绍如何基于提交月份(submissionmonth)对多维时间序列数据进行智能前向填充,确保每个 sku-位置组合在目标时间范围内(如 jan–dec 2023)拥有完整、连续的月度预测记录,且仅沿 submissionmonth 维度向前扩散最新有效快照。
本文介绍如何基于提交月份(submissionmonth)对多维时间序列数据进行智能前向填充,确保每个 sku-位置组合在目标时间范围内(如 jan–dec 2023)拥有完整、连续的月度预测记录,且仅沿 submissionmonth 维度向前扩散最新有效快照。
在供应链预测、财务滚动 forecast 或运营仪表盘等场景中,原始数据常以“快照表”形式存在:每次提交(如每月初)生成一组针对未来若干月的预测值(forecastmonth),但提交本身并不连续。为支持按自然月(如 2023-01 至 2023-12)聚合分析或可视化,需将离散提交“延展”为连续月度视图——即:对每个 (sku, location) 分组,以其最新可用 submissionmonth 的全部 forecastmonth 行为模板,向前填充至当前月份(如 Dec-2023)。
核心思路不是简单调用 df.ffill(),而是通过 重塑(pivot → reindex → ffill → unstack) 实现按逻辑维度的定向填充。具体步骤如下:
✅ 步骤详解
- 标准化时间索引:将 submissionmonth 解析为 datetime,便于生成完整月份序列;
- 构建目标月份范围:使用 pd.date_range(..., freq="M") 生成从最早到最晚提交月的月末日期序列,再格式化回 "Mon-YYYY" 字符串(与原始数据一致);
-
重塑数据结构:
- 以 ["submissionmonth", "forecastmonth"] 为多级索引,使每行唯一对应一个提交月 × 预测月组合;
- unstack("forecastmonth") 将 forecastmonth 转为列,形成宽表:行=submit month,列=forecast month,值=cost 等字段;
-
对齐并前向填充:
- reindex(..., axis=0) 将宽表行索引扩展为完整月份(缺失月份行自动补 NaN);
- .ffill() 沿行方向(即 submissionmonth 维度)向下填充,确保每个新月份行继承其上方最近的有效快照;
- 还原长表结构:.stack("forecastmonth") 将 forecastmonth 列压回行索引,再 .reset_index() 得到标准长格式 DataFrame。
? 完整可运行代码
import pandas as pd
# 原始输入数据(已提供)
input_data = pd.DataFrame([
{"submissionmonth": "Jan-2023", "sku": "A1","location": "sup1","forecastmonth": "Jan-2023","cost": 100},
{"submissionmonth": "Jan-2023", "sku": "A1","location": "sup1","forecastmonth": "Feb-2023","cost": 105},
{"submissionmonth": "Jan-2023", "sku": "A1","location": "sup1","forecastmonth": "Mar-2023","cost": 108},
{"submissionmonth": "Jan-2023", "sku": "A1","location": "sup1","forecastmonth": "Apr-2023","cost": 106},
{"submissionmonth": "Apr-2023", "sku": "A1","location": "sup1","forecastmonth": "Apr-2023","cost": 101},
{"submissionmonth": "Apr-2023", "sku": "A1","location": "sup1","forecastmonth": "May-2023","cost": 102},
{"submissionmonth": "Apr-2023", "sku": "A1","location": "sup1","forecastmonth": "Jun-2023","cost": 109},
{"submissionmonth": "Apr-2023", "sku": "A1","location": "sup1","forecastmonth": "Jul-2023","cost": 104},
{"submissionmonth": "Oct-2023", "sku": "A2","location": "sup2","forecastmonth": "Oct-2023","cost": 101},
{"submissionmonth": "Oct-2023", "sku": "A2","location": "sup2","forecastmonth": "Nov-2023","cost": 102},
{"submissionmonth": "Oct-2023", "sku": "A2","location": "sup2","forecastmonth": "Dec-2023","cost": 109},
{"submissionmonth": "Oct-2023", "sku": "A2","location": "sup2","forecastmonth": "Jan-2024","cost": 104},
])
# Step 1: 解析 submissionmonth 为 datetime
submission_months = pd.to_datetime(input_data["submissionmonth"], format="%b-%Y")
# Step 2: 生成完整月份序列(月末日期 → 格式化字符串)
full_months = pd.date_range(
start=submission_months.min(),
end=submission_months.max(),
freq="M"
).strftime("%b-%Y")
# Step 3–5: 重塑 → 对齐 → 填充 → 还原
output_data = (
input_data
.set_index(["submissionmonth", "forecastmonth"])
.unstack("forecastmonth")
.reindex(full_months, axis=0)
.ffill()
.stack("forecastmonth")
.reset_index(names=["submissionmonth", "forecastmonth"])
)
# (可选)保持原始列顺序
output_data = output_data[input_data.columns]
print(f"Input shape: {input_data.shape} → Output shape: {output_data.shape}")
output_data.head(10)
⚠️ 注意事项与进阶建议
- 分组独立性:上述方案默认全局填充。若需严格按 (sku, location) 分组分别前向填充(例如 A1-sup1 和 A2-sup2 互不影响),应在 set_index 前添加 .groupby(["sku", "location"]) 并对每组单独处理,最后 pd.concat;
- 截止月份控制:示例中 end=submission_months.max() 仅覆盖已有提交范围。若需强制延伸至指定截止月(如 "Dec-2023"),请显式设置 end=pd.to_datetime("2023-12-31");
- 性能优化:对大数据集,避免 unstack/stack 的内存开销,可改用 pd.merge_asof 或 reindex + groupby().apply() 结合 pd.date_range 生成补全索引;
- 字段一致性:ffill() 会同步填充所有数值列(如 cost, quantity)。若某些列(如 submissionmonth)不应被填充,需在 reset_index() 后手动修正,或改用 assign() 显式更新。
该方法兼顾准确性与可读性,是处理“快照式时序补全”问题的标准范式,适用于 Pandas 1.4+,无需额外依赖。











