論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説(ジャストインタイム型データサイエンス教育)
時系列分析
Time Series Analysis
時間と共に変化するデータをモデル化する — 予測と理解の両軸
時系列予測状態空間ARIMA

📍 あなたが今見ているもの

本ページでは、 時系列分析を統合的に解説します。 定常性ACF/PACFARIMA季節調整状態空間モデルProphet予測評価を一気通貫で扱います。

時系列データは「時間的な順序が重要」「過去が未来に影響」という特徴があり、 通常の機械学習とは異なる前提・手法が必要です。 SSDSE-B は時系列でないですが、 多くの公的統計(気象・経済・売上)は時系列です。

🔖 🔖 キーワード索引(チップから該当箇所へジャンプ)

論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:

時系列とは 時系列分解 定常性 ADF検定 差分・対数差分 ACF/PACF AR MA ARMA ARIMA SARIMA 指数平滑 状態空間 Prophet 予測評価 VAR

💡 30秒で分かる結論

🕐 1. 時系列とは

時間順に並んだ観測値の列 $\{y_t\}_{t=1}^T$。 観測の順序と時間間隔に意味がある。 通常の機械学習の独立同分布仮定が成り立たない。

典型的な構成要素

🧩 2. 時系列分解

加法モデル:$y_t = T_t + S_t + R_t$(トレンド + 季節 + 残差)

乗法モデル:$y_t = T_t \cdot S_t \cdot R_t$

🎯 解説: SSDSE-B-2026 の都道府県・年次データから時系列を抽出して可視化し、 時系列解析の入口を作る。
1
2
3
4
5
6
7
8
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose

# 例:気象庁公開の月次気温データを想定(実データURL読み替え可)
ts = pd.read_csv('data/raw/temperature_monthly.csv',
                  index_col='date', parse_dates=True)['temp']
res = seasonal_decompose(ts, model='additive', period=12)
res.plot()
📥 入力例: data/raw/SSDSE-B-2026.csv 47 都道府県 × 14 年(2010-2023)の人口時系列
📤 実行例: 東京都: 単調増加 13160→14048 秋田県: 単調減少 1086→ 931 北海道: 緩やかに減少 5506→5224
💬 読み方: 時系列プロットが解析の出発点。 トレンド・季節性・残差を視認してから手法を選ぶ。 単位根があるかは ADF 検定で確認。 異常値・欠損は前処理が必須。

STL 分解(Seasonal-Trend Loess)

季節性が時間変動する場合に有効。 ロバストオプション付き。

⚖️ 3. 定常性

定常時系列:平均・分散・自己相関が時間によらず一定。 多くのモデルが定常性を要求する。

3.1 ADF(拡張ディッキー–フラー)検定

帰無仮説:単位根あり(非定常)。 p < 0.05 で棄却=定常。

🎯 解説: 都道府県時系列に対し移動平均(SMA, EMA)を計算して、 トレンドを抽出し短期変動を除去する。
1
2
3
from statsmodels.tsa.stattools import adfuller
res = adfuller(ts)
print(f'ADF統計量: {res[0]:.3f}, p値: {res[1]:.4f}')
📥 入力例: 東京都 月次人口時系列 (2010-2023, n=168)
📤 実行例: SMA(12): 12 ヶ月移動平均で季節性除去 EMA(α=0.1): 指数移動平均で滑らか化 → 緩やかな増加トレンドを可視化
💬 読み方: SMA は単純平均で遅れが大きい。 EMA は最近データに重み、 反応が速いが滑らかさはやや低下。 窓サイズ=周期(12 ヶ月)で季節性が完全に消える。

3.2 差分・対数差分

🎯 解説: 時系列の自己相関関数 ACF と偏自己相関関数 PACF をプロットして、 AR(p) と MA(q) の次数を決定する。
1
2
ts_diff = ts.diff().dropna()
ts_log_diff = ts.apply(lambda x: x).pipe(lambda s: s.apply(__import__('numpy').log)).diff().dropna()
📥 入力例: 東京都 人口差分系列 (定常化後)
📤 実行例: ACF: lag=1 で 0.6、 lag=2 で 0.3、 急速減衰 PACF: lag=1 のみ有意、 lag>=2 はほぼ 0 → AR(1) モデルが妥当
💬 読み方: ACF が急速減衰+PACF が打ち切り → AR モデル。 ACF が打ち切り+PACF が急速減衰 → MA モデル。 両方減衰なら ARMA。 95% 信頼帯 (±1.96/√n) を超える lag が「有意」。

KPSS検定

ADF と逆で、 帰無仮説が定常。 両方を併用するのが推奨(Hyndman 流)。

📊 4. 自己相関関数 (ACF) と偏自己相関 (PACF)

$$\rho_k = \mathrm{Corr}(y_t, y_{t-k})$$

ACF は「ラグ k での相関」、 PACF は「他のラグの影響を取り除いた」相関。

ACF/PACF の読み方

パターン 推定モデル
PACF が p 次で切れる、 ACF が減衰AR(p)
ACF が q 次で切れる、 PACF が減衰MA(q)
ACF/PACF とも減衰ARMA
🎯 解説: ADF 検定(拡張ディッキー・フラー検定)で時系列の単位根の有無を検定し、 定常性を統計的に判定する。
1
2
3
4
5
6
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(14, 4))
plot_acf(ts_diff, lags=24, ax=axes[0])
plot_pacf(ts_diff, lags=24, ax=axes[1])
plt.show()
📥 入力例: 東京都 GDP 時系列 (2010-2023)
📤 実行例: ADF 統計量 = -1.32, p = 0.62 → 単位根あり(非定常) 1 階差分後: ADF = -4.15, p = 0.001 → 定常
💬 読み方: H0: 単位根あり、 H1: 定常。 p < 0.05 で定常。 ARIMA の d を決める基本ツール。 KPSS 検定(H0: 定常)と併用で頑健化。 長期トレンドがある場合は trend='ct' オプション。

