ADF 검정이란? 파이썬으로 시계열 정상성 확인하는 방법
주가 그래프가 잘 그려진다고 해서 바로 시계열 모델에 사용할 수 있는 것은 아닙니다.시계열 분석 전에는 데이터에 추세가 남아 있는지, 정상성을 어떻게 판단할지 먼저 확인할 필요가 있습니다.ADF 검정의 p-value만 보는 것보다 원가설과 차분 전후 변화를 함께 이해하는 것이 중요합니다.
주가 데이터를 파이썬으로 불러오고 결측치도 처리했습니다. 그렇다면 이제 바로 머신러닝이나 시계열 예측 모델에 넣으면 될까요?
그 전에 확인해야 할 중요한 개념이 있습니다.
바로 ‘정상성’입니다.
금융 시계열을 공부하다 보면 ADF 검정, 단위근, 차분이라는 용어가 거의 빠지지 않고 등장합니다. 처음 보면 통계학 공식부터 알아야 할 것처럼 느껴지지만 핵심 질문은 의외로 단순합니다.
“시간이 지나도 이 데이터의 통계적 성질이 비교적 안정적으로 유지되는가?”
이번 글에서는 앞서 살펴본 「파이썬 시계열 데이터 전처리 방법: 결측치·이상치·수익률 변환까지」의 다음 단계로, 정상성의 개념과 ADF 검정 실행 방법을 실제 파이썬 코드 중심으로 알아보겠습니다.
1. 시계열 정상성이란 무엇일까?
일반적인 머신러닝 데이터와 달리 시계열 데이터에는 순서가 있습니다.
오늘의 값은 어제와 관계가 있을 수 있고, 작년의 패턴이 올해에도 영향을 줄 수 있습니다.
문제는 데이터의 평균이나 변동성이 시간에 따라 계속 변한다면 과거의 관계를 이용해 미래를 설명하기가 훨씬 어려워질 수 있다는 점입니다.
예를 들어 어떤 주가가 다음과 같이 움직인다고 가정해보겠습니다.
100
105
110
116
121
128
가격 자체에는 뚜렷한 상승 추세가 있습니다.
초반의 평균과 후반의 평균이 서로 다릅니다.
반면 가격 자체가 아니라 하루 동안 얼마나 변했는지 수익률로 변환하면 다음처럼 전혀 다른 형태가 나타날 수 있습니다.
df["return"] = df["Close"].pct_change()
그래서 금융 시계열에서는 가격 수준 자체와 가격의 변화율을 구분해서 볼 필요가 있습니다.
정상성을 엄밀하게 정의하면 여러 조건이 있지만, 시계열 분석을 처음 공부할 때는 다음 세 가지를 중심으로 이해하면 좋습니다.
- 시간에 따라 평균이 크게 변하지 않는가?
- 시간에 따라 분산이 크게 달라지지 않는가?
- 두 시점 사이의 관계가 절대적인 날짜보다는 두 시점의 간격에 의해 설명될 수 있는가?
이러한 성질이 비교적 안정적이면 정상 시계열로 볼 수 있는 기반이 생깁니다.
반대로 강한 추세를 가진 시계열이나 단위근이 존재하는 시계열은 비정상적일 가능성이 있습니다.
중요한 점은 그래프만 보고 정상성을 확정해서는 안 된다는 것입니다.
시각적인 확인과 통계 검정을 함께 사용하는 것이 좋습니다.
2. ADF 검정의 원가설부터 이해해야 한다
정상성을 확인할 때 자주 사용되는 방법 중 하나가 ADF 검정입니다.
ADF는 Augmented Dickey-Fuller의 약자입니다.
statsmodels는 adfuller()를 단변량 시계열의 단위근 여부를 검정하는 함수로 제공하고 있습니다.
여기서 가장 많이 헷갈리는 부분이 있습니다.
ADF 검정은 기본적으로 다음과 같은 관점에서 출발합니다.
원가설 H0
시계열에 단위근이 존재한다.
즉, 정상적이지 않은 시계열이라는 쪽을 원가설로 둡니다.
대립가설 H1
시계열에 단위근이 존재하지 않는다.
설정한 회귀 형태를 전제로 정상적이라고 볼 근거가 있다는 쪽입니다.
따라서 p-value가 충분히 작아 원가설을 기각할 수 있다면 단위근이 존재하지 않는다는 방향의 통계적 근거를 얻게 됩니다.
일반적으로 예시로 많이 사용하는 유의수준이 0.05라면 다음과 같이 해석할 수 있습니다.
p-value < 0.05
→ 원가설 기각을 고려
→ 단위근이 없다는 방향의 근거
p-value >= 0.05
→ 원가설을 기각할 근거가 충분하지 않음
→ 비정상 시계열 가능성을 추가 확인
여기서 표현을 정확하게 할 필요가 있습니다.
p-value가 0.05보다 크다고 해서 “단위근이 확실히 존재한다”고 증명되는 것은 아닙니다.
원가설을 기각할 증거가 충분하지 않다는 의미에 가깝습니다.
반대로 p-value가 작다고 해서 해당 데이터가 모든 의미에서 완벽한 정상성을 가진다고 단정할 수도 없습니다.
ADF 검정 결과는 그래프, 데이터 생성 과정, 다른 검정 및 모델의 가정을 함께 고려해서 해석해야 합니다.
statsmodels의 현재 ADF 결과에는 검정통계량과 p-value뿐 아니라 사용된 lag 수, 관측치 수, 1%·5%·10% 수준의 임계값 등의 정보가 포함됩니다.

