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

🔖 キーワード索引

「time series group」は統計データ分析の文脈で扱う重要概念のひとつ。 本ページでは「time series group」を取り巻く中核キーワードを以下にチップで一覧化する。 各キーワードは関連する概念・手法・道具立てを含み、 文献検索や学習計画の起点になる。

time series group統計分析SSDSE-B-2026前提条件適用範囲落とし穴関連手法Python 実装検証方法

これらのキーワードは「time series group の理解 → 適用 → 検証」のプロセスを構成する。 各章で詳しく解説する。

💡 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$

🎯 解説: 月次の気温系列を seasonal_decompose(加法モデル、周期 12)でトレンド・季節・残差の 3 成分に分けて図にする擬似コード。
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/temperature_monthly.csv(date 列=月初の日付、temp 列=月平均気温)を想定。このファイルは教材に同梱していない。
📤 実行例: 同梱していないファイルを読む擬似コードなので実測はない。2 年(24 か月)以上の月次データを用意すれば、observed・trend・seasonal・resid の 4 段の図が描かれる。
💬 読み方: period=12 の分解ではトレンドを中心化移動平均で求めるので、系列の最初と最後の 6 か月ずつはトレンドが NaN になる。季節成分は全期間で同じ形を 1 つだけ推定するため、夏の山が年々高くなるような変化は残差の側に押し出される。季節の形の変化も追いたいときは下の STL を使う。

STL 分解(Seasonal-Trend Loess)

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

⚖️ 3. 定常性

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

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

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

🎯 解説: SSDSE-F(都市別の月別平年値)から札幌市の月平均気温 12 か月分を取り出し、ADF 検定(maxlag=2)で単位根の有無を調べる。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# ── この抜粋で使う時系列を用意します ──
# SSDSE-F は「都市 × 月」の気象データ。札幌市の月平均気温を 1 本の系列にする。
import pandas as pd
from statsmodels.tsa.stattools import adfuller

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s.set_index('Month')['CN100'].astype(float)   # CN100 = 平均気温
print(ts.round(1).to_dict())

res = adfuller(ts, maxlag=2)
print(f'ADF統計量: {res[0]:.3f}, p値: {res[1]:.4f}')
📥 入力例: data/raw/SSDSE-F-2023v3.csv の札幌市・01月〜12月の 12 行(列 CN100 = 月平均気温 ℃)。1 年分の平年値なので系列長は 12。
📤 実行例(実測) {'01月': -3.2, '02月': -2.7, '03月': 1.1, '04月': 7.3, '05月': 13.0, '06月': 17.0, '07月': 21.1, '08月': 22.3, '09月': 18.6, '10月': 12.1, '11月': 5.2, '12月': -0.9} ADF統計量: -6.218, p値: 0.0000
💬 読み方: p 値 0.0000 で「単位根あり」を棄却し、数字の上では定常と出る。ただしこの系列は 1 月の -3.2℃ から 8 月の 22.3℃ まで上がって 12 月の -0.9℃ に戻る 1 周期だけで、12 点・ラグ 2 の ADF は季節の山と平均への回帰を区別できない。「季節性の無い定常系列だ」という意味には読まず、季節差分や STL で周期を除いてから判断する。

3.2 差分・対数差分

🎯 解説: 札幌の月平均気温に 1 階差分を取り、あわせて対数差分がこの系列に使えるかを確かめる。
1
2
3
4
5
6
7
8
9
10
import numpy as np

ts_diff = ts.diff().dropna()                  # 1 階差分(12 点 → 11 点)
print('1 階差分:', ts_diff.round(1).tolist())

# 対数差分は正の値だけの系列(人口・販売額など)に使う。
# 札幌の気温は 0℃ 以下の月があり、その月は log が取れない(NaN になる)
print('0℃ 以下の月:', ts[ts <= 0].index.tolist())
ts_log_diff = np.log(ts.where(ts > 0)).diff().dropna()
print('対数差分で残る点数:', len(ts_log_diff))
📥 入力例: 3.1 のコードで作った ts(札幌市の月平均気温 12 点、01月〜12月、単位 ℃)。
📤 実行例(実測) 1 階差分: [0.5, 3.8, 6.2, 5.7, 4.0, 4.1, 1.2, -3.7, -6.5, -6.9, -6.1] 0℃ 以下の月: ['01月', '02月', '12月'] 対数差分で残る点数: 8
💬 読み方: 1 階差分は 3 月→4 月の +6.2℃ が最大、10 月→11 月の -6.9℃ が最小で、7 月までがプラス、8 月以降がマイナスに分かれる。差分を取っても季節の上り下りはそのまま残るので、月次データでは季節差分(12 か月前との差)が別に要る。対数差分は 0℃ 以下の 01・02・12 月で log が取れず、11 点のうち 8 点しか残らない。

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
🎯 解説: 札幌の月平均気温の 1 階差分(11 点)について ACF と PACF を並べて描き、AR・MA の次数の手がかりを見る。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温(12 点)を 1 階差分した系列。点数が少ないので
# lags は系列長の半分未満にしないと plot_acf が落ちる。
import pandas as pd
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import matplotlib.pyplot as plt

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s.set_index('Month')['CN100'].astype(float)
ts_diff = ts.diff().dropna()          # 11 点

lags = min(len(ts_diff) // 2 - 1, 24)
fig, axes = plt.subplots(1, 2, figsize=(14, 4))
plot_acf(ts_diff, lags=lags, ax=axes[0])
plot_pacf(ts_diff, lags=lags, ax=axes[1])
plt.show()
📥 入力例: 札幌市の月平均気温 12 点(SSDSE-F、CN100)を 1 階差分した 11 点。lags は min(11 // 2 - 1, 24) = 4。
📤 実行例(実測) このブロックは図を描くだけで標準出力は無い。図に描かれる値は ACF = [1.000, 0.806, 0.452, 0.079, -0.230] (lag 0〜4) PACF = [1.000, 0.806, -0.567, -0.145, -0.144] (plot_pacf 既定の method='ywm') (statsmodels の acf・pacf で同じ値になることを確認)。
💬 読み方: ACF は lag 1 の 0.806 から lag 4 の -0.230 へなだらかに下がり、PACF は lag 1 の 0.806 のあと lag 2 で -0.567 に落ち、lag 3・4 は -0.15 前後に収まる。形だけなら AR(1)〜AR(2) の型だが、11 点の 95% 帯は ±1.96/√11 ≈ ±0.59 と広く、lag 2 の -0.567 は帯の内側に入る。この長さでは次数を言い切れず、見えている相関の大半は 1 年周期の山の形そのもの。

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

🍰 まずはやさしく

時間の順番が大切なデータの分析です。

過去のデータから未来を考えるために使います。

部活の練習記録で成長を見るようなものです。

ここでは分析に必要な手法をまとめて学びます。

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

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

🎨 直感で掴む — 時系列グループ の本質

🍰 まずはやさしく

同じものを時間順に並べたデータのグループです。

データの中に隠れたパターンを見つけるために使います。

テストの点数の変化をグラフにするようなものです。

ここでは代表的な分析の方法を紹介します。

時系列分析グループは「同じ対象を時間順に観測した値の並び」を扱う手法群の総称。 ARIMA / SARIMA、 状態空間モデル、 VAR、 Prophet、 LSTM、 Transformer 時系列予測など、 古典統計から深層学習まで一連の流れがあります。

SSDSE-B-2026 で観察できる時系列パターン:

時系列分析グループに属する代表手法:

時系列分析の典型タスク: 予測(将来 12 か月の値推定)、 異常検知(IoT センサの故障)、 因果推論(広告投下効果の DiD)、 セグメンテーション(売上のクラスタリング)、 介入評価(合成統制法、 BSTS)。 SSDSE-B-2026 の都道府県 × 年度パネルでは パネル時系列(固定効果、 動学パネル GMM)が標準。

古典 vs 深層学習の使い分け: 観測数 100 以下 → 古典(ARIMA、 Prophet)が定常仮定を活かして安定。 観測数 1000+ かつ多系列 → LSTM/Transformer が長期依存と非線形を捉える。 SSDSE-B-2026 のように 47 県 × 12 年 = 564 観測なら、 パネル ARIMA + 固定効果が現実解。

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

🍰 まずはやさしく

時系列を数式で表したルールです。

データの仕組みを正確に理解するために使います。

お小遣いの増減を計算式にするようなものです。

ここでは数式の意味を一つずつ解説します。

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

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

🔬 5. ARIMA モデル

🔬 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 を適用。 非定常データに対応。

🎯 解説: 札幌の月平均気温 12 点に ARIMA(1,1,1) を当てはめ、推定結果の要約と 12 か月先までの予測値を出す。
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)
📥 入力例: 3.1 のコードで作った ts(札幌市の月平均気温 12 点、01月〜12月、単位 ℃)。
📤 実行例(実測) SARIMAX Results ============================================================================== Dep. Variable: CN100 No. Observations: 12 Model: ARIMA(1, 1, 1) Log Likelihood -22.090 Date: Sat, 26 Sep 2026 AIC 50.180 Time: 15:41:45 BIC 51.374 Sample: 0 HQIC 49.428 - 12 Covariance Type: opg ============================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------ ar.L1 0.8020 0.222 3.614 0.000 0.367 1.237 ma.L1 0.9989 67.890 0.015 0.988 -132.063 134.061 sigma2 2.1383 144.737 0.015 0.988 -281.541 285.817 =================================================================================== Ljung-Box (L1) (Q): 1.53 Jarque-Bera (JB): 0.12 Prob(Q): 0.22 Prob(JB): 0.94 Heteroskedasticity (H): 0.90 Skew: -0.04 Prob(H) (two-sided): 0.92 Kurtosis: 2.49 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step). 12 -6.228679 13 -10.502195 14 -13.929488 15 -16.678122 16 -18.882484 17 -20.650347 18 -22.068145 19 -23.205197 20 -24.117095 21 -24.848422 22 -25.434935 23 -25.905310 Name: predicted_mean, dtype: float64
💬 読み方: ar.L1 = 0.802(p < 0.001)は「前月からの変化が翌月も続く」ことを表し、ma.L1 = 0.999 は標準誤差 67.9 で実質的に推定できていない。予測は 1 か月先の -6.2℃ から 12 か月先の -25.9℃ まで下がり続けるが、これは 10〜12 月の下り坂を延ばしただけで、翌年の夏に気温が戻ることをモデルは知らない。季節のある系列に季節項なしの ARIMA を当てるとこうなる、という例として読む。

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

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

🎯 解説: 季節項つきの SARIMA(1,1,1)(1,1,1,4) を札幌の月平均気温に当てはめ、AIC と係数を出す。12 点では周期 12 の季節差分が取れないので周期は 4 にしている。
1
2
3
4
5
6
7
8
9
10
from statsmodels.tsa.statespace.sarimax import SARIMAX
# 季節周期 s=12 の SARIMA は、差分 d=1 と季節差分 D=1(12 か月前との差)で
# 13 点を使い切るので、12 点(1 年分)では何も推定できない
# (対数尤度 0・AIC 10.0 という意味のない値が返る)。
# ここは下の状態空間モデルと同じく、季節周期を 4 にして当てはめる。
model = SARIMAX(ts.reset_index(drop=True), order=(1, 1, 1),
                seasonal_order=(1, 1, 1, 4))
