Loading [MathJax]/jax/output/CommonHTML/jax.js
본문 바로가기
통계/시계열 모형

[시계열 분석] 9. (Augmented) Dickey-Fuller Test(검정) with Python

by 부자 꽁냥이 2022. 4. 14.

이번 포스팅에서는 단위근 검정을 위한 대표적인 방법으로 Dickey-Fuller Test(검정)과 이를 확장한 Augmented Dickey Fuller Test(검정)에 대한 내용을 알아본다. 또한 Python(파이썬)을 이용하여 (Augmented) Dickey-Fuller Test(검정)을 어떻게 수행하는지 알아본다.

 

본 포스팅을 보기에 앞서 지난 포스팅에서 다룬 단위근 검정과 Dickey-Fuller Test에 대한 기본적인 내용을 읽으면 좋다.

 

여기서 다루는 내용은 다음과 같다.

 

1. Dickey-Fuller Test(검정)

2. Augmented Dickey-Fuller Test(검정)

3. (Augmented) Dickey-Fuller Test(검정) 장단점

4. Python 예제



본 포스팅에서는 수식을 포함하고 있습니다.

티스토리 피드에서는 수식이 제대로 표시되지 않을 수 있으니

PC 웹 브라우저 또는 모바일 웹 브라우저에서 보시기 바랍니다.


반응형

   1. Dickey-Fuller Test(검정)

1) 정의

Dickey-Fuller Test(검정)은 단위근을 검정하는 하나의 방법이다.

 

먼저 아래와 같이 1차 자기회귀모형 AR(1)이 있다고 하자.

Xt=ρXt1+Wt

이때 시계열 이론에 따르면 |ρ|<1인 경우 식(1.1)은 정상 과정(Stationary Process)이며 ρ=1인 경우 단위근을 갖고 식 (1.1)은 비정상 과정(Non-Stationary Process)이 된다. 


- 참고 -

1. |ρ|>1인 경우는?

시계열 이론에 따르면 |ρ|>1인 경우도 적절한 변환을 통해 Xt1의 계수를 1보다 작게 만들 수 있다고 한다. 따라서 |ρ|>1인 경우는 생각하지 않아도 된다.

2. 단위근 존재를 파악해야 하는 이유?

단위근이 있으면 ρ 추정치에 편향(Bias)이 생기며 다변량 시계열 분석 시 두 시계열에 실제로 관계가 없지만 관계가 있는 가짜 회귀 모형을 만들 수 있다고 한다.


Dickey-Fuller Test(검정)은 귀무가설 H0:ρ=1 vs H1:ρ<1을 검정한다. 만약 귀무가설을 채택한다면 식 (1.1)은 단위근을 가지며(따라서 비정상 과정(Non-Stationary Process) 이다) 기각한다면 식 (1.1)은 정상 과정(Stationary Process)이라 할 수 있다. 사실 절대값이 아닌 ρ<1이므로 완전한 의미에 정상 과정은 아니다.

 

식 (1.1)을 변형해보자. 양변에 Xt1을 빼준다. 그러면 

ΔXt=XtXt1=(ρ1)Xt1+Wt=δXt1+Wt

이 된다. 식 (1.2)를 이용하면

Xt has a unit rootXt=Xt1+WtΔXt=0Xt1+Wt

이 된다. 이는 Xt가 단위근을 갖는다는 것은 δ=0인 것과 같고 따라서 Dickey-Fuller Test(검정)은 귀무가설 H0:δ=0 vs H1:δ<0을 검정하게 된다.

 

식 (1.2)는 Xt1ΔXt의 관계를 통해 단위근을 식별하는 것이라고 생각했다. 여기서 δ<0이면 즉, Xt1이 (Xt2보다) 증가할 때(감소할 때) ΔXt이 감소하면(증가하면) 시계열 Xt1은 정상 과정이라는 것과 같다.

 

Dickey-Fuller Test(검정)은 아래와 같이 3 버전이 있다.

(1) 단위근 검정

ΔXt=δXt1+Wt

(2) 절편항을 포함하는 단위근 검정

ΔXt=a0+δXt1+Wt

(3) 절편항과 선형 추세를 포함하는 단위근 검정

