본문 바로가기
공부/통계학

구조변화 검정 (Chow-test) python

by signature95 2022. 4. 8.
728x90
반응형

경제 분석 등 시계열 데이터를 분석하다보면, 특정 큰 사건 발생으로 경제 지표의 큰 변화가 발생하여 이후 예측에 영향을 주는 경우가 존재합니다. 

 

이를 구조변화라고 하는데, 이번에는 구조 변화가 발생했다는 것을 어떻게 통계적으로 검정할 수 있는지 알아보도록 하겠습니다.

 

참고한 사이트는 다음과 같습니다.

https://pypi.org/project/chowtest/

 

chowtest

Python implementation of the Chow test (1960).

pypi.org

https://github.com/jtloong/chow_test

 

GitHub - jtloong/chow_test: Python module to calculate time series Chow break statistics.

Python module to calculate time series Chow break statistics. - GitHub - jtloong/chow_test: Python module to calculate time series Chow break statistics.

github.com

 

먼저 구조변화 검정 통계량 공식은 다음과 같습니다.

  • RSS_p 는 구조변화가 없는 선형회귀 모형에서의 잔차 제곱합입니다
  • RSS_1, 2는 구조변화가 예측되는 시점 이전, 이후의 선형회귀 모형에서의 잔차 제곱합입니다.
  • k는 구조변화 시점을 의미합니다.
  • n1, n2는 구조변화 전후 모델의 데이터 개수를 의미합니다.
  • CHOW 통계치는 F 값으로 기각역을 도출하며 이를 통해 가설 검정을 시행합니다.

 

대표적인 구조 변화 사건을 예시로 본다면 2008년 금융위기라고 할 수 있을 것입니다. 물론 최근에 코로나 19로 인해 경제적 여건이 많이 변화하였긴 하지만, 데이터가 충분하다고 판단하지 않아서 과거 2008년 금융위기를 기점으로 구조변화 검정을 시행하도록 하겠습니다.

 

먼저 데이터는 KOSPI, 삼성전자로 2001년부터 22년 3월 1일까지의 데이터로 구성하였습니다.

import yfinance as yf
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
from chow_test import chow_test

# 한글폰트 출력 가능 코드
from matplotlib import rc
rc('font', family='AppleGothic') # Mac Os
#rc('font', family='NanumGothic') # Windows Os
plt.rcParams['axes.unicode_minus'] = False


stock_price = yf.download(['^KS11','005930.KS'], '2000-01-01')

# 2008.9.16은 리만브라더스의 파산 신청일입니다.
structure_point = stock_price.loc[stock_price.index == '2008-09-16']

# 시각화
# 삼성 주가 그래프 + 구조 변화 예상 시점
stock_price['Adj Close']['005930.KS'].plot(alpha=.5)
plt.scatter(structure_point.index, 
            structure_point['Adj Close']['005930.KS'], 
            color='red', 
            marker = 'D', 
            s = 25,
            edgecolor = 'k' )
plt.title('Samsung Price (2000-2022)')
plt.show()

# KOSPI 지수 그래프 + 구조 변화 예상 시점
stock_price['Adj Close']['^KS11'].plot(alpha=.5)
plt.scatter(structure_point.index, 
            structure_point['Adj Close']['^KS11'], 
            color='red', 
            marker = 'D', 
            s = 25,
            edgecolor = 'k' )
plt.title('KOSPI index (2000-2022)')
plt.show()

 

이후 영업일 기준으로 데이터의 Nan 값은 ffill로 시행하여 전처리를 해줍니다. (코드 생략)

 

그리고 리만브라더스 파산 시점인 데이터의 인덱스를 추출합니다.

print(f'예상되는 구조변화 시점의 index 값 (리먼브라더스 파산신청) : 2270, ({stock_price_all.Date[2270]})')

print(stock_price_all.loc[stock_price_all.index ==2270].Date)

>>>

예상되는 구조변화 시점의 index 값 (리먼브라더스 파산신청) : 2270, (2008-09-16 00:00:00)
2270   2008-09-16
Name: Date, dtype: datetime64[ns]

 

그리고 chow 검정을 실시합니다.

# 구조변화 검정 시행
chow_test(stock_price_all['Adj Close_^KS11'] ,stock_price_all['Adj Close_005930.KS'], 2270, 2271, .05)

>>>

Reject the null hypothesis of equality of regression coefficients in the two periods.
Chow Statistic: 3442.51214914471, P_value: 1.1102230246251565e-16
(3442.51214914471, 1.1102230246251565e-16)

 

결과는 매우 유의한 p 값으로 나오며 두 기간 사이에 회귀계수가 동일하다는 귀무가설을 기각할 수 있게 됩니다.

 

물론 각 회귀계수를 보아도 차이가 나오긴 합니다.

먼저 전체 기간에 대한 회귀계수입니다.(계수의 p값은 매우 유의합니다.)

import statsmodels.api as sm

model = sm.OLS(stock_price_all['Adj Close_^KS11'] ,stock_price_all['Adj Close_005930.KS']).fit()
print(f'회귀계수 : {model.params.values}, p값 : {model.pvalues.values}')

>>>

회귀계수 : [0.05481694], p값 : [0.]

 

그리고 구조변화 전후 회귀계수와 p 값입니다.

 

before = stock_price_all.loc[stock_price_all.index <=2270]
after  = stock_price_all.loc[stock_price_all.index > 2270]

# 구조변화 전 모델
model_before = sm.OLS(before['Adj Close_^KS11'] ,before['Adj Close_005930.KS']).fit()
print(f'회귀계수 : {model_before.params.values}, p값 : {model_before.pvalues.values}')

# 구조변화 후 모델
model_after = sm.OLS(after['Adj Close_^KS11'] ,after['Adj Close_005930.KS']).fit()
print(f'회귀계수 : {model_after.params.values}, p값 : {model_after.pvalues.values}')

>>>

회귀계수 : [0.14971767], p값 : [0.]

회귀계수 : [0.05253771], p값 : [0.]

 

물론 위 같은 chow 검정은 특정 시점을 알지 못한다면, 검정을 시행하지 못한다는 단점이 존재합니다. 이를 위해서는 추가적인 작업이 필요할 듯 하지만, 시계열 자료에서 구조변화 검정을 파이썬으로 실시할 수 있다는 점 자체가 다행인 것 같습니다.

 

그럼 이만 마칩니다.

 

728x90

댓글