fit = model.fit(disp=False)
print(f'AIC = {fit.aic:.2f}')
print(fit.params.round(3))
📥 入力例: 札幌市の月平均気温 12 点(ts)。差分 d=1 と季節差分 D=1(s=4)の後に残るのは 7 点。
📤 実行例(実測) AIC = 42.68 ar.L1 0.978 ma.L1 0.988 ar.S.L4 -0.095 ma.S.L4 -0.997 sigma2 2.001 dtype: float64
💬 読み方: AIC = 42.68 は出るが、ma.L1 = 0.988 と ma.S.L4 = -0.997 はどちらも ±1 の境界に張り付いていて、7 点に 5 個のパラメータを当てた結果なので係数は解釈できない。差分の取り方が違う ARIMA(1,1,1) の AIC 50.180 とは使っている点数が違うので、大小で優劣を決めない。周期 12 のまま SARIMA を使うには少なくとも 2〜3 年分(24〜36 点)の月次データが要る。

5.6 自動次数選択(pmdarima)

🎯 解説: pmdarima の auto_arima で、季節周期 12 の SARIMA の次数 (p,d,q)(P,D,Q) を AIC 最小で自動探索し、12 か月先を予測する。
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)
📥 入力例: 2 年(24 か月)以上の月次系列 ts。このページの札幌の平年値(12 点)では m=12 の季節差分が取れない。
📤 実行例: pmdarima はこの教材の実行環境に入っていないので実測はない(pip install pmdarima が必要)。
💬 読み方: stepwise=True は全組合せを試さず、AIC が下がる方向に次数を 1 つずつ動かす探索なので速いが、局所的な最小で止まることがある。選ばれた次数は summary() の先頭の Model 行に SARIMAX(p,d,q)x(P,D,Q,12) の形で出るので、同じ summary の下段にある Ljung-Box の p 値と合わせて確かめる。

📈 6. 指数平滑法

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

🎯 解説: 季節成分なし・加法トレンドの指数平滑(Holt 法)を札幌の月平均気温に当てはめ、12 か月先まで予測する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ── この抜粋で使う系列を用意します ──
# 季節つき Holt-Winters は「2 周期分以上」(月次なら 24 点以上)が要る。
# SSDSE-F の平年値は 1 年 12 点しか無いので、ここは trend だけで当てはめる。
import pandas as pd
from statsmodels.tsa.holtwinters import ExponentialSmoothing

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s.set_index('Month')['CN100'].astype(float).reset_index(drop=True)

hw = ExponentialSmoothing(ts, trend='add', seasonal=None).fit()
forecast = hw.forecast(12)
print(forecast.round(1).tolist())
📥 入力例: 札幌市の月平均気温 12 点(SSDSE-F、CN100、01月〜12月)。季節つき Holt-Winters に要る 24 点に足りない。
📤 実行例(実測) [-7.0, -13.1, -19.2, -25.3, -31.4, -37.5, -43.6, -49.7, -55.8, -61.9, -68.0, -74.1]
💬 読み方: 予測は -7.0℃ から毎月 6.1℃ ずつ下がり、12 か月先は -74.1℃ になる。季節成分を入れないと、10〜12 月の下り坂(12.1 → 5.2 → -0.9℃)がトレンドとして学習され、直線でそのまま延ばされる。季節のある系列を 1 周期分しか持っていないときは、指数平滑でもまともな予測が出ないことを示す例。

🌌 7. 状態空間モデル

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

🎯 解説: 状態空間モデル(UnobservedComponents:ローカル線形トレンド+周期 4 の確率的季節)を札幌の月平均気温に当てはめ、カルマンフィルタで各成分の分散を推定する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温(12 点)。seasonal=12 は 2 周期分(24 点)以上が要り、
# 12 点では標準誤差が nan になってしまうので、ここは季節成分を 4 にする。
import pandas as pd
from statsmodels.tsa.statespace.structural import UnobservedComponents

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
ts = (f[(f['City'] == '札幌市') & (f['Month'] != '年')]['CN100']
      .astype(float).reset_index(drop=True))

ucm = UnobservedComponents(ts, level='local linear trend', seasonal=4)
fit = ucm.fit(disp=False)
print(fit.summary())
📥 入力例: 札幌市の月平均気温 12 点(SSDSE-F、CN100、01月〜12月)。
📤 実行例(実測) Unobserved Components Results ==================================================================================== Dep. Variable: CN100 No. Observations: 12 Model: local linear trend Log Likelihood -18.252 + stochastic seasonal(4) AIC 44.505 Date: Sat, 29 Aug 2026 BIC 44.288 Time: 10:13:32 HQIC 41.831 Sample: 0 - 12 Covariance Type: opg ==================================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------------ sigma2.irregular 8.8e-11 6.940 1.27e-11 1.000 -13.602 13.602 sigma2.level 7.134e-11 15.849 4.5e-12 1.000 -31.064 31.064 sigma2.trend 6.0026 10.867 0.552 0.581 -15.297 27.302 sigma2.seasonal 2.93e-11 1.999 1.47e-11 1.000 -3.918 3.918 =================================================================================== Ljung-Box (L1) (Q): 0.15 Jarque-Bera (JB): 0.69 Prob(Q): 0.70 Prob(JB): 0.71 Heteroskedasticity (H): 1.05 Skew: 0.70 Prob(H) (two-sided): 0.98 Kurtosis: 2.36 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step).
💬 読み方: 4 つの分散のうち 0 でないのは sigma2.trend = 6.0026 だけで、irregular・level・seasonal は 1e-11 程度、つまり 0 に張り付いた。12 点の動きはすべて「傾きが毎月ゆらぐトレンド」で説明され、観測ノイズも季節成分も要らないと判定されたことになる。その sigma2.trend も p = 0.581 と不確かで、分散を 4 つ推定するには系列長が足りない。

🔮 8. Prophet(Meta)

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

🎯 解説: Prophet(Meta 製)に札幌の月平均気温 12 点を渡し、トレンドなし・年周期だけのモデルで次の 12 か月を予測して平年値と並べる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温(平年値 12 点)。Prophet は日付列 ds が要るので、
# 1 月〜12 月を仮に 2023 年の各月 1 日に割り当てる。
import pandas as pd
import matplotlib.pyplot as plt
from prophet import Prophet

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
ts = (f[(f['City'] == '札幌市') & (f['Month'] != '年')]['CN100']
      .astype(float).reset_index(drop=True))

df_p = pd.DataFrame({'ds': pd.date_range('2023-01-01', periods=12, freq='MS'),
                     'y': ts.values})
# 1 年分しか無いのでトレンドは置かず(growth='flat')、
# 年周期のフーリエ次数も 3 に抑える(既定の 10 だと 12 点に過剰適合して予測が暴れる)
m = Prophet(growth='flat', yearly_seasonality=3,
            weekly_seasonality=False, daily_seasonality=False)
m.fit(df_p)
future = m.make_future_dataframe(periods=12, freq='MS')
fcst = m.predict(future)
out = fcst[['ds', 'yhat']].tail(12).assign(平年値=ts.values)
print(out.round(1).to_string(index=False))
m.plot(fcst)
plt.show()
📥 入力例: ds = 2023-01-01〜2023-12-01 の各月 1 日(仮の日付)、y = 札幌市の月平均気温の平年値 12 点(SSDSE-F、CN100)。
📤 実行例(実測) ds yhat 平年値 2024-01-01 -3.4 -3.2 2024-02-01 -2.7 -2.7 2024-03-01 1.4 1.1 2024-04-01 7.5 7.3 2024-05-01 12.8 13.0 2024-06-01 17.4 17.0 2024-07-01 21.0 21.1 2024-08-01 22.1 22.3 2024-09-01 18.6 18.6 2024-10-01 11.9 12.1 2024-11-01 4.7 5.2 2024-12-01 -0.6 -0.9
💬 読み方: 2024 年の予測は 1 月 -3.4℃、8 月 22.1℃、12 月 -0.6℃ で、どの月も平年値との差は 0.5℃ 以内。growth='flat' でトレンドを止め、年周期のフーリエ次数を 3 に抑えたので、1 年分の山の形をそのまま翌年に写した予測になっている。既定のまま(線形トレンドあり・フーリエ項 20 本)で 12 点に当てると過剰適合し、予測は 70℃ 台まで暴れる。

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

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

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

🎯 解説: 札幌の月平均気温と平均相対湿度の 2 系列に VAR(ベクトル自己回帰)を当てはめ、AIC でラグ次数(最大 2)を選んで係数を推定する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ── この抜粋で使う 2 本の系列を用意します ──
# 札幌市の月平均気温(CN100)と平均相対湿度(CN220)。どちらも 12 点なので
# maxlags は系列長に見合う小さい値にする。
import pandas as pd
from statsmodels.tsa.api import VAR

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s['CN100'].astype(float).reset_index(drop=True)    # 平均気温
ts2 = s['CN220'].astype(float).reset_index(drop=True)   # 平均相対湿度

multi_ts = pd.DataFrame({'temp': ts, 'humidity': ts2})
var = VAR(multi_ts).fit(maxlags=2, ic='aic')
print(var.summary())
📥 入力例: 札幌市の 01月〜12月 の 2 列(CN100 = 月平均気温 ℃、CN220 = 平均相対湿度 %)、各 12 点。
📤 実行例(実測) Summary of Regression Results ================================== Model: VAR Method: OLS Date: Sat, 29, Aug, 2026 Time: 10:13:32 -------------------------------------------------------------------- No. of Equations: 2.00000 BIC: 1.81069 Nobs: 10.0000 HQIC: 1.17617 Log likelihood: -25.9193 FPE: 5.50322 AIC: 1.50811 Det(Omega_mle): 2.44587 -------------------------------------------------------------------- Results for equation temp ============================================================================== coefficient std. error t-stat prob ------------------------------------------------------------------------------ const 0.445068 7.983559 0.056 0.956 L1.temp 1.650254 0.095632 17.256 0.000 L1.humidity 0.194488 0.132484 1.468 0.142 L2.temp -0.956973 0.085927 -11.137 0.000 L2.humidity -0.158191 0.119268 -1.326 0.185 ============================================================================== Results for equation humidity ============================================================================== coefficient std. error t-stat prob ------------------------------------------------------------------------------ const 68.601071 11.130423 6.163 0.000 L1.temp 0.172027 0.133327 1.290 0.197 L1.humidity 0.807313 0.184705 4.371 0.000 L2.temp 0.114520 0.119796 0.956 0.339 L2.humidity -0.851480 0.166279 -5.121 0.000 ============================================================================== …
💬 読み方: AIC はラグ 2 を選び、使えた観測は Nobs = 10。気温の式では L1.temp 1.650・L2.temp -0.957 がともに p < 0.001 で、湿度のラグ(p = 0.142・0.185)は気温の予測に効いていない。湿度の式も自分の過去(L1 0.807・L2 -0.851)だけが効いている。10 点に 1 式 5 係数を当てた結果なので、2 系列の間に影響の向きがあると言える材料にはならない。

📊 10. 予測評価指標

TimeSeriesSplit でのバックテスト

🎯 解説: TimeSeriesSplit で「過去で学習 → 直後を予測」を 3 回くり返し、ARIMA(1,1,1) の予測誤差(MAE)を平均する。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温は 12 点しかないので、n_splits と test_size は
# 系列長に見合う小さい値にする(test_size=12 だと分割できない)。
import pandas as pd
import numpy as np
from statsmodels.tsa.arima.model import ARIMA
from sklearn.model_selection import TimeSeriesSplit

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s['CN100'].astype(float).reset_index(drop=True)

tscv = TimeSeriesSplit(n_splits=3, test_size=2)
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}')
📥 入力例: 札幌市の月平均気温 12 点。n_splits=3・test_size=2 なので、学習 6・8・10 か月 → 直後の 2 か月を予測する。
📤 実行例(実測) CV MAE: 2.603
💬 読み方: 平均 MAE 2.603℃ の中身は fold ごとに大きく違い、7〜8 月の予測は 0.79℃、11〜12 月は 0.83℃ しか外れていないのに、9〜10 月は 6.19℃ 外れた。8 月までで学習したモデルは上り坂が続くと見て 9・10 月を 21.7・21.3℃ と予測したが、実際は 18.6・12.1℃ に下がった。平均だけを報告すると、折り返し点で大きく外すという性質が見えなくなる。

