데이터분석과 개발

[시계열 분석] 시계열분해(2) - 추세 구해보기(이동평균, Loess) 본문

AI(시계열)/시계열분석

[시계열 분석] 시계열분해(2) - 추세 구해보기(이동평균, Loess)

긍정적인마인드 2023. 1. 18. 23:50

* 공부한 것을 정리한 글이므로 틀린 내용이 있을 수 있습니다.

* 더 좋은 방법 또는 틀린부분이 발견될 시 계속 수정하며 업데이트 할 예정입니다.

 

안녕하세요

오늘은 고전적인 분해법(Classical) 과 STL 분해법 2가지 방법의 특징과

각 방법에서 추세 구하는 방법이 어떻게 다른지 정리 해보려 합니다.

 

이번에는 온도 데이터 7년치를 사용해 보도록 하겠습니다.

 

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from darts.datasets import TemperatureDataset
plt.style.use('default')

df = TemperatureDataset().load().pd_dataframe()
df.columns = ['temperature']
df = df.loc[:"1987"].dropna() 
df

 

1. Classical decomposition

먼저 가장 기본적인 Classical decomposition 방법에 대해 정리해보겠습니다.

 

1. 이동평균에 사용되는 앞 뒤 데이터를 사용할 수 없고

2. 이상치 값에 영향을 많이 받고 (특이한 사건이 있는 경우)

3. 시간적으로 변하는 계절적 변화를 다룰 수 없다고 합니다.

(ex. 전기수요: 예전에는 난방때문에 겨울에 계절적패턴, 현대에는 에어컨 때문에 계절적 패턴이 생김)

 

이런 문제점들이 있어서 classic 방식은 잘 사용하지 않는다고 합니다.

 

* 추세(trend) 구해보기

 

classical 에서의 추세는 MA(moving average) 즉 이동평균을 사용합니다.

코드로 구현해보겠습니다.

 

# window 크기 7일 경우

window_size = 7
fig , ax = plt.subplots(figsize=(10, 5))
ax.plot(df.index, df['temperature'], color='gray',lw=1, label='temperature')
ax.plot(df.index, df['temperature'].rolling(window=window_size, center=True).mean(),lw=2, label= f'rolling_{window_size}')
ax.set_title('Heart rate')
ax.legend()

 

 

이동평균의 특징 상 window 크기가 커질수록 더욱 부드러운 추세선을 얻을 수 있습니다.

그 대신 이동평균선이 실제데이터보다 뒤로 밀리는 현상이 나타나기도 합니다.

window size 가 30일때의 경우를 살펴보겠습니다.

 

 확대를 해보면 앞부분의 데이터 손실을 시각적으로도 확인할 수 있습니다. (이건 그냥 해보고 싶었습니다..ㅎㅎ)

 

 

2. STL decomposition

STL 시계열 분해의 특징을 알아보겠습니다.

STL 은 다른 시계열 분해보다 많은 장점을 가지고 있는 것 같습니다.  

사실 STL 의 수식을 찾아서 정리해보고 싶었는데 잘 안보여서 사이트에 나온 내용을 가져왔습니다.

 

STL 장점

  • SEAT와 X11과는 다르게, STL은 월별이나 분기별 데이터를 포함하여 어떤 종류의 계절성도 다룰 수 있고
  • 계절적인 성분이 시간에 따라 변해도 괜찮습니다. 계절성분의 변화율을 사용자가 조절할 수 있습니다.
  • 추세-주기의 매끄러운 정도를 사용자가 조절할 수 있습니다.
  • 가끔 있는 이상값이 추세-주기와 계절성분에 영향을 주지 않게 만들 수 있습니다(즉, 사용자가 강력한 분해법을 명시할 수 있습니다). 하지만, 이상값은 나머지 성분(remainder)에 영향을 줄 것입니다.

STL 단점의 경우

  • 거래일이나 달력 변동을 자동으로 다루지 않고,
  • 덧셈 분해만 지원합니다. ( mstl 방식이 따로 있습니다)

출처: https://otexts.com/fppkr/stl.html

 

* 추세(trend) 구해보기

 

classical 방법의 추세 추출은 데이터손실, 뒤로 밀리는 등 여러 문제가 있는 반면