🔬 5. ARIMA モデル

5.1 AR(p) モデル

$$y_t = c + \phi_1 y_{t-1} + \cdots + \phi_p y_{t-p} + \varepsilon_t$$

$\phi_i$ は「ファイ・サブ・アイ」自己回帰係数。 過去の自分を線形結合。

5.2 MA(q) モデル

$$y_t = \mu + \varepsilon_t + \theta_1 \varepsilon_{t-1} + \cdots + \theta_q \varepsilon_{t-q}$$

$\theta_j$ は「シータ・サブ・ジェイ」移動平均係数。 過去のショックを線形結合。

5.3 ARMA(p, q)

AR と MA の組合せ。 定常データ向け。

5.4 ARIMA(p, d, q)

d 階差分してから ARMA を適用。 非定常データに対応。

🎯 解説: STL 分解(Seasonal-Trend decomposition using Loess)で時系列をトレンド・季節・残差に分解する。
1
2
3
4
5
6
from statsmodels.tsa.arima.model import ARIMA
model = ARIMA(ts, order=(1, 1, 1))
fit = model.fit()
print(fit.summary())
forecast = fit.forecast(steps=12)
print(forecast)
📥 入力例: 東京都 月次小売販売額 (2015-2023, n=108)
📤 実行例: トレンド成分: 緩やかに増加 季節成分: 12 月にピーク (年末商戦) 残差: 分散 = 1.2 (白色雑音的)
💬 読み方: STL はロバスト平滑化で外れ値に強い。 加法分解(y = T+S+R)が基本だが、 トレンドに比例した季節性は乗法分解(log 変換)で扱う。 周期 m は事前知識または FFT で推定。

5.5 SARIMA(p,d,q)(P,D,Q,s)

季節成分を別の (P,D,Q) で表現。 s は季節周期(月次なら 12、 週次なら 7)。

🎯 解説: ARIMA(p,d,q) モデルで都道府県時系列を予測し、 95% 信頼区間付きで未来値を出力する。
1
2
3
4
from statsmodels.tsa.statespace.sarimax import SARIMAX
model = SARIMAX(ts, order=(1, 1, 1), seasonal_order=(1, 1, 1, 12))
fit = model.fit(disp=False)
print(fit.aic)
📥 入力例: 東京都 人口時系列 (2010-2023, n=14)
📤 実行例: ARIMA(1,1,1) AIC = 124.3 予測 2024: 14102 ± 42 予測 2025: 14154 ± 65 予測 2026: 14202 ± 88
💬 読み方: 予測区間は予測ホライズンとともに広がる(誤差の累積)。 短期予測は精度高、 長期予測は不確実性大。 構造変化(コロナ等)があると予測精度は急落。 動的予測(1 期先ずつ更新)が頑健。

5.6 自動次数選択(pmdarima)

🎯 解説: SARIMA(季節 ARIMA)で月次データの季節性を明示的にモデル化する。
1
2
3
4
import pmdarima as pm
auto = pm.auto_arima(ts, seasonal=True, m=12, stepwise=True, suppress_warnings=True)
print(auto.summary())
forecast = auto.predict(n_periods=12)
📥 入力例: 全国小売販売額 月次時系列 (2010-2023, n=168)
📤 実行例: SARIMA(1,1,1)(1,1,0)[12] AIC = 1245.7 → 12 ヶ月周期の季節性を反映
💬 読み方: SARIMA(p,d,q)(P,D,Q)[m] で 季節周期 m。 月次は m=12、 四半期は m=4、 週次は m=52。 季節差分 D=1 で季節周期を除去。 季節成分の AR/MA 次数 P,Q は ACF/PACF の m, 2m, … で読む。

📈 6. 指数平滑法

古典的な予測手法。 過去ほど指数的に重みを下げる。

🎯 解説: VAR(ベクトル自己回帰)モデルで複数都道府県の時系列を同時にモデル化し、 相互影響を捉える。
1
2
3
from statsmodels.tsa.holtwinters import ExponentialSmoothing
hw = ExponentialSmoothing(ts, trend='add', seasonal='add', seasonal_periods=12).fit()
forecast = hw.forecast(12)
📥 入力例: 東京都・神奈川県・千葉県・埼玉県 人口時系列
📤 実行例: VAR(2) 係数行列 A1, A2 (4×4) → 東京の増加が神奈川にラグ 1 で波及
💬 読み方: VAR は多変量 ARMA の AR 版。 変数数 k、 ラグ p で k²p 個のパラメータ。 サンプル数が必要。 グレンジャー因果検定、 インパルス応答分析と組み合わせて使う。 共和分があれば VECM。

🌌 7. 状態空間モデル

観測 $y_t = Z \alpha_t + \varepsilon_t$、 状態 $\alpha_{t+1} = T\alpha_t + \eta_t$。 Kalman フィルタで隠れ状態を再帰的に推定。

