📈 时间序列 — ARIMA预测+季节分解

从趋势中看见未来,从周期中理解规律

📖 时间序列概述

时间序列(Time Series)是按时间顺序排列的数据点序列,广泛存在于金融、气象、销售、IoT等领域。时间序列分析的目标是理解过去、预测未来

时间序列四大组成部分: Y(t) = Trend + Seasonal + Cyclical + Irregular 趋势(Trend): 长期上升/下降方向 ─────────────────────────────────→ 季节(Seasonal): 固定周期重复 ∿∿∿∿∿∿∿∿∿∿∿∿ (年/月/周) 循环(Cyclical): 非固定周期波动 ∿~~~∿~~~∿ (经济周期3-5年) 不规则(Irregular): 随机噪声 · · · · · ·
方法类型适用场景优点缺点
移动平均平滑短期预测简单滞后
指数平滑平滑趋势+季节自适应权重参数敏感
ARIMA统计非季节平稳理论基础强需平稳
SARIMA统计季节性数据处理季节参数多
Prophet混合业务预测自动调参精度一般
LSTM深度学习复杂非线性强大需大数据

🔢 季节分解

季节分解将时间序列拆分为趋势+季节+残差三个部分,是最基础也最重要的分析步骤。

加法分解 vs 乘法分解: 加法: Y(t) = T(t) + S(t) + R(t) ┌──────────────────────────────────┐ │ 适用于: 季节波动幅度恒定 │ │ 例: 每年冬季销量固定多100件 │ └──────────────────────────────────┘ 乘法: Y(t) = T(t) × S(t) × R(t) ┌──────────────────────────────────┐ │ 适用于: 季节波动随趋势增大 │ │ 例: 销量越高,季节波动越大 │ │ 取对数后等价于加法: log(Y) = ...│ └──────────────────────────────────┘ 如何判断用哪种? 画图看季节波动幅度是否随时间变化: 恒定 → 加法 增大 → 乘法
import numpy as np
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose

np.random.seed(42)

# 生成模拟时间序列: 趋势 + 季节 + 噪声
n = 200
t = np.arange(n)
trend = 0.05 * t + 10                # 线性趋势
season = 5 * np.sin(2 * np.pi * t / 12)  # 12期季节性
noise = np.random.randn(n) * 1.5
data = trend + season + noise

dates = pd.date_range('2020-01', periods=n, freq='ME')
ts = pd.Series(data, index=dates)
print(f"时间序列: {len(ts)}期, 均值={ts.mean():.2f}")

# 季节分解(加法模型)
decomposition = seasonal_decompose(ts, model='additive', period=12)
print(f"趋势范围: [{decomposition.trend.dropna().min():.2f}, "
      f"{decomposition.trend.dropna().max():.2f}]")
print(f"季节范围: [{decomposition.seasonal.min():.2f}, "
      f"{decomposition.seasonal.max():.2f}]")
print(f"残差范围: [{decomposition.resid.dropna().min():.2f}, "
      f"{decomposition.resid.dropna().max():.2f}]")

🔬 平稳性检验与差分

ARIMA要求数据平稳(均值、方差不随时间变化)。ADF检验是判断平稳性的标准方法。

ADF检验 (Augmented Dickey-Fuller): H₀: 序列有单位根 → 非平稳 H₁: 序列无单位根 → 平稳 判定: p值 < 0.05 → 拒绝H₀ → 平稳 p值 ≥ 0.05 → 不拒绝H₀ → 非平稳 非平稳怎么办? 差分! 一阶差分: Y'ₜ = Yₜ - Yₜ₋₁ 二阶差分: Y''ₜ = Y'ₜ - Y'ₜ₋₁ d = 差分次数 → ARIMA(p,d,q)的d ⚠️ 差分次数d一般不超过2!
from statsmodels.tsa.stattools import adfuller

# 原始序列ADF检验
result = adfuller(ts.dropna())
print(f"原始序列 ADF统计量={result[0]:.4f}, p={result[1]:.4f}")
print(f"→ {'非平稳 ❌' if result[1] > 0.05 else '平稳 ✅'}")

# 一阶差分
ts_diff = ts.diff().dropna()
result_diff = adfuller(ts_diff)
print(f"\n一阶差分 ADF统计量={result_diff[0]:.4f}, p={result_diff[1]:.6f}")
print(f"→ {'非平稳 ❌' if result_diff[1] > 0.05 else '平稳 ✅'}")
# 差分1次后变平稳 → d=1

📊 ACF/PACF确定p和q

ACF(自相关函数)和PACF(偏自相关函数)是确定ARIMA参数p和q的关键工具。

ACF/PACF识别规则: ACF (Auto-Correlation Function) PACF (Partial ACF) ───────────────────────── ───────────────────── 衡量Yₜ和Yₜ₋ₖ的相关性 排除中间滞后影响的相关 拖尾 → MA过程 截尾 → AR过程 识别规则: ┌─────────────────────────────────────────────┐ │ ACF拖尾 + PACF截尾(p阶) → AR(p)模型 │ │ ACF截尾(q阶) + PACF拖尾 → MA(q)模型 │ │ ACF拖尾 + PACF拖尾 → ARMA(p,q)模型 │ └─────────────────────────────────────────────┘ 截尾: 某阶之后突然降为0(落在置信区间内) 拖尾: 呈指数/正弦衰减,逐渐趋近0
from statsmodels.tsa.stattools import acf, pacf

