時系列データを扱うとき、原系列が傾向変動・季節変動・不規則変動という基本成分の合成で成り立っていると捉えることがある。 傾向変動は中長期的な増加・減少といった変化であり、季節変動は例えば 1 ヶ月や 1 年といった周期的な変化を指している。 不規則変動は、前者 2 つに当てはまらない変化で、誤差変動と特異的変動に分けて考える場合もあるようだ。
原系列が基本成分の合成と考える場合でも、捉え方として加法モデルと乗法モデルにさらに分かれる。 まず、加法モデルでは傾向変動 と季節変動 、誤差変動 の和を原系列 と捉える。 この考え方では、傾向変動の大きさに関係なく一定の季節変動が加えられる。
一方で、乗法モデルでは積と捉える。 つまり、傾向変動が大きくなれば、それに比例して季節変動も大きくなる、という考え方。
これらは扱うデータやタスクによって使い分ける必要があるらしい。 今回は、Python の statsmodels を使って原系列を基本成分に分解してみる。
使った環境は次のとおり。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.14.6 BuildVersion: 18G3020 $ python -V Python 3.7.7
下準備
まずは、必要なパッケージをインストールしておく。
$ pip install statsmodels seaborn
現系列を基本成分に分解する
現系列を基本成分に分解するには seasonal_decompose()
関数を使う。
次のサンプルコードでは、旅客機の乗客数のデータを分解してみた。
なお、モデルとしては乗法モデルを仮定している。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import pandas as pd import seaborn as sns from statsmodels import api as sm from matplotlib import pyplot as plt from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() def main(): # 旅客機の乗客数のデータセットを読み込む df = sns.load_dataset('flights') df['year-month'] = df.month.astype(str) + ', ' + df.year.astype(str) df['year-month'] = pd.to_datetime(df['year-month'], format='%B, %Y') df = df.set_index('year-month') # 時系列データを傾向変動・季節変動・残差に分解する decompose_result = sm.tsa.seasonal_decompose(df['passengers'], # 乗法モデルを仮定する model='multiplicative') # これでもグラフが描ける # decompose_result.plot() # 描画する領域を用意する fig, axes = plt.subplots(nrows=4, ncols=1, figsize=(8, 8), sharex=True) # 原系列 axes[0].set_title('Observed') axes[0].plot(decompose_result.observed) # 傾向変動 axes[1].set_title('Trend') axes[1].plot(decompose_result.trend) # 季節変動 axes[2].set_title('Seasonal') axes[2].plot(decompose_result.seasonal) # 残差 (不規則変動 = 誤差変動 + 特異的変動) axes[3].set_title('Residual') axes[3].plot(decompose_result.resid) # グラフを表示する plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python decompose.py
次のようなグラフが得られる。 上から原系列、傾向変動、季節変動、不規則変動 (残差) となっている。
注意すべきポイントとして、seasonal_decompose()
関数では傾向変動と不規則変動は端の要素が NaN になってしまうようだ。
原系列と傾向変動で偏自己相関を見比べてみる
時系列データに周期性、つまり季節成分が含まれるか調べる方法のひとつに、偏自己相関を調べるというのがある。 今回は、分解する前の原系列と、分解した後の傾向変動について、自己相関と偏自己相関を調べて見比べてみよう。 自己相関では、ある時点のデータ には 1 つ前のデータ が関係している可能性がある。 そして、1 つ前のデータには、さらにその 1 つ前のデータが、というような積み重ねの影響を受けている恐れがある。 偏自己相関は、そのような積み重ねの影響を消去することを目的とした統計量になっている。
次のサンプルコードでは、分解する前の原系列と分解した後の傾向変動について自己相関と偏自己相関を計算してグラフにプロットしている。
なお、データに NaN
があると自己相関がうまく計算できないようなので、傾向変動からは NaN
を除外している。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import pandas as pd import seaborn as sns from statsmodels import api as sm from matplotlib import pyplot as plt from pandas.plotting import register_matplotlib_converters register_matplotlib_converters() def main(): # 旅客機の乗客数のデータセットを読み込む df = sns.load_dataset('flights') df['year-month'] = df.month.astype(str) + ', ' + df.year.astype(str) df['year-month'] = pd.to_datetime(df['year-month'], format='%B, %Y') df = df.set_index('year-month') # 時系列データを傾向変動・季節変動・残差に分解する decompose_result = sm.tsa.seasonal_decompose(df['passengers'], # 乗法モデルを仮定する model='multiplicative') _, axes = plt.subplots(nrows=2, ncols=2, figsize=(8, 8)) # 原系列の ACF sm.tsa.graphics.plot_acf(df['passengers'], ax=axes[0][0]) # 原系列の PACF sm.tsa.graphics.plot_pacf(df['passengers'], ax=axes[1][0]) # 傾向変動の ACF sm.tsa.graphics.plot_acf(decompose_result.trend.dropna(), ax=axes[0][1]) # 傾向変動の PACF sm.tsa.graphics.plot_pacf(decompose_result.trend.dropna(), ax=axes[1][1]) # グラフを表示する plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python decompacf.py
次のようなグラフが得られる。 左側が原系列、右側が傾向変動について計算したもの。 青い帯は「相関がない」を帰無仮説とした 95% 信頼区間を表している。 つまり、帯の外にある値は 5% の有意水準で相関があることになる。
原系列の偏自己相関では、ところどころに相関があることがわかる。 それに対して、傾向変動の偏自己相関では、ラグ 1 以外に相関がある箇所は見当たらない。 つまり、季節変動を取り除くことができていることになるようだ。
いじょう。
時系列解析: 自己回帰型モデル・状態空間モデル・異常検知 (Advanced Python)
- 作者:直希, 島田
- 発売日: 2019/09/07
- メディア: 単行本
- 作者:もみじあめ
- 発売日: 2020/02/29
- メディア: Kindle版
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者:もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版