🎯 解説: 状態空間モデル(カルマンフィルタ)で時系列の潜在状態を逐次推定する。
1
2
3
4
from statsmodels.tsa.statespace.structural import UnobservedComponents
ucm = UnobservedComponents(ts, level='local linear trend', seasonal=12)
fit = ucm.fit()
print(fit.summary())
📥 入力例: 東京都 人口時系列 + 観測ノイズ
📤 実行例: 状態方程式: x_t = x_{t-1} + w_t 観測方程式: y_t = x_t + v_t 推定状態 x_t と 95% 信頼区間を出力
💬 読み方: カルマンフィルタは線形・ガウス前提のベイズ更新。 状態(位置)と観測(センサ値)を分離。 拡張カルマン(EKF)、 アンセンテッド(UKF)、 粒子フィルタは非線形拡張。 ナビゲーション・追尾・経済モデルで使用。

🔮 8. Prophet(Meta)

「トレンド + 季節 + 休日効果」のシンプルな加法モデル。 欠損・外れ値に頑健。 ビジネス時系列で人気。

🎯 解説: Prophet(Facebook OSS)で時系列予測を行い、 祝日効果やトレンドの変化点を自動検出する。
1
2
3
4
5
6
7
from prophet import Prophet
df_p = pd.DataFrame({'ds': ts.index, 'y': ts.values})
m = Prophet(yearly_seasonality=True, weekly_seasonality=False)
m.fit(df_p)
future = m.make_future_dataframe(periods=12, freq='M')
fcst = m.predict(future)
m.plot(fcst)
📥 入力例: 全国小売販売額 日次 (2018-2023)
📤 実行例: Prophet: trend: 区分線形 + 変化点 9 箇所 weekly: 月~日の週周期 yearly: 12 月ピーク holidays: 元旦・GW・お盆
💬 読み方: Prophet は分解モデル y = trend + season + holiday + noise。 ARIMA より使いやすく欠損に強い。 ただし短期予測の精度は ARIMA に劣ることも。 jp_holidays を渡せば日本の祝日に対応。

🔗 9. VAR(ベクトル自己回帰)

複数時系列を同時にモデル化:$\mathbf{y}_t = \mathbf{c} + \sum_i \mathbf{A}_i \mathbf{y}_{t-i} + \boldsymbol{\varepsilon}_t$。

応用:因果関係(Granger因果)、 インパルス応答、 ショック分解。

🎯 解説: LSTM(Long Short-Term Memory)ニューラルネットワークで時系列予測を行う深層学習アプローチ。
1
2
3
4
from statsmodels.tsa.api import VAR
multi_ts = pd.DataFrame({'temp': ts, 'humidity': ts2})
var = VAR(multi_ts).fit(maxlags=12, ic='aic')
print(var.summary())
📥 入力例: 東京都 月次人口 (2010-2023, n=168) ウィンドウ長 = 12 ヶ月
📤 実行例: LSTM(units=50, layers=2) test RMSE = 132 (人口単位) → ARIMA より僅かに改善
💬 読み方: LSTM は長期依存を学習可能。 ただし時系列が短い (n<500) と過学習。 必ず標準化+早期停止+ドロップアウトを使う。 アンサンブル(ARIMA+LSTM)が頑健。 解釈性は低い。

📊 10. 予測評価指標

TimeSeriesSplit でのバックテスト

🎯 解説: ホワイトノイズ性の検定(Ljung-Box 検定)で残差が独立同分布かを確認する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from sklearn.model_selection import TimeSeriesSplit
import numpy as np
tscv = TimeSeriesSplit(n_splits=5, test_size=12)
errors = []
y = ts.values
for tr, te in tscv.split(y):
    model = ARIMA(y[tr], order=(1,1,1)).fit()
    pred = model.forecast(steps=len(te))
    errors.append(np.mean(np.abs(y[te] - pred)))
print(f'CV MAE: {np.mean(errors):.3f}')
📥 入力例: ARIMA(1,1,1) フィット後の残差
📤 実行例: Ljung-Box (lag=10) Q = 8.2, p = 0.61 → 残差に自己相関なし、 モデル適合 OK
💬 読み方: 残差が白色雑音ならモデルは時系列構造を捉えきった証拠。 p > 0.05 を望む。 残差に自己相関 → モデル誤特定。 残差の分布が正規でない → 予測区間が誤る。 QQ プロット併用。

📊 11. モデル比較

手法 短期予測 長期予測 解釈性 特徴
ARIMA古典・標準
指数平滑高速
Prophet休日・イベント考慮
状態空間欠損対応
LSTM/RNN複雑パターン
LightGBM外部特徴量強

⚠️ 12. よくある落とし穴

落とし穴 対処
通常 k-fold で予測評価TimeSeriesSplit を使う。 未来→過去のリーク厳禁。
差分前の系列にAR検定ADF/KPSS で定常化してから ACF/PACF を見る。
R² で予測評価時系列では MAE/RMSE/MASE。 R² は誤解を生む。
季節性を無視SARIMA・季節ダミー・Prophet で季節性を明示。
未来情報の特徴量化予測時に得られない情報は使わない。 ラグ系のみ。
短期データに複雑モデルサンプル数が少ないなら ARIMA や指数平滑で十分。
予測区間を出さない点予測 + 80%/95% 予測区間を必ず併記。

🏋️ 13. 練習問題

注意:SSDSE-B は時系列ではないので、 別の時系列実データ(例:気象庁公開の日次気温、 e-Stat の月次小売販売、 政府統計局の月次失業率など)を使用してください。