ΔXt=a0+a1t+δXt1+Wt

 

---파이썬 패키지를 보니 2차 곡선형 추세를 포함하는 단위근 검정도 나온 것 같다. --

 

Dickey-Fuller Test(검정)은 3가지 추세에 대하여 더 정확하게 검정할 수 있도록 만들어 놓았으며 각 경우에 대하여 귀무가설이 참일 때 검정 통계량의 점근 분포가 달라진다. 즉, 각 경우에 기각값이 달라진다.


이때 궁금증이 생겼다. 이론적으로는 알겠으나 δ<0인 것이 정상성(Stationary)과 어떻게 직관적으로 연결될까? δ<0이라는 것은 아까도 말했지만 Xt1(Xt2보다) 증가할 때(감소할 때) ΔXt이 감소하면(증가하면) 시계열 Xt1은 정상 과정이라는 것과 같다.

 

정상 과정(Stationary Process)의 경우 아래와 같이 결정적 추세선 주위로 왔다 갔다(?)할 것이다.

 

정상 과정(Stationary Process) - Dickey-Fuller Test(검정) 설명1

먼저 Xt1값이 Xt2보다 작은 경우를 살펴보자. 만약 δ<0이면 Xt값은 추세선 쪽으로 더 가까워지거나(case 1) 멀어지더라도 조금만 멀어질 것이다(case 2).

왜냐하면 δXt2에서 Xt1로 변할 때 ΔXt1에서 ΔXt로 변하는 평균적인 변화량이므로 근사적으로

ΔXtΔXt1Xt1Xt2라고 볼 수 있고 이 값이 음수인 경우는 case 1, case 2 밖에 없기 때문이다.

 

따라서 δ<0이 의미하는 바는 시계열 데이터가 추세선 근방으로 왔다 갔다 한다고 볼 수 있고 이는 곧 해당 시계열이 정상 과정(Stationary Process) 임을 암시한다.

 

이번엔 Xt1값이 Xt2보다 큰 경우를 살펴보자. 이때에도 δ<0이면 Xt값은 추세선 쪽으로 더 가까워지거나(case 1) 멀어지더라도 조금만 멀어질 것이다(case 2).

앞에서 설명했던 것과 동일하게 이 경우에도 δ<0이면 해당 시계열이 정상 과정(Stationary Process) 임을 암시한다.

 

따라서 δ<0이라는 것은 해당 시계열이 정상 과정임을 직관적으로 이해할 수 있다. 식 (1.1)을 식 (1.2)로 바꾸는 이유가 아마도 대립 가설(H1:δ<0) 을 바꿔줌으로써 시계열의 정상성을 좀 더 직관적으로 이해할 수 있게 함이었다는 생각이 들었다.


아직 끝나지 않았다...

 

데이터를 살펴보고 모델링을 하겠지만 안타깝게도 우리는 모형 (1.3)~(1.5) 중에서 어떤 모형이 진짜인지 알 수 없다. 따라서 δ 뿐만 아니라 a0,a1에 대해서도 검정을 해야 한다. Dickey, Fuller 1981년 논문 "Likelihood Ratio Statistics for Autoregressive Time Series with a Unit Root"에서 선형 추세 계수 a0,a1δ를 동시에 검정할 수 있는 통계량과 그에 대한 기각값을 시뮬레이션을 통해 테이블로 제시했다.

 

절편이 없는 단위근 검정(1.3)의 경우는 하나만 검정하면 된다.

(τ1) H0:δ=0 vs H1:δ<0

 

다음으로 절편이 있는 단위근 검정(1.4)은 아래와 같이 귀무가설 두 개를 검정한다. 

(ϕ1) H0:ΔXt=Wt (Reduced Model)  vs H1:ΔXt=a0+δXt1+Wt (Full Model) 

(τ2) H0:δ=0 vs H1:δ<0

 

마지막으로 절편과 선형 추세가 있는 단위근 검정(1.5)은 세 개의 귀무가설을 검정한다. 

(ϕ2) H0:ΔXt=Wt (Reduced Model)  vs H1:ΔXt=a0+a1t+δXt1+Wt (Full Model) 

