728x90

*본 내용은 시계열데이터 강의 내용 중 일부분을 요약한 내용입니다

 

시계열 변수 추출 7종

Feature Engineering 통해  새 변수를 창출

추가된 새 변수가 무조건 종속변수에 영향을 못 줄수도 있다

예로들어 광고비 1,2,3,4(백만원 단위),  GDP 1,2,3,4(조 단위) 변수가 있을때 컴퓨터는 이 두 변수를 같은 것으로 취급한다

 

1. 빈도(Frequency): 계절성 패턴(seasonality)이 나타나기 전까지의 데이터 갯수(사람이 정함)

사람이 정하는 것은 데이터를 년단위로 짜를건지, 시간단위로 짜를 건지 등 을 말한다

예로들어 한 컬럼의 데이터가 2019,2020,2021 있을때, 사람은 연도라는 걸 알지만 컴퓨터는 지정하지 않으면 모르므로 

년도라고 지정 해줘야 한다

 

다만, 데이터를 분석할때 년도,월,일 등 분해 해서 알아봐야 한다-> 어떤 변수가 종속변수에 영향을 줄지는 해보지 않곤 모른다. 이 분해 과정에서 생기는 nan는 여러 방법으로 채울수가 있다.

nan 채우는 방법's

*빈도를 먼저 설정 시 장점

 Freq를 만일 년도로 설정하고 월,일 등으로 날짜를 분해할때, 중간에 누락된 공백을 찾아서 nan로 넣어주고, 분석자는   nan를 채워주면 중간에 빠짐없이 데이터를 채울수 있다

 ex) 2020-03-29~ 2021-03-29 데이터에서 일별로 분해 시, 본 데이터에서 누락된  2020-09-21 데이터가 nan로 채워지   게 된다. 이를 위 method로 해결

 

 

2.추세(Trend): 시계열이 시간에 따라 증가, 감소 또는 일정 수준을 유지하는 경우

 

3.계절성(Seasonality): 규칙적인 기간 동안에 나타나는 패턴을 말합니다. 연간,월간,일간,시간 등 반복되는 규칙만 존재한다면 계절성으로 불립니다.

 

4.주기(Cycle): 일정하지 않은 빈도로 발생하는 패턴

현실 데이터는 계절성과 주기의 명확한 구분이 어렵다(혼재 되어 있는 경우가 많다)

영업 환경이나 전략 변화에 의하여 나타나는 경우가 많다. 

 

5.시계열 분해(추세/계절성/잔차): 본래 데이터를 추세,계절성,잔차로 분해가 가능하다

본래데이터(observed)= 추세(trend) + 계절성(seasonal)+ 잔차(residual) 

 

6.더미변수(Dummy variables): 이진수(0 또는 1)의 형태로 변수를 생성하는 것으로 휴일,이벤트,캠페인 등을 생성 가능

1. 더미변수를 하지 않았을때 의미

Y=ax가 있을때 Y는 매출, X는 계절(봄,여름,가을,겨울 )

a 계수가 3이라고 한다면 계절이 바뀔때마다 3씩 매출에 영향을 준다 로 해석가능하다

즉, 계절이 변할때 마다 매출이 어떻게 변하는지를 알고자 할때 더미 변수를 하지 않고 사용하는게 적합하다

 

2. 더미변수를 사용하고자 할때 의미

각 계절별로 '따로따로' 매출에 얼마큼 영향을 주는지 알고자 할때 쓰인다

 

7.지연값(Lagged values): 변수의 지연된 값을 독립변수로 반영하는 것으로,ARIMA/VAR/NNAR 등이 활용

ex) 광고비를 투입한 시점부터 매출에 영향을 준 걸까? 라는 의문점에서부터 시작된다
즉 영향을 주고 받는 시간 차이가 발생한다. 

★위 이론 내용을 토대로 실습 진행

Dataset: BikeSharingDemand

 

Import

# Ignore the warnings
import warnings 
# warnings.filterwarnings('always') #항상 경고문이 뜨게 한다
warnings.filterwarnings('ignore') # 경고 뜨지 않게 설정

# System related and data input controls
import os

# Data manipulation and visualization
import pandas as pd
pd.options.display.float_format = '{:,.2f}'.format #options클래스 안에 display 안에 float_format
pd.options.display.max_rows = 10 #전체데이터에서 row data 앞에 5개, 뒤에 5개 만 보이게 함
pd.options.display.max_columns = 20
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Modeling algorithms
# General
import statsmodels.api as sm
from scipy import stats

