시계열 분해
IT 위키
더 많은 작업
- Time Series Decomposition; 시계열 분해법, 분해 시계열
- 시계열을 추세, 계절, 순환, 불규칙 요인으로 나누어 각 요인의 영향을 따로 살펴보는 시계열 분석 기법
판매량, 전력 수요, 방문자 수 같은 시계열에는 장기적인 증가·감소, 해마다 되풀이되는 패턴, 경기 변동 같은 여러 움직임이 겹쳐 있다. 시계열 분해는 이를 요인별로 떼어 내어 패턴을 이해하고, 계절 조정(계절 요인을 뺀 시계열)을 만들거나 예측 모형을 고르는 데 쓴다. 분해 자체는 예측 모형이라기보다 탐색적 데이터 분석 도구에 가깝다.
| 요인 | 기호 | 뜻 | 예 |
|---|---|---|---|
| 추세 요인(trend) | Tt | 장기적으로 증가하거나 감소하는 움직임 | 인구 증가에 따른 수요 증가 |
| 계절 요인(seasonal) | St | 일정한 주기(월, 분기, 요일 등)로 되풀이되는 변동. 주기가 고정되어 있다 | 여름철 에어컨 판매, 연말 소매 매출 |
| 순환 요인(cyclical) | Ct | 주기가 일정하지 않은 2년 이상의 오르내림 | 경기 확장·수축 |
| 불규칙 요인(irregular) | It | 위 요인으로 설명되지 않는 무작위 변동(잔차) | 일시적 사건, 측정 오차 |
- 순환 요인은 따로 추정하기 어려워서 실제 분해에서는 추세와 묶어 추세-순환(trend-cycle) 요인 하나로 다루는 경우가 많다. R
decompose(),stl()과 statsmodels의 분해 결과도 추세(trend), 계절(seasonal), 나머지(remainder/resid) 세 성분이다.
| 항목 | 가법 모형 | 승법 모형 |
|---|---|---|
| 계절 변동의 폭 | 수준과 관계없이 일정 | 수준에 비례해 커지거나 작아짐 |
| 계절 요인의 단위 | 원 자료와 같은 단위(더하고 빼는 양) | 비율(계절 지수, 평균 1) |
| 그래프에서 판단 | 산과 골의 높이 차가 일정 | 시간이 갈수록 산과 골의 차가 벌어짐 |
| 관계 | 로그를 취한 승법 모형은 가법 모형이 된다 |
이동평균 기반 고전 분해(classical decomposition)는 다음 순서로 진행한다.
- 주기 m에 맞춘 중심 이동평균으로 추세를 추정한다. m이 짝수(월별 12)이면 2×12 중심 이동평균을 써서 앞뒤 m/2개 시점의 추세는 구할 수 없다.
- 원 자료에서 추세를 빼거나(가법) 나누어(승법) 계절+불규칙 성분을 얻는다.
- 같은 계절 위치(예: 모든 1월)끼리 평균 내어 계절 요인을 구하고, 합이 0(가법) 또는 평균이 1(승법)이 되도록 중심화한다.
- 원 자료에서 추세와 계절 요인을 제거한 나머지가 불규칙 요인이다.
고전 분해는 계절 패턴이 해마다 똑같다고 가정하고, 양 끝의 추세가 비며, 이상치에 약하다.
STL(Seasonal-Trend decomposition using Loess)은 Cleveland 등(1990)이 제안한 방법으로, 국소 회귀(loess)를 반복 적용해 계절과 추세를 추정한다.
- 계절 패턴이 시간에 따라 천천히 바뀌는 것을 허용한다(계절 창 크기로 조절, R에서
s.window = "periodic"이면 고정). - robust 옵션으로 이상치의 영향을 줄일 수 있다.
- 월·분기뿐 아니라 임의의 주기에 쓸 수 있고, 양 끝에서도 추세가 계산된다.
- 가법 분해이므로 승법 구조의 자료는 로그를 취한 뒤 적용한다.
| 항목 | 고전 분해 | STL |
|---|---|---|
| 추세 추정 | 중심 이동평균 | loess 반복 |
| 계절 패턴 | 매년 고정 | 시간에 따라 변할 수 있음 |
| 양 끝 값 | 추세 결측 | 계산됨 |
| 이상치 | 민감 | robust 옵션으로 완화 |
| 승법 모형 | 직접 지원 | 로그 변환 후 적용 |
| R | decompose(x, type = "multiplicative") |
stl(x, s.window = "periodic")
|
| Python | seasonal_decompose(y, model="multiplicative") |
STL(y, period=12).fit()
|
x <- ts(y, start = c(2020, 1), frequency = 12)
d_add <- decompose(x) # 가법 고전 분해
d_mul <- decompose(x, type = "multiplicative") # 승법 고전 분해
d_mul$figure # 12개월 계절 지수
plot(d_mul)
s <- stl(log(x), s.window = "periodic", robust = TRUE) # 로그 후 STL
plot(s)
import numpy as np
import pandas as pd
from statsmodels.tsa.seasonal import seasonal_decompose, STL
# 수준이 커질수록 계절 폭도 커지는 가상의 월별 자료 5년치
rng = np.random.default_rng(7)
t = np.arange(60)
trend = 100 + 3 * t
season = 1 + 0.2 * np.sin(2 * np.pi * t / 12) # 계절 지수(승법)
y = pd.Series(trend * season * rng.normal(1, 0.02, 60),
index=pd.date_range("2020-01", periods=60, freq="MS"))
add = seasonal_decompose(y, model="additive", period=12)
mul = seasonal_decompose(y, model="multiplicative", period=12)
print("가법 계절 요인(1~6월):", add.seasonal.iloc[:6].round(2).tolist())
print("승법 계절 지수(1~6월):", mul.seasonal.iloc[:6].round(3).tolist())
print("이동평균 추세 앞 7개:", mul.trend.iloc[:7].round(1).tolist())
stl = STL(np.log(y), period=12, robust=True).fit() # 로그를 취해 승법을 가법으로
print("STL(log) 계절 요인(1~6월):", np.exp(stl.seasonal.iloc[:6]).round(3).tolist())
print(f"STL 잔차 표준편차={stl.resid.std():.4f}")
실행 결과:
가법 계절 요인(1~6월): [-2.09, 20.83, 30.76, 37.44, 34.07, 21.58]
승법 계절 지수(1~6월): [0.996, 1.109, 1.161, 1.199, 1.174, 1.112]
이동평균 추세 앞 7개: [nan, nan, nan, nan, nan, nan, 116.6]
STL(log) 계절 요인(1~6월): [0.98, 1.096, 1.156, 1.183, 1.174, 1.101]
STL 잔차 표준편차=0.0154
- 자료는 실제 계절 지수가 1월 1.0, 2월 1.1, 3월 약 1.17, 4월 1.2가 되도록 만들었다. 승법 분해의 계절 지수(0.996, 1.109, 1.161, 1.199, …)가 이를 잘 복원한다. 4월은 평균보다 약 20% 높다는 뜻이다.
- 가법 분해는 4월 계절 요인을 +37.44로 고정해 버린다. 실제로는 추세가 109인 첫해 4월의 효과가 약 +22(109 × 0.2), 추세가 253인 마지막 해 4월은 약 +51이므로, 가법 모형은 초기에는 과대, 후기에는 과소 추정한다. 계절 폭이 수준과 함께 커지는 자료에는 승법 모형이 맞다.
- 12개월 중심 이동평균을 쓰므로 처음 6개(와 마지막 6개) 시점의 추세는 nan이다.
- 로그를 취한 STL도 비슷한 계절 지수를 내고, 로그 척도 잔차의 표준편차가 0.0154로 작아(약 1.5% 수준의 변동) 남은 불규칙 요인이 작음을 보여 준다.