ARIMA와 SARIMA로 시계열 차분·계절성 차수 정하기

ARIMA와 SARIMA에서 정상성을 위한 차분, ACF·PACF 기반 차수 선택, 계절성 반영과 잔차 진단 방법을 정리한다.

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

정상성을 맞춘 뒤 차수를 고르는 ARIMA

ARIMA(Autoregressive Integrated Moving Average)는 정상 시계열을 전제로 자기회귀(AR), 차분(I), 이동평균(MA)을 결합하는 예측 모델이다. 추세나 분산 변화가 남은 데이터를 곧바로 적합하기보다 차분과 변환으로 조건을 정리한 다음, 과거값과 예측오차의 관계를 모델에 넣는다.

ARIMA(p, d, q)의 각 항은 다음 역할을 맡는다.

  • AR(p)는 현재값을 p개 시차의 과거값 선형결합으로 설명한다.
  • I(d)는 d차 차분을 통해 정상성을 확보한다.
  • MA(q)는 예측오차의 q개 시차 선형결합을 반영한다.

정상성은 평균, 분산, 공분산이 시간에 따라 바뀌지 않는 상태를 뜻한다. 적합 뒤에는 잔차가 평균 0이고 등분산이며 자기상관이 없는 백색잡음에 가까운지도 확인해야 한다.

계절성이 분명한 시계열에는 SARIMA(Seasonal ARIMA)를 쓴다. 표기는 SARIMA(p, d, q)(P, D, Q)s이며, 비계절 성분에 계절 주기 s와 계절 차분 D, 계절 AR·MA 차수 P·Q를 더한 곱 구조다.

차분과 계절성은 과하게 적용하지 않는다

차분 d의 목적은 추세를 제거해 정상성을 얻는 데 있다. ADF 단위근 검정에서 p-value가 0.05보다 크면 차분을 늘려볼 수 있으며, d는 2 이하를 권장한다. 차분 뒤 ACF와 PACF가 빠르게 0으로 수렴하는지, 분산이 지나치게 커지지는 않았는지를 같이 봐야 한다. 분산 과대는 과차분 신호가 될 수 있다.

분산 자체가 시간에 따라 커지는 데이터라면 Box–Cox 또는 로그 변환을 먼저 적용해 안정화할 수 있다. 학습 전에는 정규화와 결측치 처리도 필요하다. 모수는 최대우도(MLE)로 추정하며, 최적화가 실제로 수렴했는지 확인해야 한다.

계절 주기 s는 월별 데이터의 12, 주별 데이터의 7처럼 도메인 지식에서 정하거나 ACF의 계절 피크로 찾는다. 계절 차분 D를 적용한 뒤에도 ADF 검정을 다시 수행하며, D는 일반적으로 {0,1}에서 선택한다. D를 과도하게 적용하면 정보 손실과 과적합으로 이어질 수 있다.

ACF·PACF로 탐색 범위를 좁히는 방법

ACF는 주로 q 후보를, PACF는 주로 p 후보를 정하는 데 사용한다. 절단(cutoff)과 감쇠(decay) 패턴을 보고 후보군을 만든 뒤 제한된 그리드에서 비교한다.

  • PACF가 특정 시차에서 절단되고 ACF가 지수 감쇠하면 AR 신호로 보고 p를 늘린다.
  • ACF가 절단되고 PACF가 감쇠하면 MA 신호로 보고 q를 늘린다.
  • 두 패턴이 모두 감쇠하면 혼합형으로 보고 p와 q를 0~2 범위에서 함께 탐색한다.

계절 차수도 같은 방식으로 후보를 정한다. p, q, P, Q를 {0,1,2} 같은 제한 그리드에서 탐색하고 AIC 또는 BIC가 작은 모델을 우선 검토한다. P와 Q는 0~1, 최대 2 범위에서 탐색한 뒤 BIC로 복잡도에 대한 벌점을 반영할 수 있다.

BIC를 우선지표로 두면 단순 모델을 선호하게 된다. BIC가 같을 때는 AIC와 잔차 진단을 비교한다. Ljung–Box 검정에서 잔차의 무상관성을 확인하고, 잔차 ACF에 유의한 패턴이 보이면 차수를 다시 조정한다. 추정이 실패하면 초기값을 바꾸거나 enforce_stationarityenforce_invertibility 옵션을 점검한다.

예측 파이프라인에서의 ARIMA와 SARIMA

수요 예측에서는 월·주 단위 판매량과 프로모션 시즌 영향을 반영할 수 있다. SKU별 학습 시간이 짧고 배치 재학습 자동화에 맞추기 쉽다.

용량 계획과 인프라 모니터링에서는 CPU나 트래픽의 일·주기 계절성을 모델에 반영한다. 예측 구간별 신뢰구간은 임계치 기반 알림에 사용할 수 있다.

금리와 물가지수처럼 장기 추세와 계절성이 같이 나타나는 지표에는 d와 D를 함께 고려한다. 이벤트성 변동은 외생변수를 추가하는 SARIMAX로 확장할 수 있다.