(ϕ3) H0:ΔXt=a0+Wt (Reduced Model)  vs H1:ΔXt=a0+a1t+δXt1+Wt (Full Model) 

(τ3) H0:δ=0 vs H1:δ<0

2) 검정 통계량

검정 통계량은 계산하기 위해 식 (1.3)~(1.5)을 모델로 하는 선형 회귀 모형을 최소 제곱법으로 추정한다.

예를 들어 식 (1.3)의 경우 ΔXt를 반응 변수, Xt1을 설명 변수로 하는 절편 없는 선형 모형을 최소 제곱법으로 추정한다.  식 (1.5)의 경우 ΔXt를 반응 변수, 시간 인덱스 tXt1을 설명 변수로 하는 절편 있는 선형 모형을 최소 제곱법으로 추정한다. 

 

이때 Xt1에 대응하는 회귀 계수 추정치를 ˆδ라 할 때 τ1,τ2,τ3검정 통계량 T 다음과 같다.

T=ˆδstd(ˆδ)

이때 std(ˆδ)ˆδ의 표준편차 추정치이다. 

그리고 ϕ1,ϕ2,ϕ3 검정에 대한 검정 통계량 F는 아래와 같이 계산한다.

F=SSE( RM )SSE( FM )DF of Residual for RM DF of Residual for FM ÷SSE( FM )DF of Residual for FM 

여기서 SSE()은 모델에 대한 잔차 제곱합이며 RM은 Reduced Model, FM은 Full Model이며 DF of Residual for RM와 DF of Residual for FM은 각각 Reduced Model, Full Model에 대한 오차 자유도이며 이는 데이터 개수에서 추정하려는 회귀 계수를 빼면 얻을 수 있다.

3) 검정 방법

통계량 TF이 계산되면 다음으로 Dickey가 제시한 기각값 테이블을 비교해야 한다(TF에 대한 테이블이 따로 있다). 이때 각 T 통계량과 F 통계량에 대한 기각값을 T,F라 하면

T<T인 경우 기각,  F>F인 경우 기각한다.

 

이때 T 통계량에 대한 테이블은 다음과 같다.

dickey_fuller_tau_table.xlsx
0.01MB
(Augmented) Dickey Fuller Test(검정) 기각값 테이블1

다음은 F 통계량에 대한 테이블이다. 

dickey_fuller_phi_table.xlsx
0.01MB
(Augmented) Dickey Fuller Test(검정) 기각값 테이블2


   2. Augmented Dickey-Fuller Test(검정)

1) 정의

Dickey-Fuller Test(검정)은 AR(1) 모형에서 단위근 검정이라고 한다면 Augmented Dickey-Fuller Test(검정)은 AR(p) 모형에서의 단위근 검정이다.

 

먼저 AR(p) 모형을 생각해보자.

Xt=ϕ1Xt1++ϕpXtp+Wt

여기서 Wt는 백색 잡음이다.

단위근을 의미하는 귀무가설을 만들고 싶다. 앞에서 Dickey-Fuller Test(검정)처럼 (H0:ρ=1 vs H1:ρ<1 임을 상기하자) 간단하게 만들고 싶은데 지금 상태로는 간단하게 정리가 안된다.

 

식 (2.1)을 변형해보자. Back Shift 연산자 B를 도입해보자. 이 말인즉슨, BpZt=Ztp이다.

 

식 (2.1)은 다음과 같이 쓸 수 있다.

(1ϕ1BϕpBp)=Wt

이때 ρ=ϕ1+ϕ2++ϕp,

βj=(ϕj+1+ϕj+2++ϕp),j=1,2,,p1

라고 하면 식 (2.2)는 다음과 같다.

[(1ρB)(β1B+β2B2++βp1)(1B)]Xt=WtXt=ρXt1+β1ΔXt1+β2ΔXt2++βp1ΔXtp+1+Wt

이제 식 (2.1)을 (2.3)의 우변으로 대체한다.

Xt=ρXt1+β1ΔXt1+β2ΔXt2++βp1ΔXtp+1+Wt

