????????在之前的學習中,我們層層遞進的介紹了時序模型的發展,從AR到MA到ARMA,再到ARIMA。本質就是把數據處理的操作和模型結合在一起了,實際上昨天提到的季節性差分也可以合并到模型中,讓流程變得更加統一。
季節性差分用S來表示,所以這個模型叫做SARIMA模型
一、SARIMA模型
????????SARIMA (Seasonal AutoRegressive Integrated Moving Average)是標準ARIMA模型的擴展。它專門用于處理具有明顯季節性模式的時間序列數據。????????
????????可以把SARIMA想象成一個“雙核”的ARIMA模型:
1. 一個非季節性核心,用來處理數據的整體趨勢。
2. 一個季節性核心,用來處理數據中的周期性模式。
1.1 SARIMA模型的參數
SARIMA模型由兩組參數定義:(p,d,q) 和 (P,D,Q,m)
ps:注意大小寫
1. 非季節性部分: (p, d, q) 這里和之前arima一致
2. 季節性部分: (P, D, Q, m),這是一套全新的參數 (P, D, Q, m)。它負責處理序列的長期、周期性的依賴關系。
標準寫法 SARIMA(p, d, q)(P, D, Q)m
這里之所以m單獨拿出來,為了在概念上強調 m 的獨特性
m 是舞臺,P,D,Q 是演員
m 定義了季節性的“舞臺”有多大。它不是一個需要通過模型擬合來“學習”的階數,而是數據本身固有的、預先定義的結構性屬性。m可以理解為季節周期。
在分析數據之初,我們通過觀察或業務常識就能確定m。看到月度數據,我們立刻知道m=12;看到季度數據,我們知道m=4。
m 告訴模型:“你要關注的周期性規律是每隔m個時間點重復一次的。” 它為季節性核心 (P, D, Q) 的所有運算提供了基準尺度。
?
(P, D, Q)季節性階數,它們描述了在這個季節性尺度上,模型的具體行為。
- P: 在m的尺度上,需要看過去幾個季節的自己?(y_{t-m}, y_{t-2m}, …)
- D: 在m的尺度上,需要做幾次差分?(y_t - y_{t-m})
- Q: 在m的尺度上,需要看過去幾個季節的誤差?(error_{t-m}, error_{t-2m}, …)
這些階數通常需要通過分析季節性差分后的ACF/PACF圖來確定。
這里我們強調下(p,d,q)和(P,D,Q)的區別。差別在于它們看的不是上一個時間點,而是上一個季節的同一時間點。
普通差分 (d) 是 y_t - y_{t-1} (今天 - 昨天)。季節性差分 (D) 是 y_t - y_{t-m} (今年8月 - 去年8月)。
它的作用是消除季節性帶來的增長趨勢,讓季節性數據變得平穩。如果用電量每年夏天都比上一年夏天高,D=1就能消除這個影響。
普通AR (p) 認為 y_t 和 y_{t-1}, y_{t-2}… 相關。季節性AR (P) 認為 y_t 和 y_{t-m}, y_{t-2m}… 相關。也就是說,模型認為今年8月的用電量和去年8月、前年8月的用電量有直接關系。
普通MA (q) 認為 y_t 的誤差和 t-1, t-2 時刻的誤差相關。季節性MA (Q) 認為 y_t 的誤差和 t-m, t-2m 時刻的誤差相關。也就是說,模型認為對今年8月的預測誤差,可以根據去年8月、前年8月的預測誤差來進行修正。
總結:季節性階數是對比不同季節的同一時刻的差別。
1.2 SARIMA模型的理解
?
sarima不是單純的對arima做了一次季節差分,而且做了季節性的一些其他特征捕捉:季節自回歸P、季節移動平均Q。
SARIMA的完整工作流是這樣的:
1. 季節性層面分析:模型首先利用 (P, D, Q)m 這一套完整的“季節性ARIMA”來處理數據。它進行季節性差分(D),然后用季節性自回歸(P)和季節性移動平均(Q)來解釋季節性平穩后的數據中的模式。這個過程的輸出是一個“季節性影響被剝離后”的殘差序列。
2. 非季節性層面分析:接著,模型再將我們熟悉的 (p, d, q) 應用于第一步產生的殘差序列上。它對這個序列進行普通差分(d),然后用AR(p)和MA(q)來捕捉其中剩余的、短期的、非季節性的模式。
總結:SARIMA不是在ARIMA上打個補丁,而是構建了一個與非季節性部分 (p,d,q) 平行且完整的季節性分析系統 (P,D,Q)m。這兩個系統協同工作,一個負責宏觀的、周期性的規律,另一個負責微觀的、短期的波動,最終結合成一個強大而全面的預測模型。
1.3 SARIMA實戰
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.statespace.sarimax import SARIMAX
import warnings
warnings.filterwarnings('ignore')
# 顯示中文
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False# 1. 加載數據
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/airline-passengers.csv'
df = pd.read_csv(url, header=0, index_col=0, parse_dates=True)
df.columns = ['Passengers']# 2. 劃分訓練集和測試集(保留最后12個月作為測試)
train_data = df.iloc[:-12]
test_data = df.iloc[-12:]print("--- 訓練集 ---")
print(train_data.tail()) # 觀察訓練集最后5行
print("\n--- 測試集 ---")
print(test_data.head()) # 觀察測試集前5行# 3. 可視化原始數據
plt.figure(figsize=(12, 6))
plt.plot(train_data['Passengers'], label='訓練集')
plt.plot(test_data['Passengers'], label='測試集', color='orange')
plt.title('國際航空乘客數量 (1949-1960)')
plt.xlabel('年份')
plt.ylabel('乘客數量 (千人)')
plt.legend()
plt.show()# 進行季節性差分 (D=1, m=12)
seasonal_diff = df['Passengers'].diff(12).dropna()
# 再進行普通差分 (d=1)
seasonal_and_regular_diff = seasonal_diff.diff(1).dropna()# 繪制差分后的數據
plt.figure(figsize=(12, 6))
plt.plot(seasonal_and_regular_diff)
plt.title('經過一次季節性差分和一次普通差分后的數據')
plt.show()# ADF檢驗
result = adfuller(seasonal_and_regular_diff)
print(f'ADF Statistic: {result[0]}')
print(f'p-value: {result[1]}') # p-value越小,越說明數據平穩# 繪制ACF和PACF圖
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 8))
plot_acf(seasonal_and_regular_diff, lags=36, ax=ax1) # 繪制36個時間點
plot_pacf(seasonal_and_regular_diff, lags=36, ax=ax2)
plt.show()from pmdarima import auto_arima # 一個方便的自動調參庫
# 使用auto_arima自動尋找最優模型
# 我們告訴它d=1, D=1, m=12是固定的,讓它去尋找p,q,P,Q的最優組合
# 默認使用AIC作為評估標準
auto_model = auto_arima(train_data['Passengers'],start_p=0, start_q=0,max_p=2, max_q=2,m=12,start_P=0, seasonal=True,d=1, D=1,trace=True,error_action='ignore',suppress_warnings=True,stepwise=True)print(auto_model.summary())# 從auto_arima的結果中獲取最優參數best_order = auto_model.order
best_seasonal_order = auto_model.seasonal_order# 擬合模型
model = SARIMAX(train_data['Passengers'],order=best_order,seasonal_order=best_seasonal_order)
results = model.fit(disp=False)# 打印模型診斷圖
results.plot_diagnostics(figsize=(15, 12))
plt.show()# 預測未來12個點
predictions = results.get_prediction(start=test_data.index[0], end=test_data.index[-1])
pred_mean = predictions.predicted_mean # 預測均值
pred_ci = predictions.conf_int() # 預測的置信區間# 繪制預測結果
plt.figure(figsize=(12, 6))
plt.plot(df['Passengers'], label='原始數據')
plt.plot(pred_mean, label='SARIMA 預測', color='red')
plt.fill_between(pred_ci.index,pred_ci.iloc[:, 0],pred_ci.iloc[:, 1], color='pink', alpha=0.5, label='置信區間')
plt.title('SARIMA模型預測 vs. 真實值')
plt.xlabel('年份')
plt.ylabel('乘客數量 (千人)')
plt.legend()
plt.show()
@浙大疏錦行