サイトアイコン 天文学者のpython・音楽・お料理レシピ

Pythonで実証!実データに定常化手法を適用してみる(株価・GDP・売上データ)

みなさんこんにちは!このブログでは主に

の4つのトピックについて発信しています。

今回の記事では時系列データの分析について扱います!

これまでの記事では、時系列データの分析における「偽物の相関」の危険性↗や、定常性の確認↗非定常性の性質↗、そして定常化の代表的な方法↗について学んできました。

今回はいよいよ 実データを用いたケーススタディ に入ります。
株価、GDP、売上データといった実務でよく登場する時系列データを例に、実際にPythonで非定常性を確認し、差分・トレンド除去・季節調整といった処理を適用していきます。

👉 この記事を読むことで、

が理解でき、読者のみなさんが自分のデータに応用できるイメージを持てるはずです。



Abstract | 実データで定常化を確認する

時系列データの分析において、非定常性を無視してしまうと「偽物の相関」や誤った結論につながるリスクがあります。

本記事では、株価・GDP・売上データという代表的な時系列を例に、Pythonを使って以下を実証します。

結果、いずれのケースでも「処理前は非定常 → 処理後は定常」と判定されることを確認します。

この記事を読めば、実務で直面する時系列データに対して、適切に定常化を行う具体的な方法を理解できるようになります。



Background | 前回までのおさらい

これまでのシリーズでは、時系列データを扱う際に避けては通れない「定常性」について、基礎から実証まで解説してきました。

これらを踏まえて、今回の第5回では 実データに実際に適用してみる ことを目的とします。
理論とシミュレーションで学んだ知識を、株価やGDP、売上データといった実務的な時系列に当てはめ、どのように定常性を確認・処理していくかを実証していきます。



Data | ケーススタディの対象データ

今回のケーススタディでは、実務でよく登場する3種類の時系列データを取り上げ、Pythonで実際に取得・分析を行います。
それぞれに異なる特徴があり、非定常性の性質も異なるため、定常化の手法を検証するには格好の教材となります。

1. 株価データ(例:S&P500 日次株価)

全体像

今回使用するデータ

2. GDPデータ(米国四半期GDP)

全体像

今回使用するデータ

3. 小売売上データ(米国小売売上高:月次)

全体像

今回使用するデータ

👉 このように、株価・GDP・小売売上の3つは、それぞれ「ランダムウォーク」「トレンド」「季節性」という異なる非定常性を代表しています。
次章の Method | 定常性の確認手法 では、それぞれに適した前処理をどのように行うのかを整理します。



Method | 定常性の確認手法

今回取り上げる株価・GDP・小売売上データは、それぞれ異なる形で非定常性を含んでいます。
そのため、データの性質に応じた前処理 を施し、定常性を確認する必要があります。ここでは代表的な3つの手法を整理します。

差分(Differencing)

トレンド除去(Detrending)

季節調整(Seasonal Adjustment)

ADF検定による確認

👉 ここまでで、対象データに対して「どんな方法を適用し、どう確認するのか」が整理できました。
次の Result | Pythonによる実装と検証 では、実際にコードを動かし、処理前後での変化を図表と検定結果で確認していきます。



Result | Pythonによる実装と検証

ここからは、実際にPythonを使って株価・GDP・小売売上データを取得し、差分・トレンド除去・季節調整を適用して、処理前後で定常性が改善されるかを検証します。

分析の流れは共通しており、

  1. データの取得と可視化
  2. 前処理(差分・トレンド除去・季節調整など)
  3. ADF検定による定常性の確認

を順に行います。

準備 | 共通関数の定義

まずはプロットとADF検定を毎回簡単に呼び出せるように、前回記事↗と同じように共通関数を定義しておきます。

ライブラリーインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
import statsmodels.api as sm
from statsmodels.tsa.seasonal import seasonal_decompose
import yfinance as yf
import pandas_datareader.data as web

プロット関数