📊 11. モデル比較

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

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

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

🔬 時系列分析の三層理解 — 数理・実装・運用を貫く視点(R498 補強)

時系列分析を本当に使いこなすには、 三つの層を行き来できる視点が要る。 第一層は確率過程としての数理(定常性・自己相関構造・スペクトル)、 第二層は推定アルゴリズムと実装(最尤・状態空間・ベイズ)、 第三層は運用(モデル監視・データドリフト・再学習)。 三層を分離して教える教科書は多いが、 実務で困るのは三層が連動するから。 例えば「予測精度が落ちた」原因が、 第三層のデータドリフトに見えても、 第一層の潜在的な非定常性を捉え損ねていた、 という事態は珍しくない。 本節では SSDSE-B-2026 の出生数(都道府県別 2012-2023 年系列を全国に集計したもの)を例として、 三層を貫く実例を提示する。

第一層: 確率過程としての時系列

時系列 $\{Y_t\}_{t \in \mathbb{Z}}$ を、 確率変数の族として捉える。 観測値はその1 つの実現に過ぎない。 ここが回帰分析と決定的に異なる点で、 「同じ条件で何度も観測できる」前提が無い。 ゆえに、 1 本の時系列から母過程の性質を推定するには、 定常性(時刻によらず分布が同じ)とエルゴード性(時間平均が空間平均に等しい)の二つを仮定する必要がある。 この仮定が崩れると、 標本平均は母平均を推定しない(バイアスを持つ)。

弱定常 (weak stationary) は、 (1) $E[Y_t]$ が一定、 (2) $Var[Y_t]$ が一定、 (3) $Cov(Y_t, Y_{t+h})$ が $h$ のみに依存、 の三条件で定義される。 出生数のような社会指標は明らかに非定常(少子化トレンド)なので、 そのまま AR(p) を当てはめても係数は不安定。 通常は差分 $\nabla Y_t = Y_t - Y_{t-1}$ を取って一階差分系列を定常化し、 ADF 検定(帰無仮説: 単位根あり)で確認する。

第二層: 推定アルゴリズム

ARIMA(p, d, q) のパラメータ推定は、 通常は条件付き最尤法 (CMLE)か状態空間表現での Kalman filter + MLが使われる。 statsmodels の SARIMAX は後者で、 欠損値や外生変数の取り扱いも統一的にできる。 一方、 Prophet は加法分解モデル + STAN/L-BFGSでベイズ的に MAP 推定する。 LSTM のような深層学習はBPTT (Backpropagation Through Time)で勾配を伝播し、 Adam で最適化する。 アルゴリズムが違うので、 同じ系列に当てても予測結果は微妙に異なる。

第三層: 運用

本番運用では、 (a) 入力データの遅延・欠損監視、 (b) 予測誤差(MAPE 等)の時系列監視、 (c) 入力分布のドリフト検出(KS 統計量・PSI)、 (d) 再学習トリガー、 (e) ロールバック手順、 の 5 点を SLA として明文化する。 これが無いと、 ある日突然予測が外れ始めた時に「モデルが悪いのか、 データが悪いのか」が切り分けられず、 対応が後手に回る。

層 中心概念 代表的ツール 失敗時の症状
第一層 (数理)定常性・自己相関・スペクトルADF 検定・ACF/PACF・周期図回帰係数が不安定、 予測区間が極端に広い
第二層 (実装)推定アルゴリズム・収束statsmodels・Prophet・PyTorch最尤推定の非収束、 局所解、 数値発散
第三層 (運用)監視・再学習・SLAAirflow・MLflow・Evidently AI予測ドリフト、 計算遅延、 業務影響

図 1: 出生数全国系列の基本散布図(年×出生数)

年度と全国出生数の点プロット(2012〜2023 年度)

横軸を年度、 縦軸を全国出生数(SSDSE-B-2026 の 47 都道府県の合計)とした散布図。 線で繋ぐ前にまず点として可視化することで、 系列の形と外れ年が一目で見える。 2012 年度の 103.7 万人から 2023 年度の 72.7 万人へ 11 年で 29.9% 減り、 前年より増えたのは 2015 年度(+2,124 人)だけ。 減り方は一定ではなく、 2019 年度(−53,149 人、 −5.8%)と 2022〜2023 年度(−5.0%、 −5.6%)で急になっている。 SSDSE-B-2026 は 2012 年度からなので、 それ以前(たとえば東日本大震災の 2011 年)は含まれない。

図 2: 出生数の年次変動ヒストグラム

都道府県別の出生数の前年度比のヒストグラム

差分 $\nabla Y_t = Y_t - Y_{t-1}$ を県の規模に依らず比べるため、 前年度比 $\nabla Y_t / Y_{t-1}$(%)にして 47 都道府県 × 11 期 = 517 個の分布を描いた。 中央が左寄り(マイナス側)になっていれば、 全期間を通じて減少傾向が支配的だと分かる。 実際、 中央値は −3.47%(平均 −3.53%)で、 前年より増えたのは 517 個中 37 個(7.2%)だけ。 山は −5〜−2% に集まるほぼ左右対称の形で、 左端の −13.0% は 2023 年度の鳥取県、 右端の +5.6% は 2013 年度の福島県。 人口の少ない県ほど前年度比が大きく振れやすい点も、 時系列モデルの誤差の大きさを考えるときの手がかりになる。

図 3: 地域ブロック別の出生数分布(箱ひげ図)

7 地方別の出生数の 2012→2023 年度変化率の箱ひげ図

出生数の水準は県の人口規模で決まってしまうので、 ここでは 2012→2023 年度の変化率(%)を 7 地方ごとに箱ひげで比較した(灰色の点が各都道府県)。 全国は −29.9% だが、 北海道・東北は中央値 −37.3%(秋田県 −44.8% が全国最大の減少)、 関東は中央値 −28.5% で東京都(−19.6%、 減少が最小)から栃木県(−37.7%)まで幅が広い。 平均水準だけでなくばらつきの違いも見え、 「全国の系列 1 本」 では地方ごとの減り方の違いが消えてしまう。 時系列モデリングを始める前に、 こうしたパネル構造を確認する習慣を付けたい。

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

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

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

① データ準備

🎯 解説: SSDSE-B-2026 を 2 段見出しのまま読み込み、東京都の総人口(A1101)を年度順に並べた千人単位の年次系列 y を作る。
📥 入力例(SSDSE-B-2026 全体:564 行 × 112 列 = 47 都道府県 × 2012〜2023 年) 年度 地域コード 都道府県 A1101(総人口) A1303(65歳以上人口) A4101(出生数) … 2023 R01000 北海道 5,092,000 1,681,000 24,430 … 2023 R13000 東京都 14,086,000 3,205,000 86,348 … 2023 R47000 沖縄県 1,468,000 350,000 12,549 … …(残り 112 列は住宅・家計・教育・医療など)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='shift_jis', header=[0,1])
# 2 段見出しは (英字コード, 日本語名) の順なので、結合すると 'Prefecture_都道府県' になる
df.columns = ['_'.join(c).strip() for c in df.columns]
df['SSDSE-B-2026_年度'] = pd.to_numeric(df['SSDSE-B-2026_年度'], errors='coerce')
tokyo = (df[df['Prefecture_都道府県'] == '東京都']
           .set_index('SSDSE-B-2026_年度').sort_index())
y = pd.to_numeric(tokyo['A1101_総人口'], errors='coerce') / 1000  # 千人単位
print(y.head())
# 2012 年: 13,234 千人
# 2018 年: 13,887 千人
# 2023 年: 14,086 千人 — 緩やかに増加
📤 実行例(実測) SSDSE-B-2026_年度 2012 13234.000 2013 13307.000 2014 13399.000 2015 13515.271 2016 13646.000 Name: A1101_総人口, dtype: float64
💬 読み方: 2012 年度 13,234 千人から毎年 7 万〜13 万人ずつ増えている。2015 年度だけ 13515.271 と端数が付くのは国勢調査の確定値(13,515,271 人)だからで、同じく国勢調査年の 2020 年度も 14,047.594 になる。系列は 2020 年度のあと 2021 年度に 14,010 千人へ一度減っていて、以下の STL・ADF・ARIMA はこの 12 点を使う。

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

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

🎯 解説: 東京都の総人口(年次 12 点)を STL(period=2、robust=True)でトレンドと残差に分け、両端のトレンド値と残差の標準偏差を出す。
1
2
3
4
5
6
7
from statsmodels.tsa.seasonal import STL
# 年次データには季節周期が無いが、STL は period を必ず必要とする。
# period=None だと「周期を決められない」と怒られるので、最小の 2 を渡す。
stl = STL(y, period=2, robust=True).fit()
print(f'トレンド成分: {y.index[0]:.0f}={stl.trend.iloc[0]:.0f}, '
      f'{y.index[-1]:.0f}={stl.trend.iloc[-1]:.0f}')
print(f'残差 SD: {stl.resid.std():.1f}')
📥 入力例: 上で作った y(東京都の総人口、2012〜2023 年度、千人)。
📤 実行例(実測) トレンド成分: 2012=13218, 2023=14058 残差 SD: 21.5
💬 読み方: トレンドは 2012 年度 13,218 千人から 2019 年度 13,975 千人まで年 10 万人前後ずつ上がり、2020 年度以降は 14,058 千人で横ばいになった。残差 SD は 21.5 千人だが、ほとんどの年は 0 近くで、2021 年度の -61.5 千人と 2019 年度の +25.3 千人が大半を占める。robust=True が 2021 年度の減少を外れ値として扱った結果で、period=2 の季節成分(±20 千人以内のジグザグ)に意味はない。

実測では、トレンドは 13,218 千人(2012 年度)から上がって 2020 年度以降は 14,058 千人で横ばい、残差の標準偏差は 21.5 千人(総人口の 0.2% 未満)。

③ 定常性検定(ADF)

🎯 解説: 東京都の総人口(年次 12 点)に ADF 検定をかけ、原系列と 1 階差分で単位根の有無を比べる。
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.605, p = 0.9877 → 単位根を棄却できない(非定常)

# 1階階差で再検定
res2 = adfuller(y.diff().dropna())
print(f'diff ADF stat = {res2[0]:.3f}, p = {res2[1]:.4f}')
# → ADF = -3.047, p = 0.0307 → 5% で棄却 → I(1) → ARIMA(p,1,q) 候補
📥 入力例: y(東京都の総人口、2012〜2023 年度の 12 点、千人)。差分系列は 11 点。
📤 実行例(実測) ADF stat = 0.605, p = 0.9877 diff ADF stat = -3.047, p = 0.0307
💬 読み方: 原系列は統計量 0.605・p = 0.9877 で単位根を棄却できず、1 階差分は -3.047・p = 0.0307 で 5% 水準なら棄却できる。そこで d = 1 の ARIMA(p,1,q) を候補にするのが次の④。ただし 11 点の ADF は検出力が低く p = 0.03 も境界に近いので、KPSS も併せて見ておく。

④ ARIMA モデル推定と予測

🎯 解説: 東京都の総人口に ARIMA(1,1,0) を当てはめ、2024〜2028 年度の 5 期先予測と 95% 予測区間を出す。
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.85, σ² ≈ 1600