3. 파이썬에서 ADF 검정 실행하기
먼저 필요한 라이브러리를 불러오겠습니다.
import pandas as pd
from statsmodels.tsa.stattools import adfuller
CSV 형태의 금융 데이터를 불러온다고 가정해보겠습니다.
df = pd.read_csv("stock_data.csv")
df["Date"] = pd.to_datetime(df["Date"])
df = df.sort_values("Date")
df = df.set_index("Date")
이제 종가 데이터를 대상으로 ADF 검정을 실행합니다.
series = df["Close"].dropna()
result = adfuller(series)
print("ADF Statistic:", result[0])
print("p-value:", result[1])
print("Used Lag:", result[2])
print("Number of Observations:", result[3])
print("Critical Values:", result[4])
예를 들어 결과가 다음처럼 나왔다고 가정해보겠습니다.
ADF Statistic: -1.42
p-value: 0.57
유의수준을 5%로 정했다면 p-value가 0.05보다 큽니다.
따라서 이 경우에는 단위근이 존재한다는 원가설을 기각할 근거가 충분하지 않다고 해석할 수 있습니다.
이번에는 수익률로 변환해보겠습니다.
df["return"] = df["Close"].pct_change()
return_series = df["return"].dropna()
result_return = adfuller(return_series)
print("ADF Statistic:", result_return[0])
print("p-value:", result_return[1])
가격 데이터에서는 원가설을 기각하지 못했지만 수익률에서는 매우 작은 p-value가 나타날 수도 있습니다.
그렇다면 가격 수준보다 수익률 시계열이 정상성 측면에서 분석에 더 적합할 가능성을 검토할 수 있습니다.
ADF의 검정통계량과 임계값을 직접 비교하는 것도 가능합니다.
result = adfuller(df["return"].dropna())
print("ADF Statistic:", result[0])
for key, value in result[4].items():
print(
f"Critical Value ({key}): {value}"
)
예를 들어 ADF Statistic이 5% 임계값보다 더 작은, 즉 더 음수인 값을 보인다면 해당 유의수준에서 원가설 기각을 검토할 수 있습니다.
p-value만 한 줄 확인하는 것보다 검정통계량과 임계값까지 함께 보는 습관을 들이면 결과를 이해하는 데 도움이 됩니다.
4. 정상적이지 않다면 차분하면 끝일까?
ADF 검정 결과 비정상성이 의심된다고 해서 데이터를 바로 버릴 필요는 없습니다.
대표적으로 사용할 수 있는 방법이 차분입니다.
차분은 현재 값에서 이전 값을 빼는 방식입니다.
df["Close_diff"] = df["Close"].diff()
pandas의 Series.diff()는 시리즈 원소의 이산 차분을 계산하는 기능으로 제공됩니다.
차분 후 첫 번째 값에는 비교할 이전 값이 없기 때문에 NaN이 발생합니다.
따라서 ADF 검정을 실행하기 전에 제거합니다.
diff_series = df["Close"].diff().dropna()
result_diff = adfuller(diff_series)
print("ADF Statistic:", result_diff[0])
print("p-value:", result_diff[1])
가격 수준에서는 p-value가 높았지만 1차 차분 후 p-value가 충분히 작아진다면 차분을 통해 단위근 문제가 완화됐을 가능성을 생각해볼 수 있습니다.
로그 변환과 차분을 결합하기도 합니다.
import numpy as np
df["log_close"] = np.log(df["Close"])
df["log_diff"] = (
df["log_close"].diff()
)
이 값은 로그수익률과 연결되는 개념입니다.
df["log_return"] = np.log(
df["Close"] / df["Close"].shift(1)
)
두 식을 비교해보면 다음 관계를 확인할 수 있습니다.
log(Pt) - log(Pt-1)
=
log(Pt / Pt-1)
따라서 금융 데이터에서 로그수익률이 자주 등장하는 이유를 정상성 문제와도 연결해서 이해할 수 있습니다.
하지만 여기에서도 주의해야 합니다.
차분을 여러 번 반복한다고 무조건 좋은 것은 아닙니다.
필요 이상으로 차분하면 원래 데이터가 가지고 있던 장기적인 정보나 구조를 잃을 수 있습니다.
따라서 ADF 결과 하나만 보고 기계적으로 차분 횟수를 늘리기보다 시계열 그래프와 모델 목적을 함께 봐야 합니다.
또 하나 중요한 것이 adfuller()의 설정입니다.
ADF 검정에는 상수만 포함할지, 추세까지 포함할지 등을 정하는 regression 옵션이 있습니다. 공식 statsmodels 문서에서도 상수, 상수와 선형 추세, 이차 추세 등을 포함하는 선택지를 제공합니다.
예를 들어 명확한 추세를 고려하고 싶다면 다음과 같이 실행할 수 있습니다.
result = adfuller(
series,
regression="ct",
autolag="AIC"
)
따라서 어떤 데이터든 같은 ADF 코드를 복사해서 p-value만 비교하는 방식보다는 데이터가 어떤 특성을 갖고 있는지 먼저 이해하는 것이 중요합니다.
5. ADF 검정에서 자주 하는 실수
ADF 검정을 실행하는 코드는 몇 줄밖에 되지 않습니다.
오히려 어려운 부분은 결과를 어떻게 해석하느냐입니다.
첫 번째 실수는 p-value가 작으면 “이 데이터는 좋은 데이터”라고 판단하는 것입니다.
ADF 검정은 모델의 예측력이 좋은지 평가하는 시험이 아닙니다.
단위근과 관련된 통계 검정입니다.
두 번째는 p-value가 크면 해당 데이터를 사용할 수 없다고 판단하는 것입니다.
비정상 시계열도 분석 목적에 따라 차분하거나 수익률로 변환할 수 있고, 비정상성을 전제로 하는 분석 방법도 존재합니다.
세 번째는 주가 두 개가 모두 추세를 가지고 있다는 이유만으로 단순 회귀분석을 하고 높은 설명력이 나왔다고 판단하는 것입니다.
서로 관련이 없는 두 시계열도 공통된 추세 때문에 강한 관계가 있는 것처럼 나타날 수 있습니다.
이른바 허구적 회귀 문제를 염두에 둘 필요가 있습니다.
네 번째는 ADF 검정 하나만으로 정상성을 확정하는 것입니다.
statsmodels는 ADF뿐 아니라 KPSS 같은 다른 시계열 통계 검정도 제공합니다. 공식 API에서도 ADF는 단위근 검정, KPSS는 정상성 관련 검정 기능으로 별도로 제공됩니다.
분석 목적에 따라 서로 다른 귀무가설을 가진 검정을 함께 살펴보는 것도 하나의 방법입니다.
시계열 분석 전에 다음 항목을 확인해보면 좋습니다.
- 원본 시계열 그래프에 강한 추세가 있는가?
- ADF 검정의 원가설을 정확히 이해했는가?
- p-value뿐 아니라 검정통계량과 임계값도 확인했는가?
- 가격과 수익률에서 각각 결과가 어떻게 달라지는가?
- 차분 전후의 시계열 형태를 직접 비교했는가?
- 필요 이상의 차분을 하고 있지는 않은가?
- 분석 목적에 맞는 ADF의 회귀 설정을 사용했는가?
결국 ADF 검정에서 가장 중요한 것은 p-value < 0.05라는 공식 하나를 외우는 것이 아닙니다.
현재 분석하는 시계열에 단위근이 있는지를 어떤 가설 아래에서 검정하고 있는지 이해하는 것입니다.
파이썬에서는 몇 줄의 코드만으로 검정을 실행할 수 있지만, 결과를 의미 있게 만드는 것은 데이터의 특성과 분석 목적에 대한 판단입니다.
주가처럼 추세가 뚜렷한 데이터라면 가격 수준과 수익률을 각각 비교해보고, 필요한 경우 차분 전후의 결과도 확인해보는 방식으로 접근하는 것이 좋습니다.
여기까지 이해했다면 다음 단계에서는 정상성을 넘어 시간에 따른 자기상관 구조를 살펴볼 수 있습니다.
다음 글에서는 **「ACF와 PACF란? 파이썬으로 시계열 자기상관 분석하는 방법」**을 통해 과거 값이 현재 값과 얼마나 연결되어 있는지, 그리고 ARIMA 같은 시계열 모델의 차수를 판단할 때 ACF와 PACF가 어떻게 활용되는지 이어서 살펴보겠습니다.
참고 자료
statsmodels 공식 문서는 adfuller()를 Augmented Dickey-Fuller 단위근 검정 함수로 제공하고 있으며, 현재 문서에서 검정통계량, p-value, lag, 관측치 수 및 임계값 등의 결과를 확인할 수 있습니다.
pandas 공식 문서에서는 Series.diff()를 시계열을 포함한 Series의 이산 차분을 계산하는 기능으로 제공합니다.
금융소비자보호 및 개인 유의사항
본 글은 파이썬을 활용한 시계열·금융 데이터 분석 방법을 설명하기 위한 교육 및 정보 제공 목적의 콘텐츠입니다. 특정 금융상품의 가입, 매수, 매도 또는 투자 행동을 권유하기 위한 내용이 아닙니다.
ADF 검정 결과는 분석 기간, 데이터 빈도, 전처리 방법, lag 설정, 추세 포함 여부 등에 따라 달라질 수 있습니다. 통계 검정 결과만으로 향후 금융시장 움직임이나 투자 성과를 판단하는 것은 적절하지 않을 수 있으며, 실제 의사결정에서는 추가적인 분석과 위험요인을 함께 검토할 필요가 있습니다.
금알남
댓글 0
첫 댓글을 남겨보세요.