Q1. 月次データを STL 分解し、 トレンド・季節・残差をプロットしなさい。
🎯 解説: 時系列の交差検証(Time Series Cross Validation, walk-forward validation)で予測精度を頑健に評価する。
1
2
3
from statsmodels.tsa.seasonal import STL
res = STL(ts, period=12, robust=True).fit()
res.plot()
📥 入力例: 東京都 人口時系列 14 年
📤 実行例: walk-forward (10 学習→1 予測 × 4 fold) RMSE_avg = 87 MAPE_avg = 0.62%
💬 読み方: 通常の k-fold CV は時系列の時間順序を壊すので NG。 必ず walk-forward(過去で学習、 未来で評価)。 sklearn の TimeSeriesSplit を使う。 fold 数は 5-10 が標準。
Q2. ADF / KPSS で定常性を検定し、 必要なら差分を取りなさい。
🎯 解説: 周波数領域解析(FFT/ピリオドグラム)で時系列の周期成分を抽出する。
1
2
3
from statsmodels.tsa.stattools import adfuller, kpss
print('ADF p:', adfuller(ts)[1])
print('KPSS p:', kpss(ts, nlags='auto')[1])
📥 入力例: 全国小売販売額 月次 (n=168)
📤 実行例: FFT スペクトル ピーク 1: 周期 12 ヶ月 (季節性) ピーク 2: 周期 60 ヶ月 (景気循環?)
💬 読み方: FFT は時系列を正弦・余弦の和に分解。 ピーク位置 = 主要周期。 ピリオドグラム = |FFT|^2。 非定常性があるとピークがぼやける → STFT/ウェーブレット変換へ。 サンプリング定理 (周波数 < f_s/2) に注意。
Q3. auto_arima と Prophet で 12 期予測し、 MASE で比較しなさい。
🎯 解説: Grangerの因果性検定で「X の過去が Y の予測精度を改善するか」を統計的に検証する。
1
2
3
4
5
6
7
8
import pmdarima as pm
import numpy as np
train, test = ts[:-12], ts[-12:]
auto = pm.auto_arima(train, seasonal=True, m=12)
pred = auto.predict(n_periods=12)
naive = train.iloc[-12:].values  # 直近1年で繰り返し
mase = np.mean(np.abs(test - pred)) / np.mean(np.abs(np.diff(train)))
print(f'MASE = {mase:.3f}')
📥 入力例: 東京都 人口 と 東京都 GDP 時系列
📤 実行例: Granger 検定 (lag=2) F = 4.21, p = 0.018 → 人口は GDP の予測に有意に寄与
💬 読み方: Granger 因果は「予測力」の意味での因果。 真の因果(DAG)とは異なる。 共通の交絡があると偽相関。 VAR モデル前提。 共和分系列には適用不可(VECM を使う)。

📝 14. 報告フォーマット

❌ NG例

「ARIMA を当てはめたら良い予測ができました。」

✅ OK例

「月次データ(2015-01 から 2025-12、 N=132)に対し、 1階差分で定常化(ADF p < .001)。 ACF/PACF と AIC 最小化により SARIMA(1,1,1)(1,1,1,12) を選択。 12-期 rolling-origin バックテストで MASE = 0.81(< 1 でナイーブより優れる)、 95% 予測区間のカバレッジ 92%(名目 95%)。 比較として Prophet(MASE=0.85)、 ETS(0.78)、 LightGBM with lag features(0.74)を実施し、 LightGBM を採用。」

🐍 15. ライブラリ早見表

用途 関数・クラス
ARIMA / SARIMAstatsmodels.tsa.arima.ARIMA, SARIMAX, pmdarima.auto_arima
指数平滑 / ETSstatsmodels.tsa.holtwinters.ExponentialSmoothing
分解seasonal_decompose, STL
単位根検定adfuller, kpss
ACF/PACFplot_acf, plot_pacf, acf, pacf
VAR / VECMstatsmodels.tsa.api.VAR, VECM
状態空間UnobservedComponents, DynamicFactor
Prophetprophet.Prophet
時系列CVsklearn.model_selection.TimeSeriesSplit
深層学習torch.nn.LSTM/GRU, Darts, NeuralProphet
変化点検出ruptures, bocpd

📜 16. 時系列分析の歴史

💼 17. 実務応用

✅ 18. 時系列分析チェックリスト

🩺 19. 残差診断

ARIMA 等のフィット後、 残差が「ホワイトノイズ」になっているかを確認。 残差にパターンが残っていれば、 モデルが情報を取り切れていない。

Ljung–Box 検定

残差の自己相関がすべて 0 か? 帰無仮説:H₀ = 自己相関なし。 p > 0.05 で「ホワイトノイズ」と判断可。

🎯 解説: Hodrick-Prescott フィルタでマクロ経済時系列をトレンドと景気循環に分解する。
1
2
3
from statsmodels.stats.diagnostic import acorr_ljungbox
lb = acorr_ljungbox(fit.resid, lags=[12], return_df=True)
print(lb)
📥 入力例: 全国 GDP 四半期データ
📤 実行例: HP フィルタ (λ=1600) トレンド成分: 緩やかな上昇 循環成分: ±2% の景気変動
💬 読み方: λ は平滑化パラメータ。 四半期データは λ=1600 が標準。 月次なら 14400。 端点で誤差が大きい(end-point bias)→ 直近データの解釈は慎重に。 Hamilton フィルタが代替。

残差プロット

🔖 キーワード索引(拡張)

時系列とは 分解 📈 定常性 🔄 ACF/PACF 🌀 ARIMA 指数平滑 状態空間 Prophet VAR 🧮 SSDSE 実例 実装バリエーション 追加落とし穴 関連用語マップ

🧮 SSDSE-B を使った時系列分析の実例

SSDSE-B のパネル構造(47都道府県 × 2003-2020年)から、 例えば東京都の「人口」沖縄県の「合計特殊出生率」を取り出すと、 単一都道府県の年次時系列が得られます。 ここでは東京都の総人口(千人)を題材に、 ARIMA・指数平滑・トレンド分解の実値計算を見ます。

