ARIMA·SARIMA·ETS를 이용한 시계열 예측과 정상성 검정
ADF·KPSS 정상성 검정부터 ARIMA, SARIMA, ETS 모형 선택·진단·운영 자동화까지 시계열 예측 절차를 정리한다.
2026-08-14 · 최초 발행 2025-10-14
예측 이전에 확인할 패턴과 정상성
수요 예측, 이상 탐지, 용량 계획에 쓰는 시계열 데이터에는 추세·계절성·노이즈가 섞여 있다. 이 구조를 분리하지 않은 채 모형부터 고르면, 학습 결과와 운영 중 성능 변화의 원인을 구분하기 어렵다.
시계열은 시간 순서에 따라 관측한 연속 데이터 집합이다. 정상성은 평균과 분산이 일정하고 자기공분산이 시차에만 의존하는 성질을 말한다. ARIMA, SARIMA, ETS를 적용할 때는 데이터의 정상성 및 계절성 여부를 먼저 판단하고, 그 결과를 변환과 차분, 후보 모형 선택으로 연결한다.
ADF와 KPSS는 상반된 귀무가설을 둔다.
- ADF의 귀무가설 H0는 단위근이 존재해 비정상이라는 것이다. p<0.05이면 정상으로 판정한다.
- KPSS의 귀무가설 H0는 정상이다. p<0.05이면 비정상으로 판정한다.
한 가지 검정 결과만 따르기보다 ADF와 KPSS를 함께 읽으면 정상성 판단을 더 견고하게 할 수 있다. ARIMA(p,d,q)는 자기회귀(AR), 차분(I), 이동평균(MA)을 결합한 모형이며, SARIMA(P,D,Q,s)는 여기에 주기 s의 계절 성분을 더한다. ETS(Error-Trend-Seasonal)는 오차·추세·계절 성분을 가법 또는 승법으로 조합하는 지수평활 계열 모형이다.
변환부터 운영까지 이어지는 흐름
결측과 이상치를 처리한 뒤 로그 변환, Box-Cox 변환, STL 분해를 수행한다. ADF와 KPSS의 결과를 교차 해석하고 필요하면 차분 d, 계절 차분 D, 변환을 반복 적용한다.
후보 모형은 ACF/PACF로 초기값을 추정하고 AIC/BIC 최소화 탐색으로 좁힐 수 있다. ARIMA와 SARIMA는 이 방식으로 파라미터를 다루며, ETS는 ANN/AAN/AAA 등 오차·추세·계절 조합을 선택한다. 승법 계절성이 있는 데이터는 ETS의 승법 계절 조합도 검토 대상이다.
학습 결과는 단일 분할만으로 끝내지 않는다. 롤링 원점 또는 expanding window 방식의 시계열 분할로 백테스트하고, Ljung-Box를 이용한 잔차 백색성, Q-Q 기반 정규성, 이분산성을 확인한다. 점예측과 예측구간을 함께 산출해 결정 임계값과 연결하고, 예를 들어 월별 MAPE EWMA로 성능 드리프트를 감지해 재학습 트리거를 운영한다.
데이터에서 검정, 모형 선택, 학습, 배포로 이어지는 흐름은 파이프라인으로 관리한다. 검정 결과 충돌, 구조적 변화, 결측 급증 같은 예외 조건도 규칙으로 명시해야 한다.
계절성과 운영 부담에 따른 모형 선택
| 지표 | ARIMA | SARIMA | ETS |
|---|---|---|---|
| 성능 | 비계절·단기 패턴 우수성 | 강한 계절성·복합 패턴 우수성 | 추세·계절 분해형 데이터에 견조성 |
| 확장성 | 파라미터 적음, 탐색 부담 낮음 | 계절 차원 추가로 탐색 비용 증가 | 조합 다수이나 상태공간 최적화로 효율성 |
| 일관성 | 재현성 높음 | 재현성 높음 | 구조적 변화에 안정적(승법/가법 선택 유연성) |
| 안정성 | 이상치 민감성 중간 | 과적합 위험 증가 가능 | 이상치·스케일 변화에 상대적 강건성 |
| 운영 편의 | 해석 용이, 튜닝 직관성 | 파라미터 많아 운영 복잡성 증가 | 구성 해석 용이, 자동 선택 구현 용이 |
SARIMA는 표현력이 높은 대신 탐색과 운영의 복잡도가 커질 수 있다. ETS는 이상치와 스케일 변화에 상대적으로 강건하지만 일부 비선형 패턴에는 한계가 있다. ARIMA는 단순하고 해석하기 쉽지만 강한 계절성을 다루는 데 제약이 있다.
리테일에서는 SKU×매장 단위의 월·주간 예측을 SARIMA 또는 ETS 기반 파이프라인으로 자동화할 수 있다. 인프라 용량 계획에서는 CPU와 트래픽의 일중·주중 계절성을 반영해 SARIMA나 ETS-승법을 적용한다. 비계절적이고 변동성이 높은 금융 지표의 단기 예측에는 ARIMA를 적용하고 잔차 GARCH 결합을 옵션으로 둘 수 있다.
IoT 이상 탐지에서는 ETS 예측구간을 벗어난 값을 알람으로 삼고, 지연·누락 데이터는 로버스트하게 처리한다. 마케팅 KPI는 캠페인 전후의 구조적 변화를 모니터링하면서 ADF/KPSS를 재검정하고 모형을 다시 선정한다.
적절한 차분과 계절 모델링을 전제로 베이스라인 대비 MAPE 1030% 개선 가능성이 있다. 재고 품절률은 515%p 감소하고 초과 재고는 820% 감소할 수 있으며, 예측구간에 맞춰 임계값을 재설계하면 오탐 알람률 2040% 감소를 기대할 수 있다. 해석 가능한 예측 체계를 마련하면 운영 표준화와 거버넌스를 강화하고 모델 성능 드리프트에도 더 이르게 대응할 수 있다.
ADF·KPSS 검정과 모형 평가 예제
재현성을 위해 합성 월간 데이터를 사용한다. 전제조건은 Python 3.11+, statsmodels 0.14+, pandas 2+, numpy 1.26+다.
# python>=3.11
import numpy as np
import pandas as pd
from scipy import stats
from statsmodels.tsa.stattools import adfuller, kpss
from statsmodels.tsa.seasonal import STL
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.tsa.holtwinters import ExponentialSmoothing
from statsmodels.stats.diagnostic import acorr_ljungbox
from sklearn.metrics import mean_absolute_error
# 1) 데이터 생성: 추세 + 계절 + 노이즈 (월별 7년)
rng = pd.date_range("2018-01-01", periods=84, freq="MS")
np.random.seed(42)
trend = np.linspace(50, 80, len(rng))
season = 10 * np.sin(2 * np.pi * np.arange(len(rng)) / 12)
noise = np.random.normal(0, 3, len(rng))
y = trend + season + noise
y = pd.Series(y, index=rng, name="y")
# 2) 변환 후보: 로그/Box-Cox (양수 데이터 가정)
y_pos = y - y.min() + 1 # 양수 보정
y_bc, lam = stats.boxcox(y_pos)
# 3) 정상성 검정 함수
def stationarity_tests(series):
adf_p = adfuller(series, autolag="AIC")[1]
kpss_p = kpss(series, regression="c", nlags="auto")[1]
return adf_p, kpss_p
adf_p, kpss_p = stationarity_tests(y_bc)
print(f"ADF p={adf_p:.4f} (p<0.05 -> 정상), KPSS p={kpss_p:.4f} (p<0.05 -> 비정상)")
# 4) 차분 결정: 간단 규칙 기반
d = 0
D = 0
y_tr = pd.Series(y_bc, index=y.index)
if adf_p >= 0.05 or kpss_p < 0.05:
y_tr = y_tr.diff().dropna()
d = 1
# 계절성 감지: STL 분해 기반
stl = STL(pd.Series(y_bc, index=y.index), period=12, robust=True).fit()
season_strength = 1 - np.var(stl.resid) / np.var(stl.resid + stl.seasonal)
has_seasonality = season_strength > 0.5 # 임계값 예시
if has_seasonality:
y_tr = pd.Series(y_bc, index=y.index).diff(12).dropna()
D = 1
print(f"차분 d={d}, 계절 차분 D={D}, 계절성 감지={has_seasonality}")
# 5) 학습/검증 분할
split = int(len(y) * 0.8)
y_train, y_test = y[:split], y[split:]
# 6) ARIMA/SARIMA/ETS 학습 및 예측
def eval_forecast(y_trn, y_tst, model_name):
if model_name == "ARIMA":
# SARIMAX로 ARIMA 표현, 계절 없음
mod = SARIMAX(y_trn, order=(1, d, 1), seasonal_order=(0, 0, 0, 0),
enforce_stationarity=False, enforce_invertibility=False)
fit = mod.fit(disp=False)
elif model_name == "SARIMA":
s = 12
mod = SARIMAX(y_trn, order=(1, d, 1), seasonal_order=(1, D, 1, s),
enforce_stationarity=False, enforce_invertibility=False)
fit = mod.fit(disp=False)
elif model_name == "ETS":
trend = "add"
seasonal = "add" if has_seasonality else None
fit = ExponentialSmoothing(y_trn, trend=trend, seasonal=seasonal, seasonal_periods=12).fit()
else:
raise ValueError("Unknown model")
fc = fit.forecast(steps=len(y_tst))
mae = mean_absolute_error(y_tst, fc)
lb_p = acorr_ljungbox(fit.resid if hasattr(fit, "resid") else y_trn - fit.fittedvalues,
lags=[12], return_df=True)["lb_pvalue"].iloc[0]
return fc, mae, lb_p
for name in ["ARIMA", "SARIMA", "ETS"]:
fc, mae, lb_p = eval_forecast(y_train, y_test, name)
print(f"{name}: MAE={mae:.2f}, Ljung-Box p={lb_p:.3f} (p>0.05 -> 잔차 백색성)")
# 7) 예외/오류 처리 권장 패턴
# - ADF 정상·KPSS 비정상: 추세 제거(트렌드 항 포함) + 추가 차분 재검정
# - 구조적 변화 감지(Breakpoint): 윈도 기반 적합 또는 분할 적합
# - 결측 급증: ETS 먼저 적용(로버스트), 이후 SARIMA 재시도
ADF p<0.05와 KPSS p>0.05를 동시에 만족하면 정상성 확보 가능성이 높다. Ljung-Box p>0.05는 잔차 백색성 판단에 권장하는 기준이다. ETS는 이상치와 스케일 변화에 상대적으로 강건하고, 계절성이 강할 때는 SARIMA가 성능 우위를 보일 가능성이 있다.
변환, 차분, 재검정을 순환하는 절차를 고정하고 AIC/BIC 최소화와 롤링 백테스트를 함께 적용하면 과적합을 줄일 수 있다. 예측구간에 임계값과 SLAs를 연동해 오탐·미탐 비용의 균형을 맞추며, 소규모로 시작한 뒤 파이프라인 자동화와 거버넌스를 확장하는 방식이 적합하다.