ADF와 KPSS로 시계열 정상성 판단하기

ADF·KPSS 검정의 상반된 귀무가설을 함께 해석해 단위근, 추세, 차분 여부를 판단하는 시계열 정상성 검정 방법을 정리한다.

2026-08-14 · 최초 발행 2024-04-29

모델 적합 전에 확인할 정상성

시계열 모델은 정상성(stationarity)이라는 전제 위에서 해석된다. 약한 정상성에서는 평균, 분산, 자기공분산이 시간에 따라 변하지 않는다. 실제 데이터는 로그 변환, 차분, 계절 차분으로 이 상태에 가깝게 만드는 전처리를 거치는 경우가 많다.

단위근(Unit Root)은 비정상성을 만드는 대표적인 원인이다. AR(1) 모형 y_t = ρ y_{t-1} + ε_t에서 ρ=1이면 단위근이 존재하며, 시계열은 랜덤워크 성질을 보인다. 이 상태에서 OLS 회귀를 적용하면 허구적 회귀(spurious regression)의 위험이 커진다.

ADF(Augmented Dickey-Fuller)와 KPSS(Kwiatkowski–Phillips–Schmidt–Shin)는 이 전제를 서로 다른 방향에서 확인한다. ADF의 귀무가설 H0는 단위근 존재, 즉 비정상성이고 대립가설 H1은 정상성이다. 반대로 KPSS는 수준 정상 또는 추세 정상을 H0로 두고, 단위근 등을 포함한 비정상성을 H1로 둔다.

검정 사양이 결과를 바꾸는 지점

상수항 c, 추세항 ct, 고차 추세 ctt 가운데 무엇을 포함할지는 데이터 패턴에 맞춰야 한다. 가격 수준처럼 명목 수준을 다룰 때는 ct, 수익률이나 스프레드에는 c를 기본값으로 설정한다.

ADF는 지연차분 항을 넣어 잔차 자기상관을 완화한다. autolag=AIC/BIC를 통한 자동 래그 선택이 권장된다. KPSS에서는 Newey–West 방식으로 장기분산을 추정하며, 대역폭 nlags 선택이 필요하다. 잔차 상관이 강하면 더 보수적으로 대역폭을 늘리는 판단이 필요하다.

두 검정을 함께 쓰는 이유도 여기에 있다. ADF는 비정상성을, KPSS는 정상성을 귀무가설로 두므로 결과가 일치하면 판단 근거가 강해진다. 서로 반대되는 결과가 나오면 구조적 변동이나 계절 단위근을 추가로 확인해야 한다.

표본이 작으면 검정력이 떨어질 수 있다. 이때 분해·분할(sample split)이나 부트스트랩을 보조적으로 사용한다. 정책 변화나 코로나처럼 구조적 단절이 의심되는 시계열에는 Zivot–Andrews, Perron 류의 단절 내생 검정을 추가 적용할 수 있다. 계절 단위근 가능성도 있으므로 계절 차분 D=1을 검토하고, 로그 변환·Box-Cox로 분산을 안정화한 뒤 검정한다.

ADF와 KPSS를 함께 읽는 방법

항목 ADF KPSS 판단 시 유의점
성능 단위근 검출력 양호(추세 정확 지정 시) 정상성 확인에 유리(장기분산 추정 민감) 결정적 항·대역폭 선택 영향 큼
확장성 고빈도·대용량 데이터에 autolag로 실용적 대역폭 추정 비용 존재하나 선형 시간 복잡도 수준 병렬 배치 테스트 용이
일관성 과소지정(추세 누락) 시 size 왜곡 위험 과대지정(불필요 추세 포함) 시 과도 기각 위험 모형 미스펙 위험 관리 필요
안정성 잔차 자기상관 강하면 민감 장기분산 추정에 민감, 이분산성에 취약 견고성 진단 병행 필요
운영 편의 구현 간단, 해석 직관적 귀무가 정상으로 의사결정 보완적 두 검정 병행으로 의사결정 오류 감소

입력 데이터에서 모형 선택까지의 흐름

정상비정상모호입력: 시계열 y_t전처리: 결측/이상치 처리,로그/변환, 계절성 점검결정적 성분 선택(c / ct / ctt)ADF 실행autolag=AIC/BIC, maxlag규정KPSS 실행nlags='auto', regression 동일ADF p <= α ?KPSS p α ?의사결정 결합결론모형 적합:ARMA/ARIMA(p,0,q)차분·추세 포함·계절 차분 결정구조변동 검정·장기기억·변환재평가

ADF가 기각되고(p<=α) KPSS가 채택되면(p>α) 정상성의 근거가 강하다. 반대로 ADF가 채택되고(p>α) KPSS가 기각되면(p<=α) 비정상성의 근거가 강하므로 차분 또는 추세 포함을 검토한다.

둘 다 기각되거나 둘 다 채택되는 경우는 모호하다. 추세 사양을 바꾸고, 래그와 대역폭을 다시 선택하며, 구조적 변동과 계절 단위근 검정을 추가한다.