이때 식 (2.4)에서 ρ=1면 단위근을 갖게 된다. 따라서 단위근을 의미하는 귀무가설과 대립 가설을 쉽게 정의할 수 있다.

H0:ρ=1 vs H1:ρ<1

이때 δ=ρ1이라 하고 식 (2.4)에서 양변에 Xt1을 빼주면 다음과 같다.

ΔXt=δXt1+β1ΔXt1+β2ΔXt2++βp1ΔXtp+1+Wt

그러면 Augmented Dickey-Fuller Test(검정)은 다음의 가설을 검정하게 된다.

H0:δ=0 vs H1:δ<0

 

Augmented Dickey-Fuller Test(검정) 또한 세 가지 케이스가 있다.

(1) 단위근 검정

ΔXt=δXt1+p1j=1βjΔXtj+Wt

(2) 절편항을 포함하는 단위근 검정

ΔXt=a0+δXt1+p1j=1βjΔXtj+Wt

(3) 절편항과 선형 추세를 포함하는 단위근 검정

ΔXt=a0+a1t+δXt1+p1j=1βjΔXtj+Wt

 

절편이 없는 단위근 검정(2.6)의 경우는 하나만 검정하면 된다.

(τ1) H0:δ=0 vs H1:δ<0

 

다음으로 절편이 있는 단위근 검정(2.7)은 아래와 같이 귀무가설 두 개를 검정한다. 

(ϕ1) H0:ΔXt=p1j=1βjΔXtj+Wt (Reduced Model)  vs H1:ΔXt=a0+δXt1+p1j=1βjΔXtj+Wt (Full Model) 

(τ2) H0:δ=0 vs H1:δ<0

 

마지막으로 절편과 선형 추세가 있는 단위근 검정(2.8)은 세 개의 귀무가설을 검정한다. 

(ϕ2) H0:ΔXt=p1j=1βjΔXtj+Wt (Reduced Model)  vs H1:ΔXt=a0+a1t+δXt1+p1j=1βjΔXtj+Wt (Full Model) 

(ϕ3) H0:ΔXt=a0+p1j=1βjΔXtj+Wt (Reduced Model)  vs H1:ΔXt=a0+a1t+δXt1+p1j=1βjΔXtj+Wt (Full Model) 

(τ3) H0:δ=0 vs H1:δ<0

2) 차수 p의 결정

부분 자기 상관 함수(Partial Autocorrelation Function)를 이용할 수도 있으나 여기에서는 AIC(Akaike Information Criterion) 또는 BIC(Bayesian Information Criterion)을 최소화하는 차수를 결정한다.

3) 검정 통계량

이 부분은 앞서 살펴본 Dickey-Fuller Test(검정)과 매우 유사하다. 먼저 검정 통계량은 계산하기 위해 식 (2.6)~(2.8)을 모델로 하는 선형 회귀 모형을 최소 제곱법으로 추정한다.

예를 들어 식 (2.6)의 경우 ΔXt를 반응 변수, Xt1,ΔXtj,j=1,,p1을 설명 변수로 하는 절편 없는 선형 모형을 최소 제곱법으로 추정한다.  식 (2.8)의 경우 ΔXt를 반응 변수, 시간 인덱스 tXt1,ΔXtj,j=1,,p1을 설명 변수로 하는 절편 있는 선형 모형을 최소 제곱법으로 추정한다. 

 

이때 Xt1에 대응하는 회귀 계수 추정치를 ˆδ라 할 때 τ1,τ2,τ3의 검정 통계량 T은 다음과 같다.
T=ˆδstd(ˆδ)
이때 std(ˆδ)ˆδ의 표준편차 추정치이다. 
그리고 ϕ1,ϕ2,ϕ3 검정에 대한 검정 통계량 F는 아래와 같이 계산한다.
F=SSE( RM )SSE( FM )DF of Residual for RM DF of Residual for FM ÷SSE( FM )DF of Residual for FM 
여기서 SSE()은 모델에 대한 잔차 제곱합이며 RM은 Reduced Model, FM은 Full Model이며 DF of Residual for RM와 DF of Residual for FM은 각각 Reduced Model, Full Model에 대한 오차 자유도이며 이는 데이터 개수에서 추정하려는 회귀 계수를 빼면 얻을 수 있다.

