My Data Story

시계열 데이터 분석 절차(1/6) - 시계열 데이터 패턴 추출 본문

Time Series Analysis/1. 시계열 분석 절차

시계열 데이터 분석 절차(1/6) - 시계열 데이터 패턴 추출

Hwasss 2021. 11. 9. 19:32
728x90

 

◈  '시계열 데이터 분석 절차' 목차 

1. 시계열 데이터 분석 절차(1/6) - 시계열 데이터 패턴 추출

    시계열 데이터에 대한 정의를 살펴보고, 시계열 분석을 위해 어떤 시계열 변수를 추출해야 하는 지 살펴보자. 

2. 시계열 데이터 분석 절차(2/6) - 시계열 데이터 분리

3. 시계열 데이터 분석 절차(3/6) - 시계열 데이터 전처리(1)

4. 시계열 데이터 분석 절차(4/6) - 시계열 데이터 전처리(2)

5. 시계열 데이터 분석 절차(5/6) - 시계열 레퍼런스 모델 구현 및 성능 확인

6. 시계열 데이터 분석 절차(6/6) - 분석 종료 위한 잔차 진단5. 시계열 데이터 분석 절차(5/5) - 분석 종료 위한 잔차 진단


1. 시계열 데이터 (Time Series Data)

시계열 데이터는 일정한 시간 간격으로 기록된 확률과정의 샘플이다.

y = { y(t) : t = ... , -2, -1, 0, 1, 2, ...}

x = { x(t) : t = ... , -2, -1, 0, 1, 2, ...}

 

독립변수 x(t)와 알고자 하는 종속변수 y(t)가 시간 단위(t) 를 포함한다.

모델의 출력은 y의 시간 t에서의 예측값이다. 

 

2. 대표적인 시계열 변수 추출 (Feature Engineering)

시계열 데이터 분석 할 때 대표적으로 추출하는 시계열 변수에 대해 살펴보자.

2.1 시계열 변수 추출 종류

■ 빈도 (Frequency)

계절성 패턴 (Seasonality)이 나타나기 전까지의 데이터 갯수로 사람이 정해야 한다.

 

예시1 - 계절성이 1년에 1회 나타날 경우

수집된 데이터 단위 빈도 
Annual 1
Quarterly 4
Monthly 12
Weekly 52

 

예시2 - 수집된 데이터 단위가 "일(Day)" 인 경우

계절성  빈도 
Weekly 7
Annual 365

※ 빈도 설정을 위한 Python 함수 옵션 

더보기
Alias Description
B Business day
D Calendar day
W Weekly
M Month end
Q Quarter end
A Year end
AS Year start
H Hourly frequency
T, min Minutely frequency
S Secondly frequency
Alias Description
B Business day
D Calendar day
W Weekly
M Month end
Q Quarter end
A Year end
AS Year start
H Hourly frequency
T, min Minutely frequency
S Secondly frequency

 

■ 추세 (Trend)

추세는 시계열이 시간에 따라 증가, 감소 또는 일정 수준을 유지하는 경우를 뜻한다. 

수학적으로 이해해보았을 때, 확률과정 Y(t)은 추정이 가능한 추세 함수 f(t) 와 정상 확률 과정 Y(t)' 의 합이다. 

즉 Y(t) = f(t) + Y(t)' 이다. 

 

■ 계절성 (Seasonality)

일정한 빈도로 주기적으로 반복되는 패턴(m), 특정한 달/요일에 따라 기대값이 달라지는 것을 뜻한다. 

 

■ 주기 (Cycle)

태양의 흑점 수의 변화 또는 남극 빙하의 변화와 같이 주기가 긴 경우의 변동을 갖는 시계열을 뜻한다. 

Business Cycle 이라는 용어를 사용하기도 한다.

 

2.2 시계열 변수 추출  실습

■ 빈도(frequency) 설정

#1.Data Loading
location = 'https://raw.githubusercontent.com/cheonbi/DataScience/master/Data/Bike_Sharing_Demand_Full.csv'
raw_all = pd.read_csv(location)

#2.Data 탐색
raw_all.shape
raw_all.describe(includ='all') #명목형 변수도 포함하여 요약 통계 출력
raw_all.info()

#3.Data 준비하기 
#(1)datetime 속성 변경
raw_all['datetime'] = pd.to_datetime(raw_all['datetime'])

#(2)index 변경
if raw_all.index.dtype=='int64' : 
    raw_all.set_index('datetime', inplace=True)
    
#(3)각 컬럼에 Null 확인
raw_all.isnull().sum()

#(4)시계열 데이터의 빈도 설정
raw_all.asfreq('H')

#(5)빈도 설정으로 특정 간격에 데이터 없을 수 있음
#null인 컬럼 확인
raw_all.asfreq('H').isnull().sum()

#(6)null이 존재하는 데이터 row 확인
raw_all.asfreq('H')[raw_all.asfreq('H').isnull().sum(axis=1)>0]

#(7)null로 존재하는 데이터 고려해서 빈도 설정
raw_all = raw_all.asfreq('H', method='ffill')

 

■ 시계열 분해 (추세 + 계절성 + 잔차)

import statsmodels.api as sm
import matplotlib.pyplot as plt