def plot_series(original, transformed, labels, titles):
    """
    Plot original and transformed time series.
    """
    fig, axs = plt.subplots(2, 1, figsize=(10, 6))
    axs[0].plot(original, label=labels[0])
    axs[0].set_title(titles[0])
    axs[0].legend()

    axs[1].plot(transformed, label=labels[1], color="orange")
    axs[1].set_title(titles[1])
    axs[1].legend()

    plt.tight_layout()
    plt.show()

ADF検定関数

def run_adf_test(series, label):
    """
    Run ADF test and print p-value.
    """
    result = adfuller(series)
    print(f"ADF Test ({label}): p-value = {result[1]:.4f}")
    return result[1]



1. 株価データ(S&P500 日次株価)|差分による定常化

まずは、ランダムウォーク的な性質を持つ代表例として、S&P500の日次株価を扱います。
株価の水準系列は、一般に平均や分散が一定とはみなしにくく、非定常系列の典型例です。
そこで、前回学んだ 一次差分 を適用し、定常化できるかを確認します。

Pythonによる実装

S&P500の日次データを取得し、一次差分を計算します。

# Fetch S&P500 daily data
ticker = "^GSPC"
df = yf.download(
    ticker,
    start="2014-12-31",
    end="2025-01-01",
    interval="1d",
    auto_adjust=True,
    progress=False
)

# Extract close price series
sp500 = df["Close"].dropna()

# Apply first differencing
sp500_diff = sp500.diff().dropna()

ここでは auto_adjust=True を指定しているため、株価系列は分割や配当の影響を調整したものになっています。
実務でも、こうした調整後系列を使うほうが扱いやすい場面が多いです。

続いて、元の系列と差分系列を上下2段のプロットで比較します。

# Plot original and differenced series
plot_series(
    original=sp500,
    transformed=sp500_diff,
    labels=["S&P500 Close", "First Difference"],
    titles=["Non-stationary series (S&P500 Close)", "After Differencing"]
)

最後に、元系列と差分系列それぞれにADF検定を適用します。

# Run ADF tests
p1 = run_adf_test(sp500, "S&P500 Close")
p2 = run_adf_test(sp500_diff, "First Difference")

時系列プロットの様子

プロットの結果は下図1のように、上段にS&P500の終値系列、下段にその一次差分系列が表示されます。

上段の株価系列は、長期的な上昇傾向を持ち、平均が一定のまわりで上下しているようには見えません。
一方、下段の差分系列は、0付近を中心として変動するノイズ的な系列に近づいており、見た目にも定常化されたことがわかります。

図1. 株価の1次差分の結果: S&P500日次株価終値の原系列(上)と差分系列(下)

ADF検定の結果

ADF検定の結果は、例えば次のようになります。

ADF Test (S&P500 Close): p-value = 0.9827
ADF Test (First Difference): p-value = 0.0000

元系列では p値 > 0.05 となり非定常、
トレンド除去後系列では p値 < 0.05 となり定常と判定されます。

結果の解釈

結果を見ると、

ことが確認できます。

これは、株価がランダムウォーク的に振る舞う系列であり、水準そのものを直接分析するのではなく、変化量に変換して扱うべき ことを意味します。
実務で株価そのものではなく、日次リターンや対数リターンを分析対象にすることが多いのも、この性質によるものです。

👉 このように、差分化はランダムウォーク型の非定常系列に対する基本的かつ有効な定常化手法であることが、実データでも確認できました。



2. GDPデータ|トレンド除去による定常化

次に、長期的なトレンドを持つ代表例として、米国の四半期GDPを扱います。
GDPのようなマクロ経済系列は、時間とともに右肩上がりに推移することが多く、そのままでは平均が一定とはみなせません。
そこでここでは、時間に対する単回帰でトレンドを推定し、その残差を取り出すことで、定常化できるかを確認します。

ただし、ここで重要なのは、どの期間を切り出すかによって結果が変わることです。
長い期間をまとめて扱うと、景気後退局面や構造変化の影響で、単純な線形トレンドでは捉えきれない場合があります。
今回はその点を踏まえ、2010年以降のGDP系列を用いて検証します。

Pythonによる実装