보간/클리핑비정상정상계절 존재없음적합부적합입력: 시계열 y_t결측/이상치 처리분산 안정화변환(로그/Box-Cox)정상성 검정(ADF)비계절 차분 d += 1계절성 탐지(ACF/도메인)계절 차분 D 결정 (s, D{0,1})비계절 모델 경로ACF/PACF 분석 p,q,P,Q후보 생성그리드 서치(AIC/BIC 최소)모델 적합(ARIMA/SARIMA)잔차 진단(Ljung–Box, 정규성,등분산)예측/신뢰구간/성능평가(RMSE, MAPE)
항목 ARIMA SARIMA
성능(계절 데이터) 낮음~보통 높음
확장성(탐색/학습 비용) 낮음 중간~높음
일관성(계절 패턴 변화 대응) 낮음 보통
안정성(과적합 위험) 중간 높음
운영 편의(튜닝/배포) 높음 보통

파이썬으로 SARIMA 후보를 탐색하고 검증하기

전제조건은 Python 3.10+, pandas 2.2+, statsmodels 0.14+, numpy 1.26+다. 예시에서는 결측을 보간한 statsmodels CO2 월별 시계열을 사용한다.

# env: python>=3.10, pandas>=2.2, statsmodels>=0.14, numpy>=1.26
import warnings
warnings.filterwarnings("ignore")

import numpy as np
import pandas as pd
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.stattools import acf, pacf
from statsmodels.tsa.statespace.sarimax import SARIMAX
from statsmodels.datasets import co2
from scipy import stats

# 1) 데이터 로드·전처리
dta = co2.load_pandas().data  # weekly CO2 with NaN
y = dta['co2'].resample('MS').mean().interpolate('linear')  # monthly start, linear fill
y = np.log(y)  # 분산 안정화

# 2) d 결정: ADF 반복
def select_d(series, max_d=2, alpha=0.05):
    d = 0
    s = series.copy()
    while d < max_d:
        pval = adfuller(s.dropna(), autolag='AIC')[1]
        if pval <= alpha:
            return d, s
        d += 1
        s = s.diff().dropna()
    return d, s

d, y_d = select_d(y)

# 3) 계절 s/D 결정: 월별 → s=12, D=0/1 판단
s = 12
def need_seasonal_diff(series, s, alpha=0.05):
    sd = series.diff(s).dropna()
    pval = adfuller(sd, autolag='AIC')[1]
    return 1 if pval > alpha else 0

D = need_seasonal_diff(y, s)

# 4) 후보 p,q,P,Q 그리드 + BIC 최소 탐색
def grid_search_sarima(series, d, D, s, max_pq=2, max_PQ=1):
    best, best_bic = None, np.inf
    for p in range(max_pq+1):
        for q in range(max_pq+1):
            for P in range(max_PQ+1):
                for Q in range(max_PQ+1):
                    if p==q==0 and P==Q==0:
                        continue
                    try:
                        model = SARIMAX(series, order=(p,d,q),
                                        seasonal_order=(P,D,Q,s),
                                        enforce_stationarity=True,
                                        enforce_invertibility=True)
                        res = model.fit(disp=False)
                        if res.bic < best_bic and np.isfinite(res.bic):
                            best_bic, best = res.bic, res
                    except Exception:
                        continue
    return best

res = grid_search_sarima(y, d, D, s)

# 5) 진단: 잔차 검정, 요약
from statsmodels.stats.diagnostic import acorr_ljungbox
lb = acorr_ljungbox(res.resid.dropna(), lags=[12], return_df=True)
print(f"Selected model: ARIMA{res.model.order}x{res.model.seasonal_order}")
print(f"AIC={res.aic:.2f}, BIC={res.bic:.2f}")
print("Ljung-Box p-value @lag12:", float(lb['lb_pvalue'].iloc[0]))

# 6) 홀드아웃 평가 및 예측
train = y.iloc[:-24]
test = y.iloc[-24:]

best_res = SARIMAX(train,
                   order=res.model.order,
                   seasonal_order=res.model.seasonal_order,
                   enforce_stationarity=True,
                   enforce_invertibility=True).fit(disp=False)

fc = best_res.get_forecast(steps=24)
pred = fc.predicted_mean
# 로그 역변환
pred_exp = np.exp(pred)
test_exp = np.exp(test)

mape = (np.abs(test_exp - pred_exp) / test_exp).mean()
print(f"MAPE={mape:.3%}")

# 예외/에러 핸들링 팁:
# - 수렴 실패: start_params 제공 또는 trend='c'/'n' 변경
# - 과한 차수: BIC 폭증, 잔차 ACF 유의 → 차수 축소
# - 누락 계절성: 잔차 ACF에 s 배수 피크 → P,Q 또는 D 조정

코드처럼 d와 D는 단위근 검정 결과에 따라 최대 1~2 범위에서 제한한다. 파라미터 탐색은 작은 그리드에서 시작하고, 진단 결과를 보고 필요할 때 확장한다. 로그 변환을 적용했다면 예측값을 역변환할 때 지수 적용이 필요하다.

계절 데이터에서 SARIMA를 도입하면 비계절 ARIMA 대비 MAPE가 520% 개선된 사례가 다수 있다. BIC 기반 단순화는 파라미터 수를 2040% 줄이고 재학습 시간을 단축할 수 있다. 시차별 영향과 계절 주기 영향을 분해해 해석할 수 있으며, 잔차 진단 루틴은 운영 중 예측 품질을 점검하는 기준이 된다.

ARIMASARIMA시계열 예측정상성계절성