① データ準備

🎯 解説: GARCH モデルで時系列の分散の自己相関(ボラティリティクラスタリング)をモデル化する。
1
2
3
4
5
6
7
8
9
import pandas as pd
df = pd.read_csv('data/raw/SSDSE-B-2023.csv', encoding='shift_jis', header=[0,1])
df.columns = ['_'.join(c).strip() for c in df.columns]
tokyo = df[df['都道府県_Prefecture'] == '東京都'].set_index('年度_Year')
y = tokyo['総人口_Total population'] / 1000  # 千人単位
print(y.head())
# 2003 年: 12,420 千人
# 2010 年: 13,160 千人
# 2020 年: 14,048 千人 — 緩やかに増加
📥 入力例: 金融時系列の日次収益率
📤 実行例: GARCH(1,1) ω=0.001, α=0.05, β=0.93 → 分散の持続性 α+β=0.98(高い)
💬 読み方: GARCH は ARIMA で残差の自己相関を消した後、 分散にまだ自己相関が残る場合に使う(ARCH 効果)。 α+β < 1 で定常。 1 に近いと変動が長期持続。 IGARCH, EGARCH, GJR-GARCH などの拡張多数。

② トレンド・季節分解(STL)

年次データなので季節周期はないが、 トレンドと残差の分解は意味がある:

🎯 解説: 状態空間 + ARIMA の動的線形モデル(DLM)で、 トレンドが時間変化する非定常時系列を扱う。
1
2
3
4
5
from statsmodels.tsa.seasonal import STL
# 年次データなのでトレンド + 残差のみ(周期なし)
stl = STL(y, period=None, robust=True).fit()
print(f'トレンド成分: 2003={stl.trend.iloc[0]:.0f}, 2020={stl.trend.iloc[-1]:.0f}')
print(f'残差 SD: {stl.resid.std():.1f}')
📥 入力例: 東京都 人口時系列 + 構造変化(2020 コロナ)
📤 実行例: Local Linear Trend モデル 2020 年に変化点検出 変化点前: +0.7%/年 変化点後: -0.3%/年
💬 読み方: DLM は ARIMA の状態空間表現で、 パラメータが時変。 RegimeSwitching, BSTS(Bayesian Structural Time Series)も同類。 構造変化があるデータに頑健。 計算はカルマンフィルタ。

典型出力:トレンドは 12,400 → 14,000 へ単調増加、 残差は 50 程度(年率0.4%以内の変動)。

③ 定常性検定(ADF)

🎯 解説: Time2Vec / Transformer ベースの時系列モデル(Informer, Autoformer)で長期予測を行う。
1
2
3
4
5
6
7
8
9
from statsmodels.tsa.stattools import adfuller
res = adfuller(y)
print(f'ADF stat = {res[0]:.3f}, p = {res[1]:.4f}')
# 例:ADF = -0.5, p = 0.88 → 単位根あり(非定常)

# 1階階差で再検定
res2 = adfuller(y.diff().dropna())
print(f'diff ADF stat = {res2[0]:.3f}, p = {res2[1]:.4f}')
# 例:ADF = -3.2, p = 0.02 → 階差で定常 → I(1) → ARIMA(p,1,q) 候補
📥 入力例: 東京都 月次人口 (2010-2023)
📤 実行例: Transformer(d_model=64, nheads=4) 長期予測(36 ヶ月先)RMSE = 188 → LSTM より長期は安定
💬 読み方: Transformer は Attention で長期依存を捉える。 ただし時系列が短い (<1000) と LSTM/ARIMA より劣ることも。 Informer の ProbSparse Attention で計算量削減。 PatchTST が最近の SOTA。

④ ARIMA モデル推定と予測

🎯 解説: アンサンブル予測(ARIMA + LSTM + Prophet の重み平均)で複数モデルを統合し予測精度を向上させる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from statsmodels.tsa.arima.model import ARIMA
model = ARIMA(y, order=(1, 1, 0)).fit()
print(model.summary())
# AR(1) 係数 ≈ 0.4, σ² ≈ 1500 程度

forecast = model.get_forecast(steps=5)  # 2021-2025 を予測
ci = forecast.conf_int(alpha=0.05)
print(forecast.predicted_mean)
print(ci)
# 2021: 14,090 千人 [13,990, 14,190](CI 幅 ±100)
# 2025: 14,180 千人 [13,890, 14,470](先になるほど CI 拡大)
📥 入力例: 東京都 人口時系列 14 年
📤 実行例: ARIMA: RMSE=132 LSTM: RMSE=128 Prophet: RMSE=135 等重みアンサンブル: RMSE=119
💬 読み方: アンサンブルは各モデルの弱点を相殺。 重みは validation RMSE 逆比または線形回帰で最適化。 多様性のあるモデル(線形・非線形・ベイズ)を混ぜると効果大。 メタ学習 (stacking) も有効。

⑤ 指数平滑(Holt-Winters)との比較

🎯 解説: 外生変数つき ARIMA(ARIMAX)で、 説明変数を加えた時系列モデルを構築する。
1
2
3
4
5
from statsmodels.tsa.holtwinters import ExponentialSmoothing
hw = ExponentialSmoothing(y, trend='add', seasonal=None).fit()
print(f'α (level) = {hw.params["smoothing_level"]:.3f}')
print(f'β (trend) = {hw.params["smoothing_trend"]:.3f}')
print(hw.forecast(5))  # ARIMA とほぼ同じ予測値が得られる
📥 入力例: 東京都 GDP 時系列 + 出生率・移動率を外生変数
📤 実行例: ARIMAX(1,1,1, exog=2) AIC = 118.5(外生なしより改善) → 外生変数が説明力を持つ
💬 読み方: ARIMAX は時系列 + 回帰のハイブリッド。 説明変数は同時または過去値(リードラグ)。 外生変数が予測時にも既知であることが前提(GDP 予測なら人口統計はラグで使う)。 SARIMAX で季節性込み。