まずはFREDから米国の実質GDP系列を取得します。
ここでは、単回帰によるトレンド除去が機能しやすい 2010年以降 のデータに絞ります。

# Fetch quarterly real GDP data from FRED
gdp = web.DataReader("GDPC1", "fred", start="2010-01-01", end="2026-01-01")["GDPC1"].dropna()

続いて、時間 index を説明変数にした単回帰を行い、線形トレンドを除去します。

# Create time trend
t = np.arange(len(gdp))

# Fit linear trend model
X = sm.add_constant(t)
model = sm.OLS(gdp, X).fit()

# Extract detrended series (residuals)
gdp_detrended = pd.Series(model.resid, index=gdp.index)

そのうえで、元系列とトレンド除去後の系列を上下2段で比較します。

# Plot original and detrended series
plot_series(
    original=gdp,
    transformed=gdp_detrended,
    labels=["Real GDP", "Detrended GDP"],
    titles=["Non-stationary series (Real GDP)", "After Detrending"]
)

最後に、元系列とトレンド除去後系列の両方にADF検定を適用します。

# Run ADF tests
p1 = run_adf_test(gdp, "Real GDP")
p2 = run_adf_test(gdp_detrended, "Detrended GDP")

時系列プロットの様子

時系列プロットの結果は下図2のようになります。上段のGDP系列は、2010年以降に限って見ても、全体として右肩上がりのトレンドを持っています。
そのため、水準系列のままでは平均が一定とは言えず、非定常とみなすのが自然です。

一方、下段のトレンド除去後系列では、長期的な上昇傾向が取り除かれ、ゼロ付近で上下する系列が得られます。
2020年ごろにはコロナ禍による大きな一時的変動が見られますが、トレンドそのものが大きく折れ曲がっているわけではないため、2010年以降の期間では単回帰によるデトレンドが比較的うまく機能します。

図2. トレンド除去の結果: 米国GDPの原系列(上段)と線形トレンド除去後(下段)

ADF検定の結果

ADF検定の結果は、例えば次のようになります。

ADF Test (Real GDP): p-value = 0.9862
ADF Test (Detrended GDP): p-value = 0.0020

元系列では p値 > 0.05 となり非定常、
トレンド除去後系列では p値 < 0.05 となり定常と判定されます。

結果の解釈

この結果から、2010年以降のGDP系列については、非定常性の主因が線形トレンドであり、そのトレンドを除去することで定常系列に近づけられることが確認できます。

ただし、ここで大事なのは、この結果は分析期間に依存するということです。
実際、2000年以降のGDP系列を使うと、2008–2009年のリーマンショックによる大きな落ち込みが含まれるため、単回帰による一本の直線では全体の動きを十分に表現できません。
その場合、デトレンド後の残差に非定常性が残り、ADF検定でも定常と判定されにくくなります。

つまり、GDPのようなマクロ系列では、

ということです。

実務でも、GDP水準をそのまま扱うのではなく、成長率トレンド除去後の残差を分析対象にしますが、その前提として「どの期間ならその処理が妥当か」を確認することが欠かせません。

👉 このケースからわかるのは、トレンド除去は有効な定常化手法だが、ショックや構造変化をまたぐと単純な線形回帰だけでは足りないことがある、という点です。



3. 小売売上データ|季節調整による定常化

最後に、強い季節性を持つ代表例として、米国の小売売上データを扱います。
小売売上は、年末商戦や季節イベントの影響を受けやすく、毎年ほぼ同じ時期に似たパターンが繰り返されます。こうした系列は、そのままでは季節要因が大きすぎて、基調的な変動やショックを見誤りやすくなります。FRED の RETAILSMNSA は月次・未季節調整の系列なので、この目的に適しています。

ここでは、株価やGDPと同じく

  1. データ取得
  2. 定常化処理
  3. 図示で比較
  4. ADF検定

の順に確認します。

Pythonによる実装

まず、FREDから小売売上の未季節調整系列を取得します。

# Fetch monthly retail sales data from FRED
retail = web.DataReader("RETAILSMNSA", "fred", start="2010-01-01", end="2026-01-01")["RETAILSMNSA"].dropna()

