目录

一、ARIMA 模型族

1. ARIMA(p,d,q)

1.1 核心思想

1.2 计算流程

1.3 代码示例

2. SARIMA​编辑

2.1 核心思想

2.2 SARIMA 与 ARIMA 的 5 大本质不同

2.3 代码演示

3. ARIMAX

3.1 核心思想

3.2 ARIMAX 与纯 ARIMA 的 5 大本质不同

3.3 代码演示:

3.4 ARIMAX vs ARIMA 的核心结论

二、现代时间序列模型

1. Prophet(Facebook)

1.1 适用场景

1.2 核心操作步骤:

1.3 代码骨架:

2. TBATS

2.1 适用场景

2.2 核心操作步骤

2.3 代码骨架

3. VAR / VECM

3.1 VAR (p) 向量自回归

3.11 适用场景

3.2 VECM 向量误差修正模型

3.21 适用场景

3.22 核心操作步骤

4. 终极算法本质一句话总结(区别于传统 ARIMA)

三、完整模型对比总结表(7大时序模型)


一、ARIMA 模型族

前提:序列平稳

通用核心思想:非平稳序列先差分转化为平稳序列;利用序列自身滞后值(自回归 AR)、历史预测误差(移动平均 MA)建立线性模型

1. ARIMA(p,d,q)

1.1 核心思想

通过d次差分消除趋势实现平稳,结合自回归项与移动平均项建模。

:d阶差分后平稳序列;p自回归阶数;d差分阶数;q移动平均阶数;

:白噪声

  1. 公式讲解:使用差分消除趋势;由序列滞后信息与历史误差共同预测未来。
  2. 优点:统计学理论完善,可进行显著性检验;
  3. 缺点:原生不支持季节性;要求差分后平稳;长步预测效果差;
  4. 应用场景:无明显周期性的单变量时序,部分金融指标、平稳负荷序列。

1.2 计算流程

1. 差分算法(确定 d)【计算机的“平稳性体检”】,把序列化成平稳