4) 검정 방법

이는 앞서 Dickey-Fuller Test(검정)에서 살펴본 것과 같이 테이블을 이용하여 기각 여부를 결정한다.

반응형

   3. (Augmented) Dickey-Fuller Test(검정) 장단점

- 장점 -

1. 직관적이다.

귀무가설과 대립 가설의 의미를 직관적으로 이해할 수 있다.

2. 구현이 쉽다.

(Augmented) Dickey-Fuller Test(검정)에서 선형 회귀 모형을 이용한 회귀 계수와 잔차 제곱합 등을 이용하여 검정 통계량이 만들어지고 기각 테이블도 있으므로 구현이 쉽다.

- 단점 -

1. 불완전한 대립 가설

사실 AR(1)의 정상성 조건은 |ρ|<1인데 대립 가설이 ρ<1이므로 대립 가설이 완벽하진 않다. 즉, 검정 통계량이 매우 작다면 주어진 시계열이 정상성을 갖는다고 확실히 말할 수 있는지 의문이 든다. 이 부분은 따로 찾아봐야 할 것 같다. 

2. 약한 검정력(Low Power)

(Augmented) Dickey-Fuller Test(검정)의 검정력은 약하다고 알려져 있으며 특히 실제 ρ가 1보다 작지만 가까운 경우(예 ρ=0.95) 이를 잘 식별하지 못한다고 한다. 다시 말해 단위근을 갖지 않는 경우이지만 단위근을 갖는다고 결론을 내릴 가능성이 많다고 한다. 하지만 이는 대부분의 단위근 검정들이 갖고 있는 단점이기도 하다.

3. 데이터 개수(Sample Size)가 적은 경우 검정 정확성 문제

실제로 귀무가설이 참일 때 검정 통계량의 분포는 점근 분포(Asymptotic Distribution)라서 샘플 사이즈가 작은 경우 검정 통계량의 소표본 분포가 점근 분포와 매우 달라질 수 있어서 검정의 정확도가 떨어질 수 있다. 다시 말해 샘플 사이즈가 작은 경우 귀무가설이 참일 때 기각하는 제1종 오류를 범하는 비율이 테이블에서 정한 신뢰 수준과 매우 큰 차이가 날 수 있다.

 


   4. Python 예제

1) 함수

Augmented Dickey-Fuller Test(검정)을 수행하는 파이썬 함수를 만들어보자. 이 함수는 R에서 "urca" 패키지의 ur.df 함수를 참고했다.

 

먼저 ΔXtj,j=1,,p1 변수들을 하나의 행렬(Matrix)로 만들어주는 embed 함수와 F 통계량을 계산하는 get_f_stat 함수를 만들었다. get_f_stat 함수는 statsmodels에서 제공하는 회귀 분석 결과 2개(reduced_model, full_model)를 받는다.

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import statsmodels.api as sm
def embed(z, lags):
res = []
for i in range(lags):
temp_z = np.expand_dims(z[lags-1-i:len(z)-i], axis=1)
res.append(temp_z)
res = np.concatenate(res, axis=1)
return res
def get_f_stat(reduced_model, full_model):
df_num = reduced_model.df_resid - full_model.df_resid
df_denom = full_model.df_resid
numerator = reduced_model.mse_resid*reduced_model.df_resid - full_model.mse_resid*full_model.df_resid
denominator = full_model.mse_resid*full_model.df_resid
f_stat = (numerator/df_num)/(denominator/df_denom)
return f_stat

 

다음은 실제로 Augmented Dickey-Fuller Test(검정)을 수행하는 함수이다. 시계열 데이터 y, 차수는 lags, 검정 케이스는 test_type으로 지정한다. test_type=None이면 그냥 단위근 검정이고 drift는 절편 포함, trend는 절편에 선형 추세를 포함하는 모형의 단위근 검정을 수행한다. 마지막으로 최적 차수를 계산하고 싶다면 selectlags에 'AIC' 또는 'BIC'를 지정하며 None인 경우는 lags 지정 값을 차수 p로 고정시킨다.