続いて、12か月周期を指定して季節分解を行います。月次データなので、周期は 12 です。

# Apply seasonal decomposition
decomp = seasonal_decompose(retail, model="additive", period=12)

# Extract seasonally adjusted residual series
retail_adjusted = decomp.resid.dropna()

ここでは seasonal_decompose により、系列を trend・seasonal・residual に分解しています。
そのうえで、季節性を取り除いた後の系列として、ここでは residual を分析対象にしています。seasonal_decompose は単純な分解法であり、statsmodels でもより高度な手法として STL などが案内されていますが、今回は「季節要因を分離する」という考え方をつかむことを優先して、この方法を使います。

そのうえで、元系列と季節調整後系列を上下2段で比較します。

# Plot original and seasonally adjusted series
plot_series(
    original=retail,
    transformed=retail_adjusted,
    labels=["Retail Sales (NSA)", "Seasonally Adjusted Residual"],
    titles=["Non-stationary series (Retail Sales)", "After Seasonal Adjustment"]
)

最後に、元系列と季節調整後系列の両方にADF検定を適用します。

# Run ADF tests
p1 = run_adf_test(retail, "Retail Sales (NSA)")
p2 = run_adf_test(retail_adjusted, "Seasonally Adjusted Residual")

時系列プロットの様子

時系列プロットの結果は下図3のようになります。上段の元系列を見ると、毎年ほぼ同じ時期に山と谷が現れ、強い季節性があることがわかります。特に年末に大きく伸びるパターンが繰り返されており、季節要因が系列全体を大きく支配しています。FRED の RETAILSMNSA 自体が未季節調整系列なので、このような周期性がそのまま残っています。

一方、下段の季節調整後系列では、その規則的な年周期の揺れがかなり弱まり、季節変動に隠れていた短期的な上下が見やすくなります。
このケースでは、単に「売上が増えた・減った」を見るのではなく、その変化が季節要因なのか、それともそれ以外のショックなのか を切り分けることが重要です。

図3. 季節性除去の結果: 小売売上の原系列(上段)とトレンド・季節性除去後(下段)

ADF検定の結果

ADF検定の結果は、例えば次のようになります。

ADF Test (Retail Sales (NSA)): p-value = 0.9841
ADF Test (Seasonally Adjusted Residual): p-value = 0.0000

元系列では p値 > 0.05 となり非定常、
季節調整後系列では p値 < 0.05 となり定常と判定されます。

結果の解釈

この結果から、小売売上のような季節性の強い系列では、そのまま分析すると毎年繰り返される季節パターンに引っ張られ、真に見たい変動を捉えにくいことがわかります。
そこで、季節成分を切り分けたうえで残差系列を見ることで、より安定した系列として扱いやすくなります。

ここで注意したいのは、今回使った seasonal_decompose単純な分解法だという点です。実務では、より滑らかで頑健な分解が必要な場合に STL などを使うこともあります。とはいえ、「まずは季節性を疑い、周期を指定して分解し、処理前後でADF検定を比べる」という流れ自体は、とても実践的です。

👉 このように、季節調整は、周期的な揺れが強い時系列に対して有効な定常化の入口になります。
需要予測や売上分析の場面では、まず季節性を取り除いてから、トレンドや外生ショックを評価する、という順番が重要です。



Discussion | 実務への示唆

ここまで、株価・GDP・小売売上という3種類の実データに対して、
それぞれ

という異なる定常化手法を適用してきました。

この結果からわかるのは、「非定常ならとりあえず差分を取ればよい」わけではない、ということです。
大事なのは、データに含まれている非定常性の原因を見極め、それに対応した前処理を選ぶことです。

非定常性には「種類」がある

今回の3つのケースは、それぞれ異なる種類の非定常性を持っていました。

見た目としてはどれも「そのままでは安定していない系列」に見えますが、
中身は同じではありません。
そのため、同じ前処理を一律に当てても、うまく定常化できるとは限りません。

