본문 바로가기
시계열 데이터

시계열 데이터 - 정상성 분석하기, ADF test

by Tiabet 2023. 1. 2.

 

ARIMA 모델을 분석하기 전, 파이썬을 이용해 정상성을 검증해보는 시간이 필요할 것 같다.

 

사실 이번에 정상성에 대해서 추가로 공부하면서, 새롭게 깨달은 사실이 더 있었다.

바로 정상성 데이터가 ARIMA모델 등 분석 모델에 활용된다는 것이었다.

내가 생각했을 때엔 추세-주기, 계절성을 갖는 비정상성 모델이 오히려 예측하기 쉬울것 같아서였기 때문에 처음에는 이해가 잘 가지 않았다.

 

그래서 많은 자료를 읽었는데, 결국 결론은 평균과 분산이 일정해야 예측하기가 더 쉽다 였다. 내 생각이 짧았던 것이다. 여러 통계 모델 등에서 비정상성 데이터 예측이 불가능한 것은 아니지만, 정상성 데이터여야 더 편하다.

처음 정상성과 비정상성을 공부했을 때 사용한 표현. 안타깝게도 반만 낮은 내용이다.

첫 포스팅에서 야심차게 정리한 문장이다. 지금 보니 반은 맞고, 반은 틀렸다. 0부터 T까지의 데이터를 보고 T+1의 데이터를 알 수 있는지 여부에 관한 내용은 맞았다. 하지만 마치 비정상성 데이터가 더 예측하기 쉽다는 듯이 적어놓았기 때문에 틀린 내용이다. 

 

그렇다면 정상성 데이터이 예측이 더 쉬운 이유는 뭘까? 평균과 분산이 일정하다는 의미를 잘 이해해야 한다. 만약 평균이 0인 데이터들이 관측되고 있다고 하자. 그렇다면 여태까지 관측된 모든 데이터들의 합도 0임을 의미한다. 하지만 어느 순간 관측하다 보니, 모든 데이터들의 합이 0보다 커진 상황이 발생한 것이다. 그렇다면 우리는 그 다음 데이터는 0보다 작을 것임을 예측할 수 있다. 평균이 0으로 맞춰져야 하기 때문이다. 아마 이 개념은 AR, MA, ARIMA 모델에서 더 자세히 다루게 될 것 같다.

 

추가로 추세나 계절성을 제거하고 싶으면 차분을 통해, 분산을 일정하게 만들고 싶으면 로그 변환을 통해 데이터를 정상화 가능함을 짚고 넘어가고 싶다. 조금만 생각해보면 당연하게 알 수 있는 부분이라 설명은 생략한다.

 

아무튼 여기까지 새롭게 알게 된 개념들을 정리했고, 진행했던 코딩을 간단하게 소개해보겠다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from statsmodels.tsa.stattools import adfuller
 
# Load the stock price data for Apple Inc.
df = pd.read_csv('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=AAPL&apikey=R1A10JZ8TS3877SK&datatype=csv&start_date=2010-01-01')
 
# Convert the 'Date' column to a datetime index
df.index = pd.to_datetime(df['timestamp'])
 
# Plot the 'Close' column
plt.plot(df['close'])
plt.xlabel('Date')
plt.ylabel('Closing price (USD)')
plt.title('Apple Inc. Stock Price')
plt.show()
 