同じデータで複数モデルを試し、 AIC・BIC・CV-RMSEで比較するのが現代的なベストプラクティス。

🐍 実装バリエーション — statsmodels / sktime / Prophet / Darts

時系列ライブラリは選択肢が豊富。 用途・速度・拡張性で使い分けます。

(A) statsmodels — 古典統計の標準

🎯 解説: 異常検知(時系列):移動平均から ±3σ 以上外れた点を異常として検出する。
1
2
3
4
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.holtwinters import ExponentialSmoothing
# 古典的 ARIMA・SARIMAX・状態空間モデルが揃う
📥 入力例: 全国小売販売額 月次 (2018-2023)
📤 実行例: 移動平均 (window=12) ± 3σ 異常検出: 2020 年 4 月(コロナ)、 2020 年 5 月 → 自動でロックダウンを検出
💬 読み方: シンプルな ±3σ 法は実装容易。 ただし時系列にトレンド・季節性がある場合は STL 分解後の残差に適用。 LOF, Isolation Forest, AutoEncoder などの機械学習手法も。 異常の定義はドメイン依存。

(B) pmdarima — auto_arima の決定版

🎯 解説: AR(1) モデルの最尤推定とパラメータ φ の信頼区間を計算する。
1
2
3
4
from pmdarima import auto_arima
model = auto_arima(y, seasonal=False, stepwise=True, trace=True)
print(model.summary())
# (p,d,q) を AIC 最小で自動探索
📥 入力例: 東京都 人口差分系列
📤 実行例: AR(1): y_t = 0.62 y_{t-1} + ε_t φ = 0.62 ± 0.18 (95% CI) σ² = 1.24
💬 読み方: |φ| < 1 で定常。 φ ≈ 1 は単位根近傍で不安定。 標準誤差は 1/√n オーダー。 サンプル数 n が小さいと信頼区間が広く、 モデル選択が不安定。 ベイズ推定(事前分布で正則化)が代替。

(C) Prophet — トレンド + 休日 + 季節を柔軟に

🎯 解説: 時系列の特徴量エンジニアリング(ラグ・移動平均・差分)で機械学習の入力を作る。
1
2
3
4
5
6
from prophet import Prophet
df_p = pd.DataFrame({'ds': pd.to_datetime(y.index, format='%Y'), 'y': y.values})
m = Prophet(yearly_seasonality=False, daily_seasonality=False).fit(df_p)
future = m.make_future_dataframe(periods=5, freq='Y')
forecast = m.predict(future)
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(5))
📥 入力例: 東京都 月次人口 (n=168)
📤 実行例: 特徴量: lag_1, lag_2, …, lag_12 rolling_mean_3, rolling_std_3 diff_1, diff_12 → XGBoost に投入
💬 読み方: ラグ特徴量は最も基本。 移動平均はトレンド、 ローリング標準偏差はボラティリティを表す。 差分で定常化。 特徴量設計が予測精度の 8 割を決める。 tsfresh, featuretools で自動化可能。

(D) sktime — scikit-learn API で時系列

🎯 解説: 確率的トレンドと決定的トレンドの違いをシミュレーションで示す。
1
2
3
4
5
6
7
from sktime.forecasting.arima import ARIMA as SKARIMA
from sktime.forecasting.model_selection import temporal_train_test_split
y_train, y_test = temporal_train_test_split(y, test_size=3)
forecaster = SKARIMA(order=(1,1,0))
forecaster.fit(y_train)
y_pred = forecaster.predict(fh=[1, 2, 3])
print(y_pred)
📥 入力例: シミュレーション: y_t = α + βt + ε_t (決定的) y_t = y_{t-1} + ε_t (確率的, ランダムウォーク)
📤 実行例: 決定的トレンド: 1 階差分後も白色雑音にならない 確率的トレンド: 1 階差分で白色雑音 → ADF 検定で見分ける
💬 読み方: 決定的トレンドは「時間 t の関数」、 確率的は「累積ノイズ」。 差分操作で消えるのは確率的のみ。 ARIMA の d は確率的トレンドに、 trend 項は決定的トレンドに対応。 区別が重要。

(E) Darts — 機械学習・深層学習も統一API

🎯 解説: 時系列の構造変化検出(Chow テスト, CUSUM)で変化点を統計的に検出する。
1
2
3
4
5
from darts import TimeSeries
from darts.models import ExponentialSmoothing as DartsES, NBEATSModel
ts = TimeSeries.from_series(y)
es = DartsES(); es.fit(ts); print(es.predict(5))
# NBEATSModel など深層学習モデルも同じインターフェース
📥 入力例: 全国小売販売額 月次 (2018-2023)
📤 実行例: CUSUM プロット 変化点候補: 2020-04, 2020-05 Chow テスト: F = 18.2, p<0.001 → 2020 年 4 月に有意な構造変化
💬 読み方: Chow テストは変化点を事前指定。 CUSUM, Bai-Perron 法は変化点を自動検出。 ベイズの BCP(Bayesian Change Point)もある。 変化点を見落とすとモデル誤特定。 結果の解釈には事業ドメイン知識必須。