forecast = model.get_forecast(steps=5)  # 2024-2028 を予測(出力の 12〜16 行目)
ci = forecast.conf_int(alpha=0.05)
print(forecast.predicted_mean)
print(ci)
# 2024: 14,127 千人 [14,048, 14,205]
# 2028: 14,236 千人 [13,774, 14,697](先になるほど区間が広がる)
📥 入力例: y(東京都の総人口 12 点、千人)。index が日付ではないので、予測の行番号は 12〜16(= 2024〜2028 年度)になる。
📤 実行例(実測) SARIMAX Results ============================================================================== Dep. Variable: A1101_総人口 No. Observations: 12 Model: ARIMA(1, 1, 0) Log Likelihood -56.712 Date: Sat, 26 Sep 2026 AIC 117.424 Time: 15:41:46 BIC 118.220 Sample: 0 HQIC 116.922 - 12 Covariance Type: opg ============================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------ ar.L1 0.8464 0.189 4.476 0.000 0.476 1.217 sigma2 1603.3887 1221.946 1.312 0.189 -791.581 3998.358 =================================================================================== Ljung-Box (L1) (Q): 0.48 Jarque-Bera (JB): 2.37 Prob(Q): 0.49 Prob(JB): 0.31 Heteroskedasticity (H): 3.77 Skew: -1.13 Prob(H) (two-sided): 0.23 Kurtosis: 3.16 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step). 12 14126.625812 13 14161.010324 14 14190.112380 15 14214.743519 16 14235.590602 Name: predicted_mean, dtype: float64 lower A1101_総人口 upper A1101_総人口 12 14048.144275 14205.107349 13 13996.216173 14325.804474 14 13930.095637 14450.129123 15 13854.931511 14574.555526 16 13774.098587 14697.082617
💬 読み方: ar.L1 = 0.846 は「前年の増加幅の 85% ほどが翌年も続く」という意味で、予測は 2024 年度 14,127 千人から 2028 年度 14,236 千人へ、年ごとの増え幅を 34 → 21 千人と縮めながら伸びる。95% 区間の半幅は 1 期先の ±78 千人から 5 期先の ±462 千人へ 6 倍近く広がる。sigma2 の p = 0.189 が示すとおり 12 点では誤差分散の推定も粗く、区間は目安にとどめる。

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

🎯 解説: 同じ東京都の総人口に Holt の線形トレンド指数平滑(季節なし)を当てはめ、平滑化係数 α・β と 5 期先予測を出して ARIMA と比べる。
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))  # α=β=1 なので、直近 1 年の増分(+48 千人)をそのまま延ばした値になる
📥 入力例: y(東京都の総人口 12 点、千人)。
📤 実行例(実測) α (level) = 1.000 β (trend) = 1.000 12 14134.000000 13 14182.000000 14 14230.000001 15 14278.000001 16 14326.000002 dtype: float64
💬 読み方: α と β がともに 1.000 に張り付き、「最新の値と最新の増分だけを信じる」設定になった。そのため予測は 2023 年度の 14,086 千人に 2022→2023 年度の増分 48 千人を毎年足した直線(14,134 → 14,326)で、5 期先は ARIMA(1,1,0) の 14,236 千人より 90 千人高い。直近 1 年の動きをどこまで延ばすかで、5 年先の値がこれだけ変わる。

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

数式だけでは「分かった気になる」だけで終わりがち。 ここで SSDSE-B-2026(教育用標準データセット — 47 都道府県 × 100+ 指標、 2012-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.045$/年 と推定でき、 SARIMA(1,1,1)(0,1,1) で 2024 年の値を予測する流れが典型。

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

🧮 SSDSE-B-2026 を使った時系列分析実演(R498 補強)

SSDSE-B-2026 は都道府県 × 年 × 各種指標のパネルデータ。 ここから全国を集計した出生数時系列を作り、 第一層から第三層までの分析を順に実行する。

ステップ 1: データロードと前処理

このコードでやること: SSDSE-B-2026 を読み込み、 年ごとの全国出生数を集計して時系列 DataFrame を作る。

📥 入力データ (SSDSE-B-2026 抜粋):

SSDSE-2026 年度 都道府県コード 都道府県 出生数 ... SSDSE-B-2026 2012 R01000 北海道 38686 ... SSDSE-B-2026 2012 R13000 東京都 107401 ... ... SSDSE-B-2026 2023 R47000 沖縄県 12549 ...
1
2
3
4
5
6
7
8
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
# 列名サンプル: 年度, 都道府県コード, 都道府県, 総人口, 出生数, ...
ts = df.groupby('年度')['出生数'].sum().reset_index()
ts.columns = ['year', 'births']
print(ts.tail(5))

📤 実行例:

year births 7 2019 865212 8 2020 840808 9 2021 811611 10 2022 770750 11 2023 727269

💬 全国出生数が単調減少。 2012 → 2023 で約 30% 減。 これだけで「日本の少子化は加速している」と裏付けられる。

ステップ 2: 定常性の検定 (ADF テスト)

このコードでやること: 出生数の原系列と一階差分系列それぞれに ADF 検定を適用し、 単位根の存在を確認する。

1
2
3
4
5
6
7
8
from statsmodels.tsa.stattools import adfuller

stat_raw, p_raw, *_ = adfuller(ts['births'])
print(f'原系列 ADF stat={stat_raw:.3f}, p={p_raw:.3f}')

diff = ts['births'].diff().dropna()
stat_d, p_d, *_ = adfuller(diff)
print(f'差分系列 ADF stat={stat_d:.3f}, p={p_d:.3f}')

📤 実行例:

原系列 ADF stat=18.457, p=1.000 差分系列 ADF stat=-2.693, p=0.075

💬 原系列は p=1.000 で単位根を棄却できない → 非定常。 差分系列は p=0.075 で、 5% 水準では棄却できないが 10% 水準では定常と見なせる。 トレンド除去の観点から d=1 でのモデリングが妥当。

📝 より正確な分析: 年次データは 2012–2023 の 12 点のみで ADF 検定の検出力が低い。 一階差分系列の p=0.075 は 5% 有意水準では単位根を棄却できず(10% では棄却可能)、 「差分すれば厳密に定常」と断定はできない。 実務では KPSS 検定の併用や、 トレンド項付きモデルで頑健性を確認するのが望ましい。

ステップ 3: ARIMA(1,1,1) モデルの当てはめと予測

このコードでやること: ARIMA(1,1,1) を学習し、 翌年 (2023) の出生数を予測する。

1
2
3
4
5
6
7
8
9
from statsmodels.tsa.arima.model import ARIMA

model = ARIMA(ts[ts['year'] <= 2022]['births'], order=(1, 1, 1))  # 2023 をホールドアウト
result = model.fit()
fc = result.get_forecast(steps=1)
mean = fc.predicted_mean.iloc[0]
ci = fc.conf_int(alpha=0.05).iloc[0]
print(f'2023 年予測: {mean:,.0f} 人')
print(f'95% 予測区間: [{ci.iloc[0]:,.0f}, {ci.iloc[1]:,.0f}]')

📤 実行例:

2023 年予測: 756,080 人 95% 予測区間: [721,418, 790,742]

💬 厚生労働省人口動態統計の 2023 年実数は約 727,277 人。 予測 756,080 人は実数を約 +4% 上回るが、 95% 予測区間 [721,418, 790,742] は実数を含む。 これは学習データに 2023 年を含めず 2022 年までで学習した正しいホールドアウト設定での「素朴な ARIMA」で、 これだけ近い数字が出るのは出生数の強い単調減少傾向のおかげ。 構造変化があれば崩れる。

ステップ 4: 残差診断

このコードでやること: 残差の Ljung-Box 検定で自己相関が残っていないかを確認する。

1
2
3
4
from statsmodels.stats.diagnostic import acorr_ljungbox

lb = acorr_ljungbox(result.resid, lags=[3, 6], return_df=True)
print(lb)

📤 実行例:

lb_stat lb_pvalue 3 0.006813 0.999851 6 0.183056 0.999881

💬 ラグ 3, 6 とも p > 0.5 で帰無仮説(自己相関なし)を棄却できない。 残差は白色雑音と見なせ、 モデルは妥当。

ステップ 5: パネル時系列としての視点

このコードでやること: 47 都道府県それぞれの出生数減少率を計算し、 ばらつきを見る。

1
2
3
4
pivot = df.pivot_table(index='年度', columns='都道府県', values='出生数')
rate = (pivot.iloc[-1] / pivot.iloc[0] - 1) * 100  # 2012→2023 変化率
print(rate.sort_values().head(5))
print(rate.sort_values().tail(5))

📤 実行例:

秋田県 -44.8 岩手県 -41.4 静岡県 -38.4 青森県 -37.9 栃木県 -37.7 ... 沖縄県 -26.5 埼玉県 -26.1 福岡県 -25.9 大阪府 -24.3 東京都 -19.6

💬 全国では約 -30% だが、 県別ではばらつきが大きい。 東北・北関東は -38〜-45%、 都市部(東京・大阪など)は -20〜-26% 台。 単純な全国時系列モデルでは、 こうした地域異質性を見落とす。 パネル時系列モデル(固定効果 + 時系列構造)が必要になる場面。

📊 主要時系列モデルの比較表(R498 補強)

「どのモデルを使えばよいか」は、 データの長さ・季節性の有無・外生変数の有無・解釈性要件で決まる。 以下の比較表は、 出生数のような中小規模社会データを念頭に、 実務的な選択基準をまとめたもの。

モデル 最小データ数 季節性 外生変数 解釈性 学習速度 代表ライブラリ
単純移動平均5+××◎◎pandas .rolling()
指数平滑 (Holt-Winters)20+○ (加法/乗法)×○◎statsmodels ETSModel
ARIMA(p,d,q)30+×△○○statsmodels ARIMA
SARIMA(p,d,q)(P,D,Q,s)2 季節以上◎△ (SARIMAX)○○statsmodels SARIMAX
状態空間モデル30+○ (状態として埋め込み)◎○△statsmodels UnobservedComponents
Prophet60+ (推奨)◎ (週・年)○ (regressor)○○prophet
VAR (多変量)100+ × 変数数×○△○statsmodels VAR
LSTM/GRU500+○ (暗黙)◎×△PyTorch / TensorFlow
Transformer (Informer 等)5,000+◎ (位置符号化で)◎××PyTorch (専用実装)
勾配ブースティング (LightGBM)500+△ (lag 特徴で)◎△◎LightGBM, XGBoost

出生数のように 12 観測しかない短い系列では、 LSTM や Transformer は過学習するのが当たり前で、 ARIMA や指数平滑が現実解。 「最新の深層学習を使いたい」気持ちは分かるが、 データ量と相談すること。

🧮 数式に値を入れて手で計算する: 時系列のグループ別集計

合成 2 店舗 × 3 日売上で店舗別合計を計算する。

Step 1: データ

日店舗売上
1A100
1B80
2A110
2B90
3A120
3B85

Step 2: 店舗別合計

A: 100+110+120 = 330 B: 80+90+85 = 255

🐍 Python で再現

1
2
3
import pandas as pd
df = pd.DataFrame({'日':[1,1,2,2,3,3], '店舗':['A','B','A','B','A','B'], '売上':[100,80,110,90,120,85]})
print(df.groupby('店舗')['売上'].sum())

📤 実行結果

店舗 A 330 B 255

💬 手計算 (Step 2) と Python 出力が完全一致。

🐍 15. ライブラリ早見表

🐍 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 で「ホワイトノイズ」と判断可。

🎯 解説: 札幌の月平均気温に ARIMA(1,1,1) を当てはめ、残差に自己相関が残っていないかを Ljung–Box 検定(ラグ 5)で調べる。
📥 入力例: SSDSE-F-2023v3.csv の札幌市・01月〜12月の月平均気温(CN100)12 点。残差も 12 点(1 点目は差分の初期値)。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
# ── この抜粋で使う当てはめ結果を用意します ──
# 札幌市の月平均気温 12 点。lags は残差の長さ未満にする。
import pandas as pd
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.stats.diagnostic import acorr_ljungbox

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = f[(f['City'] == '札幌市') & (f['Month'] != '年')]
ts = s['CN100'].astype(float).reset_index(drop=True)
fit = ARIMA(ts, order=(1, 1, 1)).fit()

lb = acorr_ljungbox(fit.resid, lags=[5], return_df=True)
print(lb)
📤 実行例(実測) lb_stat lb_pvalue 5 3.796622 0.579053
💬 読み方: ラグ 5 までの Ljung–Box 統計量 3.80・p = 0.579 で「自己相関なし」を棄却できない。ただし残差は 12 点しかなく、1 点目の -3.2 は 1 月の値がそのまま残差に入ったもの。検出力の低い検定で棄却できなかっただけなので、「モデルが十分」とまでは言えない。

残差プロット

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

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

🎯 解説: statsmodels の代表的な時系列クラス(ARIMA・SARIMAX・ExponentialSmoothing)を読み込むだけの確認用コード。
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・状態空間モデルが揃う
📥 入力例: データは読まない(import だけ)。
📤 実行例(実測) このブロックは標準出力を出さない(import だけ)。
💬 読み方: 3 つとも statsmodels.tsa 以下にあり、このページの ARIMA・SARIMA・Holt の例はすべてこの 3 クラスで書いている。ここの import が通れば、上のブロックは追加インストールなしで動く。

(B) pmdarima — auto_arima の決定版

🎯 解説: pmdarima の auto_arima で、東京都の総人口 y に合う非季節 ARIMA の次数 (p,d,q) を AIC 最小で自動探索する。
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 最小で自動探索
📥 入力例: y(東京都の総人口、2012〜2023 年度の 12 点、千人)。
📤 実行例(実測。pmdarima 2.1.1・statsmodels 0.14.5 で実行。Date・Time の行は実行時刻) Performing stepwise search to minimize aic ARIMA(2,1,2)(0,0,0)[0] intercept : AIC=121.323, Time=0.02 sec ARIMA(0,1,0)(0,0,0)[0] intercept : AIC=121.481, Time=0.00 sec ARIMA(1,1,0)(0,0,0)[0] intercept : AIC=117.652, Time=0.01 sec ARIMA(0,1,1)(0,0,0)[0] intercept : AIC=inf, Time=0.01 sec ARIMA(0,1,0)(0,0,0)[0] : AIC=132.801, Time=0.00 sec ARIMA(2,1,0)(0,0,0)[0] intercept : AIC=118.103, Time=0.01 sec ARIMA(1,1,1)(0,0,0)[0] intercept : AIC=117.085, Time=0.01 sec ARIMA(2,1,1)(0,0,0)[0] intercept : AIC=inf, Time=0.01 sec ARIMA(1,1,2)(0,0,0)[0] intercept : AIC=119.074, Time=0.02 sec ARIMA(0,1,2)(0,0,0)[0] intercept : AIC=inf, Time=0.01 sec ARIMA(1,1,1)(0,0,0)[0] : AIC=117.678, Time=0.01 sec Best model: ARIMA(1,1,1)(0,0,0)[0] intercept Total fit time: 0.117 seconds SARIMAX Results ============================================================================== Dep. Variable: y No. Observations: 12 Model: SARIMAX(1, 1, 1) Log Likelihood -54.542 Date: Sat, 26 Sep 2026 AIC 117.085 Time: 21:57:27 BIC 118.677 Sample: 0 HQIC 116.082 - 12 Covariance Type: opg ============================================================================== coef std err z P>|z| [0.025 0.975] ------------------------------------------------------------------------------ intercept 46.5517 29.749 1.565 0.118 -11.755 104.858 ar.L1 0.3069 0.367 0.835 0.404 -0.413 1.027 ma.L1 0.8468 0.693 1.221 0.222 -0.512 2.206 sigma2 955.9917 1137.497 0.840 0.401 -1273.461 3185.444 =================================================================================== Ljung-Box (L1) (Q): 0.08 Jarque-Bera (JB): 1.56 Prob(Q): 0.77 Prob(JB): 0.46 Heteroskedasticity (H): 4.02 Skew: -0.76 Prob(H) (two-sided): 0.21 Kurtosis: 1.94 =================================================================================== Warnings: [1] Covariance matrix calculated using the outer product of gradients (complex-step).
💬 読み方: auto_arima は定数(intercept)付きの ARIMA(1,1,1) を AIC 117.085 で選んだが、2 位の定数付き ARIMA(1,1,0)(117.652)や定数なしの ARIMA(1,1,1)(117.678)との差は 0.6 以下しかない。④で手で当てた ARIMA(1,1,0) の AIC 117.424 との差も 0.34 で 2 に届かないので、この 12 点からはどれが良いとは言えず、単純な方を選んでよい。ar.L1・ma.L1 の P>|z| も 0.40・0.22 で、係数の意味を読むには点数が足りない。

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

🎯 解説: Prophet で東京都の総人口(年次 12 点)の 5 年先までを予測し、予測値 yhat と不確実性区間を出す。
1
2
3
4
5
6
7
8
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)
# ds は各年の 1 月 1 日なので、未来も年初('YS')で刻む。'Y' だと年末になり、
# 最初の予測点が 2023-12-31(最後の観測 2023-01-01 と同じ年)になってしまう
future = m.make_future_dataframe(periods=5, freq='YS')
forecast = m.predict(future)
print(forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(5))
📥 入力例: ds = 2012〜2023 年の各 1 月 1 日、y = 東京都の総人口(千人)。
📤 実行例(実測) ds yhat yhat_lower yhat_upper 12 2024-01-01 14082.522184 14061.185283 14100.726561 13 2025-01-01 14097.419039 14053.192658 14136.586362 14 2026-01-01 14112.275193 14031.475178 14183.564561 15 2027-01-01 14127.131347 14001.299556 14242.770132 16 2028-01-01 14141.987501 13962.516130 14305.265613 (yhat_lower・yhat_upper はサンプリングで求めるので、実行ごとに少し変わる)
💬 読み方: 予測は 2024 年 14,083 千人から 2028 年 14,142 千人へ年 15 千人ずつ増える直線で、ARIMA(1,1,0)(2028 年度 14,236)や Holt(14,326)よりずっと緩い。Prophet が 2020 年以降の頭打ちを傾きの変化として取り込んだためで、2024 年の予測は 2023 年度の実測 14,086 千人をわずかに下回る。区間は Prophet の既定の 80%(interval_width=0.8)なので、ARIMA の 95% 区間と幅をそのまま比べない。

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