# Calculate the logged data
df['log'= np.log(df['close'])
 
# Plot the logged data
plt.plot(df['log'])
plt.xlabel('Date')
plt.ylabel('Logged data')
plt.title('Apple Inc. Stock Price Logged Data')
plt.show()
 
# Calculate the differenced data of logged data
df['logdiff'= df['log'].diff()
 
# Plot the differenced data
plt.plot(df['logdiff'])
plt.xlabel('Date')
plt.ylabel('Differenced data of Logged')
plt.title('Apple Inc. Stock Price Differenced Logged Data')
plt.show()
 
# Calculate the differenced data
df['diff'= df['close'].diff()
 
# Plot the differenced data
plt.plot(df['diff'])
plt.xlabel('Date')
plt.ylabel('Differenced data')
plt.title('Apple Inc. Stock Price Differenced Data')
plt.show()
 
cs

사용했던 애플 주식 가격의 변화를 나타낸 그래프들.

우선 저번 포스팅에서 사용했던 코드를 살짝 수정하여 가격, 가격의 차분, 가격의 로그, 차분의 로그 순으로 그려보았다. 저번 포스팅에서 살짝 실수한 것이, 차분에다가 로그를 씌우는 실수를 했다. 실제 진행할 때는 로그를 씌우고 차분을 해야 한다.

 

이제 정상성을 판단하는 일이 남았다. 정상성을 판단하는 방법은 첫 번째 포스팅에서 백색함수 부분에서 잠시 설명했던 부분과 이어진다.

본 블로그 첫 번째 포스팅에서 정상성 데이터를 공부할 때 썼던 내용

즉, 어떤 시계열 데이터가 정상성인지 비정상성인지 판단하는 방법은 자기상관함수에서 시작한다고 생각했다. 다만 후에 공부를 해본 결과, 정상성을 검정하는 방법에는 3가지가 있었다.

1. 그래프로 판단하기 - 이건 이전 포스팅에서 9가지 예제 그래프를 보고 정상성인지 비정상성인지 판단한 과정이다.

2. 단위근을 갖는지 판단하기 - Augmented-Dicky Fuller Test, 줄여서 ADF test라고 부르는 검정 방법을 사용한다.

3. 자기상관함수로 판단하기 - 앞서 언급한 자기상관이 존재하는지 파악하여 정상성인지 여부를 판단한다.

 

본 포스팅에선 '단위근'을 갖는지 여부로 판단하는 ADF test를 사용하여 정상성 검정을 진행하도록 하겠다.

자기상관함수(ACF), 편자기상관함수(PACF)에 대해선 다음 포스팅에서 더 자세히 다루도록 하겠다.

 

ADF test는 단위근 검정의 여러 방식 중 하나이다. KPSS test와 더불어서 많이 쓰이는 방식이라고 한다. ADF test를 설명하려면 AR 모델에 대해서도 알아야 하고 단위근이 무엇인지도 알아야 한다. 하지만 아직 내 포스팅에서 AR 모델은 무엇이고, 후방이동 기호 B는 무엇이고.. 이런 내용들을 다루지 않았다. (내가 복잡한 수식을 유달리 싫어하는 부분도 있다.) 그래서 최대한 내가 이해했고, 또 내가 이미 다루었던 내용들을 기반으로 쉽게 풀어서 설명을 작성해보겠다.

 

 

단위근을 간단하게 정리하자면,T의 데이터와 T+1의 데이터의 연관성을 나타낸 지수이다. 간단한 선형회귀 모델을 하나 살펴보자.

이 식에서 β1이 바로 단위근의 개념이다. 만약 β1이 1이라고 생각해보자. y(t-1)은 재귀적으로 y(t-2)에 대해서 표현할 수 있고, 이는 y(0)까지 반복될 것이므로 다음과 같이 표현할 수 있다.

글씨를 쓸 수가 없어서 그냥 손으로 써버렸다.

오차항 e가 삭제되지 않고 점점 쌓여서 결국 정상성 데이터는 분산이 일정하다는 가정에 벗어나게 된다. 

(설명을 약간 덧대자면, 선형회귀 모델에서 오차 e는 평균이 0, 분산이 V인 정규분포를 따른다. 따라서 총 분산은 tV가 될 것이다.)

따라서 β1, 단위근이 1이 되면 그 데이터는 '단위근을 갖는다'라고 하게 되고, 이에 따라 단위근 검정을 진행할 때 귀무가설을 단위근을 갖는다, 대립가설을 단위근을 갖지 않는다 라고 설정하게 된다. β1이 1보다 작다면, 이전 시간대의 오차항들이 미치는 영향은 훨씬 작아지게 될 것이고, 데이터의 비정상성이 훨씬 줄어들 것이다. 어차피 우리는 귀무가설을 기각하기만 하면 데이터가 비정상적이라고 판단하기 때문에, 초점을 β1이 1인가 아닌가에 맞추면 될 것이다. 그리고 귀무가설을 기각하는 조건은 생각보다 까다롭기 때문에,  p-value가 아주 작다면 구한 β1 이 아마 1보다 아주 작은 수가 나온 것이다. (0에 가까운 정도로)

 

통계학을 따로 배우지 않았다면 선형회귀와 가설 검정을 다소 이해하기 힘들 텐데, 그래서 정말 직관적으로 이해할 수 있는 방법도 있다.

본 블로그 첫 번째 포스팅에서 정상성 데이터를 공부할 때 썼던 내용

T-1의 데이터가 T의 데이터에 영향을 주면 안 된다. 영향을 주게 되면 비정상성 데이터이기 때문이다. β1이 1이면 영향을 아주 많이 받는 것이다. 따라서 β1이 1이면 안 된다. T-1의 데이터가 T의 데이터에 영향을 받는다면 밑의 그림처럼 Random Walk의 형태를 가질 것이며, 이는 즉 비정상성을 보인다는 말과 같다. 이 부분은 자기상관하고 통하는 부분이 있어서, 어찌 보면 ADF도 자기상관함수, ACF를 이용한다고 볼 수 있겠다.

니케이지수를 변동성을 사용해 예측한 그래프. 비정상 시계열Random Walk와 통하는 부분이 있다.

 

실제로 ADF 테스트는 다음과 같은 수식에서 시작한다.

역삼각형 기호는 차분을 의미한다.

이 수식에서 데이터 값에 해당하는 y값, 차분값들을 다 대입하면, 알파(단위근)의 분산과 평균을 추정할 수 있다. 이 추정치를 가지고 T-test를 돌리는 것이 ADF 테스트이다. 통계학을 배우지 않았다면 이 부분을 절대 이해하지 못 했을 것이다.. 알파의 평균과 분산을 구하는 방법은 선형회귀에서 모수를 추정하는 방법이 있는데 그것이 적용되는 것이라고 생각된다. 

 

길고 길었던 ADF Test 파헤치기가 끝이 났다. 이제 ADF Test를 실행할 수 있는 adfuller 함수를 직접 사용해보기로 했다.

 

코딩해줄 때 주의할 점은 차분 데이터를 그대로 adfuller 함수에 넣게 되면 텅 빈 데이터 (nan)가 하나씩 생겨서 오류가 발생한다는 점이다. (T개의 데이터가 있으면 차분 데이터는 T-1개가 존재하게 된다.) 그래서 dropna 함수를 사용해 df에서 해당하는 칼럼의 nan 데이터를 삭제해주는 과정을 거쳤다.

adfuller 함수는 nan 데이터가 포함되어 있으면 풀지 못한다. 주의.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import pandas as pd
from statsmodels.tsa.stattools import adfuller
 
# Load the stock price data for Apple Inc.
df = pd.read_csv('https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=AAPL&apikey=R1A10JZ8TS3877SK&datatype=csv')
 
# Convert the 'Date' column to a datetime index
df.index = pd.to_datetime(df['timestamp'])
 
# Calculate the differenced and logged data
df['diff'= df['close'].diff()
df['log'= np.log(df['close'])
df['diff_log'= df['log'].diff()
 
df = df.dropna(subset=['diff'])
df = df.dropna(subset=['diff_log'])
 
# Create a list of column names to iterate over
columns = ['close''diff''log''diff_log']
 
# Iterate over the columns and perform the ADF test on each
for col in columns:
    result = adfuller(df[col])
    print(f'Column: {col}')
    print(f'Test statistic: {result[0]}')
    print(f'p-value: {result[1]}')
    if result[1< 0.05:
        print('The time series is stationary')
    else:
        print('The time series is non-stationary')
    print()
cs

 

차분 데이터와 로그 차분 데이터는 정상성을 띈다.

분석 결과, 애플 주식의 차분 데이터와 로그의 차분 데이터는 정상성을, 일반 가격 데이터와 로그 데이터는 비정상성을 띄는 것을 확인할 수 있었다. 다른 자료들과 비교해봐도 비슷한 결과가 많아서 분석이 알맞게 진행된 것 같다.

 

이번 포스팅에선 데이터의 정상성을 검정하는 방법 중 ADF test를 알아보았다. 다음 포스팅에선 정상성을 검정하는 방법 중 자기상관함수, ACF와, 비슷한 개념인 PACF에 대해 포스팅해보도록 하겠다.

 

참고 : https://geniewishescometrue.tistory.com/entry/Time-Series-Stationarity-%EC%8B%9C%EA%B3%84%EC%97%B4%EB%B6%84%EC%84%9D-%EC%A0%95%EC%83%81%EC%84%B1-ADF-Test-%EC%B0%A8%EB%B6%84

 

[Time Series] 정상성 검정/단위근 검정(ADF Test)과 확률적 보행

시계열 분석에 있어 정상성 검정의 필요성과 관련 개념들을 정리하고, 단위근 검정 (정상성 검정) 방법을 정리합니다. 정상성 검정 정상성 시계열이 어떤 시점에서의 평균과 분산이 일정하고,

geniewishescometrue.tistory.com

https://assaeunji.github.io/statistics/2021-08-08-stationarity/

 

시계열 분석 시리즈 (1): 정상성 (Stationarity) 뽀개기

이번 포스팅은 실전 시계열 분석: 통계와 머신러닝을 활용한 예측 기법 책과 Forecasting: Principles and Practice책을 기반으로 정상성에 대해 자세하게 정리하였습니다.

assaeunji.github.io

https://hongl.tistory.com/98

 

Augmented Dickey-Fuller Test - Stationary 확인

시계열 데이터를 처리하기 위해서는 데이터를 stationary 하게 만든 이후에 AR, MA, ARMA, ARIMA 모델 등을 적용해야 합니다. 지난 포스트에서 알아봤듯이 stationary 하기 위해서는 시계열 데이터의 평균,

hongl.tistory.com