Today I learned
1. [라이브세선] 시계열 데이터의 개요
MLops: 머신러닝 + operation, 주피터 노트북에만 코딩하고 끝나는게 아닌 사내 operation 시스템에 적용하는 것 까지 목표로
키워드: ARIMA, Prohphet, Docker, BentoML
(1) 시계열 분석?
- 과거의 흐름으로 미래를 예측하는 방법론
- 시게열 분석은 통계학&데이터 사이언스의 전공 과목에서도 전공 선택으로, 난이도가 높음
- 과거의 트렌드로 미래를 예측할 수 있다는 논리로 많은 회사들이 시게엶 모델을 적용하고 있음
- Meta - Prophet 모델, 딥러닝: LSTM, RNN, Transfomer(LLM)모델 등이 개발
- 추천 컨텐츠: 파이썬 시계열 예측 분석
https://github.com/jpub-dongdong9/TimeSeriesForecastingInPython/tree/master
GitHub - jpub-dongdong9/TimeSeriesForecastingInPython: Jpub 출간, 한국어 번역서를 위한 소스 코드 저장소입니
Jpub 출간, 한국어 번역서를 위한 소스 코드 저장소입니다. Contribute to jpub-dongdong9/TimeSeriesForecastingInPython development by creating an account on GitHub.
github.com
(2) 시계열 예측의 이해
- 시계열 데이터의 정의
- 시간에 따라 정렬된 데이터
- 주기적으로 기록되며, 동일한 시간단계로 분포한다고 정의
- 시계열 데이터의 구성 요소
a. 계절성(Seasonality)
- 일정한 경향에 따라 반복되는 패턴
- 주로 연간 주기를 따르며, 특정 계절에 따라 데이터가 변동을 보이는 경우
- 일반적으로 시간에 관련된 고정된 주기를 가지고 있음(예시. 여름에 에어컨 더 잘 팔림)
b. 추세(Trend)
- 지속적으로 일관된 방향으로 변화하는 장기적 패턴
- 패턴은 증가, 감소, 일정한 상태
c. 주기성(Cycles)
- 경제적, 사회적 ,정치적 요인에 의해 발생하는 불규칙한 변동 패턴
- 장기적이고 불규칙한 패턴 - 계절성과의 차이점
| 개념 | 계절성 | 주기 |
| 규칙성 | 일정한 간격으로 반복(매년,매월) | 불규칙적으로 발생 |
| 발생 원인 | 계절적인 요인(기후 등) | 경제, 사회적인 요인(경기순환) |
| 예측 가능성 | 규칙적이므로 예측 가능 | 변동성이 커서 예측 어려움 |
| 시간 범위 | 1년 이내 | 수년 이상 |
d. 잔차(Residual)
- 흔히 오차라고 얘기함, 시계열이 통계 기반이라 '잔차'라고 한다
- 시게열 데이터에서 추세와 계절성을 제거한 후 남는 무작위적 변화량

- (a) 200 거래일 동안의 구글 주식 가격: 추세 (2015년이후로 꾸준히 상승)
- (b) 200 거래일 동안의 구글 주식 가격의 일일 변동 → 3가지다 없음!
- (c) 미국의 연간 파업 수: 추세(하락 후 상승), 주기
(3) 시계열 분해(Time Series Decomposition)
- -시계열 분해를 통해 각 구성요소를 시각화하면 데이터로 파악하기 어려운 추세와 게절적 패턴을 파악하는데 큰 도움이 된다.
- 계절적 구성요소: 시계열의 계절적 패턴, 주기는 일정 기간동안 반복적으로 발생
- 추세는 시계열에서 느리게 움직이는 큰 변화를 말함
- 잔차는 추세 및 계절성으로 설명할 수 없는 변동

(4) 시계열 예측 vs 회귀예측
같은데 다름
a. 시계열 데이터는 순서가 있다.
- 시계열 데이터의 순서를 섞게 되면 모델이 올바른 패턴을 학습할 수 없음
- 이렇게 될 경우 '시간 종속성을 해쳤다' 라고 말함
- 반면, 회귀분석에서는 데이터의 순서가 중요하지 않은 경우가 많음.
- 시계열 데이터는 시간에 따른 순서가 매우 매우 줜나 중요함.
b. 시계열에는 피처가 데이터 하나.
- 회귀분석의 경우, 여러 피처를 사용해 목표 변수를 예측하는 것이 일반적임.