🎯 解説: sktime の ARIMA(order=(1,1,0))で、東京都の総人口の最後の 3 年を検証用に取り分け、残り 9 年で学習して 3 期先を予測する。
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(東京都の総人口 12 点、千人)。temporal_train_test_split で学習 9 点(2012〜2020 年度)・検証 3 点(2021〜2023 年度)に分ける。
📤 実行例: sktime はこの教材の実行環境に入っていないので実測はない(pip install sktime が必要)。
💬 読み方: temporal_train_test_split は並びを保ったまま末尾を検証に回すので、未来の値で学習してしまう漏れが起きない。この分け方だと学習データは 2020 年度の 14,048 千人で終わり、検証の最初の 2021 年度は 14,010 千人に減っているので、増加の続きを予測するモデルは 2021 年度で外れやすい。

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

🎯 解説: Darts の TimeSeries に東京都の総人口を包み、ExponentialSmoothing で 5 期先を予測する。
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 など深層学習モデルも同じインターフェース
📥 入力例: y(東京都の総人口 12 点、千人)。
📤 実行例: darts はこの教材の実行環境に入っていないので実測はない(pip install darts が必要)。
💬 読み方: Darts の ExponentialSmoothing は内部で statsmodels の Holt-Winters を呼んでいる。Darts の利点は NBEATSModel などの深層学習モデルも fit・predict の同じ書き方で差し替えられる点だが、12 点の系列で深層学習モデルを学習させても意味のある比較にはならない。

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

🎯 解説: scikit-learn の TimeSeriesSplit で、東京都の総人口 12 点を 5 回の「過去で学習 → 直後で評価」に分ける枠組みだけを書く。
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 と違い「過去→未来」順を維持
📥 入力例: y(東京都の総人口 12 点、千人)。n_splits=5 なので各 fold の検証は 2 点、学習は 2・4・6・8・10 点。
📤 実行例(実測) このブロックは標準出力を出さない(図を描く・変数を定義するだけ)。
💬 読み方: 12 点を 5 分割すると、最初の fold は学習 2 点(2012・2013 年度)で 2014・2015 年度を当てることになり、ARIMA などはまともに推定できない。ループの中身(学習と評価)は書いていないので、実際には 10 章の札幌の例のように n_splits と test_size を系列長に合わせて減らしてから使う。

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

🎯 このコードでやること: SSDSE の 12 年分(2012〜2023)を使って、 時系列としての性質——トレンド・自己相関・定常性——を確かめます。 47 都道府県 × 12 年 = 564 行のうち、 「県ごとに 12 点の系列が 47 本ある」という構造を意識して扱うのがこのページの要点です。 なお pd.read_csv('data/raw/SSDSE-B-2026.csv') をパス変数にせず直書きしているのは、 初学者が「パスをどこに書くべきか」で迷わないようにするためです。 CSV を同じ階層に置けばそのまま動きます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 時系列 を 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 都道府県 × 12 年度
print('cols head:', list(df.columns[:8]))

# 2) 時系列として扱うので年度は絞らず、「年度 × 都道府県」の表に広げる
panel = df.pivot(index='年度', columns='都道府県', values='合計特殊出生率').astype(float)
print('panel shape:', panel.shape, '(12 年度 × 47 都道府県)')

# 3) 47 本の時系列を年度ごとに要約する
summary = pd.DataFrame({'平均': panel.mean(axis=1).round(3),
                        '最小': panel.min(axis=1),
                        '最大': panel.max(axis=1)})
print(summary.to_string())

# 4) 時系列 の本処理(このページの主題): 各県のピーク年と、直近年が最低かどうか
peak_year = panel.idxmax()
print('---- 時系列 結果 ----')
print('ピーク年度の分布:', peak_year.value_counts().sort_index().to_dict())
print('2023 年度が 12 年で最低の県数:', int((panel.idxmin() == 2023).sum()), '/ 47')
print('平均の lag-1 自己相関:', round(summary['平均'].autocorr(lag=1), 3))
📤 実行例(実測) shape: (564, 112) cols head: ['年度', '地域コード', '都道府県', '総人口', '総人口(男)', '総人口(女)', '日本人人口', '日本人人口(男)'] panel shape: (12, 47) (12 年度 × 47 都道府県) 平均 最小 最大 年度 2012 1.460 1.09 1.90 2013 1.479 1.13 1.94 2014 1.472 1.15 1.86 2015 1.530 1.24 1.96 2016 1.521 1.24 1.95 2017 1.513 1.21 1.94 2018 1.503 1.20 1.89 2019 1.455 1.15 1.82 2020 1.421 1.12 1.83 2021 1.400 1.08 1.80 2022 1.358 1.04 1.70 2023 1.293 0.99 1.60 ---- 時系列 結果 ---- ピーク年度の分布: {2013: 1, 2014: 1, 2015: 27, 2016: 9, 2017: 5, 2018: 4} 2023 年度が 12 年で最低の県数: 47 / 47 平均の lag-1 自己相関: 0.913

💬 47 県の合計特殊出生率を単純平均すると 2012 年度 1.460 から 2015 年度 1.530 まで上がり、その後 8 年続けて下がって 2023 年度は 1.293 になった。ピーク年度は 47 県中 27 県が 2015 年度、36 県が 2015〜2016 年度に集中していて、どの県も 2023 年度が 12 年間で最低という点まで同じ動きをしている。平均系列の lag-1 自己相関 0.913 からも分かるように、47 本の系列は共通の全国的な下降に強く引きずられているので、県ごとの違いを見たいときは年度ごとの平均を引いてから比べる。なお 47 県の単純平均は人口の重みを付けていないので、全国の合計特殊出生率(2023 年 1.20)とは一致しない。

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