acf_vals = acf(ts_diff, nlags=15)
pacf_vals = pacf(ts_diff, nlags=15)
print(f"ACF前5阶: {np.round(acf_vals[:5], 3)}")
print(f"PACF前5阶: {np.round(pacf_vals[:5], 3)}")

# 找显著滞后阶(超出95%置信区间)
threshold = 1.96 / np.sqrt(len(ts_diff))
acf_sig = [i for i in range(1, len(acf_vals)) if abs(acf_vals[i]) > threshold]
pacf_sig = [i for i in range(1, len(pacf_vals)) if abs(pacf_vals[i]) > threshold]
print(f"显著ACF滞后阶: {acf_sig}")
print(f"显著PACF滞后阶: {pacf_sig}")
# → 提示AR和MA的阶数范围

🚀 ARIMA模型训练与预测

ARIMA(p, d, q): ΔᵈYₜ = c + φ₁Yₜ₋₁ + ... + φₚYₜ₋ₚ + εₜ + θ₁εₜ₋₁ + ... + θ_qεₜ₋_q
from statsmodels.tsa.arima.model import ARIMA
from sklearn.metrics import mean_absolute_error, mean_squared_error

# 划分训练/测试集
train_size = int(len(ts) * 0.8)
train, test = ts[:train_size], ts[train_size:]
print(f"训练集: {len(train)}期, 测试集: {len(test)}期")

# 拟合ARIMA(1,1,1)
model = ARIMA(train, order=(1, 1, 1))
fitted = model.fit()
print(f"ARIMA(1,1,1): AIC={fitted.aic:.2f}, BIC={fitted.bic:.2f}")

# 预测
forecast = fitted.forecast(steps=len(test))
mae = mean_absolute_error(test, forecast)
rmse = np.sqrt(mean_squared_error(test, forecast))
mape = np.mean(np.abs((test - forecast) / test)) * 100
print(f"\n预测指标: MAE={mae:.4f}, RMSE={rmse:.4f}, MAPE={mape:.2f}%")

参数网格搜索

# 对比不同ARIMA参数
results = []
for p, d, q in [(0,1,1), (1,1,0), (1,1,1), (2,1,1), (1,1,2)]:
    try:
        m = ARIMA(train, order=(p,d,q)).fit()
        fc = m.forecast(steps=len(test))
        mae_v = mean_absolute_error(test, fc)
        results.append((f"ARIMA({p},{d},{q})", m.aic, mae_v))
        print(f"ARIMA({p},{d},{q}): AIC={m.aic:.2f}, MAE={mae_v:.4f}")
    except:
        pass

best = min(results, key=lambda x: x[1])
print(f"\n最佳(AIC): {best[0]}, AIC={best[1]:.2f}")

📐 2024-2025 时间序列前沿

🧮 时间序列方法选择

时间序列方法选择: 数据特点? │ ├── 有明显季节性? │ ├── 单一季节 → SARIMA / Prophet │ └── 多重季节 → TBATS / MSTL │ ├── 数据量大小? │ ├── <200点 → ARIMA / 指数平滑 │ └── >1000点 → LSTM / Transformer │ ├── 需要可解释性? │ └── ARIMA / Prophet (有分解) │ ├── 多变量? │ └── VAR / 深度学习模型 │ └── 实时预测? ├── 在线学习 → 增量ARIMA └── 快速推断 → 轻量模型
🏆 成就解锁:ARIMA预测+季节分解
时间序列季节分解成功(趋势9.81→19.61,季节-5.10~4.93);ADF检验原始p=0.93(非平稳),差分后p≈0(平稳);ARIMA(1,1,2)最优AIC=764.23,预测MAE=5.23!
Python验证通过 — 季节分解:趋势[9.81, 19.61],季节[-5.10, 4.93],残差[-2.63, 3.07];ADF检验原始p=0.9272(非平稳),差分后p≈0(平稳✅);ARIMA参数对比5组,最优ARIMA(1,1,2) AIC=764.23;预测指标MAE=5.23!
思考题:
1. 为什么ARIMA要求数据平稳?非平稳数据做ARIMA会怎样?
2. ACF和PACF的区别是什么?如何用它们确定p和q?
3. 加法分解和乘法分解分别适用于什么场景?
4. ARIMA和深度学习方法(LSTM)各有什么优劣?

📝 课后练习

  1. 用真实股票数据(如AAPL)做ARIMA预测,评估预测精度
  2. 实现SARIMA(带季节项),对比ARIMA在季节性数据上的效果
  3. 用Prophet做预测,对比ARIMA的预测结果
  4. 实现滚动预测(Rolling Forecast),模拟真实预测场景
  5. 用LSTM做时间序列预测,对比ARIMA的精度和速度
📚 参考资料:
• Time Series Analysis and Its Applications (Shumway & Stoffer, 2017)
• Forecasting: Principles and Practice (Hyndman & Athanasopoulos, 2021)
• statsmodels官方文档: https://www.statsmodels.org/
• Prophet文档: https://facebook.github.io/prophet/
• Nixtla NeuralForecast: https://nixtla.github.io/neuralforecast/