メインコンテンツへスキップ
見出し画像

書記が数学やるだけ#936 状態空間モデルの構成要素

    時系列分析における状態空間モデルを扱っていく。


    問題

    画像


    説明

    状態空間モデルは、以下の2つの数式で構成される。

    ・状態方程式: 目に見えない「真の状態(水準や傾き、季節性など)」が、時間の経過とともにどのように変化していくかを記述する。

    ・観測方程式: 「真の状態」に対して、測定誤差(ノイズ)が加わって、実際に手にする「観測値」が得られるプロセスを記述する。

    画像


    解答

    ローカルレベルモデルは,観測不可能な状態変数が「ドリフトのないランダムウォーク」に従う、最も基本的な構造時系列モデルである。

    画像


    ローカル線形トレンドモデルは,状態変数として「水準」と「傾き」の2成分を保持し、それぞれが相互に影響を与えながら確率的に変動するモデルである。

    画像


    季節調整モデルは,上記のトレンド成分に加え、周期的な変動を記述する「季節成分」を導入した構造モデルである。

    画像


    ここで,線形回帰モデルと状態空間モデルを比較してみる。

    import numpy as np
    import matplotlib.pyplot as plt
    import statsmodels.api as sm
    
    def run_simulation(case_name, y_data):
        n = len(y_data)
        x = np.arange(n)
        
        # 1. 線形回帰 (OLS)
        X = sm.add_constant(x)
        ols_res = sm.OLS(y_data, X).fit()
        y_ols = ols_res.predict(X)
        
        # 2. ローカルレベルモデル (LLM)
        llm = sm.tsa.UnobservedComponents(y_data, level='local level')
        llm_res = llm.fit(disp=False)
        y_llm = llm_res.smoothed_state[0]
        
        # 3. ローカル線形トレンドモデル (LLT)
        llt = sm.tsa.UnobservedComponents(y_data, level='local linear trend')
        llt_res = llt.fit(disp=False)
        y_llt = llt_res.smoothed_state[0]
        
        # プロット
        plt.figure(figsize=(10, 5))
        plt.scatter(x, y_data, alpha=0.3, color='gray', label="Observed")
        plt.plot(y_ols, label="Linear Regression", color='red', linestyle='--')
        plt.plot(y_llm, label="Local Level Model", color='blue', linewidth=2)
        plt.plot(y_llt, label="Local Linear Trend", color='green', linewidth=2)
        
        plt.title(f"Comparison: {case_name}")
        plt.legend()
        plt.grid(True, alpha=0.3)
        plt.show()
    
    # --- 各ケースの実行 ---
    np.random.seed(42)
    
    # (1) 線形に近似できるデータ (安定トレンド)
    n1 = 100
    y1 = 0.5 * np.arange(n1) + 10 + np.random.normal(0, 2, n1)
    run_simulation("Case 1: Stable Trend", y1)
    
    # (2) 傾きが変化するデータ (トレンド転換)
    n2 = 150
    slope_true = np.array([0.8] * 75 + [0.1] * 75)
    y2 = np.cumsum(slope_true) + np.random.normal(0, 2, n2)
    run_simulation("Case 2: Slope Change", y2)
    
    # (3) 水準が変化するデータ (ステップ変化)
    n3 = 100
    y3 = np.array([10] * 50 + [20] * 50) + np.random.normal(0, 2, n3)
    run_simulation("Case 3: Level Jump", y3)


    (1) 一定の傾きを持つ安定したトレンドデータ

    このケースでは、線形回帰とローカル線形トレンドモデル(LLT)が高い適合度を示す。線形回帰は全期間を通じて単一の傾きを推定するため、ノイズに対して最も安定した直線を出力する。

    一方、ローカルレベルモデル(LLM)には「傾き」の概念が存在しない。そのため、右肩上がりの推移を「水準 μt​ の逐次的な更新」として処理せざるを得ない。その結果、推定値はデータに後追いするような階段状の軌跡となり、予測値は直近の水準で水平に固定されるため、将来予測においては系統的な過小(あるいは過大)評価を招く。

    画像


    (2) トレンド(傾き)が変化する非定常データ

    途中でデータの勢いが変化する場合、線形回帰では全期間の平均的な傾きで直線を引く。その結果、変化点の前半では過小、後半では過大な予測(あるいはその逆)が生じ、構造変化に対応できない。

    LLMは水準の更新によって追従こそ可能だが、傾きの変化を単なる「水準の移動」として解釈するため、変化への適応に常に一歩遅れるラグ(時間遅れ)が発生する。

    これに対し、LLTは最も優れた適合を示す。状態変数である傾き δt​ が動的に更新されるため、変化点以降の新しいトレンドを正確に捕捉し、精度の高い将来予測が可能となる。

    画像


    (3) 水準が突発的にジャンプする非定常データ

    傾きは一定のまま、水準のみが不連続に変化(ステップ変化)するデータでは、線形回帰はジャンプを「緩やかな右肩上がりのトレンド」と誤認し、全区間でデータから大きく乖離する。

    この局面ではLLMがよく当てはまる。変化点に差し掛かると水準 μt​ が急速にアップデートされ、新しい水準へ即座に収束する。LLTも追従は可能だが、急激な水準変化を「一時的な傾きの急増」と誤認し、変化直後の予測線が過剰に跳ね上がる(オーバーシュートする)傾向があるため、注意を要する。

    画像


    本記事のもくじはこちら:


    あなたへのおすすめ