⚠️ 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 分解し、 トレンド・季節・残差をプロットしなさい。
🎯 解説: 練習問題 Q1 の解答例。札幌の月平均気温 12 点を STL(period=4、robust=True)でトレンド・季節・残差に分け、トレンド成分を表示する。
📥 入力例: SSDSE-F-2023v3.csv の札幌市・01月〜12月の月平均気温(CN100)12 点。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温(12 点)。1 本の系列(Series)として渡す。
import pandas as pd

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = (f[(f['City'] == '札幌市') & (f['Month'] != '年')]
     .set_index('Month')['CN100'].astype(float))

from statsmodels.tsa.seasonal import STL

# 12 点しかないので period は小さくする(STL は 2 周期分以上を要求する)
res = STL(s, period=4, robust=True).fit()
res.plot()
print(res.trend.round(1))
📤 実行例(実測) Month 01月 -2.5 02月 0.3 03月 3.2 04月 6.1 05月 8.9 06月 11.8 07月 9.1 08月 9.4 09月 9.6 10月 9.7 11月 9.9 12月 10.1 Name: trend, dtype: float64
💬 読み方: トレンドは 1 月 -2.5℃ から 6 月 11.8℃ まで上がったあと、7〜12 月は 9.1〜10.1℃ でほぼ横ばいになった。12 月の実測 -0.9℃ はトレンド 10.1℃ に季節 -11.0℃ を足して合わせていて、秋の下降は周期 4 の季節成分に押し込まれている。robust=True のため 7・8 月は残差 15.4・17.7℃ の外れ値扱いになっており、この「トレンド」を気温の長期傾向とは読めない。
Q2. ADF / KPSS で定常性を検定し、 必要なら差分を取りなさい。
🎯 解説: 練習問題 Q2 の解答例。札幌の月平均気温 12 点に ADF 検定(帰無仮説:単位根あり)と KPSS 検定(帰無仮説:定常)を両方かける。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# ── この抜粋で使う系列を用意します ──
# 札幌市の月平均気温(12 点)。1 本の系列(Series)として渡す。
import pandas as pd

f = pd.read_csv('data/raw/SSDSE-F-2023v3.csv', encoding='cp932', skiprows=[1])
f.columns = ['Code', 'Prefecture', 'City', 'Month'] + list(f.columns[4:])
s = (f[(f['City'] == '札幌市') & (f['Month'] != '年')]
     .set_index('Month')['CN100'].astype(float))

import warnings
from statsmodels.tsa.stattools import adfuller, kpss

print('ADF p:', round(adfuller(s, maxlag=2)[1], 4))
with warnings.catch_warnings():          # 短い系列だと p 値が範囲外だと警告が出る
    warnings.simplefilter('ignore')
    print('KPSS p:', round(kpss(s, nlags='auto')[1], 4))
📥 入力例: s(札幌市の月平均気温 12 点、index は 01月〜12月)。
📤 実行例(実測) ADF p: 0.0 KPSS p: 0.1
💬 読み方: ADF は p = 0.0 で単位根を棄却し、KPSS は p = 0.1 で定常を棄却しないので、2 つの検定は「定常」でそろった。ただし KPSS の p = 0.1 は statsmodels の数表の上限で、本当は「0.1 以上」という意味。12 点・1 周期だけの気温を定常とする結論は季節の山を平均回帰と取り違えている可能性が高く、差分を検討する前に系列を伸ばすのが先。
Q3. auto_arima と Prophet で 12 期予測し、 MASE で比較しなさい。
🎯 解説: 練習問題 Q3 の解答例の一部。月次系列の最後の 12 か月を検証に取り分け、auto_arima(季節周期 12)で予測して MASE を計算する。
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}')
📥 入力例: 3 年(36 か月)以上の月次系列 ts を自分で用意する。このページの札幌の平年値(12 点)では学習データが 0 点になり動かない。
📤 実行例: pmdarima はこの教材の実行環境に入っていないので実測はない(pip install pmdarima が必要)。
💬 読み方: このコードの MASE は分母に学習期間での「1 期前の値をそのまま使う予測」の誤差平均を使うので、1 未満ならその素朴な予測より良いと読む。naive 変数(前年同月を繰り返す季節ナイーブ予測)は計算しただけで使っていないので、季節ナイーブと比べたいときは分母を np.mean(np.abs(train.values[12:] - train.values[:-12])) に替える。

📝 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 を採用。」

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

❌ 非定常データに普通の 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」を意識する。

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

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

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

⚠️ 時系列分析の追加落とし穴 8 件(R498 補強)

  1. 未来情報の漏洩 (Lookahead bias): 特徴量計算時に、 予測時点より未来の情報を混ぜてしまうケース。 例えば「来年の人口」を使って「今年の出生数」を予測すれば、 当然精度は不当に高くなる。 必ず時点フィルタを徹底し、 cross_val_score ではなく TimeSeriesSplit を使う。
  2. 差分後にレベル予測を要求する設計: 差分系列でモデリングした後、 累積和で元のレベルに戻すと、 誤差も累積する。 長期予測ほど信頼区間が爆発的に広がるが、 ステークホルダーには「点予測」しか伝わらないと過信される。
  3. 季節性周期の取り違え: 月次データで s=7 と指定するなど、 周期定義のミス。 出生数は月次なら s=12、 四半期なら s=4、 年次ならそもそも季節項は不要。
  4. 同一系列に複数モデルを試して最良を採用: 数十モデルを試して最も精度が良いものを採用すると、 検証セットに過適合する。 必ず事前にモデル候補を固定するか、 ホールドアウトを 3 つ以上に分ける。
  5. 構造変化 (regime change) の見落とし: コロナ禍のような外的ショックは、 ARIMA の前提を破壊する。 Chow test や ruptures パッケージで変化点を検出し、 必要なら期間を分割するか、 介入変数を入れる。
  6. 異常値除去のしすぎ: ±3σ を超えるとして自動除去すると、 真のショック(リーマンショック、 震災)まで消えてしまい、 モデルは「平穏期しか知らない」状態になる。 異常は記録に残し、 ダミー変数で表現するのが正攻法。
  7. 白色雑音と独立性の混同: 「自己相関がない = 独立」とは限らない。 GARCH 型の系列は条件付き分散が時変するが、 自己相関は弱い。 残差の二乗の自己相関も見ること。
  8. 予測区間を「信頼区間」と呼ぶ: 予測の不確実性と、 母数の推定誤差は別物。 顧客向け資料では「95% 予測区間」と明示する。

📝 理解度チェック 6 問(R498 補強)

Q1: SSDSE-B-2026 の全国出生数を時系列としてモデリングするとき、 ARIMA の差分次数 d は何が妥当か。

A: d=1。 原系列は ADF 検定 p=1.000 で非定常。 1 階差分にすると p=0.075 で、 5% では有意でないが 10% 水準では定常とみなせる(年次 12 点で検出力が低い点に注意)。 d=2 は過剰差分でノイズを増やすだけ。

Q2: 年次データ 13 点に LSTM を当てる時の最大の問題は。

A: 過学習。 LSTM の最小実用データ数は数百点以上。 13 点では学習データを完全暗記して未知データに汎化しない。 まず ARIMA か指数平滑を試すべき。

Q3: 残差の Ljung-Box 検定で p=0.578 だった。 モデルは妥当か。

A: 妥当と判定できる。 帰無仮説「ラグ 6 までに自己相関なし」を棄却できない(p > 0.05)ので、 残差は白色雑音と見なせる。 ただし二乗残差の自己相関も確認するとなお良い。

Q4: 「全国出生数」と「47 都道府県別出生数」、 どちらを時系列モデリングすべきか。

A: 目的次第。 国全体の少子化政策評価なら全国系列で十分。 ただし地域差を踏まえた施策設計なら 47 都道府県別が必須。 「秋田の出生数は -44.8% で東京の -19.6% と全く違う」事実は全国系列からは消える。

Q5: cross_val_score を時系列に適用するとなぜダメか。

A: K-fold はデータをランダムに分割するため、 「未来データで学習して過去データを予測する」分割が発生する。 これは時系列の因果順序を破壊する。 必ず TimeSeriesSplit や rolling-origin 評価を使う。

Q6: 出生数予測モデルを本番運用する際、 監視すべき指標を 3 つ挙げよ。

A: (1) 予測 MAPE の時系列推移、 (2) 入力データ分布のドリフト指標 (PSI や KS 統計量)、 (3) 残差の系列相関 (Ljung-Box) と分散変化。 加えて (4) 入力データ遅延 / 欠損率、 (5) 推論レイテンシも SLA に含めるとなお良い。

✅ 時系列モデル運用チェックリスト 40 項目(R498 補強)

A. データ取り込み (1-10)

  1. ソースデータの更新頻度を明文化したか
  2. 遅延データ・修正データの取り扱いポリシーを定めたか
  3. 欠損値の補完方法(前値補完、 線形補間、 モデルベース)を選択したか
  4. 外れ値の検出ロジック(IQR・Hampel・ロバスト Z)を設定したか
  5. 季節調整(X-13ARIMA-SEATS, STL)の必要性を判断したか
  6. タイムゾーン・サマータイムの取り扱いを統一したか
  7. 同一指標の重複・改定履歴を追跡できるか
  8. 暦調整(平日数・祝日数)が必要なら実装したか
  9. ベンチマーク用の手作業予測(前年同月など)を確保したか
  10. 入力データのスキーマバリデーション(pandera, great_expectations)を組んだか

B. モデル設計 (11-20)

  1. 定常性の事前検定 (ADF, KPSS) を実施したか
  2. 自己相関構造 (ACF, PACF) を可視化したか
  3. 季節周期を周期図 (Periodogram) で確認したか
  4. 外生変数(イベント・経済指標・天候)の取り込みを検討したか
  5. 変換 (log, Box-Cox) で分散安定化を試したか
  6. モデル候補 (ARIMA, SARIMA, 状態空間, Prophet, ML) を事前列挙したか
  7. 情報量基準 (AIC, BIC, AICc) で候補を絞ったか
  8. 残差診断 (Ljung-Box, ARCH 検定) を計画したか
  9. 予測区間の幅を顧客と合意したか
  10. 解釈性要件(係数説明・寄与分解)を確認したか

C. 評価 (21-30)

  1. TimeSeriesSplit でホールドアウトを 3 分割以上に分けたか
  2. rolling-origin 評価で精度の時系列推移を確認したか
  3. MAPE, RMSE, MASE など複数指標で多面評価したか
  4. ベンチマーク(前年比、 移動平均)を上回ることを示したか
  5. 予測区間のカバレッジ率 (例: 95% 区間に実数が 95% 入るか) を測ったか
  6. 方向一致率 (DA) や符号一致率を計算したか
  7. 大きく外したケースを個別レビューしたか
  8. 構造変化点 (rupture) を検出して別期間で再評価したか
  9. 多重比較を考慮して、 複数モデルから最良を選ぶ際の楽観バイアスを補正したか
  10. ステークホルダーが理解しやすい指標 (累積誤差、 月次達成率) も併記したか

D. 運用 (31-40)

  1. 予測ジョブを Airflow / Prefect / Dagster でスケジュールしたか
  2. モデルレジストリ (MLflow) でバージョン管理したか
  3. 入力データドリフト監視を実装したか
  4. 予測誤差のリアルタイム監視ダッシュボードを用意したか
  5. 再学習トリガー(誤差閾値, データ数閾値, 時間閾値)を定義したか
  6. ロールバック手順を文書化したか
  7. 事故対応プレイブックを作成したか
  8. 説明可能性レポートを定期発行する仕組みを作ったか
  9. 計算コスト・メモリ使用量・ストレージ容量を継続監視しているか
  10. 運用引き継ぎドキュメント(オンコール対応含む)を整備したか