# Model selection
from sklearn.model_selection import train_test_split

# Evaluation metrics
# for regression
from sklearn.metrics import mean_squared_log_error, mean_squared_error, r2_score, mean_absolute_error

 

#데이터 불러오기

location = './Data/BikeSharingDemand/Bike_Sharing_Demand_Full.csv' #맨 앞에 . 점은 동일한 위치 라는 의미
raw_all = pd.read_csv(location)
raw_all

 

 

 Feature Engineering: 데이터에서 시계열패턴 추출하기

 Feature engineering 하기 전/후 비교하기 위해서 non_feature_engineering을 추가해여 생성합니다
 
 def non_feature_engineering(raw):
 	raw_nfe= raw.copy()
    
    #객체 타입인 datatime 변수를 datetime 타입으로 변경
    if 'datetime' in raw_nfe.columns:
    	raw_nfe['datetime'] = pd.to_datetime(raw_nfe['datetime'])
        raw_nfe['DateTime]= pd.to_datetime(raw_nfe['datetime']) #인덱스로 적용
   	if raw_nfe.index.dtype=='int64':
    	raw_nfe.set_index('DateTime', inplace=True)
        
        #bring back
        #if raw_nfe.dtype !='int64':
        #	raw_nfe.reset_index(drop=False, inplace=True)
        
        #시간 단위로 시간 축 설정후 결측치는 forward fill
        raw_nfe = raw_nfe.asfreq('H', method ='ffill')
        return raw_nfe
        
def feature_engineering(raw):
	raw_fe=raw.copy()
    if 'datetime' in raw_fe.columns:
    	raw_fe['datetime'] = pd.to_datetime(raw_fe['datetime'])
        raw_fe['DateTime]= pd.to_datetime(raw_fe['datetime']) #인덱스로 적용
   	if raw_fe.index.dtype=='int64':
    	raw_fe.set_index('DateTime', inplace=True)
        
        #시간 단위로 시간 축 설정후 결측치는 forward fill
        raw_fe = raw_fe.asfreq('H', method ='ffill')
        
        #시계열 분해_추세
        result= sm.tsa.seasal_decompose(raw_fe['count'], model ='additive')
        Y_trend = pd.DataFrame(result.trend)
        Y_trend.fillna(method='ffill', inplace=True)
        Y_trend.fillna(method='bfill', inplace=True)
        Y_trend.columns = ['count_trend']
        
        #시계열 분해_계절성
        Y_seasonal = pd.DataFrame(result.seasonal)
        Y_seasonal.fillna(method='ffill', inplace=True)
        Y_seasonal.fillna(method='bfill', inplace=True)
        Y_seasonal.columns = ['count_seasonal']
        
        #원본과 합치기
        pd.concat([raw_fe, Y_trend,Y_seasonal], axis=1).isnull().sum()
        if 'count_trend' not in raw_fe.columns:
        	raw_fe = pd.concat([raw_fe, Y_trend, Y_seasonal], axis=1)
            
        #이동평균으로 일별, 주별 count 생성    
        Y_count_Day = raw_fe[['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']
        Y_count_Week = raw_fe[['count']].rolling(24*7).mean()
        Y_count_Week.fillna(method='ffill', inplace=True)
        Y_count_Week.fillna(method='bfill', inplace=True)
        Y_count_Week.columns = ['count_Week']
        
        #원본과 합치기
        if 'count_Day' not in raw_fe.columns:
          raw_fe = pd.concat([raw_fe, Y_count_Day], axis=1)
        if 'count_Week' not in raw_fe.columns:
          raw_fe = pd.concat([raw_fe, Y_count_Week], axis=1)
        
        Y_diff = raw_fe[['count']].diff()
        Y_diff.fillna(method='ffill', inplace=True)
        Y_diff.fillna(method='bfill', inplace=True)
        Y_diff.columns = ['count_diff']
        if 'count_diff' not in raw_fe.columns:
        	raw_fe=pd.concat([raw_fe,Y_diff],axis=1)
            
        #날짜 변수 생성
        raw_fe['temp_group'] = pd.cut(raw_fe['temp'], 10)
        raw_fe['Year'] = raw_fe.datetime.dt.year
        raw_fe['Quater'] = raw_fe.datetime.dt.quater
        raw_fe['Quater_ver2'] = raw_fe.datetime.dt.quater+(raw_fe.Year- raw_fe.min())*4
        raw_fe['Month'] =raw_fe.datetime.dt.Month
        raw_fe['Day'] = raw_fe.datetime.dt.day
        raw_fe['Hour'] = raw_fe.datetime.dt.hour
        raw_fe['DayofWeek'] = raw_fe.datetime.dt.dayofweek      
        
        #지연값 변수 생성
        raw_fe['count_lag1'] = raw_fe['count'].shift(1)
        raw_fe['count_lag2'] = raw_fe['count'].shift(2)
        raw_fe['count_lag1'].fillna(method='bfill', inplace=True)
        raw_fe['count_lag2'].fillna(method='bfill', inplace=True)  
        
        if 'Quater' in raw_fe.columns:
        	if 'Quater_Dummy' not in raw_fe.columns:
            	raw_fe = pd.concat([raw_fe, pd.get_dummies(raw_fe['Quater], prefix='Quater_Dummy', drop_first=True)], axis=1)
                del raw_fe['Quater']
        return raw_fe
                
        
        

 

 

 

데이터 분리

시계열 데이터는 분리할때 시간축이 반드시 있어야 한다. 

기존에 알고 있는 train_test_split, k-fold 등 분리 방법은 비시계열 데이터 분리 방법으로써 시간차원 없다

시계열데이터에 위 방법을 사용하면 시간개념이 사라지므로 1스텝 교차검사,2스텝 교차검사 분리 방법을 사용해야한다.

1-step 교차검사

하얀 점들이 예측해야할 test 미래 시기이다. 시간 단위로 index가 구성되어 있으므로 

하얀점들 각각이 2021-03-15 09:00, 2021-03-15 10:00, 2021-03-15 11:00 ~~~ 로 구성되어 있을것이다. 

시계열데이터는 각 시기를 예측해야한다. 비시계열은 아래처럼 test를 한 곳에 뭉쳐서 성능값이 하나 나온다

만일 90%라고 나와도, 가까운 미래 정확도가  98%나오고 먼 미래 일수록 그 정확도가 떨어진다. 이 수치를 종합해서 90%라고 해도 시계열 데이터 입장에서는 신뢰할수 없는 성능 수치이다.

그래서 각 시기별로 각각 정확도를 구하고, 정확도를 구한 시기는 train으로 들어간다. 

 

 

★위에 이론 내용을 토대로 실습 진행

실습은 시계열데이터지만 전통적인 데이터분리 방법을 적용했다(강의 구성이 그렇게 되어있다...)

 

### Data split of cross sectional
def datasplit_cs(raw, Y_colname, X_colname, test_size, random_seed=123):
	X_train, X_test, Y_train, Y_test = train_test_split(raw[X_colname], raw[Y_colname], test_size=test_size, random_state=random_seed)
    print('X_train:', X_train.shape, 'Y_train:', Y_train.shape)
    print('X_test:', X_test.shape, 'Y_test:', Y_test.shape)
    return X_train, X_test, Y_train, Y_test
    
### Data split of time series => 특정 날짜를 기준으로 나눈다
def datasplit_ts(raw, Y_colname, X_colname, criteria):
	raw_train = raw.loc[raw.index < criteria,:]
    raw_test = raw.loc[raw.index >= criteria,:]
    Y_train = raw_train[Y_colname]
    X_train = raw_train[X_colname]
    Y_test = raw_test[Y_colname]
    X_test = raw_test[X_colname]
    print('Train_size:', raw_train.shape, 'Test_size:', raw_test.shape)
    print('X_train:', X_train.shape, 'Y_train:', Y_train.shape)
    print('X_test:', X_test.shape, 'Y_test:', Y_test.shape)
    return X_train, X_test, Y_train, Y_test
    
Y_colname =['count']

#중복되는 count & 문자형 변수
X_remove = ['datetime', 'DateTime', 'temp_group', 'casual', 'registered']
X_colname= [col for col in raw_fe.columns if col not in Y_colname+X_remove] 
X_train, X_test, Y_train, Y_test = datasplit_ts(raw_fe, Y_colname, X_colname, '2012-07-01')
728x90

+ Recent posts