#Split Time Series Data as Trend + Seasonal + residual
sm.tsa.seasonal_decompose(raw_all['count'], model='additive').plot()
plt.show()

result = sm.tsa.seasonal_decompose(raw_all['count'], model='additive')
print(result.observed)
print(result.trend)
print(result.seasonal)
print(result.resid)

#Residual statics
pd.DataFrame(result.resid).describe()

#Create trend, seasonal columns
Y_trend = pd.DataFrame(result.trend)
Y_trend.fillna(method='ffill', inplace=True)
Y_trend.fillna(method='bfill', inplace=True)

Y_seasonal = pd.DataFrame(result.seasonal)
Y_seasonal.fillna(method='ffill', inplace=True)
Y_seasonal.fillan(method='bfill', inplace=True)

raw_all = pd.concat([raw_all, Y_trend, Y_seasonal], axis=1)
raw_alll.isnull().sum()

 

■ 이동 평균(moving average) 

#moving average values using rolling function
Y_count_Day = raw_all[['count']].rolling(24).mean()
Y_count_Day.fillna(method='ffill', inplace=True)
Y_count_Day.fillna(method='bfill', inplace=True)
Y_count_Day.columns=['count_Day']

raw_all = pd.concat([raw_all, Y_count_Day], axis=1)

 

■ 차분 (diff)

diff 값 = 현재 row 값 - 이전 row의 값 이다. 그래서 첫번째 row의 diff 값은 Null 값이다.

Y_diff = raw_all[['count']].diff()
Y_diff.fillna(method='ffill', inplace=True)
Y_diff.fillna(method='bfill', inplace=True)
Y_diff.columns=['count_diff']

 

■ 연속형 변수 그룹화

#split 'temp' into 10 groups
raw_all['temp_group'] = pd.cut(raw_all['temp'], 10)

 

■ 시간 단위 추출하기 

#feature extraction of time information 
raw_all['Year'] = raw_all.datetime.dt.year
raw_all['Quarter'] = raw_all.datetime.dt.quarter
raw_all['Quarter2'] = raw_all['Quarter'] + (raw_all.Year - raw_all.Year.min())*4
raw_all['Month'] = raw_all.datetime.dt.month
raw_all['Day'] = raw_all.datetime.dt.day
raw_all['Hour'] = raw_all.datetime.dt.hour

 

■ 지연값(Lagged Values) 추출하기

지연값(Lagged Values)은 특정 변수에 대해 지연된 시점이 반영된 값으로 ARIMA/VAR/NNAR 등에 활용된다.

#calculation of lags of Y
raw_all['count_lag1'] = raw_all['count'].shift(1)
raw_all['count_lag2'] = raw_all['count'].shift(2)

raw_all['count_lag1'].fillna(method='bfill', inplace=True)
raw_all['count_lag2'].fillna(method='bfill', inplace=True)

 

■ 더미화 (dummy)

범주형 변수 (Categorical Variable)을 이진수(0 또는 1) 형태로 변수를 생성하는 것이다.

#pd.get_dummies() : 변수 Quarter에 대한 더미 생성 
#prefix : 생성된 더미 컬럼명 앞에 붙일 단어
#drop_first : 생성된 더미 컬럼 중 가장 첫번째 컬럼 삭제
quarter_dummy = pd.get_dummies(raw_all['Quarter'], prefix='Quarter_Dummy', drop_first=True)
raw_all = pd.concat([raw_all, quarter_dummy], axis=1)
del raw_all['Quarter']

 

3. 시계열 분석 시간 영역 선택

시계열 분석 시, 분석 효과에 도움이 될만한 시간 영역(해상도)를 선택해야 한다. 

다시 말해, 예측 정확성이 높은 시간 영역을 제대로 선정해야 한다. 

 

예를 들어 내년 매출액을 예측하는 시계열 분석을 진행할 경우, 

빈도가 Month인 데이터로 시계열 예측 모델을 생성하여, 내년 1월 ~ 12월의 예상 매출액을 구해 합산할 수도 있고

빈도가 Quarter인 데이터로 시계열 예측 모델을 생성하여, 내년 1Q ~ 4Q의 예상 매출액을 구해 합산할 수도 있고

빈도가 Year인 데이터로 시계열 예측 모델을 생성하여, 내년 1년 치 예상 매출액을 구할 수도 있다. 

 

어떤 빈도의 데이터로 시계열 분석을 해야 더 예측력이 높은 지는 알 수 없다. 

이는 모두 직접 실행해봐야 알 수 있는 부분이고 경험적 노하우가 강한 측면이 있다.

일반적으로 세분화된 시간 영역을 사용할 경우 예측 정확도가 더 나은 경향이 있다. 

물론 너무 세분화된 시간 영역을 사용할 경우에는 예측 에러가 증가할 수 있다. 

 

시계열 예측 정확성이 높다는 것은 다시 말해 과거 패턴이 미래에도 그대로 유지 된다는 의미이다. 

반대로 시계열 예측 에러가 높다는 것은 패턴이 점차적으로 또는 갑자기 변경되어 예측값이 실제값에서 크게 벗어난다는 의미이다. 

 

앞으로 시계열 예측 정확성을 높일 수 있도록 현실적인 데이터 패턴을 반영하는 데이터 전처리에 대해 살펴보자.