❓ 実務 FAQ 7 件(R498 補強)

Q. ARIMA と Prophet、 結局どちらが良いのか?

A: 短期 (1-3 期先) 予測なら ARIMA が安定、 中長期 (1-3 年先) や明確な季節性 (週・年) があるなら Prophet が手早い。 ARIMA は事前の定常化や次数決定が要るが、 統計理論的に裏付けが深い。 Prophet はパラメータが少なく、 ビジネス向け説明 (休日効果・トレンド変化点) が容易。 両方試して評価指標で比較するのが王道。

Q. データが 30 点しかない。 ARIMA を当てて良いか?

A: ARIMA(1,1,1) なら可能だが、 信頼区間は広くなる。 まずは指数平滑 (Holt) を試し、 それより明確に良い場合のみ ARIMA を採用するのが安全。 30 点で SARIMA(p,d,q)(P,D,Q,12) はパラメータ過多になるので避ける。

Q. 説明変数を増やせば予測は良くなるか?

A: 必ずしも。 自由度が増えて検定統計量が膨らみ、 真の関係を見つけにくくなる。 また外生変数自体を将来予測しなければならず、 「外生変数の予測誤差」が出力に伝播する。 「説明変数を増やす前に時系列構造を尽くす」が原則。

Q. グラフを見て「明らかにトレンドがある」のに ADF 検定で p=0.06。 どう解釈する?

A: ADF は検出力が低いことで有名。 KPSS 検定(帰無仮説: 定常)と併用し、 両方の結論から判断する。 視覚的にトレンドが明らかなら、 差分を取って差分系列の検定結果を優先する判断もアリ。

Q. 時系列予測で「過去 3 ヶ月の MAPE が悪化した」場合、 何から確認すべきか?

A: (1) 入力データの異常(欠損・スキーマ変更)、 (2) 入力分布のドリフト、 (3) 残差の自己相関再発、 (4) 外的要因イベント、 の順で切り分ける。 (1) が原因ならデータパイプライン側の修正で済むが、 (2)(3) ならモデル再学習が必要。

Q. パネル時系列(47 都道府県 × 13 年)でモデリングしたいが、 どこから始めるべきか?

A: まず全国合計と各県個別の双方で簡単なモデル (ARIMA) を作り、 予測精度を比較する。 全国合計の方が安定なら集約モデルで足りる。 県差が大きいなら、 固定効果付きパネル ARIMA や、 階層ベイズモデル (pymc, stan) を検討する。

Q. AIC が最小のモデルを選んだら、 残差診断が失敗した。 どうする?

A: AIC は予測精度と表裏一体だが、 残差白色性は別の評価軸。 残差に強い自己相関が残るなら、 たとえ AIC が高くても次数を増やすか、 別構造 (SARIMA, 状態空間) を検討する。 AIC は判断材料の 1 つに過ぎない。

📖 時系列分析の歴史と実例(R498 補強)

歴史的マイルストーン

国内実例 — 出生数予測の社会的意義

SSDSE-B-2026 が捉える出生数は、 国立社会保障・人口問題研究所が公表する「日本の将来推計人口」の基礎指標。 厚生労働省は毎年「人口動態統計」を発表し、 将来推計はコーホート要因法と時系列予測を併用する。 例えば近年は ARIMA + 構造変化検出を組み合わせ、 リーマンショック、 東日本大震災、 コロナ禍の影響を介入変数で表現している。 単純な ARIMA だけでは 2020-2022 年の急減を当てられない。 予測の社会的影響は甚大で、 1% の予測誤差が将来の年金財政や医療費推計に億単位で響く。

海外実例 — 電力需要予測

世界中の電力会社は時系列予測の最大の利用者の 1 つ。 30 分単位の電力需要予測は、 発電計画と需給バランス維持に直結し、 1% の予測精度向上が年間数十億円のコスト削減になる。 古典的には SARIMAX (休日・気温・湿度を外生変数として組み込み)、 近年は LightGBM + Lag 特徴 + 気象データ、 さらに Transformer 系が試されている。 ただし説明責任の観点から、 規制当局向けには ARIMA / 線形モデルの併用が義務化されている地域もある。

小売実例 — 需要予測と在庫最適化

大手小売チェーンは数万 SKU × 数千店舗の時系列を持つ。 各 SKU の系列は短い (1-3 年) ことが多く、 単独 ARIMA では不安定。 そこで階層時系列 (hierarchical time series) やグローバルモデル (LightGBM や DeepAR) で複数系列を一括学習する。 Amazon の Forecast、 Walmart の M5 Forecasting Competition の上位解法はこの系列。

🎯 R498 まとめ — 時系列分析を「使いこなす」ために

時系列分析は「過去から未来を語る学問」。 過去の系列を尊重し、 構造を見抜き、 不確実性を素直に提示することが、 良い予測者の条件である。 SSDSE-B-2026 のような社会データは、 単なる練習材料ではなく、 私たち自身の社会の未来を映し出す鏡でもある。

📑 時系列分析の用語辞典(R498 補強)

時系列分析の議論で出てくる用語は、 統計学・経済学・機械学習それぞれの文脈で微妙に意味が違う。 混乱を避けるため、 主要用語を一覧でまとめておく。

用語 定義 登場場面
定常性 (stationarity)平均・分散・自己共分散が時間によらず一定。 ARIMA 適用の前提。前処理、 ADF/KPSS 検定
自己相関 (ACF)$Y_t$ と $Y_{t-h}$ の相関係数。 ラグごとに測る。MA 次数の同定
偏自己相関 (PACF)中間ラグの影響を取り除いた純粋な自己相関。AR 次数の同定
単位根 (unit root)$Y_t = Y_{t-1} + \epsilon_t$ のような、 差分しないと定常化しない構造。ADF 検定の帰無仮説
共和分 (cointegration)複数の単位根過程の線形結合が定常になる関係。経済時系列のペアトレード
ホワイトノイズ平均 0・分散一定・自己相関なしの理想雑音。残差診断の理想形
季節成分12 ヶ月、 7 日など固定周期の繰り返しパターン。SARIMA・STL 分解
トレンド長期的な水準変化。 線形・非線形どちらもあり得る。差分化・回帰
レジーム変化系列のデータ生成過程が突然変化する現象。Chow test, Markov 切替
カバレッジ率95% 予測区間に実数が入る割合の実測値。 50% を切れば予測過信。運用監視
MAPE / MAE / RMSE予測精度指標。 MAPE は比率、 MAE は絶対値、 RMSE は二乗誤差の平方根。評価レポート
MASEナイーブ予測(前期と同じ)の MAE で割った相対誤差。 1 未満ならナイーブより優秀。スケール非依存比較
TimeSeriesSplit時系列順序を保ったまま、 拡張型 (expanding) または固定型 (rolling) で分割。交差検証
白色性検定残差が独立な雑音か検証する。 Ljung-Box, Box-Pierce が代表的。モデル診断
状態空間モデル観測方程式と状態方程式で系列を表現。 Kalman filter で推定。柔軟な構造化モデリング

代表ライブラリの使い分け

Python での時系列分析に使われる主要ライブラリは、 役割が微妙に重なりつつも棲み分けている。 statsmodels は古典的時系列モデル (ARIMA, SARIMAX, 状態空間, VAR) の本命で、 推定統計量・診断ツールが豊富。 prophet は Facebook 由来で、 トレンド変化点・休日効果・季節性を加法的に組み合わせる Bayesian モデル。 ビジネス用途に最適化されている。 sktime は scikit-learn 互換 API で、 古典モデルから機械学習まで一貫したインターフェースを提供。 darts は深層学習を含む最新モデル群を統合し、 確率予測やアンサンブルも扱える。 tsfresh は時系列特徴量自動抽出に特化、 機械学習モデルへの入力作りに便利。

統計検定 vs 機械学習評価の対応関係

統計学派は「モデルの妥当性」を残差診断と尤度比検定で語る。 機械学習派は「予測精度」をホールドアウト評価で語る。 両者は対立せず補完的。 残差診断で妥当と認められたモデルが、 予測評価で良いとは限らない(過適合のリスクが残る)。 逆に予測精度が良くても残差に強い自己相関があれば、 「たまたま当たっただけ」かもしれない。 両方の観点で多面評価することが、 時系列分析の信頼性を高める唯一の道。

よくある質問の追加(学習者向け)

Q. 「日本の出生数を予測する」という問題に対して、 学習者がまず触れるべきモデルは何か。 答えは指数平滑法 (Holt または Holt-Winters)。 ARIMA より前提が少なく、 結果の解釈もしやすい。 まず最も単純なモデルから出発し、 残差診断や予測精度が許容範囲なら、 そこで止める。 これは「最小限のモデルから始めて、 必要に応じて複雑化する」というオッカムの剃刀の原則の実践でもある。

Q. 季節性が「あるかもしれない」と感じた時の確認手順は。 ステップ 1: 系列を月別 (または曜日別) にグルーピングして箱ひげ図で水準差を見る。 ステップ 2: 周期図 (periodogram) や Fourier 分解でスペクトルピークを探す。 ステップ 3: 自己相関図 (ACF) で周期に対応するラグでピークが立つかを確認。 ステップ 4: 季節差分後の系列が定常化するかを ADF/KPSS で確認。 この 4 ステップで季節性の存在と周期長が決まる。

Q. 短期予測と長期予測で気をつける点は違うのか。 大きく違う。 短期 (1〜3 期先) では誤差が小さく、 点予測が実用的。 長期 (10 期以上) では予測区間が指数的に広がり、 「中心値」よりも「シナリオ分析」の方が有用。 長期予測を点で報告すると過信を招き、 経営判断を誤らせる。 「2030 年の出生数は 60 万人」ではなく、 「2030 年の出生数は 55-70 万人の範囲、 中央値 62 万人」のような表現が望ましい。

学習のロードマップ

時系列分析を体系的に学ぶには、 以下の順序がオススメ。 Step 1: 記述統計と可視化 (折れ線, ACF, 季節分解) で系列の性質を観察。 Step 2: 単純な平均・移動平均・指数平滑で基準線を引く。 Step 3: ARIMA/SARIMA で古典時系列モデルを習得。 同時に定常性・残差診断を理解する。 Step 4: 状態空間モデルで柔軟なモデリング技法を身につける。 Step 5: Prophet で実務的なベイズ加法分解を体験。 Step 6: 多変量 (VAR) と外生変数 (SARIMAX) に拡張。 Step 7: 機械学習 (LightGBM, DeepAR) と深層学習 (LSTM, Transformer) を、 データ量と相談しながら導入。 Step 8: パイプライン・監視・再学習の運用面を構築。 各ステップで SSDSE-B-2026 のような公的データを使い、 自分の手を動かすことが最大の近道。

推薦書籍・参考文献

古典では Box, Jenkins, Reinsel "Time Series Analysis: Forecasting and Control" が原点で、 ARIMA 法の体系を学ぶならまずこれ。 Hamilton "Time Series Analysis" (1994) は経済時系列の理論を網羅し、 状態空間モデルや Markov 切替も詳述。 邦書では沖本竜義「経済・ファイナンスデータの計量時系列分析」 が日本語の決定版。 実務寄りには Hyndman & Athanasopoulos "Forecasting: Principles and Practice" (オンライン無料) があり、 R 言語の実装例が豊富。 機械学習・深層学習方面では Kaggle の時系列コンペ (M5 Forecasting, Web Traffic Forecasting) のソリューションが実践的な学びの宝庫。 また、 統計数理研究所や国立社会保障・人口問題研究所が公表する将来推計レポートを読み込むと、 「公的機関がどう時系列分析を業務応用しているか」が分かり、 アカデミックとの橋渡しになる。

