グレンジャー因果検定について
概要
仕事でグレンジャー因果検定を使う機会があったので、グレンジャー因果検定について勉強したことを記載します。また、statsmodelsのAPIを使い、株式データを使って簡単な検定をしてみました。
間違いがありましたら、コメントいただけたら嬉しいです。
グレンジャー因果検定
考え方
時系列データ , において、 が増減すると も同じように増減するという関係なのかを検証したい。
未来の の値の予測に、現在と過去の の値を使って予測した時より、 の値も加えて予測したほうが精度が改善される時、 から にGrengerの意味で因果があるという。
モデルにVARを利用し、F検定で統計的有意性の検定します。
確かに が増減したら も同じように増減する関係なら、 の予測に の値も知っているモデルの方が精度が高く、その逆も言えると考えるのは感覚的も理解しやすいです。しかし、一般的に変数が多ければモデルの精度は高くなりやすいので、それが統計的有意性があるかを調べる必要があり、それを2つのモデルの誤差の比が十分に大きいかF検定で調べます。
ARモデル(自己回帰モデル)
グレンジャー因果検定で使うVARモデル(ベクトル自己回帰モデル)を理解するために、ARモデル(自己回帰モデル)を理解する。
AR(p)モデルは、 時点での の値を 期内の の過去値の加重和で表現したモデル。 は定数項、 は自己回帰係数、 はホワイトノイズ(期待値0, 分散を持ち、自己共分散0の誤差項)。
()
(正確にはは実数に値をもつ確率変数)
VARモデル(ベクトル自己回帰モデル)
VAR(p)モデルは、AR(p)モデルをベクトルに拡張したもの。 と は の列ベクトル、 は の行列。
()
(正確には は実n次元数ベクトルに値をもつ確率変数ベクトル)
例えば、下記のようなVAR(2)モデルは
下記のように書き換えられる
検定方法
考え方で記載したとおり、 を予測するとき、 の値を含めた方が精度が高い、つまり
のほうが
より誤差が小さいことを示したい。
から にGrengerの意味で因果がないことを帰無仮説とし、2つのモデルの誤差を使いF検定で帰無仮説を棄却する流れになる。
上記の式だと、 ( の係数が )と同値になる。
F検定統計量は、式 (a) をOLSで推定した時の誤差(残差平方和) と、式 (b) をOLSで推定した時の誤差(残差平方和) を使って以下のように表され、 は漸近的にカイ2乗分布 ( ) に従うので、 の値が の95%点より大きければ帰無仮説を棄却できる。
ここで は制約(xの係数を0とする制約)の数、 はサンプル数。
F検定
F検定では2つの分散の比がF分布に従うかを調べることで、帰無仮説を母分散が等しい(2つの分散に差がない、今回の場合は2つのモデルの誤差に差がない)、対立仮説を母分散が等しくない(2つの分散に差がある、今回の場合は2つのモデルの誤差に差がある)として、検定を行います。
上記の説明ではF検定統計量を使用していますが、以下の例に記載するstatsmodelsの例ではp値を使用し、p値が0.05未満であれば有意水準5%で有意であり帰無仮説は棄却するとして、検定を行います。
注意点
サンプル数が多い時
単純にサンプル数が多ければ多いほどp値は小さくなりやすいので、サンプル数が多い時はp値に惑わされないよう注意する必要があるようです。
交絡因子について
交絡因子(間接的に影響する変数)が存在していても「Grengerの意味で因果がある」となる可能性があります。因果関係を調べるには、十分ではないことに注意する必要があるようです。
株式データを使って検定
statsmodelsのグレンジャー因果検定
statsmodelsに、grangercausalitytestsが用意されているのでそれを使い、データはkaggleのdatasetのS&P500のデータを使います。
- statsmodels ドキュメント: statsmodels.tsa.stattools.grangercausalitytests — statsmodels
- Kaggle Dataset : S&P 500 stock data | Kaggle
株に関しては完全に素人なのですが、単純に前日の市場が開いてる間に値上がりすれば(前日終値 - 前日始値 > 0)買いな株と判断され、前場の寄り付き前に値上がりして、当日の始値は前日の終値より高いじゃないか(当日始値 - 前日終値 > 0)、逆に前日に値が下がったら、寄り付き前に値下がるのじゃないかと考え検証してみます。
値上がり金額 を とし、始値と前日終値との差を とした時、 から へGrengerの意味で因果があるかを調べます。
grangercausalitytestsの第一引数にはarray_likeなデータを指定しますが、
The data for test whether the time series in the second column Granger causes the time series in the first column.
と書かれている通り、1番目のカラムには結果側のデータ 、2番目のカラムには起因側のデータ となっている配列を指定します。今回は前日の値上がり金額に対して検証したいのでmaxlagを1にします。
import pandas as pd from statsmodels.tsa.stattools import grangercausalitytests import matplotlib.pyplot as plt pd.options.plotting.backend = "plotly" PATH = '/kaggle/input/sandp500/individual_stocks_5yr/individual_stocks_5yr/' # Exxon Mobil Corporation のデータ XOM = pd.read_csv(f'{PATH}/XOM_data.csv', index_col='date', parse_dates=True) # 期間を限定 XOM = XOM[(XOM.index >= pd.to_datetime('2017-07-01')) & (XOM.index <= pd.to_datetime('2017-12-31'))] # 終値と始値の差 XOM['price_increase'] = XOM['close'] - XOM['open'] # 始値と前日終値との差 XOM['before_opening'] = XOM['open'] - XOM['close'].shift() # データ確認 XOM.head() XOM[['price_increase', 'before_opening']].plot() # グレンジャー因果検定の結果取得 x = XOM[['before_opening', 'price_increase']][1:].values results = grangercausalitytests(x=x, maxlag=[1], verbose=False) print(results)
>> {1: ({'ssr_ftest': (2.137422914716022, 0.14633552782619325, 121.0, 1), 'ssr_chi2test': (2.190416871279229, 0.1388717255785229, 1), 'lrtest': (2.171295251305196, 0.1406077610113857, 1), 'params_ftest': (2.137422914716034, 0.14633552782619272, 121.0, 1.0)}, [<statsmodels.regression.linear_model.RegressionResultsWrapper at 0x7f86b1e70c10>, <statsmodels.regression.linear_model.RegressionResultsWrapper at 0x7f86b1e87d90>, array([[0., 1., 0.]])])}
4つのタイプで検定された結果が返ってきます。lagのkeyに対して、各検定の結果が格納されています。検定名がkeyで、valueは1番目の要素に検定統計量、2番目の要素にp値が入ってます。params_ftestとssr_ftestはF分布に従うとした場合の検定、ssr_chi2testとlrtestはカイ2乗分布に従うとした場合の検定です。
全ての検定でp値が0.05を大きく上回り帰無仮説が棄却されませんでした...やはりそんな簡単な話じゃなさそうですね😇
例えば、前日に値上げされたら、当日の始値と前日の始値の差はプラスになりそうです。前日終値が前日始値よりすでに高いので。値上げ金額を とし、始値と前日始値との差を とした時であれば、 から へGrengerの意味で因果がある、となりそうなので、検定してみます。
# 値上がり金額 XOM['price_increase'] = XOM['close'] - XOM['open'] # 始値と前日始値の差 XOM['diff_open'] = XOM['open'] - XOM['open'].shift() XOM[['price_increase', 'diff_open']].plot() x = XOM[['diff_open', 'price_increase']][1:].values results = grangercausalitytests(x=x, maxlag=[1], verbose=False) results
>> {1: ({'ssr_ftest': (168.13795446547945, 1.220451046664481e-24, 121.0, 1), 'ssr_chi2test': (172.30666408032607, 2.3194627990098294e-39, 1), 'lrtest': (108.01805908422944, 2.663402872664279e-25, 1), 'params_ftest': (168.13795446547954, 1.2204510466644514e-24, 121.0, 1.0)}, [<statsmodels.regression.linear_model.RegressionResultsWrapper at 0x7f86b1e87a90>, <statsmodels.regression.linear_model.RegressionResultsWrapper at 0x7f86b1f10e90>, array([[0., 1., 0.]])])}
今回は4つの検定でp値が0.05を大きく下回ったため帰無仮説を棄却し、Grengerの意味で因果があると言えそうです。
おわり
株価での検定はあまり良い例が浮かばず、無理矢理感がありました...(本当は、この銘柄が上がると数日後にこの銘柄も上がるかも、みたいなペアを見つけたかった)。ただ株式データでデータ分析するのは面白そうだと思ったので、時系列データについて勉強を進めたらまた触ってみたいと思いました。
F分布周りは完全には理解できてないので、統計検定の勉強を進めながら理解していきたいです。