自己相関分析
「時系列データの最大の特徴は、過去の自分自身と相関があるということだ。」
田中VPoEがACFプロットを表示する。
「昨日の売上が高ければ今日も高い傾向がある。先週の同じ曜日と相関がある。この自己相関の構造を理解することが、ARIMAモデルの土台になる。」
自己相関(ACF)とは
自己相関(Autocorrelation Function)とは、時系列データとそのラグ(時間遅れ)との相関係数である。
import numpy as np
import pandas as pd
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
from statsmodels.tsa.stattools import acf, pacf
# GROCERY Iの全店舗合計日次売上
grocery = train[train['family'] == 'GROCERY I'].groupby('date')['sales'].sum()
# ACFの計算
acf_values = acf(grocery, nlags=35)
# ラグごとの自己相関
for lag in [1, 7, 14, 28]:
print(f"ラグ{lag:2d}日: ACF = {acf_values[lag]:.3f}")
# ラグ 1日: ACF = 0.85 ← 前日との相関が最も高い
# ラグ 7日: ACF = 0.90 ← 1週間前との相関が非常に高い(週次季節性)
# ラグ14日: ACF = 0.82 ← 2週間前とも高い
# ラグ28日: ACF = 0.80 ← 4週間前とも高い
ACFプロット
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
# ACFプロット
plot_acf(grocery, lags=50, ax=axes[0])
axes[0].set_title('ACF(自己相関関数)')
axes[0].set_xlabel('ラグ(日)')
# PACFプロット
plot_pacf(grocery, lags=50, ax=axes[1], method='ywm')
axes[1].set_title('PACF(偏自己相関関数)')
axes[1].set_xlabel('ラグ(日)')
plt.tight_layout()
plt.show()
偏自己相関(PACF)とは
偏自己相関(Partial Autocorrelation Function)とは、中間のラグの影響を除いた、特定のラグとの純粋な相関である。
ACF: Y(t) と Y(t-k) の相関(中間のラグの影響を含む)
PACF: Y(t) と Y(t-k) の相関(Y(t-1)...Y(t-k+1) の影響を除去)
ACFとPACFからARIMAパラメータを読み取る
| ACFの挙動 | PACFの挙動 | モデル |
|---|---|---|
| 緩やかに減衰 | ラグpで急にゼロ | AR(p) |
| ラグqで急にゼロ | 緩やかに減衰 | MA(q) |
| 緩やかに減衰 | 緩やかに減衰 | ARMA(p,q) |
定常性検定
ARIMAモデルを適用するには、データが定常(平均・分散が時間に依存しない)である必要がある。
ADF検定(拡張ディッキー・フラー検定)
from statsmodels.tsa.stattools import adfuller
def adf_test(series, name=''):
"""ADF検定を実行し結果を表示"""
result = adfuller(series.dropna(), autolag='AIC')
print(f"=== ADF検定: {name} ===")
print(f"検定統計量: {result[0]:.4f}")
print(f"p値: {result[1]:.4f}")
print(f"使用ラグ数: {result[2]}")
print(f"観測数: {result[3]}")
for key, value in result[4].items():
print(f" 臨界値({key}): {value:.4f}")
if result[1] < 0.05:
print("→ 帰無仮説を棄却: 定常である")
else:
print("→ 帰無仮説を棄却できない: 非定常の可能性")
print()
# 原系列のADF検定
adf_test(grocery, '原系列')
# 1次差分のADF検定
adf_test(grocery.diff().dropna(), '1次差分')
# 7日差分のADF検定(季節差分)
adf_test(grocery.diff(7).dropna(), '7日差分')
KPSS検定
ADF検定と相補的な検定。ADF検定とKPSS検定の両方を使うことで、より信頼性の高い判断ができる。
from statsmodels.tsa.stattools import kpss
def kpss_test(series, name=''):
"""KPSS検定を実行"""
result = kpss(series.dropna(), regression='c', nlags='auto')
print(f"=== KPSS検定: {name} ===")
print(f"検定統計量: {result[0]:.4f}")
print(f"p値: {result[1]:.4f}")
if result[1] < 0.05:
print("→ 帰無仮説を棄却: 非定常である")
else:
print("→ 帰無仮説を棄却できない: 定常である")
print()
# ADF: 帰無仮説=非定常、KPSS: 帰無仮説=定常
# 両方の結果を合わせて判断する
# ADF棄却 & KPSS棄却しない → 定常
# ADF棄却しない & KPSS棄却 → 非定常
# 両方棄却 → トレンド定常の可能性
# 両方棄却しない → 判断が困難
差分による定常化
# 1次差分: トレンドを除去
diff1 = grocery.diff().dropna()
# 季節差分: 季節性を除去(7日周期)
diff7 = grocery.diff(7).dropna()
# 1次差分 + 季節差分: 両方を除去
diff1_7 = grocery.diff().diff(7).dropna()
# 各差分系列のACF/PACFを確認
fig, axes = plt.subplots(3, 2, figsize=(14, 12))
for i, (data, title) in enumerate([
(diff1, '1次差分'),
(diff7, '季節差分(7)'),
(diff1_7, '1次+季節差分')
]):
plot_acf(data, lags=35, ax=axes[i, 0])
axes[i, 0].set_title(f'ACF: {title}')
plot_pacf(data, lags=35, ax=axes[i, 1], method='ywm')
axes[i, 1].set_title(f'PACF: {title}')
plt.tight_layout()
plt.show()
まとめ
| 項目 | ポイント |
|---|---|
| ACF | 時系列とそのラグとの相関。季節周期でスパイク |
| PACF | 中間ラグの影響を除いた純粋な相関 |
| ADF検定 | 定常性の検定。p < 0.05なら定常 |
| 差分 | 非定常データを定常化する手法 |
チェックリスト
- ACFとPACFの違いを説明できる
- ACF/PACFプロットからARIMAのパラメータを読み取れる
- ADF検定を実行し結果を解釈できる
- 差分による定常化の方法を理解した
次のステップへ
自己相関と定常性の分析ができたところで、次は外部要因(祝日、石油価格、プロモーション)の分析に進もう。
推定読了時間: 30分