Prophet:趋势+季节+节假日效应分解
一、直观理解:把业务曲线拆成可加积木
Prophet 把一条观测序列看成几块可加(或可乘)部件之和:
- :慢变趋势(分段线性或逻辑增长);
- :周期季节(周、年等),用傅里叶项刻画;
- :节假日/事件的脉冲效应;
- :噪声。
对业务人员而言,这很像:“销量 = 长期增长 + 每年旺季形态 + 春节冲高 + 随机扰动”。即使不完全使用 Facebook/Meta 的 Prophet 库,同一分解思想也可以在 MATLAB 里用回归或 trenddecomp 等工具实现。
它的卖点不是理论最优,而是:对缺失值、节假日、趋势拐点比较宽容,解释性强,调参直觉好。建模竞赛里,若题目提供日历特征或明显节假日,这种分解非常加分。
二、适用场景与优缺点
| 类型 | 说明 |
|---|---|
| 适用场景 | 日/周粒度业务数据;有多个季节周期;存在已知节假日或促销事件;需要组件解释而非只给黑箱预测;序列有缺失但不想复杂插补。 |
| 核心优点 | 结构可解释;易加入回归因子与节假日表;对趋势变点有正则;实现预测区间较方便;与业务沟通成本低。 |
| 局限与缺点 | 默认可加结构可能不符;强噪声或高频金融序列未必合适;变点过多会过拟合;MATLAB 无官方 Prophet 时需自建等价回归;仍需训练期外验证。 |
使用前检查:
- 时间索引规则、时区、是否含营业日;
- 节假日是否“提前/延后效应”(窗口宽度);
- 趋势是线性还是有饱和上界(逻辑增长);
- 与 SARIMA、Holt-Winters 的基线对比。
三、核心原理
1. 趋势项
分段线性趋势:
其中 标记 是否越过变点, 为基斜率, 为斜率变化。变点可自动按时间网格放置,并对 做稀疏先验(类似 L1),避免处处折线。
逻辑增长趋势用于有容量上界 的场景:
2. 季节项
对周期 (年 ,周 )用傅里叶级数:
控制季节曲线的灵活度:年季节常用 ,周季节 。更大的 更易过拟合。
3. 节假日项
每个节假日 对应指示变量(可含前后窗口):
为该事件的效应幅度。相似节日可共享先验,以稳定估计。
整体上,Prophet 的估计常借助贝叶斯/正则化回归思想;在 MATLAB 中可用普通最小二乘或 fitlm 对设计矩阵回归作为工程近似。
四、与 SARIMA / 指数平滑的分工
| 需求 | 更合适的方向 |
|---|---|
| 短序列、强统计诊断、单位根 | SARIMA |
| 只需水平/趋势/季节三组件、要快 | Holt-Winters |
| 多节假日、业务解释、缺失多 | Prophet 式分解 |
| 多个内生变量互相影响 | VAR |
Prophet 不是“更高级的 ARIMA”,而是结构化回归式时间模型。若残差仍有强自相关,可对残差再拟合 ARIMA(回归–ARIMA 两步法)。
五、MATLAB 示例(等价可加分解)
下列示例用分段趋势 + 傅里叶季节 + 节假日哑变量,演示与 Prophet 相同的信息结构。
%% 模拟 2 年日数据:趋势 + 周季节 + 年季节 + 节假日
rng(3);
t0 = datetime(2022,1,1);
t = t0 + days(0:730);
n = numel(t);
x = days(t - t0); % 数值时间
trend = 100 + 0.05*x;
weekS = 5*sin(2*pi*weekday(t)/7);
yearS = 8*sin(2*pi*x/365.25) + 3*cos(4*pi*x/365.25);
holidayDates = datetime(2022,2,1) + calyears(0:1); % 示例“春节”
isHol = ismember(t, holidayDates) | ismember(t, holidayDates+days(1));
y = trend + weekS + yearS + 12*isHol + 2*randn(n,1);
%% 训练/测试:最后 60 天
T0 = n - 60;
tr = 1:T0; te = T0+1:n;
%% 设计矩阵
% 1) 线性趋势 + 少量变点斜率增量
knots = quantile(x(tr), [0.3 0.5 0.7]);
A = zeros(n, numel(knots));
for j = 1:numel(knots)
A(:,j) = max(0, x - knots(j));
end
% 2) 周/年傅里叶
phi = 2*pi*x;
FWweek = [cos(phi/7), sin(phi/7), cos(2*phi/7), sin(2*phi/7)];
FWyear = [cos(phi/365.25), sin(phi/365.25), ...
cos(2*phi/365.25), sin(2*phi/365.25), ...
cos(3*phi/365.25), sin(3*phi/365.25)];
% 3) 节假日
H = double(isHol(:));
X = [ones(n,1), x(:), A, FWweek, FWyear, H];
tbl = array2table(X, "VariableNames", ...
["const","time", compose("kp%d",1:numel(knots)), ...
"cw1","sw1","cw2","sw2","cy1","sy1","cy2","sy2","cy3","sy3","hol"]);
tbl.y = y(:);
mdl = fitlm(tbl(tr,:), "y ~ . - 1"); % 已含 const
yhat = predict(mdl, tbl);
rmse = sqrt(mean((y(te) - yhat(te)).^2));
fprintf("测试 RMSE = %.3f\n", rmse);
%% 组件可视化(用系数还原)
b = mdl.Coefficients.Estimate;
g = X(:,1:2+numel(knots)) * b(1:2+numel(knots));
s = X(:,3+numel(knots):end-1) * b(3+numel(knots):end-1);
h = X(:,end) * b(end);
figure;
subplot(4,1,1); plot(t,y); hold on; plot(t,yhat,"LineWidth",1.2);
xline(t(T0),"k--"); title("观测与拟合/预测"); legend("y","fit");
subplot(4,1,2); plot(t,g); title("趋势 g(t)");
subplot(4,1,3); plot(t,s); title("季节 s(t)");
subplot(4,1,4); plot(t,h); title("节假日 h(t)");
若已安装 Python Prophet,也可在 MATLAB 中通过 py 接口调用;竞赛若不允许外部库,用上述回归分解即可写清原理与结果。
更完整的流程还应:对残差做 autocorr/lbqtest;若自相关明显,对残差 estimate(arima(...)) 再叠加。
六、如何解释结果
报告建议包含:
- 组件设定:趋势类型、变点个数与位置、季节周期与傅里叶阶数、节假日表;
- 各组件图:趋势是否在某时期变陡,季节峰谷是否符合业务;
- 节假日效应系数及窗口选择依据;
- 训练期外 RMSE/MAE,以及对比 SARIMA、Holt-Winters、季节朴素;
- 残差诊断:是否仍有周期泄漏;
- 不确定性:可用残差bootstrap或分位回归给区间,不要只给点预测。
解释时避免把傅里叶项单个系数“逐项讲故事”,应看合成后的季节曲线形态。
七、常见误区
1. 变点过多导致趋势跟着噪声走
变点网格太密、惩罚太弱,会把随机起伏当成结构变化,预测区间失真。
2. 节假日窗口乱设
只标节日当天,却忽略“节前备货/节后回落”,效应会被季节项或残差吸收。
3. 可加/可乘结构选错
波动随水平增大时,应对 取对数或改用可乘季节。
4. 不设基线对比
若季节朴素(去年同日)已经很好,复杂分解的增量价值有限。
5. 用未来才知道的回归因子
例如把“当日是否突发新闻”当特征却无法在预测时获得,属于信息泄漏。
八、小结
Prophet 风格模型的核心是:
用可解释的趋势、季节与节假日组件拼出预测,并把业务日历信息显式写进模型。
它特别适合带日历结构的业务序列。在 MATLAB 中即使没有原生 Prophet,也可用 fitlm 构造等价设计矩阵完成分解与预测。务必坚持训练期外评价、残差检验,并与 SARIMA/指数平滑基线比较,避免“分解很好看但测不准”。