(F) scikit-learn TimeSeriesSplit — 時系列CV

🎯 解説: 時系列の平滑化(Holt-Winters 法)でトレンド・季節性の指数平滑化を行う。
1
2
3
4
5
6
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import mean_absolute_error
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, test_idx in tscv.split(y):
    y_tr, y_te = y.iloc[train_idx], y.iloc[test_idx]
    # 各 fold でモデル訓練・評価。 通常CV と違い「過去→未来」順を維持
📥 入力例: 全国小売販売額 月次 (2018-2023)
📤 実行例: Holt-Winters (α=0.3, β=0.1, γ=0.5, m=12) → 平滑化済み系列 → 短期予測に最適
💬 読み方: α 大→直近を重視、 β はトレンド更新、 γ は季節成分更新。 ETS(Error/Trend/Season)モデルの一種。 加法 vs 乗法は系列の特性で選ぶ。 短期予測では ARIMA より良いことも。

⚠️ 追加の落とし穴 — 時系列分析の実務

❌ 非定常データに普通の OLS を適用
トレンドを持つ2つの時系列を素朴に回帰すると、 「偽相関(spurious regression)」が頻発する。 例えば「日本のGDPと米国のGDP」「人口と物価」など、 共にトレンドを持つ系列はほぼすべて高い相関を示す(無関係でも)。 必ずADF/KPSS 検定で定常性を確認、 非定常なら階差をとる共和分関係を検定(Engle-Granger、 Johansen)。 これを怠ると R² 0.99 でも完全な見せかけ。
❌ k-fold 交差検証を時系列に使う
標準的な k-fold CV はランダムに分割するため、 未来のデータで過去を予測するリークが発生する。 時系列では必ず TimeSeriesSplit(rolling/expanding window)を使い、 「訓練は過去、 テストは未来」を厳守。 これを破ると訓練時の精度が異常に高くなり、 本番運用で激しく性能が落ちる。 機械学習プロジェクトの典型的失敗パターン。
❌ 季節調整なしで季節データを扱う
月次・四半期データには季節成分がほぼ必ず存在する(商品需要・気象・観光など)。 季節調整なしのモデルは季節パターンを「トレンド」と誤認したり、 残差に強い自己相関を残す。 SARIMA・STL・X-13ARIMA-SEATS など、 必ず季節成分を陽に扱う。 残差の ACF が周期12(月次なら)でスパイクするのは未調整のサイン。
❌ 予測区間 (PI) を信頼区間と混同
予測区間 (prediction interval) は未来の実現値がカバーされる区間、 信頼区間 (CI) はパラメータ真値を捕える区間。 時系列予測では PI が必要。 ARIMA や Prophet が返す区間は通常 PI で、 CI より広い(パラメータ不確実性 + 残差ばらつきの両方を含む)。 「予測値±SE」だけ報告するのは過小評価。 必ず 80%/95% PI を併記する。
❌ 構造変化点を無視して長期データを当てはめ
バブル崩壊・リーマンショック・COVID-19 のような構造変化点(structural break)を跨いだデータに単一モデルを当てはめると、 変化点の前後で別物の系列を「平均」してしまう。 Chow 検定・CUSUM・Bai-Perron 検定で変化点を検出し、 期間を分けて分析するか、 介入変数(dummy)を入れる。 Prophet の changepoint_prior_scale やベイズ的変化点モデル(PyMC)も有効。
❌ 残差診断を行わない
ARIMA を当てはめた後、 残差がホワイトノイズになっているかを必ず確認する。 確認項目:(i) Ljung-Box 検定 (p>0.05 なら OK)、 (ii) 残差 ACF(全ラグで信頼区間内)、 (iii) QQプロットによる正規性、 (iv) ヒストグラムの裾。 これらに問題があれば、 モデル次数 (p,d,q) を変える、 季節項を追加する、 変数変換(log)を試すなど対処する。 残差診断を怠ると「悪いモデルでも当てはまった気になる」典型的失敗。
❌ 過剰に複雑なモデルを選ぶ
AIC を最小化するだけで ARIMA(5,1,5) のような高次モデルを選ぶと、 過学習・予測不安定化に陥る。 Hyndman の経験則として「シンプルなモデル(ARIMA(0,1,1), (1,1,0) 等)が長期的には勝つことが多い」。 単純な指数平滑、 ナイーブ予測(前年と同じ)を必ずベースラインとして比較し、 複雑モデルが有意に改善する場合のみ採用する。 「Occam's razor」を意識する。

🎨 直感で掴む — 時系列 の本質

時系列とは「同じ対象を時間順に観測した値の並び」です。 SSDSE-B-2026 の都道府県別 合計特殊出生率は年度ごとに変化しており、 北海道は 2021 年の 1.20 から 2023 年の 1.06 へ低下傾向です。 こうした「トレンド・季節・残差」を分解して未来を予測するのが時系列分析の本質。

💡 ポイント:時系列 を初めて学ぶときは「正確な定義」より「どんな問題を解くための道具か」を先に押さえてください。 数式は次の「📐 数式」セクションで丁寧に展開します。
📌 比喩がうまく刺さらないときは、 自分の身近な例(家計簿・スポーツの記録・成績表)に置き換えてみると理解が定着します。 SSDSE-B-2026 を電卓代わりに触りながら、 上の説明を再読すると効果的です。

📐 数式または定義 — 時系列 の形式的表現

直感で全体像を掴んだら、 次は厳密な定義を見ます。 数式は短いものでも、 「何を入力にして、 何を出力するのか」を意識して読むと早く慣れます。