计算机无法通过肉眼判断,它依赖假设检验的循环(Loop)

  • 执行逻辑

    1. 单位根检验(ADF检验:计算当前序列的Dickey-Fuller统计量,得到p值。

    2. 判断分支:若 p-value > 0.05(无法拒绝非平稳原假设),则 d = d + 1,执行 y_t = Y_t - Y_{t-1}(一阶差分)。

    3. 递归检验:对差分后的新序列再次进行ADF检验。重复上述过程,直到 p-value <= 0.05 或达到预设最大差分阶数(通常硬性限制 d <= 2)。

  • 隐藏的“防过差”算法:计算机不仅看平稳性,还会计算差分后的方差变化。如果差分后序列的方差比上一级增大了超过一定阈值(如标准差扩大20%)

2. 定阶算法(确定 p, q)【暴力搜索 + 信息论裁判

确定差分阶数 d 后,计算机需要找出最合适的滞后期数 p(自回归)和 q(移动平均)。它不会去“看图”,而是采用带惩罚的网格搜索

  • 执行逻辑

    1. 划定搜索空间:设定 p∈[0,Pmax],q∈[0,Qmax](通常 Pmax 和 Qmax​ 设为 5~7,因为高阶模型极易过拟合)。

    2. 暴力遍历:计算机生成所有 (p,q) 组合的笛卡尔积。例如,若最大值为5,则有 36 种组合。

    3. 对每个组合执行“快速拟合并打分”

      • 针对每一组 (p,q),利用条件最小二乘(CLS) 快速(不迭代)估算出大概的参数向量。

      • 计算该模型的 AIC(赤池信息准则):AIC=2k−2ln⁡(L),其中 k=p+q(参数个数),L 为似然函数值。

    4. 排序输出:遍历完毕后,计算机选取 AIC 值最小 的一组作为最优 (p,q)。(Hyndman-Khandakar 算法还会加入一步“逐步搜索”,比如在最优解附近微调,防止漏掉局部最优)。

3. 参数估计算法(算出 AR 和 MA 的系数

这是ARIMA中最核心、计算量最大的环节。难点在于:MA(移动平均)项涉及不可观测的历史误差 ϵ,无法直接用最小二乘法闭式求解。

计算机采用状态空间 + 卡尔曼滤波 + 迭代优化的三层嵌套算法:

  1. 状态空间重构(State-Space Representation)
    计算机将 ARIMA 方程改写为“状态方程”和“观测方程”,将不可见的 AR/MA 系数转化为状态变量。

  2. 初始化“误差”(Backcasting/逆推法):
    由于 q 阶之前的误差 ϵ0,ϵ−1...无法观测,计算机先用逆向预测(Backcasting),利用全部历史数据的反向递推,估算出初始时刻的残差初始值,作为迭代的“种子”。

  3. 核心迭代(卡尔曼滤波 + 最大似然估计 MLE)

    • 给定一组初始猜测的系数(例如全部设为 0.1),计算机启动卡尔曼滤波,沿着时间轴从头到尾递推一次。

    • 每递推一步,卡尔曼滤波会计算预测误差,并根据预测误差实时更新内部状态(这就是为什么它能处理不可观测的 MA 误差)。

    • 递推完整个序列后,计算机计算出一个负对数似然值(Negative Log-Likelihood)

    • 随后,计算机调用BFGS(拟牛顿法)Nelder-Mead(单纯形法) 优化算法:优化算法微调系数(如将 AR(1) 从 0.1 改为 0.11),再次运行卡尔曼滤波,计算新的似然值。

    • 收敛判定:当连续两次迭代的系数变化小于 10−6 或梯度接近零时,停止迭代。这组系数就是最终输出的 AR 和 MA 系数。

4. 预测算法

当系数 ϕ(AR项)和 θ(MA项)被固定下来后,计算机开始生成预测。这里存在两种完全不同的预测模式

  1. 一步预测(1-step ahead,用于拟合检验)

    • 预测 t+1 时,计算机使用 t,t−1... 时刻的历史真实值,以及 t,t−1... 时刻的历史真实误差(残差)

    • 直接代入方程:Y^t+1=c+ϕ1Yt+...+θ1ϵt+....。

    • 这种预测极其准确,但仅用于计算拟合优度。

  2. 多步预测(Multi-step ahead,即你想要的未来预测)

    • 链式递推:预测 t+2 时,计算机发现 Yt+1​ 是未知的,于是它把上一步预测出的 Y^t+1 当作真实值代入方程。

    • 致命算法约定:未来误差置零:对于 t+1 时刻之后的误差项 ϵt+1,ϵt+2...,计算机将其数学期望(即 0)直接代入,不再递归预测误差。

    • 结果显现:正因为未来的随机冲击(误差)被强制归零,所以 ARIMA 的多步预测曲线在 3~5 步后迅速收敛到序列的长期均值(平稳序列中心),且置信区间呈扇形急剧扩大。

5. 额外补充:计算机如何处理“常数项” c?

算法还会判断是否包含常数项 c。在差分(d>0)后,如果序列均值显著不为零,算法会估计 c;若均值围绕零波动,则会强制令 c=0,因为包含多余的常数项会显著增大多步预测的累积误差(容易导致预测发散)

6. 一句话总结这四步算法的计算机本质:

  • d:由机器循环执行差分 + 单位根检验决定(硬判断);

  • p, q:由机器执行全网格搜索 + AIC打分决定(贪心选择);

  • 系数:由机器执行卡尔曼滤波递推计算似然值,并由 BFGS优化器 疯狂迭代数千次直至收敛(非线性求解);

  • 预测值:由机器执行线性递推,并将所有未发生的误差强行设定为 0(期望归零)。

1.3 代码示例

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.stattools import adfuller
import warnings
warnings.filterwarnings('ignore')  # 忽略版本警告,保持输出清爽
配置中文字体,避免标题/标签显示为乱码
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
----- 1. 模拟数据(生成一个非平稳序列:线性趋势 + 白噪声)-----
np.random.seed(42)
n = 150
time = np.arange(n)
原始数据:斜率 0.5 的趋势 + 随机波动
data = 0.5 * time + np.random.normal(0, 3, n)
series = pd.Series(data, index=pd.date_range('2020-01-01', periods=n, freq='D'))
----- 2. 平稳性检验(确认是否需要差分)-----
ADF检验: p-value > 0.05 说明非平稳,需要差分
result = adfuller(series)
print(f"ADF 检验 p-value: {result[1]:.4f}")  # 通常 p 值远大于 0.05,确认非平稳
----- 3. 差分处理(确定 d=1)并对差分后序列再次做平稳性检验 -----
一阶差分去除趋势
diff_series = series.diff().dropna()
diff_result = adfuller(diff_series)
p 值极小(如 2.6e-08)时用科学计数法显示,避免被 :.4f 截断成 0.0000 的假象
def fmt_p(p):
return f"{p:.2e}" if p < 0.0001 else f"{p:.4f}"
print(f"一阶差分后 ADF 检验 p-value: {fmt_p(diff_result[1])}  (若 <0.05 则已平稳,d=1 足够)")
----- 3.5 差分效果可视化(上下对比:原序列 vs 一阶差分序列)-----
先弹出本图,关闭后才能继续后面的网格搜索与预测
fig, axes = plt.subplots(2, 1, figsize=(12, 7), sharex=True)
上图:原始序列(明显上升趋势 → 非平稳)
axes[0].plot(series.index, series, color='blue', label=f'原始序列 (非平稳, ADF p={result[1]:.4f})')
axes[0].set_title('差分前:带线性趋势,ADF p-value > 0.05 → 非平稳')
axes[0].legend()
axes[0].grid(True)
下图:一阶差分序列(围绕 0 上下波动 → 平稳)
axes[1].plot(diff_series.index, diff_series, color='green', label=f'一阶差分序列 (平稳, ADF p={fmt_p(diff_result[1])})')
axes[1].axhline(0, color='gray', linestyle='--', linewidth=0.8)  # 0 参考线
axes[1].set_title('差分后:围绕 0 波动,ADF p-value < 0.05 → 平稳,d=1 足够')
axes[1].legend()
axes[1].grid(True)
plt.suptitle('一阶差分效果对比', fontsize=14)
plt.tight_layout()
plt.show()   # 先看差分效果,关掉本窗口后继续执行网格搜索
----- 4. AIC 网格搜索选择最优 (p, q)(d 已由差分验证确定为 1)-----
p_range = range(0, 5)   # p: 0~4
q_range = range(0, 5)   # q: 0~4
d = 1
grid_results = []   # 保存所有 (p, q, AIC) 结果
best_aic = np.inf
best_order = None
best_model = None
print("\n===== 开始 AIC 网格搜索 (p×q = 5×5 = 25 种组合) =====")
for p in p_range:
for q in q_range:
try:
model = ARIMA(series, order=(p, d, q))
fitted = model.fit()
grid_results.append({'p': p, 'q': q, 'AIC': fitted.aic})
print(f"ARIMA({p},{d},{q})  AIC = {fitted.aic:>8.2f}")
# 记录最小 AIC 对应的最优模型
if fitted.aic < best_aic:
best_aic = fitted.aic
best_order = (p, d, q)
best_model = fitted
except Exception as e:
# 某些阶数组合可能无法收敛/估计失败,跳过并记录 NaN
print(f"ARIMA({p},{d},{q}) 拟合失败: {e}")
grid_results.append({'p': p, 'q': q, 'AIC': np.nan})
将网格搜索结果整理成二维表格展示(行=p,列=q)
grid_df = pd.DataFrame(grid_results)
aic_pivot = grid_df.pivot(index='p', columns='q', values='AIC')
print("\n===== AIC 网格搜索热力表格(行=p,列=q,数值越小模型越好)=====")
print(aic_pivot.round(2))
----- 4.5 导出 AIC 网格结果为 Excel(供 Origin 生成热力图)-----
export_file = 'AIC_网格热力图.xlsx'
注意:用 index_label='p' 让第一列(A列)是 p 值,第一行是 q 值,形成 Origin 可直接画热力图的矩阵格式
aic_pivot.to_excel(export_file, sheet_name='AIC', index_label='p')
print(f"\n>>> AIC 网格结果已导出: {export_file}(行=p,列=q,可直接在 Origin 中画热力图)")
print(f"\n>>> 网格搜索选出的最优模型: ARIMA{best_order}  AIC = {best_aic:.2f}")
----- 5. 打印网格搜索最优模型的详细结果(作为"反例"展示)-----
注意:这里打印 ARIMA(3,1,4) 是故意的——用它的 summary 展示过拟合证据
(ma.L1~ma.L4 的 P>|z| 全 > 0.86、标准误高达 11+,明显无效参数)
真正用于预测/出图的最终模型,在下面第 6 节选定为简约推荐 ARIMA(1,1,2)
fitted_model = best_model
print("\n", fitted_model.summary())  # 查看 AR/MA 系数的显著性(P>|z|)
----- 6. 三方模型对比:网格最优 vs 简约推荐 vs 手动预设 -----
(2,1,2) 是原始代码里"凭经验预设"的模型,保留它作为对比基准,
代表"不经过网格搜索、拍脑袋选的模型"
manual_model = ARIMA(series, order=(2, 1, 2)).fit()
(1,1,2) 是简约推荐:AIC 与全局最优几乎并列(差 <2),但参数更少、更稳健
parsimonious_order = (1, 1, 2)   # 最终推荐使用的模型阶数
parsimonious_model = ARIMA(series, order=parsimonious_order).fit()
后续预测/出图统一改用简约推荐模型(避免展示 AIC 最小但已过拟合的模型)
fitted_model = parsimonious_model
print("\n===== 模型效果对比(AIC 越小越好)=====")
print(f"网格搜索最优 ARIMA{best_order}  : AIC = {best_aic:.2f}  (AIC 最小,但注意检查系数是否过拟合)")
print(f"简约推荐   ARIMA{parsimonious_order}         : AIC = {parsimonious_model.aic:.2f}  (AIC 与最优差 <2,参数却更少)")
print(f"手动预设   ARIMA(2,1,2)         : AIC = {manual_model.aic:.2f}  (原始代码凭经验选的基准)")
print("(规则:AIC 相差 <2 算没差别,此时应选参数较少的简约模型)")
----- 7. 模型诊断:残差白噪声检验(Q检验)-----(针对最终推荐的简约模型)
判断标准看 summary 里的 Ljung-Box Q 检验:Prob(Q) > 0.05 说明残差无自相关
resid = fitted_model.resid
print(f"\n残差均值: {resid.mean():.6f}(ARIMA 残差均值不强制为 0,应以 Ljung-Box 检验为准)")
----- 8. 进行预测(未来 10 步),并输出 95% 置信区间 -----
forecast_steps = 10
forecast_result = fitted_model.get_forecast(steps=forecast_steps)  # get_forecast 可同时拿到置信区间
forecast = forecast_result.predicted_mean
ci = forecast_result.conf_int()
print(f"\n未来 {forecast_steps} 天的预测值: \n{forecast}")
print(f"\n预测 95% 置信区间: \n{ci}")
----- 9. 可视化绘图 -----
plt.figure(figsize=(12, 5))
plt.plot(series.index, series, label='原始数据 (非平稳)', color='blue')
plt.plot(forecast.index, forecast, label=f'预测趋势 ARIMA{parsimonious_order}', color='red', marker='o', linestyle='--')
绘制置信区间(半透明带)
plt.fill_between(forecast.index, ci.iloc[:, 0], ci.iloc[:, 1],
color='red', alpha=0.2, label='95% 置信区间')
plt.axvline(x=series.index[-1], color='gray', linestyle=':', label='预测起始点')
plt.title(f'ARIMA{parsimonious_order} 简约推荐模型预测效果 (AIC={fitted_model.aic:.2f})')
plt.legend()
plt.grid(True)
plt.show()

运行结果

ADF 检验 p-value: 0.9757
                            SARIMAX Results                                
==============================================================================
Dep. Variable:                      y   No. Observations:                  150
Model:                 ARIMA(2, 1, 2)   Log Likelihood                -393.343
Date:                Mon, 10 Aug 2026   AIC                            796.685
Time:                        12:12:36   BIC                            811.705
Sample:                    01-01-2020   HQIC                           802.787
- 05-29-2020

Covariance Type:                  opg
             coef    std err          z      P&gt;|z|      [0.025      0.975]
ar.L1         -1.0618      0.162     -6.562      0.000      -1.379      -0.745
ar.L2         -0.2378      0.147     -1.615      0.106      -0.526       0.051
ma.L1          0.3906      0.156      2.500      0.012       0.084       0.697
ma.L2         -0.4378      0.177     -2.474      0.013      -0.785      -0.091
sigma2        11.4444      1.378      8.307      0.000       8.744      14.145
Ljung-Box (L1) (Q):                   3.12   Jarque-Bera (JB):                 0.09
Prob(Q):                              0.08   Prob(JB):                         0.96
Heteroskedasticity (H):               1.35   Skew:                            -0.01
Prob(H) (two-sided):                  0.29   Kurtosis:                         3.12
Warnings:
[1] Covariance matrix calculated using the outer product of gradients (complex-step).
残差均值 (应接近0): 1.179711
未来 10 天的预测值:
2020-05-30    74.132958
2020-05-31    73.687345
2020-06-01    74.459684
2020-06-02    73.745602
2020-06-03    74.320122
2020-06-04    73.879932
2020-06-05    74.210683
2020-06-06    73.964185
2020-06-07    74.147252
2020-06-08    74.011498
Freq: D, Name: predicted_mean, dtype: float64

其中网格搜索法是我们进行模拟指定的

真正的网格搜索法如下:

print("\n===== 开始 AIC 网格搜索 (p×q = 5×5 = 25 种组合) =====")
for p in p_range:
    for q in q_range:
        try:
            model = ARIMA(series, order=(p, d, q))
            fitted = model.fit()
            grid_results.append({'p': p, 'q': q, 'AIC': fitted.aic})
            print(f"ARIMA({p},{d},{q})  AIC = {fitted.aic:>8.2f}")
            # 记录最小 AIC 对应的最优模型
            if fitted.aic < best_aic:
                best_aic = fitted.aic
                best_order = (p, d, q)
                best_model = fitted
        except Exception as e:
            # 某些阶数组合可能无法收敛/估计失败,跳过并记录 NaN
            print(f"ARIMA({p},{d},{q}) 拟合失败: {e}")
            grid_results.append({'p': p, 'q': q, 'AIC': np.nan})
将网格搜索结果整理成二维表格展示(行=p,列=q)
grid_df = pd.DataFrame(grid_results)
aic_pivot = grid_df.pivot(index='p', columns='q', values='AIC')
print("\n===== AIC 网格搜索热力表格(行=p,列=q,数值越小模型越好)=====")
print(aic_pivot.round(2))
----- 4.5 导出 AIC 网格结果为 Excel(供 Origin 生成热力图)-----
export_file = 'AIC_网格热力图.xlsx'
注意:用 index_label='p' 让第一列(A列)是 p 值,第一行是 q 值,形成 Origin 可直接画热力图的矩阵格式
aic_pivot.to_excel(export_file, sheet_name='AIC', index_label='p')
print(f"\n>>> AIC 网格结果已导出: {export_file}(行=p,列=q,可直接在 Origin 中画热力图)")
print(f"\n>>> 网格搜索选出的最优模型: ARIMA{best_order}  AIC = {best_aic:.2f}")
----- 5. 使用网格搜索选出的最优模型,打印详细拟合结果 -----
fitted_model = best_model
print("\n", fitted_model.summary())  # 查看 AR/MA 系数的显著性(P>|z|)

搜索结果:

我们可以看到(2,1)和(4,3)的AIC值差不多,但是参数少,拟合也够好,也与最优几乎持平,性价比最高。故我们选择(p,q)=(1,2)作为网格搜索的最优结果。综上所述,可以得到最后训练的ARIMA模型参数:

model = ARIMA(series, order=(1, 1, 2)) #中间的“1”是差分数d,之前算出为1


2. SARIMA

2.1 核心思想

在 ARIMA 基础上增加季节差分、季节自回归、季节移动平均项,专门处理周期性时序。

2.2 SARIMA 与 ARIMA 的 5 大本质不同

  1. 多了 3 个超参数(P,D,Q,m):ARIMA 只有 (p,d,q),SARIMA 多了季节自回归阶数 P、季节差分阶数 D、季节移动平均阶数 Q,以及固定的季节周期 m(如 12 表示月度,7 表示周度)。

  2. 差分逻辑不同(先剥皮再切块):ARIMA 只做 lag-1 差分;SARIMA 强制先做季节差分lag-m),再做普通差分(lag-1。且算法硬性限制 D + d ≤ 2

  3. 参数搜索策略不同(贪心避炸):ARIMA 是全网格暴力搜;SARIMA 为了避开数百倍的组合爆炸,采用分步贪心(先固定 p,q 搜 P,Q,再反过来搜 p,q),最后用爬山算法微调。

  4. 状态矩阵维度暴涨:拟合系数时,ARIMA 的状态向量维度通常 < 10;SARIMA 因引入 lag-m,维度会变成 max(p, P×m, q, Q×m)。若 m=12,矩阵维度直接突破几十,计算机必须强制加岭回归正则化防止矩阵奇异报错。

  5. 预测曲线形态不同:ARIMA 多步预测快速收敛成一条直线(均值回归);SARIMA 因方程里强行绑定“去年同期”数值,预测曲线会持续保持波浪形状,但超过 2 个季节周期后波幅会扭曲。

2.3 代码演示

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
from sklearn.metrics import mean_absolute_error
plt.rcParams['font.sans-serif'] = ['SimHei']  # 修复matplotlib中文显示
plt.rcParams['axes.unicode_minus'] = False    # 修复负号显示
# 1. 生成带明显年度季节性(m=12)的模拟数据
np.random.seed(42)
time = np.arange(120)  # 10年数据
# 趋势 + 正弦波(周期12) + 噪声
data = 0.3 * time + 8 * np.sin(2 * np.pi * time / 12) + np.random.normal(0, 2, 120)
# 注意:本机 pandas 2.0.3 用 'M'('ME' 需 pandas>=2.2;pandas 2.2+ 若见 FutureWarning 可改 'ME')
series = pd.Series(data, index=pd.date_range('2010-01-01', periods=120, freq='M'))
划分训练集(前108个月)和测试集(后12个月)
train, test = series[:-12], series[-12:]
2. 拟合纯 ARIMA(1,1,1) —— 它不认识季节
model_arima = ARIMA(train, order=(1, 1, 1))
fit_arima = model_arima.fit()
forecast_arima = fit_arima.forecast(12)
3. 拟合 SARIMA(1,1,1)(1,1,1,12) —— 明确告诉它周期 m=12
model_sarima = SARIMAX(train,
order=(1, 1, 1),           # 非季节 (p,d,q)
seasonal_order=(1, 1, 1, 12)) # 季节 (P,D,Q,m)
fit_sarima = model_sarima.fit(disp=False)  # disp=False 静默迭代
forecast_sarima = fit_sarima.forecast(12)
4. 绘图对比(一眼看出差异)
plt.figure(figsize=(14, 5))
plt.plot(train.index, train, label='训练集', color='gray', alpha=0.6)
plt.plot(test.index, test, label='真实值(测试集)', color='black', linewidth=2)
ARIMA 预测(蓝色) —— 迅速变直
plt.plot(test.index, forecast_arima, label='ARIMA 预测(直线收敛)',
color='blue', marker='o', linestyle='--')
SARIMA 预测(红色) —— 保留波浪
plt.plot(test.index, forecast_sarima, label='SARIMA 预测(波浪形状)',
color='red', marker='s', linestyle='-', linewidth=2)
plt.title('SARIMA vs ARIMA 预测效果核心差异(红色维持波动,蓝色收敛为直线)')
plt.legend()
plt.grid(True)
plt.show()
打印误差对比
print(f"ARIMA 平均绝对误差: {mean_absolute_error(test, forecast_arima):.2f}")
print(f"SARIMA 平均绝对误差: {mean_absolute_error(test, forecast_sarima):.2f}")

运行结果:

我们可以看到,ARIMA是直线收敛,SARIMA为波形收敛,明显SARIMA的拟合效果更好,为什么?

ARIMASARIMA
记忆长度p+q 期(很短)p+q + 季节项 (P+D+Q)·m(可到一年)
对季节数据的识别❌ 看不见✅ 结构内建
多步预测形态直线/衰减维持波浪
适合数据无季节性的平稳/趋势序列有固定周期(如月度/季度数据)

ARIMA 不是"不能"波形,而是它的记忆长度不够、系数又难以稳定锁定周期;SARIMA 则是在数学结构里直接内置了"去年同期"的对照,所以对周期性数据是必然的选择。这就是为什么时序建模前要先用 ACF 图(我之前有一个作品说过这个事)或季节性分解(如 statsmodels 的 seasonal_decompose)确认周期。


3. ARIMAX

3.1 核心思想

ARIMA 基础上引入外部解释变量,捕捉除序列自身之外的外部影响因素。

:第k个外部变量;外部变量系数

3.2 ARIMAX 与纯 ARIMA 的 5 大本质不同

  1. 输入数据结构质变(二维矩阵 vs 一维向量):ARIMA 只接受单一序列(一维);ARIMAX 的算法底层要求传入 endog(目标变量)exog(外部变量矩阵)。这导致状态空间模型的观测方程从 yt=...变成了 yt=β1Xt1+β2Xt2+...+AR/MA项。

  2. 参数估计的联合迭代(不再是纯时序递推):纯 ARIMA 只优化 AR/MA 系数;ARIMAX 的优化器(BFGS)在每次迭代中,必须同步计算回归系数 β 和 AR/MA 系数。算法采用的是“带约束的极大似然估计(MLE)”,即先通过OLS估算 β 的初值,算出残差,再对残差拟合 ARIMA,反复交替迭代直至两组参数同时收敛。

  3. 定阶策略被“干扰”(X 吃掉了一部分自相关):纯 ARIMA 直接对原始序列做 ACF/PACF 定阶;ARIMAX 定阶时,算法会先剔除 X 的线性影响(计算偏残差),再对残差序列进行网格搜索确定 (p,q)。这意味着,加入强有力的 X 变量后,最优的 p,q往往会变小(因为 X 解释掉了原本需要滞后项来解释的波动)。

  4. 预测的“死穴”——必须提前知道 X 的未来值(致命差异):纯 ARIMA 预测未来只需要自己的历史值递推;ARIMAX 在调用 forecast() 时,“必须同时传入未来所有步长的外部变量 Xt+1,Xt+2...Xt+1​,Xt+2​... ”。如果未来气温、促销力度未知,算法直接报错无法运行(这也正是它最大的应用限制)。

  5. 矩阵求逆的“共线性”风险加剧:纯 ARIMA 的状态矩阵仅含滞后项,多重共线性较弱;ARIMAX 的矩阵里混入了外生变量(如气温、节假日哑变量),若 X 与时间趋势高度相关,或 X 之间存在多重共线性,卡尔曼滤波中的矩阵求逆会直接报 LinAlgError,算法必须依赖更强的岭回归正则化(L2惩罚)来强行稳定矩阵。

3.3 代码演示:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import mean_absolute_error
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX  # 注意:Python中ARIMAX由SARIMAX类实现
plt.rcParams['font.sans-serif'] = ['SimHei']  # 修复matplotlib中文显示
plt.rcParams['axes.unicode_minus'] = False    # 修复负号显示
# ----- 1. 模拟数据(基础趋势 + 外部变量X:促销活动)-----
np.random.seed(42)
n = 120
time = np.arange(n)
外部变量 X(促销计划,取 0/1 确定值)设计为"测试期脉冲式促销":
- 训练期(前100天):仅在中间有零星促销,让 ARIMAX 能学到促销系数 β≈10;
训练尾部(第80~99天)完全无促销 → 纯ARIMA从低位外推;
- 测试期(后20天):第3~8天突发促销(脉冲),其余无促销 → 纯ARIMA措手不及,
而 ARIMAX 提前拿到未来促销计划,能准确预测脉冲日的暴涨。
promotion = np.zeros(n)
promotion[35:41] = 1    # 训练期 35~40 天:促销(提供 β 的识别变异)
promotion[60:64] = 1    # 训练期 60~63 天:促销
promotion[102:108] = 1  # 测试期(index 100~119)第3~8天:突发促销脉冲
目标销量 Y = 基础趋势(0.2时间) + 促销带来的销量(系数10X) + 随机噪声
sales = 0.2 * time + 10 * promotion + np.random.normal(0, 2, n)
series = pd.Series(sales, index=pd.date_range('2020-01-01', periods=n, freq='D'))
exog_df = pd.DataFrame({'promotion': promotion}, index=series.index)
划分训练集(前100天)和测试集(后20天)
train_series = series[:-20]
test_series = series[-20:]
train_exog = exog_df[:-20]
test_exog = exog_df[-20:]  # 关键:未来20天的促销情况必须已知!
----- 2. 纯 ARIMA(2,1,0) —— 忽视外部变量 -----
model_arima = ARIMA(train_series, order=(2, 1, 0))
fit_arima = model_arima.fit()
forecast_arima = fit_arima.forecast(20)  # 不需要传 X,纯靠自己猜
----- 3. ARIMAX 模型 —— 引入促销变量 (外生矩阵) -----
order=(p,d,q) 依旧,但通过 exog 传入外部变量
model_arimax = SARIMAX(train_series,
order=(2, 1, 0),        # ARIMA部分
exog=train_exog,        # 训练期间的外部变量
enforce_stationarity=False,
enforce_invertibility=False)
fit_arimax = model_arimax.fit(disp=False)
【核心差异点】预测未来20天,必须同时传入未来20天的促销计划!
forecast_arimax = fit_arimax.forecast(steps=20, exog=test_exog)
----- 4. 可视化对比 -----
plt.figure(figsize=(14, 5))
plt.plot(train_series.index, train_series, label='训练集', color='gray', alpha=0.6)
plt.plot(test_series.index, test_series, label='真实销量', color='black', linewidth=2)
纯ARIMA预测(忽视促销导致低估)
plt.plot(test_series.index, forecast_arima, label='纯ARIMA(忽视促销)',
color='blue', linestyle='--', marker='o')
ARIMAX预测(捕捉到促销带来的销量暴涨)
plt.plot(test_series.index, forecast_arimax, label='ARIMAX(引入促销外生变量)',
color='red', linewidth=2, marker='s')
plt.axvline(x=train_series.index[-1], color='gray', linestyle=':', label='预测起点')
plt.title('ARIMAX vs ARIMA 核心差异:外部变量捕捉突变冲击')
plt.legend()
plt.grid(True)
plt.show()
打印误差对比
print(f"纯ARIMA 误差 (忽视外部因素): {mean_absolute_error(test_series, forecast_arima):.2f}")
print(f"ARIMAX 误差 (引入促销变量): {mean_absolute_error(test_series, forecast_arimax):.2f}")

3.4 ARIMAX vs ARIMA 的核心结论

在同一道题里 ARIMAX 更好,根本原因:数据是"促销直接决定销量"的机制,且 ARIMAX 预测时能收到未来的促销计划。

销量 = 0.2×时间 + 10×促销 + 小噪声(σ≈2)

促销系数 10 远大于噪声,属于主导性、可预期的外部冲击。预测测试期第 3~8 天突发促销时:

预测逻辑误差 (MAE)
纯 ARIMA只看过去销量,不知道促销要来 → 低位直线外推,撞上真实值的暴涨尖峰5.90
ARIMAX提前拿到未来 20 天促销计划(exog=test_exog),β≈10 直接把脉冲预测出来3.39

ARIMAX 的优势本质是"先知"——它把促销→销量的因果关系做进了模型结构(多一项 β·X_t),而 ARIMA 只能靠历史销量猜。


二、现代时间序列模型

1. Prophet(Facebook)

1.1 适用场景

日/周/月维度的商业运营指标(日活、订单量、网站流量)。数据有明显年/周周期性、存在节假日冲击(双11、春节),且大量缺失值或异常值无需清洗。

1.2 核心操作步骤

  1. 准备数据框:必须包含两列,ds(日期时间)和 y(数值)。
  2. 实例化模型:设置趋势变点灵敏度(changepoint_prior_scale)和季节性。
  3. 添加自定义节假日(如春节、双11)。
  4. 拟合模型 + 生成未来时间框架 + 预测。

1.3 代码骨架

from prophet import Prophet
1. 准备数据(列名必须为 ds 和 y)
df = pd.DataFrame({'ds': dates, 'y': sales})
2. 实例化并添加节假日
model = Prophet(yearly_seasonality=True, weekly_seasonality=True)
model.add_country_holidays(country_name='US')  # 或手动 model.add_seasonality()
3. 拟合与预测
model.fit(df)
future = model.make_future_dataframe(periods=30)  # 未来30天
forecast = model.predict(future)
4. 出图(自带可视化)
fig1 = model.plot(forecast)
fig2 = model.plot_components(forecast)


2. TBATS

2.1 适用场景

数据同时存在多个不同长度的周期(如电力负荷:日周期24小时 + 周周期168小时;或交通流量:周周期 + 年周期),且你懒得手动调参去设周期。

2.2 核心操作步骤

  1. (Python需安装 tbats 包)传入单变量序列(numpy数组)。
  2. 指定可能的周期列表(如 [7, 365.25]),算法会自动判断周期是否显著。
  3. 拟合模型(内部自动进行 Box-Cox 变换和 ARMA 误差修正)。
  4. 调用 forecast 获取多步预测。

2.3 代码骨架

from tbats import TBATS
1. 准备数据(一维数组)
y = series.values
2. 实例化(指定可能周期,算法自动筛选)
estimator = TBATS(seasonal_periods=[7, 365.25],  # 常见周期
use_box_cox=True,              # 自动决定是否变换
use_trend=True,
use_damped_trend=True)
3. 拟合与预测
fitted_model = estimator.fit(y)
y_forecast = fitted_model.forecast(steps=30)  # 未来30步
还可输出 fitted_model.summary() 查看最终选中的周期

3. VAR / VECM

3.1 VAR (p) 向量自回归

3.11 适用场景

多个经济/业务指标相互影响,需要联合预测且不关心谁因谁果(如 GDP、CPI、失业率)。要求所有变量平稳(不平稳需先差分)。

3.12 核心操作步骤

  1. 将多个时间序列合并为二维矩阵(行=时间,列=变量)。
  2. 进行 ADF 单位根检验,非平稳则做差分至平稳。
  3. 确定最优滞后期数(通过 AIC/BIC 遍历 maxlags)。
  4. 用选定的滞后阶数拟合 VAR 模型,并预测未来多步。

3.13 代码骨架

from statsmodels.tsa.vector_ar.var_model import VAR
from statsmodels.tsa.stattools import adfuller
1. 准备多变量数据(DataFrame,每列一个变量)
data = df[['GDP', 'Inflation', 'Unemployment']]
2. 平稳性检验(略),若非平稳做差分
data_diff = data.diff().dropna()
3. 定阶(自动选择最佳滞后)
model = VAR(data_diff)
lag_order = model.select_order(maxlags=15)  # 打印 lag_order.aic 最优值
best_lag = lag_order.aic  # 或手动指定
4. 拟合与预测
fitted = model.fit(best_lag)
预测未来5步(需要传入最近 best_lag 期的历史值)
forecast_input = data_diff.values[-best_lag:]
forecast = fitted.forecast(y=forecast_input, steps=5)
print(forecast)  # 返回二维数组,行=未来时间,列=各变量预测值

3.2 VECM 向量误差修正模型

3.21 适用场景

多个变量本身不平稳(带随机游走),但它们在经济学上存在长期稳定比例关系(如现货价格与期货价格、汇率与利率)。需要同时刻画长期均衡短期偏离修正

3.22 核心操作步骤
  1. 输入原始非平稳的多变量数据(不要提前差分)。
  2. 进行 Johansen 协整检验,确定协整秩 r(即存在多少个长期均衡关系)。
  3. 指定 coint_rank = r 和短期滞后期 k_ar_diff,拟合 VECM。
  4. 调用 predict 获取预测(VECM 会内生地处理短期波动向长期均衡回归的过程)。

3.23 代码骨架

from statsmodels.tsa.vector_ar.vecm import VECM, select_coint_rank
1. 准备原始非平稳数据(DataFrame)
data = df[['Spot_Price', 'Futures_Price']]  # 假设两者有协整关系
2. 确定协整秩(Johansen检验)
rank_result = select_coint_rank(data, det_order=0, k_ar_diff=1)  # k_ar_diff为短期滞后
print(rank_result.rank)  # 输出最优协整秩,假设为 1
3. 拟合 VECM(指定协整秩=1)
model = VECM(data, coint_rank=1, k_ar_diff=1)  # k_ar_diff=1表示短期带1阶滞后
fitted = model.fit()
4. 预测未来5步
forecast = fitted.predict(steps=5)
注意:VECM的预测结果会呈现出“向长期均衡回归”的修正特性

4. 终极算法本质一句话总结(区别于传统 ARIMA)

模型算法内核本质到底在算什么?
Prophet分段线性回归 + 傅里叶级数拟合全局最小二乘解,不递推,纯回归。
TBATS指数平滑状态空间 + 三角季节分解卡尔曼滤波递推状态,但周期自动挖掘。
VAR多变量最小二乘矩阵求解(OLS)把所有变量滞后项做线性回归,矩阵求逆一次出结果。
VECM矩阵特征值分解(秩分解) + 降秩回归先找协整特征向量,再修正短期系数。

三、完整模型对比总结表(7大时序模型)

模型核心侧重点(算法灵魂)最适合的使用场景适合的题目致命短板
ARIMA单变量自回归:只用自己的过去和过去的误差线性外推。无明显周期、平稳化后的单变量金融指标、平滑负荷。数据无季节波动,差分后平稳,只有一个变量长步预测必收敛为直线;原生完全不认“去年同期”。
SARIMA单变量+固定周期:在ARIMA基础上强行绑定“去年同期”的数值。有固定月度(12)、季度(4)或周度(7)周期的单变量数据。数据有明显的单一固定周期(如每月固定高峰),且周期已知。只能处理一个季节周期;多周期(如日+周+年)直接崩溃。
ARIMAX单变量+外部先知:把“已知的未来外部冲击(X)”当作额外输入特征。外部因素主导的预测(促销、天气预报、节假日日期表)。未来外部变量取值已知(如未来一周天气已报、促销已排期)。预测时必须提前拿到X的未来值;拿不到就无法运行。
Prophet全局分段回归:把趋势切成几段直线,用傅里叶级数拟合周/年,并加入自定义节假日。日/周/月维度的商业KPI(DAU、订单量、网站流量)。年/周周期性、有明确节假日冲击、数据有缺失或异常值懒得清洗。默认加法结构;数据量太少(<2个周期)时拟合效果极差。
TBATS多重周期自动挖掘:用指数平滑+三角分解,自动识别多个不同长度的周期。电力负荷(24小时+168小时)、城市交通流量(周+年)。同时存在多个不同长度的周期(如日周期+周周期),且不想手动定周期。模型极复杂,训练极慢;结果解释性差(老板看不懂)。
VAR多变量联动(全量OLS):把几个变量的所有历史滞后值放在一个矩阵里做回归。宏观经济多指标联合预测(GDP、CPI、失业率)。多个变量相互影响,且所有变量均平稳(或差分后平稳)。变量一旦超过8个,参数呈平方级爆炸;仅捕捉线性关系。
VECM多变量长期均衡:在VAR基础上加了个“纠偏项”,拉回长期稳定比例关系。现货与期货价格、汇率联动、大宗商品套利分析。多变量本身不平稳,但长期存在稳定比例关系(协整)。建模流程极其繁琐(必须先做Johansen协整检验);超纲题常用来坑人。

更多推荐