ADF에서는 autolag=AIC를 기본으로 두고, 데이터 길이 n에 대해 maxlag≈⌊12·(n/100)^(1/4)⌋ 경험칙을 적용할 수 있다. KPSS는 nlags='auto'를 기본으로 사용한다. 고주파 노이즈가 높거나 이분산성이 있으면 nlags 수동 확대를 검토한다.

검정 전에는 결측값과 무한대 값을 제거하거나 보간한다. 이후 Ljung–Box로 잔차 자기상관을, ARCH LM으로 이분산을 점검한다. 문제가 있으면 래그나 대역폭을 조정하고, 변환 전후 결과를 비교해 안정성을 확인한다.

데이터 성격에 따른 적용

금융 시계열에서는 가격이 비정상이고 수익률은 정상 경향을 보일 수 있다. ADF와 KPSS 결과를 함께 보고 차분 필요 여부를 정한다. 변동성 군집이 있으면 로그 수익률과 GARCH 결합도 검토 대상이다.

GDP나 물가처럼 추세를 가진 거시지표와 수요 예측 데이터는 ct 사양으로 검정한 뒤 1차 차분 또는 추세 정상성을 판단한다. 정책 단절이 있다면 구간을 나누거나 단절 내생 검정을 적용한다.

운영·제품 지표에서는 장기 드리프트 탐지에 활용할 수 있다. 비정상으로 판단되면 알람 기준을 높이고 계절 차분 도입을 검토한다. A/B 실험의 시계열 효과를 분석하기 전 정상성을 확보하면 허구적 상관을 줄일 수 있다.

정상화 뒤 ARIMA/SARIMA를 적합하면 MAPE/RMSE가 520% 개선 가능하다(도메인·데이터 의존). 허구 회귀 위험을 낮추고 신뢰구간과 유의성 검정의 타당성을 높이는 효과도 있다. 차분·추세 포함 판단을 자동화하면 실험과 모델링의 반복 횟수를 2040% 절감할 수 있다.

Python에서 ADF와 KPSS 실행하기

사전 조건은 Python 3.10+, statsmodels 0.14.x, pandas 2.x, numpy 1.26+다(최신 정보 확인 필요).

설치:

  • pip install statsmodels pandas numpy
import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import adfuller, kpss
rng = np.random.default_rng(42)

# 1) 데이터 생성: 단위근(랜덤워크) + 추세 + 잡음
n = 500
eps = rng.normal(0, 1, n)
y = np.cumsum(eps) + 0.05 * np.arange(n)  # 비정상 예시
ts = pd.Series(y, index=pd.date_range("2015-01-01", periods=n, freq="D"))

def stationarity_tests(x: pd.Series, regression="ct", alpha=0.05):
    x = pd.Series(x).dropna()
    # ADF: 단위근 귀무가설, autolag로 래그 선택
    adf_stat, adf_p, adf_lags, adf_nobs, adf_crit, adf_icbest = adfuller(
        x.values, regression=regression, autolag="AIC"
    )
    # KPSS: 정상성 귀무가설, 대역폭 자동
    kpss_stat, kpss_p, kpss_crit, kpss_nlags = kpss(
        x.values, regression=regression, nlags="auto"
    )
    decision = None
    if adf_p <= alpha and kpss_p > alpha:
        decision = "정상성 가능성 높음"
    elif adf_p > alpha and kpss_p <= alpha:
        decision = "비정상 가능성 높음(차분/추세 포함 필요)"
    else:
        decision = "모호: 사양/단절/계절성 재점검 필요"
    return {
        "ADF": {"stat": adf_stat, "p": adf_p, "lags": adf_lags, "nobs": adf_nobs, "crit": adf_crit},
        "KPSS": {"stat": kpss_stat, "p": kpss_p, "nlags": kpss_nlags, "crit": kpss_crit},
        "decision": decision,
        "regression": regression,
    }

res = stationarity_tests(ts, regression="ct", alpha=0.05)
print(res["decision"])
print("ADF p-value:", res["ADF"]["p"])
print("KPSS p-value:", res["KPSS"]["p"])

# 2) 차분 후 재검정
d1 = ts.diff().dropna()
res_d1 = stationarity_tests(d1, regression="c", alpha=0.05)
print(res_d1["decision"])
print("ADF p-value (diff1):", res_d1["ADF"]["p"])
print("KPSS p-value (diff1):", res_d1["KPSS"]["p"])

비정상 예시인 ts는 ADF p>0.05, KPSS p<=0.05 경향을 보여 비정상으로 판단한다. 1차 차분한 d1은 ADF p<=0.05, KPSS p>0.05 경향으로 정상화 여부를 확인한다.

ADF와 KPSS에는 같은 regression 사양을 사용한다. 로그 변환 뒤에도 검정을 수행해 분산 안정화를 확인하고, 결과가 민감하면 α=0.01/0.10 감도 분석과 autolag(AIC/BIC) 교차 검증을 적용한다.

래그가 과도하면 자유도가 줄어 검정력이 낮아지고, 너무 적으면 자기상관이 남아 size 왜곡이 발생한다. KPSS도 대역폭이 과도하면 과도 기각, 부족하면 과도 채택으로 이어질 수 있으므로 데이터 특성에 맞춘 튜닝이 필요하다.

정상성시계열 분석단위근ADF 검정KPSS 검정