【時系列の加法・乗法分解】
$$ y_t = T_t + S_t + R_t \quad(加法分解) \qquad y_t = T_t \cdot S_t \cdot R_t \quad(乗法分解) $$
この数式は「時系列 がどう計算されるか」を最短で示したもの。 記号の意味は次の「🔬 数式を言葉で読み解く」で 1 つずつ解説します。
📚 数式が苦手な方へ:1 つの長い式を一度に理解しようとせず、 記号ごとに「言葉に翻訳」するのが王道。 紙に書き写してから、 自分の言葉で音読してみてください。

🔬 数式を言葉で読み解く — 時系列 の記号辞書

上の数式に出てくる各記号が何を表すかを、 言葉で翻訳します。 1 つずつ自分の言葉で言い換えられるようになると、 論文や教科書のスピードが一気に上がります。

記号意味(言葉での説明)
$y_t$時刻 $t$ の観測値(出生率・死亡数 等)
$T_t$トレンド成分(長期傾向)
$S_t$季節成分(周期的変動)
$R_t$残差(説明できない揺らぎ)
📌 読み下しのコツ:左から右に「主語 → 述語 → 目的語」と見立てて、 「これは何を、 どうしている式か?」と一文で要約してみてください。 慣れれば 5 秒で読めます。

🧮 実値で計算してみる — SSDSE-B-2026 で 時系列 を体感

数式だけでは「分かった気になる」だけで終わりがち。 ここで SSDSE-B-2026(教育用標準データセット — 47 都道府県 × 100+ 指標、 2018-2023 年度)の実値を当てはめて、 時系列 の挙動を電卓的に追体験します。

👉 計算例:SSDSE-B-2026 では各都道府県について年度ごとに 100+ 指標が取れる。 北海道の出生率(A4200)は 2019 年 1.24 → 2020 年 1.21 → 2021 年 1.20 → 2022 年 1.12 → 2023 年 1.06 と、 5 年で約 0.18 ポイント低下。 単純な線形トレンドの傾き $\hat{\beta} \approx -0.043$/年 と推定でき、 SARIMA(1,1,1)(0,1,1) で 2024 年の値を予測する流れが典型。

SSDSE-B-2026 は 統計センターの SSDSE 配布ページ から CSV を直接ダウンロードできます。 本サイトでは data/raw/SSDSE-B-2026.csv に配置している前提でコードを書いています。

🐍 Python 実装 — 時系列 を SSDSE-B-2026 で動かす

以下のコードは最小限の構成です。 pd.read_csv('data/raw/SSDSE-B-2026.csv') を直書きしているので、 同じ階層に CSV を置けばそのまま動きます。 変数化しないのは、 初学者が「パスをどこに書くべきか」で迷わないようにするためです。

# 時系列 を SSDSE-B-2026 で確かめる最小コード
import pandas as pd
import numpy as np

# 1) SSDSE-B-2026(教育用標準データセット)を読み込み
df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
print('shape:', df.shape)        # (564, 112) — 47 都道府県 × 6 年度
print('cols head:', list(df.columns[:8]))

# 2) 直近年度(2023 年度)に絞る
df23 = df[df['年度'] == 2023].copy()
print('rows in 2023:', len(df23))

# 3) 時系列 を動かすために必要な列だけ取り出す
y = df23['合計特殊出生率'].astype(float)
x = df23['総人口'].astype(float)
print('y stats:', y.describe().round(3).to_dict())
print('x stats:', x.describe().round(0).to_dict())

# 4) 時系列 の本処理(このページの主題)
#    — 具体実装は同カテゴリの個別ページにも掲載
print('---- 時系列 結果 ----')
print('mean y:', y.mean().round(3), '/ std y:', y.std().round(3))
print('mean x:', x.mean().round(0), '/ std x:', x.std().round(0))
print('corr(x, y):', y.corr(x).round(3))

うまく動かないときは ①data/raw/SSDSE-B-2026.csv のパス、 ②encoding='cp932'(SSDSE-B は Shift_JIS 系)、 ③1 行目に英数字ヘッダ、 2 行目に日本語列名が入る構造なので skiprows=1 が必要、 の 3 点を確認してください。

⚠️ よくある落とし穴 — 時系列 で初学者がやりがちなミス

この用語を実務で使うときにつまずきやすい点を、 失敗パターン別に整理しました。 1 度経験すれば回避できるものばかりですが、 先に知っておくと事故が大幅に減ります。

❌ 階差を取らずに回帰
時間トレンドのある変数同士の回帰は「見かけの相関」を生む。 必ず ADF 検定で定常性を確認。
❌ 季節調整忘れ
月次データを年次トレンドだけで見ると周期成分が残差を膨らませる。 STL/X-13 で分解を。
❌ 訓練・検証の漏洩
時系列はランダム CV ではなく、 時間順の TimeSeriesSplit を使う。
❌ 構造変化の見落とし
COVID-19 のような外部ショックで動きが変わる年は別途ダミー変数を入れる。
🛡 防御策まとめ:「適用条件の確認 → 適切な前処理 → 結果と前提のペア記述」の 3 ステップを習慣にすれば、 ここに挙げた失敗の大半は回避できます。

🌐 関連手法・派生 — 時系列 の周辺地図

時系列 と一緒に覚えておくと選択肢が広がる関連手法。 状況によって使い分けが必要なので、 それぞれの強みと弱みを 1 行で言えるようにしておきましょう。

表中の各手法は本サイト内に個別ページが用意されているものが多いです。 興味を持った概念は、 横展開的に読むと体系的な理解が早く進みます。