시계열 데이터 처리
| topics | 100-데이터분석 & AI 101 머신러닝 |
| types | 이론 학습 레퍼런스 |
| tags |
시계열 데이터 처리
왜 필요한가
시계열 데이터는 시간 순서가 중요한 정보를 담고 있다. 일반 데이터처럼 무작위로 섞으면 시간적 패턴이 깨져서 모델이 제대로 학습하지 못한다.
시계열 특성을 무시하면?
- 미래 데이터로 과거를 예측하는 데이터 누수(leakage) 발생
- 시간적 트렌드와 계절성을 학습하지 못함
- 실제 환경에서는 성능이 크게 떨어짐
핵심: 시계열 데이터는 시간 순서를 유지하면서 처리해야 한다.
시계열 데이터 특성
shuffle=False로 설정
일반 데이터와 달리, 시계열 데이터는 shuffle을 하지 않는다.
왜 shuffle을 하면 안 될까?
시간도 중요한 데이터이기 때문이다.
from sklearn.model_selection import KFold
# <span id="시계열-데이터는-shufflefalse"></span>시계열 데이터는 shuffle=False
kf = KFold(n_splits=5, shuffle=False)
예시
데이터: [2020-01, 2020-02, 2020-03, ..., 2023-12]
shuffle=True (X - 잘못된 방법):
훈련: [2023-01, 2020-05, 2022-03, ...]
검증: [2020-02, 2023-11, 2021-08, ...]
→ 미래 데이터로 과거를 예측하게 됨!
shuffle=False (O - 올바른 방법):
훈련: [2020-01, 2020-02, ..., 2022-12]
검증: [2023-01, 2023-02, ..., 2023-12]
→ 과거 데이터로 미래를 예측
데이터 누수(leakage): 미래 정보가 모델 학습에 포함되어, 실제로는 불가능한 예측을 하게 되는 것. 평가 점수는 좋아 보이지만 실전에서는 쓸모없다.
지점별 Cutoff 전략
Cutoff란?
시계열 데이터를 어느 시점부터 사용할지 결정하는 것이다.
왜 필요할까?
지점별로 패턴이 다를 수 있고, 시장 특성이 다를 수 있기 때문이다.
FULL vs 50
FULL: 장기 데이터 사용
- 전체 시계열 데이터를 모두 사용
- 장기적인 트렌드와 계절성을 학습
장점
- 더 많은 데이터로 학습 가능
- 장기 패턴을 잘 잡음
단점
- 오래된 패턴이 현재와 맞지 않을 수 있음
- 시장 환경이 바뀌면 성능 저하
50: 최근 패턴에 최적화
- 최근 50개 데이터포인트만 사용
- 최신 트렌드에 집중
장점
- 최근 패턴을 더 잘 반영
- 시장 변화에 빠르게 적응
단점
- 데이터가 적어 과적합 위험
- 계절성 등 장기 패턴을 놓칠 수 있음
지점별로 다르게 처리하는 이유
지점마다 패턴이 다를 수 있다
예시: 매장 매출 예측
- 도심 매장: 평일 점심시간에 피크
- 주거지 매장: 주말 오후에 피크
- 관광지 매장: 계절별로 큰 변화
→ 각 지점의 특성에 맞는 cutoff 전략이 필요하다
시장 특성이 다를 수 있다
예시: 주식 종목별 예측
- 안정적인 종목: FULL 데이터로 장기 트렌드 학습
- 변동성 큰 종목: 최근 데이터(50)로 빠른 적응
실전 팁
1. TimeSeriesSplit 사용
sklearn의 TimeSeriesSplit을 사용하면 시계열에 맞는 교차검증이 가능하다.
from sklearn.model_selection import TimeSeriesSplit
tscv = TimeSeriesSplit(n_splits=5)
for train_idx, val_idx in tscv.split(X):
X_train, X_val = X[train_idx], X[val_idx]
y_train, y_val = y[train_idx], y[val_idx]
# 모델 학습 및 검증
model.fit(X_train, y_train)
score = model.score(X_val, y_val)
동작 방식
Fold 1: 훈련[0:100] 검증[100:120]
Fold 2: 훈련[0:120] 검증[120:140]
Fold 3: 훈련[0:140] 검증[140:160]
...
항상 과거 데이터로 훈련하고 미래 데이터로 검증한다.
2. 최적 Cutoff 찾기
여러 cutoff 값을 실험해보고 검증 성능이 가장 좋은 것을 선택한다.
cutoffs = [30, 50, 100, 200, 'full']
best_cutoff = None
best_score = float('inf')
for cutoff in cutoffs:
if cutoff == 'full':
X_cut = X
else:
X_cut = X[-cutoff:]
# 모델 학습 및 평가
score = evaluate_model(X_cut, y)
if score < best_score:
best_score = score
best_cutoff = cutoff
print(f"최적 cutoff: {best_cutoff}")
3. 지점별로 개별 모델 학습
지점마다 패턴이 크게 다르면, 전체 통합 모델보다 지점별 개별 모델이 더 나을 수 있다.
# <span id="지점별로-모델-저장"></span>지점별로 모델 저장
models = {}
for location in locations:
# 해당 지점 데이터만 추출
X_loc = X[X['location'] == location]
y_loc = y[X['location'] == location]
# 개별 모델 학습
model = XGBRegressor()
model.fit(X_loc, y_loc)
# 저장
models[location] = model