stl 에서 사용하는 loess 추세 추출은 loess 알고리즘을 사용하여 기존의 데이터 손실이 없고 이상치에 둔감하며, 뒤로 밀리지 않습니다.

 

Loess 알고리즘이란

가중평균 회귀를 사용하여 추세를 추출하고 원래 데이터와 추출된 추세의 가중치를 한번 더 조정하여 이상치에 영향을 덜 받는 추세를 추출하는 방법입니다. 

그렇다면 이동평균 방법과 어떤 차이점이 있는지를 위주로 window size를 5라고 하고 정리해보겠습니다.

 

(1) 데이터 손실

 

loess 의 경우 정한 window size 데이터 개수를 정해 한칸씩 이동하면서 추세를 추출합니다.

이동평균도 rolling 하면서 window size만큼 평균으로 추세를 추출하지만 loess의 경우 가중평균을 이용해 추세를 추출한다는 점이 다르다고 합니다.

 

window size 안에 데이터가 순서대로 x좌표를 (x1,y1),  (x2,y2), (x3,y3), (x4,y4), (x5,y5) 라고 한다면 

(x1,y1) 에 가까운 데이터에 가중치를 더 부여하여 가중평균 회귀직선을 그립니다.

그린 가중평균회귀 직선의 y1 값을 1차적인 추세로 뽑습니다. 이런식으로 추세를 뽑게 되면 데이터 손실이 일어나지 않게 됩니다. 그림의 빨간 X가 가중평균회귀직선으로 뽑은 추세 데이터가 됩니다.

 

가중평균회귀를 사용한 1차적인 추세 뽑기

 

(2) 이상치에 둔감

 

이동평균선의 경우 모든 값들이 평균값에 들어가기 때문에 이상치가 평균에 반영될 수 밖에 없습니다.

하지만 Loess 알고리즘의 경우 이상치에 대해서 가중치를 적게 주는 방식으로 이상치에 둔감하게 추세를 뽑을 수 있다고 합니다.

 

1차적으로 뽑은 추세 값과 원래 데이터의 차이가 클수록 가중치를 적게 주어  최종적인 추세를 만듭니다.

 

 

 

* 코드 구현 

마지막으로 코드를 사용하여 Loess 알고리즘 추세를 구해보겠습니다.

 

from statsmodels.nonparametric.smoothers_lowess import lowess

y = df['temperature'].dropna()
x = np.arange(0,len(y))
frac = 0.01

# loess 추세 구하기
res = lowess(endog=y,
            exog=x,
            frac=frac,
            it=3)
res

 

결과값을 출력해보면 array 첫번째 열에 0,1,2,3... 으로 인덱스 처럼 나오고 , 두번째 열에 추세값이 나옵니다.

0부터 2554 2555개의 추세 데이터입니다.

이제 Loess를 통해 뽑은 추세를 시각화 해보겠습니다.

# 데이터프레임에 넣어주기
df['lowess'] = res[:,1]

# 시각화
fig, ax = plt.subplots(figsize=(10,5), constrained_layout=True)
ax.plot(df.index, df['temperature'], color='gray',lw=1, label='temperature')
ax.plot(df.index, df['lowess'] ,lw=3, label= f'lowess_{frac}')
ax.legend()

 

frac=0.01

 

위의 classic방식에서 이동평균 기간으로 smoothing을 조절했다면 여기서는 frac이라는 파라미터를 조절하여 smooth한 정도를 조절할 수 있습니다. frac 파라미터가 작을수록 더 세밀한 패턴을 그립니다.

 

frac=0.05

 

지금까지 추세 trend 추출에 대해 정리해보았습니다.

도움이 되셨다면 좋아요 부탁드립니다 감사합니다~



참고:

* 시계열 분해 방법

고전적

https://otexts.com/fppkr/classical-decomposition.html

STL

https://otexts.com/fppkr/stl.html

 

http://incredible.ai/time-series/2021/06/01/Time-Series-Decomposition/

 

Loess

 

https://www.youtube.com/watch?v=Vf7oJ6z2LCc&t=187s 

https://www.statsmodels.org/dev/endog_exog.html