たとえばGDPのケースでは、単回帰によるトレンド除去が有効でしたが、
それは 2010年以降という比較的素直な期間に限定した場合 でした。
2008–2009年のような大きな景気後退をまたぐと、一本の直線で全体を説明するのは難しくなります。

つまり実務では、
「どの手法を使うか」だけでなく、「どの期間を分析対象にするか」も同じくらい重要 です。



まず見るべきは、p値ではなく系列の形

ADF検定は非常に便利ですが、実務では 最初にp値だけを見る のは危険です。
今回も、各ケースでまず系列を図示し、そのうえでADF検定を行いました。

この順番が大切なのは、ADF検定が「なぜ非定常なのか」までは教えてくれないからです。
p値が大きかったとしても、その原因が

は、グラフを見ないと判断できません。

実務で時系列データを受け取ったときには、いきなりモデルを当てるのではなく、

  1. まず時系列プロットを見る
  2. 非定常性の原因を仮説として持つ
  3. その仮説に合った前処理を試す
  4. 最後にADF検定で確認する

という順番で進めるのが安全です。



「定常化」は分析前の下ごしらえである

定常化は、それ自体が目的ではありません。
本来の目的は、その先にある

を、誤った前提なしに行える状態に整えることです。

非定常な系列をそのまま使うと、第1回で見たように、
本当は関係のない系列どうしに強い相関があるように見えてしまうことがあります。
これは、統計的にはもっともらしい結果が出ていても、
意思決定に使うと危険な典型例です。

実務では「モデルが回るかどうか」よりも、
その結果を意思決定に使ってよいかどうか のほうが重要です。
その意味で、定常化は単なる前処理ではなく、
分析結果を判断に使える形に整えるための前提条件 と言えます。



一度の処理で終わるとは限らない

今回の記事では、それぞれ代表的な1手法を使いましたが、
実際のデータでは、1回の処理だけで十分とは限りません。

たとえば、

といったことは、実務では珍しくありません。

そのため、実際の分析では

を組み合わせながら、何度か試行錯誤することになります。

重要なのは、
「正しい手法を最初から一発で当てること」ではなく、系列の性質を見ながら妥当な処理を絞り込んでいくこと です。



実務での使い分けの目安

今回の内容を、実務上の感覚としてまとめると、次のようになります。

もちろん現実のデータは、これらが一つだけとは限りません。
トレンドと季節性を同時に持つ系列もあれば、
ショックで途中から性質が変わる系列もあります。

だからこそ、
「このデータは何が原因で非定常なのか?」を先に考えること が、
時系列分析の出発点になります。



Conclusion | まとめと次回予告

今回は、これまで学んできた定常性と定常化の考え方を、
実際のデータに適用して確認しました。

扱ったのは、次の3種類の時系列です。

それぞれ、非定常性の原因は異なっていました。
株価はランダムウォーク的な動き、GDPは長期トレンド、小売売上は季節性が支配的でした。
そして、その原因に応じた前処理を行うことで、処理後の系列がより安定し、ADF検定でも定常と判定されることを確認しました。

ここで重要なのは、定常化は機械的に行うものではないという点です。
まず系列を見て、何が非定常性の原因なのかを考え、それに応じた方法を選ぶ。
そのうえで、処理後の系列をもう一度図示し、ADF検定で確認する。
この流れこそが、実務で時系列データを扱うときの基本になります。

また、GDPの例で見たように、
同じ手法でも分析期間によって結果が変わることがあります。
つまり、前処理の選択だけでなく、「どの期間を分析対象にするか」も重要です。
時系列分析では、データ全体を一括で処理するのではなく、
構造変化や大きなショックの有無を意識しながら扱う必要があります。

今回の記事を通じて、

が、より具体的にイメージできるようになったのではないでしょうか。

次回予告

次回は、こうして定常化した系列を使って、
実際に時系列モデルを当てるステップ に進みます。
定常化はあくまでスタート地点です。
その先で、どのようにモデル化し、予測や解釈につなげるのかを見ていきましょう。



References | Pythonコードの全文

今回の記事で書いたPythonコード(Jupyter Notebook)の全文は以下です。ご参考にどうぞ!




モバイルバージョンを終了