- 시계열 분석에서는 시간의 흐름에 따라 특정 시점에 관측된 값에 기반해, 주로 과거의 값(피처)을 사용하여 미래의 값을 예측합니다.

- 과거 시점의 데이터 자체가 피처 역할, 시간 의존성이 핵심임
- 자기회귀모델(AR): 과거 값을 기반으로 미래 값을 에측
- 이동평균모델(MA): 과거의 잔차(Residuals)를 활용해 예측

(5) 베이스라인 실습
|
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
pd.options.mode.chained_assignment = None
|
기본 라이브러리 불러오기 마지막은 경고 표시 끔 이소리임 |
| 깃허브에 있는 데이터를 복사하여 파일 칸으로 가져오는 코드임. | |
|
df = pd.read_csv('/content/TimeSeriesForecastingInPython/data/jj.csv')
df.head()
|
![]() |
| Plot data with train/test split | |
|
fig, ax = plt.subplots()
ax.plot(df['date'], df['data'])
ax.set_xlabel('Date')
ax.set_ylabel('Earnings per share (USD)')
ax.axvspan(80, 83, color='#808080', alpha=0.2)
plt.xticks(np.arange(0, 81, 8), [1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980])
fig.autofmt_xdate()
plt.tight_layout()
# plt.savefig('figures/CH02_F01_peixeiro.png', dpi=300)
|
- ax.axvspan(80, 83, color='#808080', alpha=0.2) 구간 80~83을 #808080으로 하이라이트 - fig.autofmt_xdate() X축 라벨이 겹치지 않도록 자동 회전 ![]() |
|
Split to train/test
- 훈련 데이터: 1960년부터 1979년까지 분기
- 테스트 데이터: 1980년도4개의 분기
|
|
|
train = df[:-4]
test = df[-4:]
test
|
![]() 요 뒤에서 4개 빼고 싹 다 train으로 돌리겠다! |
A. Predict Historical mean: 단순 평균으로 예측해볼까
|
historical_mean = np.mean(train['data'])
historical_mean
|
np.float64(4.308499987499999)
|
|
test.loc[:, 'pred_mean'] = historical_mean
test
|
![]() |
| MAPE(Mean Absolute Percentage Error) 예측 오차를 백분율로 나타낸 지표 예측값-실제값의 차이를 백분위로 표현하여 직관적ㅇ니 이해 0%에 가까울 수록 좋은 모 |
|
|
def mape(y_true, y_pred):
return np.mean(np.abs((y_true - y_pred) / y_true)) * 100
|
|
|
mape_hist_mean = mape(test['data'], test['pred_mean'])
mape_hist_mean
|
np.float64(70.00752579965119)
|
|
fig, ax = plt.subplots()
ax.plot(train['date'], train['data'], 'g-.', label='Train')
ax.plot(test['date'], test['data'], 'b-', label='Test')
ax.plot(test['date'], test['pred_mean'], 'r--', label='Predicted')
ax.set_xlabel('Date')
ax.set_ylabel('Earnings per share (USD)')
ax.axvspan(80, 83, color='#808080', alpha=0.2)
ax.legend(loc=2)
#2년간격으로 8씩 증가하는 배열을 생성하여 x축을 정리
plt.xticks(np.arange(0, 85, 8), [1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980])
#x축 눈금 자동서식
fig.autofmt_xdate()
#여백 제거
plt.tight_layout()
# plt.savefig('figures/CH02_F06_peixeiro.png', dpi=300)
|
![]() |
- 전체 평균으로 진행한 결과, 올바르지 못한 데이터임을 알 수 있음. predict 부분 봐바라...
B. Predict last year mean: 직전 데이터를 이용 - train에서 마지막 4분기 데이터 평균
|
last_year_mean = np.mean(train['data'][-4:])
last_year_mean
|
np.float64(12.96) |
|
test.loc[:, 'pred__last_yr_mean'] = last_year_mean
test
|
![]() |
|
mape_last_year_mean = mape(test['data'], test['pred__last_yr_mean'])
mape_last_year_mean
|
np.float64(15.5963680725103) |
|
fig, ax = plt.subplots()
ax.plot(train['date'], train['data'], 'g-.', label='Train')
ax.plot(test['date'], test['data'], 'b-', label='Test')
ax.plot(test['date'], test['pred__last_yr_mean'], 'r--', label='Predicted')
ax.set_xlabel('Date')
ax.set_ylabel('Earnings per share (USD)')
ax.axvspan(80, 83, color='#808080', alpha=0.2)
ax.legend(loc=2)
plt.xticks(np.arange(0, 85, 8), [1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980])
fig.autofmt_xdate()
plt.tight_layout()
# plt.savefig('figures/CH02_F07_peixeiro.png', dpi=300)
|
![]() |
C. Predict last know value: 마지막 측정 데이터 이용하기
|
last = train['data'].iloc[-1]
last
|
np.float64(9.99) |
|
test.loc[:, 'pred_last'] = last
test
|
![]() |
|
fig, ax = plt.subplots()
ax.plot(train['date'], train['data'], 'g-.', label='Train')
ax.plot(test['date'], test['data'], 'b-', label='Test')
ax.plot(test['date'], test['pred_last'], 'r--', label='Predicted')
ax.set_xlabel('Date')
ax.set_ylabel('Earnings per share (USD)')
ax.axvspan(80, 83, color='#808080', alpha=0.2)
ax.legend(loc=2)
plt.xticks(np.arange(0, 85, 8), [1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980])
fig.autofmt_xdate()
plt.tight_layout()
# plt.savefig('figures/CH02_F08_peixeiro.png', dpi=300)
|
![]() |
좋은 방향은 아닌거 같죠...?
D. Naive seasonal forecast: 단순한 게절적 예측, 마지막으로 측정된 계절성이 미래에도 반복된다! 라는 개념
|
#1979년 마지막 5분기의 데이터
test.loc[:, 'pred_last_season'] = train['data'][-4:].values
test
|
![]() |
|
mape_naive_seasonal = mape(test['data'], test['pred_last_season'])
mape_naive_seasonal
|
np.float64(11.561658552433654) 예측력 자체는 가장 좋다! |
|
fig, ax = plt.subplots()
ax.plot(train['date'], train['data'], 'g-.', label='Train')
ax.plot(test['date'], test['data'], 'b-', label='Test')
ax.plot(test['date'], test['pred_last_season'], 'r--', label='Predicted')
ax.set_xlabel('Date')
ax.set_ylabel('Earnings per share (USD)')
ax.axvspan(80, 83, color='#808080', alpha=0.2)
ax.legend(loc=2)
plt.xticks(np.arange(0, 85, 8), [1960, 1962, 1964, 1966, 1968, 1970, 1972, 1974, 1976, 1978, 1980])
fig.autofmt_xdate()
plt.tight_layout()
# plt.savefig('figures/CH02_F09_peixeiro.png', dpi=300)
|
![]() |
|
fig, ax = plt.subplots()
x = ['hist_mean', 'last_year_mean', 'last', 'naive_seasonal']
y = [70.00, 15.60, 30.46, 11.56]
ax.bar(x, y, width=0.4)
ax.set_xlabel('Baselines')
ax.set_ylabel('MAPE (%)')
ax.set_ylim(0, 75)
for index, value in enumerate(y):
plt.text(x=index, y=value + 1, s=str(value), ha='center')
plt.tight_layout()
# plt.savefig('figures/CH02_F10_peixeiro.png', dpi=300)
|
![]() |
Mape가 0에 가까울 수록 가장 좋은 모델
(6) 베이스라인 정리
- 모델은 가장 간단한모델부터 하나씩 변수를 바꿔가며 테스트하면 해석력이 좋아진다.
- 베이스라인 모델은 평균을 이용한는 것이 대표적이다.
- MAPE는 예측값이 실제값과 얼마나 차이나는지 직관적으로 측정하는 지표이다.
- 머신러닝 모델을 어렵게 진행하는 경우가 많아요... AI 도움 받아서 이해못하는 모델코드를 써서 그걸 해석하려고 함... 응용하기 힘든 경우가 많음
- 0에 대한 베이스 모델을 먼저 만들어보셈, 가장 간단하고 무식하고 이해하기 편한 기본을 만드셈
- 시계열적인 내용을 넣어보고, 첨가를 통해 베이스모델보다 좋아지냐 나빠지냐를 체크
- 왜 이 변수와 우리의 방법이 유용했냐? 를 체크해야함
- 시게열 모델에서도 사람이 이해할 수 있는 방법으로 넣어서...
2. 태블로를 활용한 데이터 시각화
'빅데이터 QAQC_3기 > 빅데이터 QAQC_3기 TIL' 카테고리의 다른 글
| TIL_251120 (0) | 2025.11.20 |
|---|---|
| TIL_251119 (0) | 2025.11.19 |
| TIL_251117 (0) | 2025.11.17 |
| TIL_251114 (0) | 2025.11.14 |
| TIL_251112 (0) | 2025.11.12 |











