季节性问题
DTC 品牌的销售数据有一个普遍特征:Q4(10-12 月)销售远高于其他季度。双11、黑五、圣诞节三波大促叠加,Q4 销售可能占全年的 35-45%。
同时,Q4 也是广告花费最高的季度。这导致一个统计陷阱:
混淆:广告花费和销售都在 Q4 飙升。如果不分离季节性,MMM 模型会把"因为节假日所以卖得多"的效果错误归因到广告花费上 — 高估广告 ROI。
这就是为什么 MMM 必须在建模前对时间序列做季节性分解:把"趋势"和"季节性"从原始信号中剥离出来,让模型专注于广告花费的增量效应。
什么是 STL
STL(Seasonal-Trend decomposition using LOESS)是一种时间序列分解方法,由 Cleveland 等人在 1990 年提出。它将任意时间序列拆解为三个分量:
- Trend(趋势):长期增长或衰退的平滑曲线
- Seasonal(季节性):周期性波动的模式(年度/季度/月度)
- Remainder(残差):去除趋势和季节性后的剩余信号
与经典的移动平均分解不同,STL 使用 LOESS(局部加权回归)做平滑,可以处理变化季节性 — 季节性模式本身随时间演变的情况。
STL 工作原理
STL 的核心是两层嵌套的 LOESS 平滑循环:
内循环(每次迭代)
- 去趋势:从原始序列减去当前趋势估计
- 季节性平滑:对每个周期的子序列做 LOESS 平滑,得到季节性分量
- 低通滤波:对季节性分量做低通滤波,去除高频噪音
- 去季节性:从原始序列减去季节性,得到去季节序列
- 趋势平滑:对去季节序列做 LOESS 平滑,得到新的趋势估计
外循环
外循环用于计算鲁棒性权重,降低异常值的影响。每次外循环结束后,用权重重新做内循环。通常 2-3 次外循环就足够收敛。
季节性窗口 (s_window):控制季节性分量的平滑度。窗口越大,季节性越稳定;窗口越小,季节性可以随时间变化。SparkX 默认设为"periodic"(固定季节性),在 Expert Mode 中可以自定义。
趋势窗口 (t_window):控制趋势分量的平滑度。通常设为季节性周期的 1.5-2 倍。
在 MMM 中的作用
STL 分解在 MMM 中发挥两个关键作用:
作用一:生成控制变量
STL 提取的季节性分量可以直接作为 MMM 模型的控制变量。这样模型就不需要自己从数据中学习季节性模式,减少过拟合风险。
# SparkX Pipeline 第 3 步(EDA)中自动执行
from statsmodels.tsa.seasonal import STL
decomp = STL(revenue_series, period=52, seasonal=13)
result = decomp.fit()
seasonal_control = result.seasonal
trend_control = result.trend
作用二:残差诊断
STL 的残差分量用于模型诊断。如果 MMM 模型拟合后,残差中仍然残留明显的季节性模式,说明模型没有充分捕捉季节性 — 可能需要增加傅里叶项阶数或调整 STL 参数。
不做 STL 分解的后果
| 问题 | 表现 | 后果 |
|---|---|---|
| 季节性未分离 | Q4 残差有系统性正偏 | 广告 ROI 被高估 15-30% |
| 趋势未分离 | 残差有长期漂移 | 新渠道效果被高估(搭趋势便车) |
| 异常值未处理 | STL 季节性分量被扭曲 | 季节性控制变量本身有问题 |
SparkX 实现
SparkX MMM 在 9 步 Pipeline 的第 3 步(EDA)自动执行 STL 分解:
- 自动检测周期:日数据 = 365,周数据 = 52,月数据 = 12
- 鲁棒模式:默认开启外循环,降低大促异常值的影响
- 可视化:EDA 报告中展示四面板图(原始 / 趋势 / 季节性 / 残差)
- 诊断提示:如果残差中检测到残留季节性(Ljung-Box 检验 p < 0.05),AI 解读会建议增加傅里叶项阶数
在 Expert Mode 中,你可以手动调整 STL 参数:
- 关闭鲁棒模式(加速但易受异常值影响)
- 自定义季节性窗口(允许季节性随时间变化)
- 使用 MSTL(Multi-Seasonal STL)处理多种周期叠加(如日数据中的周度 + 年度季节性)
实践建议
- 不要跳过 STL:即使你的数据只有 52 周,STL 也能有效分离年度季节性。不做 STL 的模型几乎一定会高估 Q4 广告效果。
- 检查残差:STL 后的残差应该看起来像白噪音。如果残差有规律性模式,说明分解不充分 — 增加季节性窗口或切换到 MSTL。
- 大促单独处理:双11、黑五这种脉冲式异常值不适合用 STL 平滑。用虚拟变量单独建模,让 STL 专注处理"正常"的季节性波动。
- 趋势 vs 趋势项:STL 的趋势分量和模型中的线性趋势项不冲突 — STL 趋势是非线性的平滑曲线,线性趋势项捕捉的是全局斜率。两者互补。
- 日数据用 MSTL:如果你的数据是日粒度,存在周度(周期 7)和年度(周期 365)两种季节性。使用 MSTL 同时分解两个周期。
STL 分解是 MMM 数据预处理的标配。它不是可选项 — 不做 STL 的 MMM 模型在季节性强的品类(电商、快消、旅游)中几乎一定会产生系统性偏差。SparkX 自动完成这一步,你不需要手动操作,但理解它的原理有助于解读结果。