最後に — 時系列分析者の心構え

時系列分析は、 一面では数学的に整った美しい学問だが、 もう一面では人間社会の不確実性と向き合う実践でもある。 完璧な予測は不可能である一方、 「不確実性を定量化し、 意思決定者に正直に伝える」ことは可能。 出生数のような社会指標の予測は、 単に数字を当てるゲームではなく、 「日本の未来をどう設計するか」という政策議論の出発点。 だからこそ、 時系列分析者には謙虚さと誠実さが要求される。 「分からないことは分からない」「予測区間が広いことには意味がある」「過去のパターンが未来も続くとは限らない」 こうした原則を忘れずに、 データと向き合うことが、 信頼される時系列分析の第一歩である。

補遺 — SSDSE-B-2026 を選んだ理由

本ページの実例で SSDSE-B-2026 を一貫して使った理由は三つ。 第一に、 47 都道府県 × 13 年間というパネル構造が、 時系列単独・パネル時系列・地域比較といった多面的議論を可能にすること。 第二に、 出生数という指標が明確な社会的意義を持ち、 学習者の動機づけになること。 第三に、 公的統計でありデータの信頼性が高く、 後から自分で再現実験ができること。 教材としての三拍子が揃っている。 他にも生産年齢人口、 高齢化率、 就業者数など、 SSDSE-B-2026 には時系列分析に適した変数が多数含まれている。 ぜひ自分で別の変数を選んで、 同じ手順で分析を再現してほしい。 そこで初めて、 「時系列分析を自分のものにした」と言えるはず。

付録 — 関連する公的データセット

SSDSE-B-2026 以外にも、 時系列分析の練習に使える公的データセットは数多い。 e-Stat (政府統計の総合窓口)からは、 月次の家計調査、 消費者物価指数 (CPI)、 鉱工業生産指数、 雇用統計が API 経由でダウンロードできる。 気象庁データは気温・降水量の日次・月次データが何十年分も公開されており、 季節性を学ぶ最良の素材。 日本銀行 (BOJ) 時系列統計データ検索サイトからは金融指標、 FRED (Federal Reserve)からは米国マクロ経済指標。 これらを組み合わせて、 例えば「気温が CPI のサービス価格に影響を及ぼすか」のような実証研究も可能。 公的データは無料で、 出典明示すれば論文・教材に再利用できる。 学習者は積極的に活用し、 自分の関心テーマで時系列分析の経験値を積んでほしい。

エピローグ

時系列分析は奥深く、 一生学び続けても飽きない領域である。 本ページが、 学習者にとってその旅の最初の地図になれば幸いである。 数式・実装・運用の三層を行き来しながら、 SSDSE-B-2026 のような実データに繰り返し触れる中で、 自然と「時系列を見る目」が育っていく。 焦らず、 しかし手は止めず、 一歩ずつ進んでいこう。 時系列の世界では、 過去を学ぶ者だけが、 未来を語る権利を持つのである — それが本稿の結論である。

🎮 触って理解する — 成分分解をステップ実行(合成の逆演算)

時系列は「トレンド + 季節 + ノイズ」という成分の重ね合わせ(加法モデル $y_t = T_t + S_t + I_t$)。 このデモでは、 上 3 段のスライダーで成分を積み上げ合成した架空データに対し、 古典的分解(classical additive decomposition)を「▶ 分解を進める」で 1 ステップずつ実行する: ① 中心化移動平均でトレンド抽出 → ② デトレンド → ③ 季節平均 → ④ 残差。 最後まで進めると、 観測系列だけを入力に、 合成に使った元の成分がほぼ復元されることを数値(RMSE)で確認できる。 さらに実測モードでは SSDSE-B-2026 の実在時系列(都道府県別 出生数 2012–2023)に同じ移動平均分解を適用する。 時系列(個別)ページにはスライダー連動のリアルタイム分解デモがあるので、 本ページのステップ実行版とあわせて往復すると理解が深まる。

データ:
—
—
—

🧭 まず試すこと

💡 直感 — なぜ「平均」だけで分解できるのか

鍵は 2 つの打ち消し。 (1) 窓幅を周期 m にそろえた移動平均は、 季節成分をちょうど 1 周期分足し合わせるので季節がゼロに消えてトレンドだけが残る(m が偶数のときは両端に重み 1/2 を置く中心化 2×m 移動平均で位相を合わせる)。 (2) デトレンド後に「周期内の同じ位置」どうしで平均すると、 今度はノイズが打ち消されて季節パターンだけが残る。 高度な最適化は一切使っていないのに成分が復元できるのは、 加法モデルという構造の仮定が強く効いているから。 これは総論としての教訓でもある — 時系列手法群(ARIMA・状態空間・Prophet)はすべて「観測 = 構造成分 + 確率成分」という同じ発想の洗練形にあたる。

⚠️ 落とし穴

🚀 発展 — STL 分解と状態空間モデルへ

ここで実行した古典的分解は 1920 年代からの最古参で、 弱点(端点・季節の時間変化・外れ値)を克服したのが STL 分解(Seasonal-Trend decomposition using LOESS): 局所回帰でトレンドを推定するため端まで計算でき、 季節成分が年々変わることも許し、 ロバストオプションで外れ値にも強い(statsmodels.tsa.seasonal.STL)。 さらに進むと、 トレンド・季節を「時間発展する潜在状態」として明示的にモデル化する状態空間モデル(構造時系列モデル、 カルマンフィルタで推定)に到達し、 欠損値・観測誤差・予測区間まで一貫して扱える。 予測が目的なら、 季節差分を組み込んだ SARIMA(ARIMA の季節拡張)や 指数平滑法(Holt-Winters)が古典二大巨頭で、 いずれも適用前に定常性の確認が前提となる。

🗺 概念マップ

時系列グループ教材を中心に、 トレンド・季節性・自己相関・ARIMA・状態空間モデル・予測検証へ繋がる学習ステップを 6 方向に整理。

時系列分析 自己相関 ARIMA / SARIMA Prophet 状態空間 LSTM 指数平滑

時系列分析の中心から、 周辺に ARIMA・Prophet・状態空間モデル・LSTM・指数平滑 が配置される。 SSDSE-B-2026 の「47 県 × 2012-2023 年の人口・出生数・婚姻件数」を題材にすると、 周期性 (年次) とトレンド (人口減少) を同時に扱う訓練材料になる。

🔗 隣接手法への橋渡し

時系列分析は「データ取得 → 定常化 → モデリング → 予測評価」の流れで、 各段階で異なる手法と接続する。

SSDSE-B-2026 の合計特殊出生率 (12 年 × 47 県) を Prophet で 2025 年まで予測する pipeline は、 上流の pd.to_datetime 変換 → Prophet → MAPE 算出という 3 段で完結する。

🌳 手法選択フロー

時系列予測でどのモデルを選ぶかは、 データの性質で 4 通りに分岐する。

  1. サンプル数が少ない (n < 100)? Yes → 指数平滑 / ARIMA(1,1,1)。 パラメータ数を抑える
  2. 季節性が明確 (年次・週次)? Yes → SARIMA / Prophet。 SSDSE-B-2026 の出生数は明確な年次周期
  3. 複数系列を同時予測 (47 県)? Yes → VAR / 階層予測。 県別 → 地域別 → 全国の整合性を保つ
  4. 長期依存・非線形? Yes → LSTM / Transformer。 データ量 (n > 1000) と GPU を要求

SSDSE-B-2026 の県別 12 年データなら、 まず ARIMA + 県別個別フィット が出発点。 LSTM は時系列が短すぎて過学習する。

🧗 解説深化 — 「まず素朴な予測を置く」ベンチマーク優先主義と、 47 本の短い系列を束で評価する視点

本ページはここまで「1 本の時系列をどうモデル化するか」を中心に見てきた。 この深化セクションでは角度を変え、 SSDSE-B-2026 が実際に突きつける状況 — 「長さ 12 点しかない年次系列が 47 本ある」 — で何をすべきかを、 実測ホールドアウト実験で確かめる。 鍵は 2 つ: ① どんなモデルより先に「素朴な予測(ベンチマーク)」を置くこと、 ② 47 本を 1 本ずつではなく「束」として横串で評価すること。 以下の数値はすべて data/raw/SSDSE-B-2026.csv(cp932, skiprows=[1])から実際に計算した実測値である。

💡 直感 — ナイーブ法とドリフト法という「最低ライン」

最も素朴な予測は 2 つ。 ナイーブ法「来年 = 今年の値」 $\hat{y}_{T+1} = y_T$(ランダムウォークの最良予測に相当)と、 ドリフト法「来年 = 今年 + これまでの年平均変化」 $\hat{y}_{T+1} = y_T + \frac{y_T - y_1}{T-1}$(始点と終点を結ぶ直線の外挿)。 パラメータ推定は事実上ゼロで、 どちらも 1 行で書ける。

SSDSE-B-2026 の出生数(A4101)で実験する。 2012–2022 年の 11 点だけを使って 2023 年を予測し、 実測の 2023 年値と突き合わせる(1 期先ホールドアウト)。 47 都道府県横串の結果:

指標(47 県横断) ナイーブ法 ドリフト法
出生数 A4101 の MAPE7.28%3.16%
総人口 A1101 の MAPE0.84%0.29%
ドリフト法が勝った県の数(出生数)47 / 47 県(全勝)
ドリフト法が勝った県の数(総人口)41 / 47 県

個別に見ると: 東京都の 2023 年出生数(実測 86,348 人)に対し、 ナイーブ法は 91,097 人(誤差 +5.5%)、 ドリフト法は 89,467 人(+3.61%)。 北海道(実測 24,430 人)ではナイーブ 26,407 人(+8.09%)に対しドリフト 25,179 人(+3.07%)。 埼玉県に至ってはドリフト予測 42,102 人 vs 実測 42,108 人で誤差 0.01%。 出生数のように強い単調トレンドを持つ系列(全国計 2012 年 1,037,165 人 → 2022 年 770,750 人 → 2023 年 727,269 人)では、 「今年の値をそのまま使う」ことが系統的に外れ、 「傾きを 1 本足す」だけで誤差が半分以下になる — これがトレンドの情報量の直感的な意味である。 逆に言えば、 SARIMA や Prophet を持ち出すならまずこの 3.16% を下回れるかが問われる。 これはページ本文で登場した MASE の思想そのもの(MASE の分母はナイーブ予測の誤差。 MASE < 1 = ベンチマーク超え)。

⚠️ 落とし穴(重要) — ベンチマーク実験が教える 4 つの罠

🚀 発展 — 束ごと学習する: グローバルモデルと階層予測

「47 本それぞれに ARIMA を当てる」個別フィットの対極に、 47 本を 1 つのモデルで丸ごと学習する発想がある(グローバルモデル / cross-learning)。 1 本あたり 12 点でも束ねれば 47 × 12 = 564 観測になり、 「都道府県」をカテゴリ特徴量にした勾配ブースティングや、 県共通の傾き + 県別切片を持つ混合効果モデルが動き始める。 国際予測コンペ M4・M5(Walmart 売上)では、 系列を束ねたグローバルな LightGBM が個別フィットの古典手法群を上回り、 業界の常識を塗り替えた。 もう 1 つの発展が階層予測(hierarchical forecasting): 47 県予測の合計と全国予測は普通は一致しない。 これを事後調整で整合させる reconciliation(bottom-up / top-down / MinT)は、 都道府県 × 全国という SSDSE 型データの実務で必ず出会う問題である。 さらに統計的に踏み込むなら、 県別時系列の束はまさにパネルデータであり、 固定効果・動学パネルの世界(パネル・因果推論)に接続する。

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

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

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