1.VIF(Variance Inflation Factor): 종속성이 높은 X들을 선택하는 방법
각 변수에 대해서 회귀분석을 실시합니다. 아래처럼 X1이라는 변수는 다른 변수의 선형결합으로 표현이 될수 있을지를 계산한게됩니다. 만일 R^2값이 크다면 X1는 다른 변수에 의해 표현이 되는 것이므로 제거 대상에 해당됩니다.
X1= aX2+bX3
이런 방법을 통해 모든 변수를 회귀분석을 통해 종속성이 높은 X들을 찾아냅니다.
위 VIF 공식에서 R^2이 클수록 VIF값도 커집니다.
#효과적인 X들을 VIF 방법을 통해 추출하기
from statsmodels.stats.outliers_influence import variance_inflation_factor
vif = pd.DataFrame()
#i번째 x들을 다른 x들로 회귀분석
#높을수록 선형결합으로 표현 될수 있으며 이는 종속성이 있는 변수이다.
vif['VIF_Factor']= [[variance_inflation_factor(X_train_feRS.values, i) for i in range(X_train_feRS.shape[1])]
vif['Feature'] = X_train_feRS.columns
vif.sort_values(by='VIF_Factor', ascending=True)
실습으로 사용방법 확인
# extract effective features using variance inflation factor
vif = pd.DataFrame()
vif['VIF_Factor'] = [variance_inflation_factor(X_train_feRS.values, i)
for i in range(X_train_feRS.shape[1])]
vif['Feature'] = X_train_feRS.columns
vif.sort_values(by='VIF_Factor', ascending=True)['Feature'][:10].values
array(['count_diff', 'holiday', 'count_seasonal', 'Day', 'windspeed',
'Quater_ver2', 'Hour', 'weather', 'Quater_Dummy_2', 'DayofWeek'],
dtype=object)
몇개의 변수를 사용했을때 성능이 가장 좋을지는 모르기 때문에 모든 경우의 수를 실행해 봐야 한다
아래 결과는 순서 VIF가 낮은 순으로 정렬 후 12개 변수를 사용했을때 결과입니다.
정화도는 0.864로 기존보다 정확도가 내려갔습니다
windspeed를 제외한 모두가 종속변수에 영향을 주고 있으며 계수값도 이전에 비하면 현실성 있는 수치를 보입니다.
Kurtosis가 6.022로 정규분포로 더욱 근사한 모양을 띠고 있으며 왜도 값도 줄어 들었음을 확인할수 있습니다
X1~X10까지의 10개 변수를 PCA 실행하면 X1^ ~X10^ 변수에 완전히 새로운 값이 채워지게 됩니다.
이 값은 기존의 변수들 값과 어떠한 연관성이 없습니다. 따라서 이 새 값은 서로 독립적으로 생기게끔 생성되었으므로 모델에 적용하면 되지만 단점은 완전히 새 값이 나오므로 어떤 의미를 가지고 있는지에 대한 재해석이 필요합니.
강의 실습에서는 VIF만을 실습했기때문에 따로 소스는 없습니다.
*정상성프로세스 이해하기
시계열이 정상성이다 라는 뜻은 시간의 흐름에 따라 "통계적 특성(Statistical Properties)"이 변하지 않는것이다.
통계적 특성(Statistical Properties):주로 평균(Mean)과 분산(Variance)/공분산(Covariance)를 얘기하지만 이를 포함한 모든 분포적 특성을 총칭함
시간 흐름에 따라 평균,분산,자기상관(autocorr), 공분산 등 통계적 특성이 변하지 않는다라는건 아래 녹색 그래프와 같은 의미이다. 평균을 낼때는 Frequency 기준으로 계산하면 같은 값이 나온다. 이외에 빨간색 그래프는 왼쪽부터 평균이 변하는 그래프, 중간 그래프는 분산이 일정치 않은 그래프, 맨 오른쪽 그래프는 평균과 분산은 일정한데 시점별로 짤랐을때는 자기상관이 있는 경우이다.
시계열 데이터에서 정상성이 왜 필요한 것일까?녹색 그래프 처럼 값이 일정하면 좋은 점은 무엇일까?
예를들어서 설명하면 한 회사의 매출액을 예측하고자 합니다. 매출액은 -값도 나올수 있으므로 그 범위는 +무한대, -무한대 입니다. 매출액이 유리천장이 있는 게 아니기 때문에 한계란 없습니다. 그러면 예측을 하는 입장에서는 이 무한대 범위에서 예측을 하면 틀릴확률이 높습니다. 그러면 Y값인 매출액을 점유율로 우회해서 표현하게되면 0~1사이의 확률값으로 표현이 되고 범위가 정해졌으므로 틀릴확률이 크게 줄어들게 됩니다. 점유율을 예측 후 이를 매출액으로 다시 변환하면 되는것이죠
이전에 올렸던 포스팅은 test 데이터를 알고 있을 경우에 FE를 진행하여 모델을 돌렸습니다. test 할 데이터에 대해서 FE를 했으므로 R^2 값이 1.0이 나왔습니다. 하지만 현실에서는 test할 데이터를 모릅니다. 미래가 어떻게 변할지는 그 누구도 모르기 때문입니다. 따라서 현실에 있을법한 test 데이터를 만들고자 합니다. 1-step 방법을 사용해서 미래 데이터 패턴을 예측하는 방법도 있지만 현 실습에서는 간단하게 진행했습니다
2011,2012년도 두 연도가 있는 자전거 수요 데이터에서, 현 실습에서는 2011년 데이터 패턴을 그대로 2012년도 데이터패턴에 적용했습니다. 여기에 모델을 적용한 후 스케일링 전처리 작업을 수행했습니다. 좀더 세부적인 교육 내용은 아래에 간단하게 적었습니다
현실성을 반영한 데이터패턴
# Test 기간의 FE데이터를 알 수 있을까? 2가지 방법소개
# 1) 과거 패턴이 반복될 것이다
# 2) 하나씩 예측하며 업데이트
'''
현재 데이터 그래프와 미래 데아터 그래프는 다르다. 이를 각각 어떻게 FE를 진행하는가?
이상적으로 가장 적합한 방법은 test 부분 영역은 아무도 모르는 영역이다-> 추정하는게 현실적으로 맞다
어떻게 추정하는가? train값으로 바로 t번재 테스트 값을 추정하고, 추정된 값을 포함한 값으로 t+1번째 값 추정 하는식
위 방법은 복잡하므로 좀더 간단한 방법으로 실습을 진행한다.
train에서 보인 패턴이 test에도 반복될거라는 가장 하에 진행
이렇게 되면 급작스러운 패턴 변화 때문에 다소 이질감이 생길수 있다.
2011 일년치를 가져와서 덜 이질감 있도록 2012년 적용
'''
len(raw_fe.loc['2012-01-01':'2012-12-31', 'count_trend'])
8784
len(raw_fe.loc['2011-01-01':'2011-12-31', 'count_trend'])
8760
# 2011년도 count_trend를 2012년 count_trend에 덮어 씌울려고 시도하면 에러가 발생
길이가 24개 차이가 나기 때문이다. 현재 시간 단위는 H이므로
24시간=> 윤달인 2월29일 데이터가 2011년도에는 없었던 것
#2월29일을 제외한 나머지 날짜를 대입
raw_fe.loc['2012-01-01':'2012-02-29', 'count_trend']=raw_fe.loc['2011-01-01':'2011-02-28', 'count_trend']
raw_fe.loc['2012-03-01':'2012-12-31', 'count_trend']=raw_fe.loc['2011-03-01':'2011-12-31', 'count_trend']
#2011-02-29데이터 만들기
step= (raw_fe['2011-03-01 00:00:00','count_trend']-raw_fe.loc['2011-02-28 23:00:00','count_trend'])/25
step_value=np.arange(raw_fe.loc['2011-02-28 23:00:00','count_trend']+step, raw_fe.loc['2011-03-01 00:00:00','count_trend'], step)
step_value= step_value[:24]
raw_fe.loc['2012-02-29','count_trend']=step_value
raw_fe.loc['2012-02-28 22:00:00':'2012-03-01 02:00:00', 'count_trend']
# calculation of lag data from Y
X_test_fe['count_lag1'] = Y_test_fe.shift(1).values # Y_test_fe.shift(1)값으로 새로 채우기 (현재는 train lag값이 들어가 있음)
X_test_fe['count_lag1'].fillna(method='bfill', inplace=True) #nan 값 채워주기
X_test_fe['count_lag2'] = Y_test_fe.shift(2).values
X_test_fe['count_lag2'].fillna(method='bfill', inplace=True)
X_test_fe['count_lag2']
DateTime
2012-07-01 00:00:00 149.00
2012-07-01 01:00:00 149.00
2012-07-01 02:00:00 149.00
2012-07-01 03:00:00 93.00
2012-07-01 04:00:00 90.00
...
2012-12-31 19:00:00 164.00
2012-12-31 20:00:00 122.00
2012-12-31 21:00:00 119.00
2012-12-31 22:00:00 89.00
2012-12-31 23:00:00 90.00
Freq: H, Name: count_lag2, Length: 4416, dtype: float64
현실성을 반영한 코드 요약
### Functionalize
### duplicate previous year values to next one
def feature_engineering_year_duplicated(raw, target):
raw_fe = raw.copy()
for col in target:
raw_fe.loc['2012-01-01':'2012-02-28', col] = raw.loc['2011-01-01':'2011-02-28', col].values
raw_fe.loc['2012-03-01':'2012-12-31', col] = raw.loc['2011-03-01':'2011-12-31', col].values
step = (raw.loc['2011-03-01 00:00:00', col] - raw.loc['2011-02-28 23:00:00', col])/25
step_value = np.arange(raw.loc['2011-02-28 23:00:00', col]+step, raw.loc['2011-03-01 00:00:00', col], step)
step_value = step_value[:24]
raw_fe.loc['2012-02-29', col] = step_value
return raw_fe
# target = ['count_trend', 'count_seasonal', 'count_Day', 'count_Week', 'count_diff']
# raw_fe = feature_engineering_year_duplicated(raw_fe, target)
### modify lagged values of X_test
def feature_engineering_lag_modified(Y_test, X_test, target):
X_test_lm = X_test.copy()
for col in target:
X_test_lm[col] = Y_test.shift(1).values
X_test_lm[col].fillna(method='bfill', inplace=True)
X_test_lm[col] = Y_test.shift(2).values
X_test_lm[col].fillna(method='bfill', inplace=True)
return X_test_lm
# target = ['count_lag1', 'count_lag2']
# X_test_fe = feature_engineering_lag_modified(Y_test_fe, X_test_fe, target)
#현실성 반영한 모델 실행하기
# Data Loading
# location = 'https://raw.githubusercontent.com/cheonbi/DataScience/master/Data/Bike_Sharing_Demand_Full.csv'
location = './Data/BikeSharingDemand/Bike_Sharing_Demand_Full.csv'
raw_all = pd.read_csv(location)
# Feature Engineering
raw_fe = feature_engineering(raw_all)
### Reality ###
target = ['count_trend', 'count_seasonal', 'count_Day', 'count_Week', 'count_diff']
raw_feR = feature_engineering_year_duplicated(raw_fe, target)
###############
# Data Split
# Confirm of input and output
Y_colname = ['count']
X_remove = ['datetime', 'DateTime', 'temp_group', 'casual', 'registered']
X_colname = [x for x in raw_fe.columns if x not in Y_colname+X_remove]
X_train_feR, X_test_feR, Y_train_feR, Y_test_feR = datasplit_ts(raw_feR, Y_colname, X_colname, '2012-07-01')
### Reality_lag ###
target = ['count_lag1', 'count_lag2']
X_test_feR = feature_engineering_lag_modified(Y_test_feR, X_test_feR, target)
###############
# Applying Base Model
fit_reg1_feR = sm.OLS(Y_train_feR, X_train_feR).fit()
display(fit_reg1_feR.summary())
pred_tr_reg1_feR = fit_reg1_feR.predict(X_train_feR).values
pred_te_reg1_feR = fit_reg1_feR.predict(X_test_feR).values
# Evaluation
Score_reg1_feR, Resid_tr_reg1_feR, Resid_te_reg1_feR = evaluation_trte(Y_train_feR, pred_tr_reg1_feR,
Y_test_feR, pred_te_reg1_feR, graph_on=True)
display(Score_reg1_feR)
# Error Analysis
error_analysis(Resid_tr_reg1_feR, ['Error'], X_train_feR, graph_on=True)
'''
R값이 0.9이며 유의한 변수가 25개로써 기존에 2개보다 많아졌음을 확인
Durbin-Watson도 2근방으로 다가 가면서 자기상관이 상당히 줄어들었으며
Kurtosis도 정규분포에 가까워짐
조건수는 현실 반영 전 모델과 비교 시 34,000 -> 32,000 감소를 보임
이는 독립변수가 그 전보다는 생겼다는 의미이다.
t검정을 봐도 유의한 변수들이 많이 생겼음을 확인할수있다. 조건수 수치를 보고 설명변수의 상태를
파악할수 있다.
'''
Scaling
본 내용 설명에 앞서 condition number에 대해서 설명을 하자면 예측 정확성 향상을 위해서는 두가지 방향이 있는데
1) Train 정확도 증가+ Test 정확도 증가
2) Train <<< Test⟺조건수(Condition Number) 감소
2번에서 train 의 정확성 보다는 test의 정확성을 더 중시한다는 의미입니다. test 정확성을 높이기 위해서는 조건수의 감소가 일어나야 합니다. x들의 특징에서 조건수를 추출하는데 이 값이 낮을수록 2번처럼 이상적수치가 나올수 있습니다.
조건수는 𝑒𝑖𝑔𝑒𝑛𝑣𝑎𝑙𝑢𝑒의 최대값,최소값의 비율을 나타냅니다. X라는 설명변수들이 서로 독립적이면 𝑒𝑖𝑔𝑒𝑛𝑣𝑎𝑙𝑢e
값들은 서로 비슷한 수치를 보입니다. 반대로 종속변수가 많을수록 두 𝑒𝑖𝑔𝑒𝑛𝑣𝑎𝑙𝑢𝑒 값은 차이가 커집니다.
ex) 𝜆1=1, 𝜆2=100
따라서 조건수를 줄이기 위해서는 설명변수안에 서로 독립적이여야 Test 예측 성능을 올릴수 있습니다.
*조건수를 감소하는 방법 3가지
1) 변수들의 단위차이로 숫자의 스케일들이 크게 다른 경우, 스케일링(Scaling)으로 해결 가능 2) 독립변수들 간에 상관관계가 높은 "다중공선성" 존재할 경우, Variance Inflation Factor(VIF)나 Principal Component Analysis(PCA)를 통한 변수선별로 해결 가능 3) 독립변수들 간 의존성이 높은 변수들에 패널티를 부여하는 정규화(Resularization)로 해결 가능
Scaling
숫자가 큰 변수가 그렇지 않는 변수들보다 크게 관여될수가 있으므로 값 조정은 필수다.
총 4가지 방법이 있습니다
1) Standard Scaler
스케일링 후의 분포의 모양은 각기 다르다(정규분포애 근사한 형태를 띈다)
2) Min-Max Scaler(𝑋𝑖𝑡−𝑚𝑖𝑛(𝑋𝑖)/𝑚𝑎𝑥(𝑋𝑖)−𝑚𝑖𝑛(𝑋𝑖))
본래 분포모양을 그대로 가져가면서 범위는 동일하게 적용한다 표편이 매우 작을때 효과적, 왜일까? 표편이 작다는 건 데이터들이 모여 있다는 뜻 -> 이상치가 없다 이상치가 있다면 표편이 커지게 된다. 극단치 때문에 비율이 왜곡된다 이러한 문제점을 해결하고자 Robust Scaler 사용
3) Robust Scaler(𝑋𝑖𝑡−𝑄1(𝑋𝑖)/𝑄3(𝑋𝑖)−𝑄1(𝑋𝑖))
25%~75% 데이터를 가지고 Min-Max Scaler 공식과 동일하게 적용
4) Normalizer
각 변수(Feature)를 전체𝑛n개 모든 변수들의 크기들로 나누어서 변환(by Cartesian Coordinates) 각 변수들의 값은 원점으로부터 반지름 1만큼 떨어진 범위 내로 변환한다.
회귀모델을 적용 후 R-squared,F-분포, t검정 등의 결과가 일관성이 없을때 믿어야 할건 데이터 라는 것이다.
일관성이 없다고 효과없는 데이터를 무작정 삭제 하는게 아니라, 시각화를 통해서 종속변수에 영향을 주는 데이터인지 아닌지를 판단해야한다.
모델적용
fit_reg1 = sm.OLS(Y_train, X_train).fit()
display(fit_reg1.summmary())
pred_tr_reg1 = fit_reg1.predict(X_train).values
pred_te_reg1 = fit_reg1.predict(X_test).values
'''
*R-squared
평균치 ybar 보다 적합이 좋은지 나쁜지를 나타낸다
1.00은 모드예측값이 실제값과 모두 동일함을 의미한다
*F-statistic(F검정)
유의확률이 0.00으로 유의수준보다 작으므로, 이 모델링은 효과가 있다.
모든 계수가 효과가 있다
*t 검정
count_diff, count_lag1를 제외하곤 모두 영향력이 없다
F검정에는 모든 계수가 효과 있다고 했는데? 이상함을 감지 해야한다
*잔차 보기
정규분포의 Kurtosis(첨도)는 3이다.
현 Kurtosis는 13078.751, 일반 첨도보다 훨씬 꼬리가 두터운 형태
*정규분포의 Skew(왜도)는 0이다.
현 Skew는 -114.255, negative 형태 (오른쪽 쏠림), 상당히 벗어난 값
*회귀분석 지표가 많은 이유는 모든 결과가 일관성 있을때 까지 시도하라는 의미
*지속적인 튜닝이 필요
*회귀분석은 값만 출력하고, 쓸지 말지는 사람이 선택한다. 따라서 합리적인 근거를 토대로
사용여부를 결정한다. 근거의 대표적인 도구가 시각화이다.
믿어야 할건 알고리즘이 아니라 데이터이다.
'''
위 결과에 대한 해석은 실습의 주석처리로 되어있습니다
일관성 없는 데이터에 대해서 시각화를 통해 정확한 정보를 얻어낸다
이상적인 그림은 모델 적용하기 전 시각화를 통해 변수에 대한 정보를 파악하는게 맞다
시각화
# histogram plot
raw_fe.hist(bins=20, grid=True, figsize=(16,12)) #x를 몇개 그룹으로 나눌것이가: bins
plt.show()
'''
count와 비슷한 분포가 상관관계가 높고, 유의하다고 나올 가능성이 높다
y분포와 비슷하게 만드는 것이 예측에 도움이 될것
'''
# box plot_계절별 각 수요 관계 파악
raw_fe.boxplot(column='count', by='season', grid=True, figsize=(12,5))
plt.ylim(0,1000)
raw_fe.boxplot(column='registered', by='season', grid=True, figsize=(12,5))
plt.ylim(0,1000)
raw_fe.boxplot(column='casual', by='season', grid=True, figsize=(12,5))
plt.ylim(0,1000)
'''
만일 계절의 y분포 네개가 똑같다면 어떨까?
계절에 의한 count 변화는 없다는 의미이며 계절변수가 쓸모 없다는 것
계절별로 패턴이 많이 달라야 예측에 도움이 된다.
'''
# box plot_휴일,평일과 수요(count) 관계 파악
raw_fe.boxplot(column='count', by='holiday', grid=True, figsize=(12,5))
plt.ylim(0,1000)
raw_fe.boxplot(column='count', by='workingday', grid=True, figsize=(12,5))
plt.ylim(0,1000)
'''
휴일과 평일 비교하는 그래프에서, 평일에 수요가 훨씬 많음을 알수있다
분포가 다르고 해석되는 것에 차이가 있으므로 y분포에 도움이 될수 있다.
'''
raw_fe[raw_fe.holiday==0].boxplot(columns = 'count', by ='Hour', grid=True, figsize=(12,5))
plt.show()
raw_fe[raw_fe.holiday ==1].boxplot(columns='count', by ='Hour', grid=True, figsize=(12,5))
plt.show()
'''
두 분포간의 차이가 있으며 개별 분포를 봐도 count값 분포가 다르므로 y에 효과가 있음을 추론가능
'''
# scatter plot for some group
#temp를 추가해서 삼차원 그래프
raw_fe[raw_fe.workingday == 0].plot.scatter(y='count', x='Hour', c='temp', grid=True, figsize=(12,5), colormap='viridis')
plt.show()
raw_fe[raw_fe.workingday == 1].plot.scatter(y='count', x='Hour', c='temp', grid=True, figsize=(12,5), colormap='viridis')
plt.show()
'''
temp도 두 그래프의 분포가 다르므로 영향을 줄거라는 걸 추론 할수있다.
'''
# box plot example_날씨에 따른 수요 변화
raw_fe.boxplot(column='count', by='weather', grid=True, figsize=(12,5))
plt.ylim(0,1000)
raw_fe.boxplot(column='registered', by='weather', grid=True, figsize=(12,5))
plt.ylim(0,1000)
raw_fe.boxplot(column='casual', by='weather', grid=True, figsize=(12,5))
plt.ylim(0,1000)
'''
날씨에 따른 count 변화가 보인다.
'''
# box plot example_년도에 따른 수요 변화
raw_fe.boxplot(column='count', by='Year', grid=True, figsize=(12,5))
plt.show()
'''
년도에 따른 수요가 다르다는 걸 알수 있다. y에 영향을 준다
'''
# box plot example_월에 따른 수요 변화
raw_fe.boxplot(column='count', by='Month', grid=True, figsize=(12,5))
plt.show()
#독립변수간의 상관관계를 파악하기 위한 시각화
#상관관계를 추정하는게 알고리즘의 목표
pd.plotting.scatter_matrix(raw_fe, figsize=(18,18), diagonal='kde')
plt.show()
색깔이 짙을수록 관계성이 강하다는 의미
# selecting of columns from correlation tables
#y값과 x들 간의 상관관계 보기
'''
아래 값이 회귀분석 결과에 나온 계수값과 비슷한지 비교해봐야 한다
유사하지 않기 때문에 t검정이 유의하지 않다고 한것이다.
count_lag2 회귀분석결과를 비교해도 다름을 알수있다.
'''
raw_fe.corr().iloc[:,8:11]
# correlation table example
raw_fe.corr().iloc[0:8,8:11].style.background_gradient().set_precision(2).set_properties(**{'font-size': '15pt'})
검증지표 방향
R-squared(R^2):추정된 (선형)모형이 주어진 데이터에 잘 적합된 정도, 계량경제학에서는 모델에 의해 설명되는 데이터 분산의 정도(퍼센트)
R-squared는 기준이 ybar이다. 이는 다른 mse,mae와 차이점이다. -> 평균적으로 추정된 값보다 얼만큼 잘 적합이 되었는지 확인 최소값은 마이너스 무한대이다.
Mean Absolute Error(MAE): 각 데이터의 단위를 보존하는 성능지표 단위보존 ? mse와 비교하면 알수있다.
Mean Squared Error(MSE):가장 많이 사용되며 큰 오차에 패널티(Penalty)를 높게 부여하는 성능지표
mse는 실제값-예측값의 제곱이므로, 0보다 작은값은 더 작게, 1보다 큰 값은 더 크게 만들고 애초에 y값 자체의 스케일이 클수도, 작을수도 있다.
mae는 절대값으로 이뤄져 있기 때문에 작은 값이 더 작게, 큰 값은 더 크게 계산되지 않는다 즉, mae는 y스케일을 그대로 보존할수 있다.
Summary
Squared 가 들어간 평가지표는, 이상치 하나만 있어도 오차가 제곱이 된다. 훨씬큰 오차가 반영됨 따라서 이상치가 있다면 yes로 표기된 평가지표를 쓰기를 추천
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스텝 교차검사 분리 방법을 사용해야한다.
하얀 점들이 예측해야할 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')
현실의 데이터 분석은 2단계인 데이터 분석이 아니라 1단계, 문제 정의/기획이 가장 중요하다
어떻게 정의 하느냐에 따라 독립,종속 변수가 달라지므로 데이터 분석의 방향이 결정되기 때문이다.
문제 정의 단계는 끊임없이 진화/변경하여 업데이트를 해야한다
데이터 관점에 따른 분류
횡단면 데이터: 어느 한 시점에서 여러 변수들이 있는 데이터
시계열 데이터: 여러개의 시점과 특정한 변수가 있는 데이터
시계열 횡단면 데이터: 여러시점과 변수들이 함께 있는 데이터
패널데이터: 꽉차 있는 시계열 횡단면 데이터
Tick: 초 보다 작은 시간단위(high Frequency)
* raw data의 2가지 타입
1. Log: 시간 순으로 데이터를 쌓은 것(정렬 개념이 아닌 특정시간에 따라 쌓는 개념
ex) 센서에서 나오는 정보를 차곡차곡 쌓아 가는 형식
2. Tabel: 특정한 형태로 가공된 것(정렬되거나 groupby 등)
ex) 주로 경연대회에서 제공해주는 데이터가 Tabel에 속하는 것같다
통계 기본 용어
1. 왜도(Skewness): 평균을 중심으로 좌우 데이터가 편향 되어 있는 정도를 표현
왜도가 왜 필요한가?
A와 B반의 키를 비교해볼때, 두 반 모두 키 평균과 퍼져있는 정도, 즉 분산이 같다고 가정합니다.
하지만 분포를 보면 그 양상은 다를수 있습니다. A반은 키가 매우 작은 학생 몇명때문에 평균이 177이고
B반은 키가 매우 큰 학생이 몇몇 존재 해서 평균이 177이 되었습니다. 아래 그림에서 교차하는 점이
177일때 왼쪽이 A반이고 오른쪽이 B반 이라면 이해하기가 쉬워집니다.
두 반 모두 평균과 분산은 같지만 우리는 각기 다른 인사이트를 얻을수 있기 때문에 왜도가 필요합니다
질 좋은 빅데이터란 stew가 없는 데이터입니다
특정한 샘플만을 통해 분석을 실행한다면 치우침(bias)은 생길수 밖에 없습니다
따라서 데이터 분석 시 이러한 함정에 빠지지 않도록 유의해야합니다
2. 첨도(Kurtosis): 분포의 뾰족함 정도
첨도가 왜 필요한가?
뾰족할수록 중심으로 몰리게 되는 분포가 만들어 집니다. 이는 양 끝 사이드가 0으로 빨리 수렴이 됩니다.
그래서 이상치가 거의 존재 하지 않을 가능성이 커집니다. 반면 첨도<0 인경우 분포를 보면
양끝 사이드가 두텁게 형성되어 있습니다. 이때 저만의 예시를 들자면 가운데 몰리는 부분을 일반적인 사람, 평범한 사람이라고 말하고 양 끝사이드에 있는 사람은 독창적이며 남들이 가지 않는 길을 걷는 사람이라고 이해했습니다
아마존의 경영전략은 A~Z까지 모든 물건을 판다는 전략입니다. 그래서 독창적인 사람이 요구하는 특이한 물건도 판매를 하게 됩니다. 따라서 아마존에게는 이 첨도의 분포를 통해 인사이트를 고객에 대한 특징과 인사이트를 얻을수 있습니다
3. 상관관계: A변수의 변화와 B변수의 변화방향의 (선형적)유사성으로 표준화된 공분산이라고도 함
상관관계는 왜 있는 것인가?
인과관계를 규명하지 못하기 때문에 대신 상관관계를 사용하는 것입니다.
강의에서 예로 든 것을 말하자면 여름이 되면 아이스크림 매출은 증가합니다. 또한 여름이 되면 익사자 수가 증가합니다.
그렇다면 여름이 원인이고 결과가 아이스크림 매출 증대, 익사자의 수 증가 인가요? 여름을 어느날부터 여름이라고 정의하는건 신의 영역이므로 인과관계 규명이 불가능 해집니다. 따라서 아이스크림 매출과 익사자의 수 사이에 어떤 상관정도가 있는지를 대신 알아보는 것입니다.
=> 아이스크림 매출이 증가하면 익사자 수가 증가한다 라는 상관관계 정보를 얻을 수는 있습니다.
가설검정
중심극한정리: 전체 모집단에서 여러 번 Sample을 추출해라. 그리고 Sample마다 평균을 구해라. 이 평균들의 분포는 정규분포이며 전체 모집단의 평균으로 수렴한다.
(개인적으로 위 강의자료 설명이 다른 교재에서 어렵게 설명한 것보다 직관적으로 알기 쉽고 좋은 설명이라고 생각합니다)
검정통계량:대립가설(나의주장)과 귀무가설(대중주장)을 비교하기 위한 검증(Evaluation)지표값, 일명 "점추정"
위 점추정을 한번이 아니라 여러번 구하면 점추정1~점추정2 라는 특정한 구간이 생기는데 이를
신뢰구간 또는 구간추정 이라고도 합니다.
유의수준: 귀무가설(대중주장)이 참이라는 전제 하에, 대립가설(나의주장)이 참이라고 "오판"할 최대 확률
ex) 유의 수준 5%라는 것은..
A와 B가 달리기 시합을 합니다. 대중들은 실력의 차이가 없다고 주장하고, 나의 주장은 a가 더 빠르다고 주장하고 있습니다. 이때 둘다 실력의 차이가 없다는 전제 하에 100번 시합중 95번은 무승부(실력 차이가 없다)이고, 5번은 a가 이길때(나의 주장이 증명된것) 이 5번이 유의 수준이 됩니다.
유의확률: 귀무가설(대중주장)이 참이라는 전제 하에, 대립가설(나의주장)이 관찰될 확률
ex) 유의수준을 5번으로 정했습니다. 이때 만일 a선수가 5번이 아니라 극단적으로 한 20번 이겼다고 가정합니다.
그러면 80번은 무승부인데 이중에 20번은 a 선수가 이기게 되므로 나의 주장(a선수가 더 잘한다)의 신뢰가 급상승 하게됩니다. 로또도 확률이 희박하듯이 20번까지 이길 확률도 크지 않을 것이기에 1%라 가정합니다.
1%를 분포 상에서 보자면 5%보다 오른쪽에 위치해 있습니다. 희박한 확률이 당첨이 된것입니다.
따라서 만일 유의확률이 유의수준보다 더 낮게 나오면 희박한 확률을 뚫고 나온 것이므로 나의 주장(대립가설)이 채택이 되고 반대로 유의 확률이 유의수준보다 크면 대중 주장이 채택됩니다.
학습방향과 알고리즘
각 알고리즘을 바라볼때의 관점을 3가지로 나눈다면 아래와 같습니다
1) 문제가 어디에 속하는지 -> "분석기획(가설/방향)" 가능 2) 알고리즘마다 입력은 무엇인지 -> "데이터전처리(준비)" 가능 3) 알고리즘마다 출력은 무엇인지 -> "결과해석(설명/검증)" 가능
알고리즘의 쓰임을 제대로 파악해야 적재적소의 알고리즘을 선택할수 있으며 선택된 알고리즘이 어떤 입력을 요구(전처리에 영향을준다) 하는지와 출력은 무엇을 주는지를 꼭!!! 알아야 한다.