🎓 レベル:標準 | 重要度:A(必須)
📎 前提:指数平滑法(SES・Holt・Holt-Winters) | 前方:状態空間モデルの枠組み(状態空間の一般形)・関連:ARMA・ARIMAモデル
要点(BLUF)
- ETS=Error/Trend/Seasonal の3軸を、それぞれ N(なし)・A(加法)・M(乗法)(トレンドは減衰 Ad も)で分類した指数平滑の体系。例:Holt-Winters 加法=ETS(A,A,A)。
- ETS は指数平滑を単一誤差源(single source of error)の状態空間モデルとして定式化したもの。だから古典指数平滑では出せなかった予測区間が解析的に出せます(→ 一般の状態空間は 状態空間モデルの枠組み)。
- 本ノートでは
ETSModelで点予測+95%予測区間を出し、AIC で真の構造 (A,A,A) を選び、状態成分 level/trend/season を真値相関 0.99 で復元します。
1. ETS の分類(Error/Trend/Seasonal)
指数平滑の各部品を、組み合わせ方で名前をつけたのが ETS です。
| 軸 | 記号 | 意味 |
|---|---|---|
| Error(誤差) | N / A / M | 残差を水準に加える(加法 A)か掛ける(乗法 M)か |
| Trend(トレンド) | N / A / Ad / M / Md | なし/加法/減衰加法(傾きを で減衰)など |
| Seasonal(季節) | N / A / M | なし/加法/乗法(振幅が水準に比例) |
たとえば 指数平滑法(SES・Holt・Holt-Winters) で見たモデルは ETS の言葉でこう呼びます:
- SES(水準のみ・加法誤差)= ETS(A,N,N)
- Holt(水準+加法トレンド)= ETS(A,A,N)、減衰版= ETS(A,Ad,N)
- Holt-Winters 加法= ETS(A,A,A)、乗法季節= ETS(A,A,M)
誤差を A/M、トレンドを N/A/Ad、季節を N/A/M と組み合わせると 30 モデルになり、ETSModel はこの族を統一的に扱います。
2. 状態空間表現:なぜ予測区間が出せるのか
ETS の本質は、指数平滑の漸化式を観測方程式+状態方程式に書き直すことです。Holt 線形(ETS(A,A,N))なら
ポイントは誤差 が観測にも状態にも同じものが入ること(single source of error, SSOE)。第4章のカルマン(状態空間モデルの枠組み)は観測ノイズと状態ノイズを別々に持つ(複数誤差源)のと対照的です。SSOE では を仮定すると、 期先予測の分布が漸化的に解析計算でき、その分散から予測区間が得られます——これが古典指数平滑(点のみ)との決定的な差です。
flowchart LR
E["誤差 ε_t(正規ノイズ)"] --> Y["観測 y_t = 前回水準 + 傾き + ε_t"]
E --> ST["状態更新 ℓ_t, b_t(同じ ε_t)"]
ST --> FC["h期先の予測分布(平均と分散)"]
FC --> PI["点予測 + 予測区間"]
コード①:ETSで予測区間つき予測(カバレッジも確認)
指数平滑法(SES・Holt・Holt-Winters) と同じ「確率的にドリフトする水準+加法季節」系列に、ETSModel(error="add", trend="add", seasonal="add") を当てます。get_prediction(...).summary_frame() で mean・pi_lower・pi_upper を取り出し、24ヶ月ホールドアウトで予測区間が実測を捕らえるか(カバレッジ)と、区間幅がホライズンで広がるかを確かめます。
import warnings
warnings.simplefilter("ignore")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語ラベル用
from statsmodels.tsa.exponential_smoothing.ets import ETSModel
# 真の構造:確率的にドリフトする水準 + 加法季節(周期12,振幅8) + 観測ノイズ
np.random.seed(3)
n, period = 132, 12
mu = np.zeros(n); mu[0] = 50.0
for k in range(1, n):
mu[k] = mu[k-1] + 0.4 + np.random.normal(0, 0.8)
t = np.arange(n)
y = mu + 8 * np.sin(2 * np.pi * t / period) + np.random.normal(0, 2.0, n)
y = pd.Series(y, index=pd.date_range("2014-01-01", periods=n, freq="MS"))
h = 24
train, test = y.iloc[:-h], y.iloc[-h:]
# ETS(A,A,A):加法誤差・加法トレンド・加法季節。状態空間なので予測区間が出せる
fit = ETSModel(train, error="add", trend="add", seasonal="add",
seasonal_periods=period).fit(disp=False)
pred = fit.get_prediction(start=len(train), end=len(train) + h - 1)
frame = pred.summary_frame(alpha=0.05) # 列: mean, pi_lower, pi_upper
covered = ((test.values >= frame["pi_lower"].values) &
(test.values <= frame["pi_upper"].values)).sum()
width1 = frame["pi_upper"].iloc[0] - frame["pi_lower"].iloc[0]
width24 = frame["pi_upper"].iloc[-1] - frame["pi_lower"].iloc[-1]
print(f"alpha={fit.smoothing_level:.3f} beta={fit.smoothing_trend:.3f} "
f"gamma={fit.smoothing_seasonal:.3f}")
print(f"1期先 95%予測区間幅 = {width1:.2f}")
print(f"24期先 95%予測区間幅 = {width24:.2f}")
print(f"95%予測区間カバレッジ = {covered}/{h} = {covered/h*100:.0f}%")
plt.figure(figsize=(9, 4.5))
plt.plot(train.index, train.values, color="gray", lw=1, label="訓練データ")
plt.plot(test.index, test.values, color="k", lw=1.2, label="実測(検証)")
plt.plot(frame.index, frame["mean"], color="C1", lw=2, label="ETS点予測")
plt.fill_between(frame.index, frame["pi_lower"], frame["pi_upper"],
color="C1", alpha=0.25, label="95%予測区間")
plt.axvline(train.index[-1], ls=":", color="k")
plt.xlabel("年月"); plt.ylabel("値"); plt.legend()
plt.title("ETS(A,A,A) による予測区間つき予測")
plt.tight_layout(); plt.show()
出力:
alpha=0.388 beta=0.000 gamma=0.000
1期先 95%予測区間幅 = 9.05
24期先 95%予測区間幅 = 19.15
95%予測区間カバレッジ = 24/24 = 100%
出力の意味:推定された は古典 Holt-Winters(指数平滑法(SES・Holt・Holt-Winters))と同じ ——同じモデルだからです。違いは出力で、ETS は 95% 予測区間を返します。区間幅は 1 期先 → 24 期先 とホライズンとともに広がり(先ほど不確実)、24 ヶ月すべて区間内(カバレッジ 24/24)。図では橙の帯が右へラッパ状に開きます。点予測だけでなく区間で語れる——これが状態空間化の恩恵です(ARMA・ARIMAモデル の ARIMA 予測区間と同じ思想)。なおホールドアウトが 24 点と少ないため 100% とやや過カバーですが、区間が広がる挙動と捕捉自体は健全です。
コード②:AICで真の構造を選び、状態成分を復元する
ETS 族の中から、トレンド有無・季節の加法/乗法・減衰の有無を変えた候補を AIC で比べ、真の構造(加法トレンド+加法季節)が選ばれるかを確かめます。さらに選んだモデルの状態成分 fit.states(level/trend/season)を取り出し、仕込んだ真の水準・季節と相関で照合します。
import warnings
warnings.simplefilter("ignore")
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import japanize_matplotlib # 日本語ラベル用
from statsmodels.tsa.exponential_smoothing.ets import ETSModel
# 真の構造(前コードと同じ):加法トレンド + 加法季節 が正解
np.random.seed(3)
n, period = 132, 12
mu = np.zeros(n); mu[0] = 50.0
for k in range(1, n):
mu[k] = mu[k-1] + 0.4 + np.random.normal(0, 0.8)
t = np.arange(n)
seasonal_true = 8 * np.sin(2 * np.pi * t / period)
y = mu + seasonal_true + np.random.normal(0, 2.0, n)
y = pd.Series(y, index=pd.date_range("2014-01-01", periods=n, freq="MS"))
# 4つの ETS 仕様を AIC で比較(trend, seasonal, damped を変える)
specs = {
"(A,N,A) トレンド無": dict(trend=None, seasonal="add", damped_trend=False),
"(A,A,N) 季節無": dict(trend="add", seasonal=None, damped_trend=False),
"(A,A,A) 加法季節": dict(trend="add", seasonal="add", damped_trend=False),
"(A,A,M) 乗法季節": dict(trend="add", seasonal="mul", damped_trend=False),
"(A,Ad,A) 減衰トレンド": dict(trend="add", seasonal="add", damped_trend=True),
}
print(f"{'仕様':<18}{'AIC':>10}")
results = {}
for name, kw in specs.items():
fit = ETSModel(y, error="add", seasonal_periods=period, **kw).fit(disp=False)
results[name] = fit
print(f"{name:<18}{fit.aic:>10.2f}")
best = min(results, key=lambda k: results[k].aic)
print(f"AIC最小 = {best}")
# 状態成分を取り出して真の構造と重ねる(best が加法季節モデル)
fit = results["(A,A,A) 加法季節"]
states = fit.states # DataFrame: level / trend / seasonal
print(f"水準と真のmuの相関 = {np.corrcoef(states['level'], mu)[0,1]:.3f}")
print(f"季節と真の季節の相関 = {np.corrcoef(states['seasonal'], seasonal_true)[0,1]:.3f}")
fig, ax = plt.subplots(3, 1, figsize=(9, 6.5), sharex=True)
ax[0].plot(y.index, states["level"], color="C0", label="ETS推定 level")
ax[0].plot(y.index, mu, color="k", ls="--", lw=1, label="真の水準 mu")
ax[0].set_ylabel("水準"); ax[0].legend(loc="upper left")
ax[1].plot(y.index, states["trend"], color="C2"); ax[1].set_ylabel("トレンド傾き")
ax[2].plot(y.index, states["seasonal"], color="C1", label="ETS推定 season")
ax[2].plot(y.index, seasonal_true, color="k", ls="--", lw=1, label="真の季節")
ax[2].set_ylabel("季節"); ax[2].legend(loc="upper left")
ax[0].set_title("ETS の状態成分(level / trend / season)と真の構造")
ax[-1].set_xlabel("年月"); plt.tight_layout(); plt.show()
出力:
仕様 AIC
(A,N,A) トレンド無 645.13
(A,A,N) 季節無 770.73
(A,A,A) 加法季節 637.05
(A,A,M) 乗法季節 661.23
(A,Ad,A) 減衰トレンド 643.44
AIC最小 = (A,A,A) 加法季節
水準と真のmuの相関 = 0.997
季節と真の季節の相関 = 0.996
出力の意味:AIC が最小なのは真の構造 (A,A,A) = 637.05。季節を外した (A,A,N) は と大きく悪化し(季節が本質)、季節を乗法にした (A,A,M) は と罰せられます(データは加法季節なので乗法は不要に複雑)。減衰トレンド (A,Ad,A) は と僅差ですが、非減衰の加法トレンドに負けます。AIC は真の構造に合うモデルを選びました(モデル選択の考え方は モデル選択と残差診断)。さらに状態成分は、水準が真の と相関 、季節が真の季節と でほぼ完全に復元。ETS は「分解しながら予測する」モデルで、states で中身を覗けるのが解釈上の強みです。
3. ETS と ARIMA の関係・使い分け
一部の ETS は ARIMA と等価です(線形・加法誤差のもの):
- ETS(A,N,N)=SES ⟺ ARIMA(0,1,1)
- ETS(A,A,N)=Holt ⟺ ARIMA(0,2,2)
- ETS(A,Ad,N)=減衰Holt ⟺ ARIMA(1,1,2)
一方、乗法のETS(誤差・季節が M)に対応する ARIMA は存在しません。両者の住み分けは:
- ETS:トレンド・季節を状態として明示的に分解するので解釈しやすく、状態空間で予測区間も自然に出る。季節の形がゆっくり変わってよい。乗法季節(振幅が水準に比例)を素直に扱える。
- ARIMA:自己相関の構造を差分と AR/MA で柔軟に表現(ARMA・ARIMAモデル)。定常化の理論が明快で、外生変数の取り込み(SARIMAX)も得意。
- 実務では両方を時間順ホールドアウトで比べて選ぶのが安全(予測の評価指標と時系列CV)。
4. 数式の直観
- ETS = 指数平滑+確率モデル:漸化式に誤差項の確率分布を載せ、状態空間として書くことで「予測の点」だけでなく「予測の分布」が手に入る。点予測は条件付き期待値、区間は条件付き分散から。
- 単一誤差源(SSOE):観測と状態を同じ が駆動する簡潔な構造。これが解析的な予測分布を可能にし、カルマン(複数誤差源、状態空間モデルの枠組み)とは別系統だが「状態空間で予測の不確実性を出す」発想は共通。
- AIC で族から選ぶ:ETS は 30 モデルの族。トレンド/季節/減衰/加法乗法を AIC で選べば、過不足ない構造に落ち着く(罰則の考え方は モデル選択と残差診断)。
⚠️ よくある誤解
- 「ETS の予測区間は無条件に正しい」ではない:区間は誤差が正規・モデルが正しい前提の目安。乗法誤差や裾の重い系列では較正がずれます。ホールドアウトでカバレッジを点検しましょう。
- 「AIC が選べば標本外でも最良」ではない:AIC は標本内の規準。最終判断は時間順ホールドアウト/ウォークフォワードでの予測精度で(予測の評価指標と時系列CV)。
- 「ETS は ARIMA の劣化版」ではない:乗法季節など ETS にしか素直に書けない構造があり、状態の解釈性も高い。優劣ではなく得意分野が違う。
- 「状態空間=カルマンと同じ」ではない:ETS は単一誤差源、カルマンは複数誤差源。書き換えはできるが定式化が異なる点に注意(状態空間モデルの枠組み)。
- 「乗法季節なのに加法で当てる」のは危険:振幅が水準に比例する系列に加法を当てると、水準が高い時期で季節を取りこぼします。AIC か対数変換で対処。
関連ノート
- 指数平滑法(SES・Holt・Holt-Winters)(SES/Holt/Holt-Winters・漸化式の土台)
- 状態空間モデルの枠組み(状態空間の一般形・カルマン=複数誤差源)
- ARMA・ARIMAモデル(ARIMA との対応・予測区間)
- モデル選択と残差診断(AIC/BIC・残差診断)
- 予測の評価指標と時系列CV(カバレッジ・時間順検証)
- STL分解(分解志向のもう一つの道具)
- 第3章 指数平滑と分解 目次
- 時系列分析・予測テキスト 全体目次