def embed(z, lags):
res = []
for i in range(lags):
temp_z = np.expand_dims(z[lags-1-i:len(z)-i], axis=1)
res.append(temp_z)
res = np.concatenate(res, axis=1)
return res
def get_f_stat(reduced_model, full_model):
df_num = reduced_model.df_resid - full_model.df_resid
df_denom = full_model.df_resid
numerator = reduced_model.mse_resid*reduced_model.df_resid - full_model.mse_resid*full_model.df_resid
denominator = full_model.mse_resid*full_model.df_resid
f_stat = (numerator/df_num)/(denominator/df_denom)
return f_stat
def augmented_df_test(y, lags, test_type=None, selectlags=None):
'''인자 검사'''
if test_type:
assert test_type in ('drift', 'trend'), 'test_type must be the one of (None, drift, trend)'
if selectlags:
assert selectlags in ('AIC', 'BIC'), 'test_type must be the one of (None, AIC, BIC)'
lags += 1
z = np.diff(y)
n = len(z)
x = embed(z, lags)
z_diff = x[:,0]
z_lag_1 = y[lags-1:n]
tt = list(range(lags, n+1))
'''
1. lags가 1보다 큰 경우 selectlags가 None이 아니면 AIC 또는 BIC 기준에 따라
차수 p를 구한다.
2. test_type 별로 검정을 수행한다.
'''
if lags > 1:
if selectlags:
crit_res = []
for i in range(2, lags+1):
z_diff_lag = x[:, 1:i]
if test_type is None:
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), z_diff_lag], axis=1)
model = sm.OLS(z_diff, X).fit()
elif test_type == 'drift':
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), z_diff_lag], axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
else:
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), np.expand_dims(tt, axis=1)], axis=1)
X = np.concatenate([X, z_diff_lag], axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
if selectlags == 'AIC':
crit_res.append(model.aic)
else:
crit_res.append(model.bic)
lags = 2+np.argmin(crit_res)
z_diff_lag = x[:, 1:lags]
if test_type is None:
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), z_diff_lag], axis=1)
model = sm.OLS(z_diff, X).fit()
tau = model.tvalues[0]
test_stat = {'tau1':tau}
elif test_type == 'drift':
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), z_diff_lag], axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
tau = model.tvalues[1]
model_phi = sm.OLS(z_diff, z_diff_lag).fit()
phi1 = get_f_stat(model_phi, model)
test_stat = {'tau2':tau, 'phi1':phi1}
else:
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), np.expand_dims(tt, axis=1)], axis=1)
X = np.concatenate([X, z_diff_lag], axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
tau = model.tvalues[1]
model_phi2 = sm.OLS(z_diff, z_diff_lag).fit()
X_temp = sm.add_constant(z_diff_lag)
model_phi3 = sm.OLS(z_diff, X_temp).fit()
phi2 = get_f_stat(model_phi2, model)
phi3 = get_f_stat(model_phi3, model)
test_stat = {'tau3':tau, 'phi2':phi2, 'phi3':phi3}
else:
if test_type is None:
model = sm.OLS(z_diff, np.expand_dims(z_lag_1,axis=1)).fit()
tau = model.tvalues[0]
test_stat = {'tau1':tau}
elif test_type == 'drift':
X = np.expand_dims(z_lag_1,axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
tau = model.tvalues[1]
rss_null = np.sum(np.square(z_diff))
df_num = len(z_diff) - model.df_resid
df_denom = model.df_resid
numorator = rss_null - model.mse_resid*model.df_resid
denominator = model.mse_resid*model.df_resid
phi1 = (numorator/df_num)/(denominator/df_denom)
test_stat = {'tau2':tau, 'phi1':phi1}
else:
X = np.concatenate([np.expand_dims(z_lag_1,axis=1), np.expand_dims(tt, axis=1)], axis=1)
X = sm.add_constant(X)
model = sm.OLS(z_diff, X).fit()
tau = model.tvalues[1]
rss_null = np.sum(np.square(z_diff))
df_num = len(z_diff) - model.df_resid
df_denom = model.df_resid
numorator = rss_null - model.mse_resid*model.df_resid
denominator = model.mse_resid*model.df_resid
phi2 = (numorator/df_num)/(denominator/df_denom)
model_phi3 = sm.OLS(z_diff, [1]*len(z_diff)).fit()
phi3 = get_f_stat(model_phi3, model)
test_stat = {'tau3':tau, 'phi2':phi2, 'phi3':phi3}
# res = model.resid
'''샘플 사이즈에 맞는 테이블을 가져온다.'''
if n < 25:
rowselec = 0
elif n < 50:
rowselec = 1
elif n < 100:
rowselec = 2
elif n < 250:
rowselec = 3
elif n < 500:
rowselec = 4
else:
rowselec = 5
if test_type is None:
cval_tau1 = np.array([(-2.66, -1.95, -1.6),(-2.62, -1.95, -1.61),(-2.6, -1.95, -1.61),
(-2.58, -1.95, -1.62),(-2.58, -1.95, -1.62),(-2.58, -1.95, -1.62)])
cval = cval_tau1[rowselec, :]
cval = np.expand_dims(cval, axis=0)
test_name = ['tau1']
elif test_type == 'drift':
cval_tau2 = np.array([(-3.75, -3, -2.63),(-3.58, -2.93, -2.6),(-3.51, -2.89, -2.58),
(-3.46, -2.88, -2.57),(-3.44, -2.87, -2.57),(-3.43, -2.86, -2.57)])
cval_phi1 = np.array([(7.88, 5.18, 4.12),(7.06, 4.86, 3.94),(6.7, 4.71, 3.86),
(6.52, 4.63, 3.81),(6.47, 4.61, 3.79),(6.43, 4.59, 3.78)])
cval = np.array([cval_tau2[rowselec, :],cval_phi1[rowselec, :]])
test_name = ['tau2', 'phi1']
else:
cval_tau3 = np.array([(-4.38, -3.6, -3.24),(-4.15, -3.5, -3.18),(-4.04, -3.45, -3.15),
(-3.99, -3.43, -3.13),(-3.98, -3.42, -3.13),(-3.96, -3.41,-3.12)])
cval_phi2 = np.array([(8.21, 5.68, 4.67), (7.02, 5.13, 4.31),(6.5, 4.88, 4.16),
(6.22, 4.75, 4.07),(6.15, 4.71, 4.05),(6.09, 4.68, 4.03)])
cval_phi3 = np.array([(10.61, 7.24, 5.91),(9.31, 6.73, 5.61),(8.73, 6.49, 5.47),
(8.43, 6.49, 5.47),(8.34, 6.3, 5.36),(8.27, 6.25, 5.34)])
cval = np.array([cval_tau3[rowselec, :], cval_phi2[rowselec, :], cval_phi3[rowselec, :]])
test_name = ['tau3', 'phi2', 'phi3']
results = pd.DataFrame(cval)
results.columns = ('1pct', '5pct', '10pct')
results.insert(0, 'TEST_NAME', test_name)
results.insert(1, 'TEST_STAT', [test_stat[x] for x in test_name])
return (results, lags-1)
반응형

2) 적용

이제 실제 데이터를 가지고 Augmented Dickey-Fuller Test(검정)을 수행해보자. 먼저 아래의 데이터를 다운받는다.

denmark.csv
0.00MB

데이터의 대한 설명은 다음과 같다.


ENTRY : period Time index from 1974:Q1 until 1987:Q3.
LRM : Logarithm of real money, M2.
LRY : Logarithm of real income.
LPY : Logarithm of price deflator.
IBO : Bond rate.
IDE : Bank deposit rate.

 

이제 데이터를 불러오자.

 

df = pd.read_csv('./dataset/denmark.csv')
df.head()

 

나는 로그 실질 화폐량(LRM)에 대하여 단위근이 있는지 알아보고 싶었다. 먼저 시계열 그래프를 그려보자.

 

import matplotlib.pyplot as plt
df['ENTRY'] = df['ENTRY'].map(lambda x: pd.to_datetime(x, format='%Y:%m'))
fig = plt.figure(figsize=(8, 5))
fig.set_facecolor('white')
plt.scatter(df['ENTRY'], df['LRM'])
plt.show()

 

정상이 아닌 시계열

주기성을 가지면서 선형 추세가 있는 것 같기도 하다. 확실한 건 시계열이 정상은 아니다 ㅎㅎ.

 

이제 Augmented Dickey-Fuller Test(검정)을 해보자. 각 검정 케이스별로 검정을 수행하고 모형 차수 p는 최대 4로 설정하고 AIC를 통해 2~4 중에서 최적 차수를 선택하도록 했다.

y = df['LRM'].values
for test_type in (None, 'drift', 'trend'):
results, lags = augmented_df_test(y, lags=4, test_type=test_type, selectlags='AIC')
print('Optimal order : ', lags)
print(results)

 

 

해석을 해보자.

 

먼저 절편이 없는 모형의 경우 최적 차수는 3이 나왔다. τ1의 검정 통계량은 0.956이며 이는 유의 수준 10% 기각값 -1.61보다 크다. 따라서 귀무가설이 채택되고 단위근이 존재한다고 할 수 있다.

 

절편이 있는 모형의 경우 최적 차수는 5가 나왔다. τ2의 검정 통계량은 -1.702이며 이는 유의 수준 10% 기각값 -2.58보다 크다. 따라서 귀무가설이 채택되고 단위근이 존재한다고 할 수 있다. 또한 ϕ1 검정 통계량은 1.849이며 이는 유의 수준 10% 기각값 3.86보다 작은 값이다. 따라서 귀무가설은 채택되며 절편항은 유의하지 않다고 판단한다.

 

마찬가지로 절편과 선형 추세가 있는 모형의 경우 τ3,ϕ2,ϕ3 모두 채택된다. 따라서 단위근이 존재하고 절편항과 선형 추세항은 유의하지 않다고 할 수 있다.


τ1,τ2,τ3에 대한 검정은 statsmodels에서 제공하는 adfuller 함수를 이용하여 Augmented Dickey-Fuller Test(검정)을 수행할 수도 있다. 아래 코드는 앞의 τ1,τ2,τ3에 대한 검정을 수행한다. adfuller 함수의 regression인자에는 'c', 'ct', 'ctt', 'nc' 값을 가질 수 있는데 각각의 의미는 다음과 같다.

  • “c” : 절편항이 있는 단위근 검정
  • “ct” : 절편항과 선형 추세가 있는 단위근 검정
  • “ctt” : 절편항, 선형 추세 그리고 이차 추세가 있는 단위근 검정
  • “n” : 아무것도 없는 단위근 검정

adfuller의 함수는 튜플 타입을 리턴하며 순서대로 검정 통계량, p-value, 최적 차수, 관측치 개수, 기각값, 최적 AIC값을 출력한다.

 

from statsmodels.tsa.stattools import adfuller
regression_types = ['nc','c','ct']
tau_list = ['tau 1', 'tau 2', 'tau 3']
for i, regression in enumerate(regression_types):
results = adfuller(y, maxlag=4, regression=regression, autolag='AIC')
print('Optimal order : ', results[2])
print(f'{tau_list[i]} statistics : {results[0]} ', results[4])

 

 

검정 통계량은 τ1을 제외하고 동일하며 검정 결과는 모두 동일하다. 또한 최적 차수도 동일하다. 그리고 기각값에서 차이가 났다. statsmodels에서는 샘플 사이즈 값에 따라 적절한 보간법(Interpolation)을 이용하여 기각값을 더 정확하게 계산해주는 것 같다.

 

이번 포스팅을 통해 듣기만 했던 Augmented Dickey-Fuller Test(검정)에 대해서 좀 더 자세히 알 수 있는 기회가 되었다. R 코드를 통해 검정 알고리즘을 수월하게 이해할 수 있었다. 단위근 검정에 대해선 Augmented Dickey-Fuller Test(검정) 말고 여러가지가 더 있는데 기회가 되면 공부하고 포스팅해야겠다.


참고자료

Augmented Dickey-Fuller (ADF) Test in R

James D. Hamilton - Time Series Analysis

Ruey S. Tsay - Analysis of Financial Time Series

Peter J. Brockwell, Richard A. Davis - Time Series : Theory and Methods

 



댓글