회귀에서 목표 에상 변수(forecast variable)의 과거 값을 이용하는 대신에, 이동 평균 모델은 회귀처럼 보이는 모델에서 과거 예측 오차(forecast error)을 이용한다.
여기서 E(t)는 백색잡음(White noise)이다. 이것을 q차 이동 평균 모델인 MA(q) 모델이라고 부르자.
물론, E(t)의 값을 관찰하지 않기 때문에, 실제로는 일반적인 회귀가 아니다.
Y(t)의 각 값을 과거 몇 개의 예측 오차(forecast error)의 가중 이동 평균으로 생각할 수 있다는 것에 주목하자. 하지만 이동 평균 평활과 헷갈리면 안된다. 이동 평균 모델은 미래 값을 예측할 때 사용한다. 이동 평균 평활은 과거 값의 추세-주기를 측정할 때 사용한다.
매개변수를 다르게 설정한 이동 평균 모델로부터 얻은 데이터의 두가지 예
MA(1): Y(t)=20+E(t)+0.8E(t-1)
MA(2): Y(t)=E(t)-E(t-1)+0.8E(t-2)
*두 가지 경우 모두, E(t)는 평균이 0이고 분산이 1인 정규 분포를 따르는 백색 잡음(White noise)이다.
위 그림은 MA(1) 모델과 MA(2) 모델로 얻은 몇몇 데이터를 나타낸다. 매개변수 θ1,…,θqθ1,…,θq 을 바꾸면 다른 시계열 패턴이 나타난다. 자기회귀 모델을 이용하는 경우처럼, 오차항 E(t)의 분산은 시계열의 패턴이 아니라 눈금만 맞출 것이다.
정상성을 나타내는 어떤 AR(p) 모델을 MA(∞) 모델로 쓸 수 있다. 예를 들어, 반복하여 대입하면, AR(1) 모델에 대해 다음과 같이 나타낼 수 있다.
-1<ϕ1<1 에 대해, k 가 커질 수록 ϕ1^ k의 값이 작아질 것이다. 그래서 결국 다음과 같은 식을 얻게 된다.
이는 MA(∞)의 과정이다.
MA 매개변수에 대한 몇몇 제한조건을 도입하면 반대 결과도 성립한다. 그러면 MA 모델을 가역적(invertible)이라고 부른다. 즉, 어떤 가역적인 MA(q) 과정을 AR(∞) 과정으로 쓸 수 있다. 가역적 모델은 단순하게 MA 모델을 AR 모델로 바꿀 수 있도록 하는 것만은 아니다. 몇 가지 수학적 특징도 갖고 있다.
예를 들어보자.
MA(1) 과정
다음과 같은 식을 생각해보자. 이것은 AR(∞)로 표현하면, 가장 최근의 오차는 현재와 과거 관측값의 선형 함수로 쓸 수 있다.
|θ|>1 이면, 가중치의 시작(lag) 값이 증가함에 따라 증가하고, 따라서 더 멀리 떨어진 관측값일수록 현재 오차에 미치는 영향이 커진다.
|θ|=1 이면, 가중치가 크기에 대해서는 상수, 멀리 떨어진 관측값과 가까운 관측값 모두 동일하게 영향을 준다.
위 예 AR(1), AR(2) 경우 모두 그럴듯하지 않기 때문에, |θ|<1 가 필요하며, 결국 가장 최근 관측값이 멀리 떨어진 관측값 보다 더 큰 가중치를 갖게 된다. 따라서, |θ|<1일때 과정은 가역적(invertible)이다.
다른 모델에 대한 가역성(invertibility) 제한조건은 정상성(stationarity) 제한조건과 비슷하다.
MA(1) 모델의 경우:−1<θ1<1−1<θ1<1.
MA(2) 모델의 경우:−1<θ2<1,θ2+θ1>−1,θ1−θ2<1.
q>=3에 대해서는 더 복잡한 조건이 성립한다. 여기서, 파이썬, R에서 모델을 다룰 때, 이러한 제한조건을 처리해준다.
뷰 클래스는 '위젯'이라고도 하는데, 쉽게 말해 화면에서는 버튼을 버튼 위젯, 시제 코드에서는 버튼 클래스라고 부른다.
또한, 다른 위젯을 담을 수 있는 위젯을 특별히 레이아웃이라 하며, 레이아웃은 ViewGroup이라는 클래스 아래에 존재한다.
위젯과 레이아웃
그러나 위젯은 단독으로 존재하지 않으며, 위젯을 담아 배치하는 틀이 바로 레이아웃이다.
레이아웃은 위젯을 포함하는 컨테이너 역할을 하므로 눈에 보이는 개념이 아니다.
위젯은
-넓은 의미로 View 클래스 하위의 모든 클래스를 지칭.
-좁은 의미로 레이아웃 이외의 클래스를 지칭.
View 클래스 계층도
안드로이드에서 View 클래스의 상속을 받은 클래스(위젯) 계층도의 일부를 살펴보자.
안드로이드의 View 클래스 계층도
위 그림을 보면 최상단에 Object 클래스가 있고, 이를 상속받은 View 클래스가 있다.
안드로이드 화면에 나타나는 모든 위젯은 View 하위에 존재한다.
레이아웃은 ViewGroup을 상속받은 LinearLayout, RelativeLayout, FrameLayout,GridLayout,TableLayout을 지칭한다.
뷰 컨테이너는 ListView,GridView,TabHost,Gallery 등이 있다. 뷰 컨테이너도 ViewGroup 클래스에서 상속받는다.
2. View 클래스의 XML 속성
View 클래스의 XML 속성은 수십 개가 넘으니 자주 사용할 것만 살펴보자. 다음은 Button의 기본 형태이며 View 클래스로부터 상속받았다.
버튼의 id,layout_width 등 몇 가지 기본적인 XML 속성이 표현되었다. 이러한 속성을 하나씩 살펴보자.
id 속성
id 속성은 모든 위젯의 아이디를 나타낸다. java 코드에서 버튼 등의 위젯에 접근할 때 id 속성에 지정한 아이디를 사용한다. 일반적으로 id 속성은 위젯에 아이디를 새로 부여하는 개념이므로 '@+id/' 형식으로 지정한다. 그 다음 새로 지정할 아이디를 넣는다. "@+id/btn1"
위젯에 접근하기 위해 Java 코드에서 다음과 같은 형식을 사용한다.
위 버튼 예제는 Java 코드에서 다음과 같은 접근 방식을 사용할 수 있다.
Button, RadioButton,CheckBox 등의 위젯은 일반적으로 클릭 또는 터치했을 때, 어떤 동작을 하기 위한 것이므로 id 속성을 지정한다.
하지만, 클릭이나 터치를 해도 아무 동작이 필요없는 글자(텍스트뷰)나 배경 이미지(이미지뷰)등은 굳이 id 속성을 지정하지 않아도 된다.
예제 4-1 id 속성의 XML 코드
layout_width, layout_height 속성
layout_width,layout_height 속성은 모든 위젯에 필수로 들어간다. 매우 중요하다.
이 둘은 각각 위젯의 너비와 높이를 나타내며 match_parent 와 wrap_content 값으로 설정할 수 있다.
match_parent: 이름 그대로 자신의 부모(대개는 레이아웃)에 너비나 높이를 맞춘다는 의미.
wrap_content: 글자가 꼭 들어갈 정도로 자신의 너비나 높이를 설정한다는 의미.
버튼의 경우 그 안에 들어갈 것이 글자이기 때문에 글자가 곡 들어갈 정도라고 표현했다. 하지만 다른 위젯에는 글자 외의 것이 들어간다. 예를 들면 이미지뷰에는 이미지가 들어가고 wrap_content는 이미지가 꼭 들어갈 정도로 너비나 높이가 설정된다.
예제를 살펴보자.
위 예제들에서 볼 수 있듯이 layout_width 속성에 wrap_content 값을 사용하면 버튼의 너비가 그 안의 글자인 '버튼입니다'에 꼭 맞는 크기가 되고, match_parent(또는 fill_parent)값을 사용하면 '버튼입니다' 글자와 관계없이 버튼을 싸고 있는 부모(linear_layout)의 너비에 꽉 차는 크기가 된다.
값을 숫자로 직접 지정할수는 있지만 단위에 주의해야 한다. 가장 단순한 방법이 px(PiXel)단위이다.
background 속성
background 속성은 위젯의 색상을 주로 #RRGGBB 값으로 지정한다. 각 값은 Red, Green, Blue를 의미하며 RR,GG,BB의 위치는 16진수 00~FF로 표현할 수 있다. 예를 들면 빨간색은 #FF0000, 파란색은 #0000FF로 지정한다. 값을 적절히 조합하면 필요한 색을 만들 수도 있다.
예제를 살펴보자.
위 예제는 레이아웃을 빨간색(#ff0000), 버튼을 초록색(#00ff00)으로 표현했다.
색상을 지정하기 위해 #AARRGGBB 방식을 사용할 수도 있다. AA는 알파 값으로 투명도이다. 00~FF로 지정할 수 있는데 00은 완전 투명을, FF는 완전 불투명을 나타낸다.
즉, #FF0000FF는 불투명파랑, #000000FF는 완전 투명이므로 색상이 나타나지 않는다. #770000FF는 반투명 파란색이다.
padding, layout_margin 속성
padding 속성을 사용하여 위젯의 경계선으로부터 위젯 안의 요소가 떨어지게 설정할 수 있다. 기본적으로는 레이아웃 내에 버튼, 텍스트 뷰 등을 여러 개 두었을 때 레이아웃의 경계선에 딱 붙어서 표현되는데, padding 속성을 사용하면 레이아웃의 경계선과 위젯 사이에 여백을 둘 수 있다.
우선 텍스트뷰, 에디트텍스트, 버튼이 있는 XML 파일을 살펴보자.
위 코드를 이용해 결과를 살펴보면 경계선에 붙어 있어 답답해 보인다. 레이아웃에 padding 속성을 사용해보자.
*padding 은 상하좌우 모두에 지정하는 속성이다. 각각 따로 지정하고 싶으면 paddingTop, paddingBottom, paddingLeft,paddingRight를 이용한다.
LinearLayout에 지정된 padding속성 때문에 그 안의 요소들의 경계선에서 30dp만큼 떨어져 출력되었다.
위 에제는 LinearLayout에 padding을 설정했지만, Button에 padding을 설정하면 버튼 안의 글자가 버튼의 경계선에서 일정 간격 떨어져서 표현된다. 이와는 반대로 위젯과 위젯 사이에 여유를 두고 싶다면 layout_margin 속성을 사용하자.
*layout_margin은 상하좌우 모두에 지정하는 속성이다. 각각 따로 지정하고 싶으면 layout_marginTop, layout_marginBottom, layout_marginLeft,layout_marginRight를 이용한다.
예제 4-12 layout_margin 속성의 XML 코드
정리하면 padding 속성에는 자신의 내부에 들어 있는 위젯과 자신의 경계선 사이 간격을 지정하고, layout_margin 속성에는 자신과 부모 레이아웃이나 위젯 사이의 간격, 주위 다른 위젯과 간격을 지정한다. 따라서, layout_margin은 각 위젯의 속성으로 지정해야 한다.
Visibility 속성
visibility 속성으로 위젯을 보일 것인지 여부를 설정할 수 있다. 세 가지 값을 지정할 수 있는데 디폴트인 visible은 보이는 상태, invisible과 gone은 안 보이는 상태이다.
invisible과 gone의 차이는, invisible은 보이지 않을 뿐 원래의 자리를 계속 유지하지만 gone은 그 자리까지 아예 내놓는다는 것이다.
visibility 속성은 XML 보다 Java 코드에서 상황에 따라 동적으로 필요한 버튼 등을 보이거나 안보이게 하는 경우에 주로 활용된다.
Enabled, Clickable 속성
위젯의 동작 여부는 enabled 속성으로 설정한다.
클릭이나 터치가 가능하게 하는 것은 clickable 속성으로 설정한다.
값은 true와 false로 지정하며 디폴트 값은 true 이다. enabled, clickable 속성은 XML 보다 Java 코드에서 주로 사용한다.
Rotation 속성
rotation 속성은 위젯을 회전시켜 출력하며 값은 각도로 지정한다. 특별 화면을 만들 때 사용한다.
지금까지는 많이 사용되는 XML 속성을 살펴보았다. 이 외에도 상당히 많지만 필요할 때마다 설명하겠다.
train_test_data=[train,test]
for dataset in train_test_data:
dataset['transaction_year_month']=dataset['transaction_year_month'].astype(str)
dataset['year']=dataset['transaction_year_month'].str[:4].astype(int)
dataset['month']=dataset['transaction_year_month'].str[4:6].astype(int)
dataset['transaction_year_month']=dataset['transaction_year_month'].astype(int)
train_test_data=[train,test]
for dataset in train_test_data:
dataset['age']=dataset['year']-dataset['year_of_completion']
dataset['is_rebuild']=(dataset['age']>=30).astype(int)
거래하는 기간까지의 아파트 나이를 특징으로 생성해주고, 샘플코드에 있는 아파트의 재건축 유무를 판단하는 특징을 만들어준다.
#상관관계 확인
k=train.shape[1] #히트맵 변수 갯수
corrmat = train.corr() #변수간의 상관관계
cols = corrmat.nlargest(k, 'transaction_real_price')['transaction_real_price'].index #price기준으로 제일 큰순서대로 20개를 뽑아냄
cm = np.corrcoef(train[cols].values.T)
f, ax = plt.subplots(figsize=(20, 6))
sns.heatmap(data = cm, annot=True, square=True, fmt = '.2f', linewidths=.5, cmap='Reds',
yticklabels = cols.values, xticklabels = cols.values)
모델링
from sklearn.linear_model import ElasticNet, Lasso
from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor
from sklearn.kernel_ridge import KernelRidge
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import RobustScaler
from sklearn.model_selection import KFold, cross_val_score, train_test_split
from sklearn.metrics import mean_squared_error
import xgboost as xgb
import lightgbm as lgb
target = train['transaction_real_price']
del train['transaction_real_price']
start = timeit.default_timer()
cv_score(models)
stop = timeit.default_timer()
print('불러오는데 걸린 시간 : {}초'.format(stop - start))
Model GradientBoosting CV score : 0.5706
RMSE : 0.0108
Model XGBoost CV score : 0.5788
RMSE : 0.0108
Model LightGBM CV score : 0.5681
RMSE : 0.0108
Model LASSO Regression CV score : 0.5526
RMSE : 0.0107
Model Elastic Net Regression CV score : 0.5602
RMSE : 0.0106
Model RandomForset CV score : 0.4911
RMSE : 0.0118
불러오는데 걸린 시간 : 910.3417148000003초
#여러개의 모델로 만들어진 predict 데이터들의 평균을 구한다.
models = [{'model':xgboost, 'name':'XGBoost'},
{'model':lightgbm, 'name':'LightGBM'},
{'model':forest, 'name' : 'RandomForest'}]
def AveragingBlending(models, x, y, sub_x):
for m in models :
m['model'].fit(x.values, y)
predictions = np.column_stack([m['model'].predict(sub_x.values) for m in models])
return predictions
[LightGBM] [Warning] feature_fraction is set=0.9, colsample_bytree=1.0 will be ignored. Current value: feature_fraction=0.9
[LightGBM] [Warning] min_data_in_leaf is set=15, min_child_samples=30 will be ignored. Current value: min_data_in_leaf=15
[LightGBM] [Warning] lambda_l1 is set=0.1, reg_alpha=0.0 will be ignored. Current value: lambda_l1=0.1
[LightGBM] [Warning] bagging_fraction is set=0.9, subsample=1.0 will be ignored. Current value: bagging_fraction=0.9
[LightGBM] [Warning] bagging_freq is set=1, subsample_freq=0 will be ignored. Current value: bagging_freq=1
불러오는데 걸린 시간 : 260.0541379999995초
sub = pd.read_csv('d:/data/real_estate/submission.csv')
sub['transaction_real_price'] = np.expm1(predictions)
sub.to_csv('d:/data/real_estate/submission.csv', index=False)
ARIMA 모형은 대표적인 통계적 시계열 예측 모형으로, 현재 값을 과거 값과 과거 예측 오차를 통해 설명한다. 이 모형을 적합하려면 시계열 Yt가 정상성 조건을 만족하는 정상 시계열(Stationary series)이어야 한다. (정확하게는 약정상성, weak stationary). 또한, 주어진 시계열 자료에 적합한 ARIMA(p,d,q)를 결정하는 절차는 Box-Jenkins method를 따르며, ACF(Autocorrelation function: 자기상관함수)로 시계열의 특성을 파악하고 적절한 차수의 ARIMA 모형을 선택한다.
Box-Jenkins method
정상성(Stationary)
우리는 시간의 순서에 따라 기록되지 않은 일반적 자료들을 분석할 때, Random sample들에 iid가정을 한다.
*iid: All samples are independent and identically distributed
시간에 종속되어있는 시게열은 상식적으로 iid 가정을 할 수 없다. 그래서 이러한 시계열 자료에 대해 예측 모형을 적합하고 통계적 검정을 하기 위해서는 분석을 단순화 시킬 수 있는 새로운 가정이 필요하다.
이중 가장 중요한 것이 시계열 모형의 확률적 성질이 시간에 따라 변하지 않는다고 가정하는 정상성 가정이다.
ARIMA모형은 해당 시계열이 약정상성(Weak Stationary)를 만족한다고 가정하며, 약정상성을 만족해야 좋은 fitting과 predict 성능을 보여줄 수 있다. 시계열 Yt가 다음의 세 조건을 만족할 때 약정상성을 가진다고 표현한다. 시계열 분서겡서 말하는 정상 시계열(Stationary series)은 약 정상성을 가지는 시계열을 말한다.
-평균이 모든 시점 t에서 동일하다 : 추세, 계절성, 순환성 등의 패턴이 보이지 않게 된다.
-분산이 모든 시점 t에서 동일하다: 자료 변화의 폭이 일정하게 된다.
-Yt와 Y(t-h) 간의 Covariation(즉, Yt의 자기 공분산 함수)이 모든 시점 t에 대해서 동일하다: 시간에 따라 상이한 자기상관적 패턴을 보이지 않게 된다.
즉, 풀어설명하자면 추세와 계절성이 있는 시계열은 정상성을 나타내는 시계열이 아니다. 일반적으로 정상성을 나타내는 시계열은 장기적으로 볼 때 예측할 수 있는 패턴을 나타내지 않을 것이다. (어떤 주기적인 행동이 있을 수 있더라도) 시간 그래프는 시계열이 일정한 분산을 갖고 대략적으로 평평하게 될 것을 나타낼 것이다.
그렇다면 다음 시계열 그래프중 어떤 것이 정상성을 나타낼까?
a) 거래일 200일 동안의 구글 주식 가격
b) 거래일 200일 동안의 구글 주식 가격의 일일 변동
c) 미국의 연간 파업 수
d) 미국에서 판매되는 새로운 단독 주택의 월별 판매액
e) 미국에서 계란 12개의 연간 가격(고정 달러)
f) 호주 빅토리아 주에서 매월 도살한 돼지의 전체 수
g) 캐나다 북서부 맥킨지 강 지역에서 연간 포획된 시라소니의 전체 수
h) 호주 월별 맥주 생산량
i) 호주 월별 전기 생산량
분명하게 계절성이 보이는 d),h),i)는 후보가 되지 못한다. 추세가 있고 수준이 변하는 a),c),e),f),i)도 후보가 되지 못한다. 분산이 증가하는 i)도 후보가 되지 못한다. 그렇다면 b)와 g)만 정상성을 나타내는 시계열 후보로 남았다.
얼핏 보면 시계열 g)에서 나타나는 뚜렷한 주기(cycle)때문에 정상성을 나타내는 시계열이 아닌 것처럼 보일 수 있다. 하지만 이러한 주기는 불규칙적(aperiodic)이다.
즉, 먹이를 구하기 힘들만큼 시라소니 개체수가 너무 많이 늘어나 번식을 멈춰서, 개체수가 작은 숫자로 줄어들고, 그 다음 먹이를 구할 수 있게 되어 개체수가 다시 늘어나는 식이다.
장기적으로 볼 때 , 이러한 주기의 시작이나 끝은 예측할 수 없다. 따라서 이 시계열은 정상성을 나타내느 시계열이다.
차분(differencing)
구글 주식 가격의 ACF(왼쪽), 일별 변동(오른쪽)
처음 그림에서 패널(a) 의 구글 주식 가격이 정상성을 나타내는 시계열이 아니었지만 (b)의 일별 변화는 정상성을 나타냈다느 것에 주목해보자. 이 그림은 정상성을 나타내지 않는 시계열을 정상성을 나타내도록 만드는 한 가지 방법을 나타낸다.
연이은 관측값들의 차이를 계산하는 것이다. 이를 차분(differencing)이라 부른다.
로그변환은 시계열의 분산 변화를 일정하게 만드는데 도움이 될 수 있다. 차분(differencing)은 시계열의 수준에서 나타내느 변화를 제거하여 시계열의 평균 변화를 일정하게 만드는데 도움이 될 수 있다. 결과적으로 추세나 계절성이 제거(또는 감소) 된다.
정상성을 나타내지 않는 시계열을 찾아낼 때 데이터의 시간 그래프를 살펴보는 것만큼, ACF 그래프도 유용하다. 정상성을 나타내지 않는 데이터에서는 ACF가 느리게 감소하지만, 정상성을 나타내는 시계열에서는, ACF가 비교적 빠르게 0으로 떨어질 것이다. 그리고 정상성을 나타내지 않는 데이터에서 r1은 종종 큰 양수 값을 갖는다.
차분을 구한 구글 주식 가격의 ACF는 단순히 white noise 시계열처럼 생겼다. 95% limitation 바깥에 자기상관(autocorrelation)값이 없고, 융-박스(Ljung-Box) Q* 통계는 h=10 에 대해 0.355라는 p-value를 갖는다. 이 결과는 구글 주식 가격의 일별
Box.test(diff(goog200), lag=10, type="LJung-Box")
#Box-Ljung test
#data: diff(goog200)
#X-Squared=11, df=10, p-value=0.4
변동이 기본적으로는 이전 거래일의 데이터와 상관이 없는 무작위적인 양이라는 것을 말해준다.
확률보행 모델
차분(difference)을 구한 시게열은 원래의 시계열에서 연이은 관측값의 차이이고, 다음과 같이 쓸 수 있다.
첫 번째 관측값에 대한 차분 y'1을 계산할 수 없기 때문에 차분을 구한 시계열은 T-1개의 값만 가질 것이다.
차분을 구한 시계열이 white noise이면, 원래 시계열에 대한 모델은 다음과 같이 쓸 수 있다.
여기서 Error(t)는 white noise를 의미한다. 이것을 정리하면 '확률보행(random walk)' 모델을 얻는다.
확률보행(random walk) 모델은 정상성을 나타내지 않은 데이터, 특별히 금융이나 경제 데이터를 다룰 때 널리 사용되고 있다. 확률보행에는 보통 다음과 같은 특징을 가진다.
-누가 봐도 알 수 있는 긴 주기를 갖는 상향 또는 하향 추세가 있다.
-갑작스럽고 예측할 수 없는 방향 변화가 있다.
미래 이동을 예측할 수 없고 위로 갈 확률이나 아래로 갈 확률이 정확하게 같기 때문에 확률보행 모델에서 얻어낸 예측값은 마지막 관측값과 같다. 따라서, 확률보행 모델은 단순(naive) 예측값을 뒷받침한다.
밀접하게 연관된 모델은 차분값이 0이 아닌 평균값을 갖게 한다. 그러면,
c값은 연이은 관측값의 차이의 평균이다. c가 양수이면, 평균 변화는 y(t)값에 따라 증가한다. 따라서 y(t)는 위쪽 방향으로 이동하는 경향을 나타낼 것이다. 하지만 c가 음수이면, y(t)는 아래쪽 방향으로 이동하는 경향을 나타낼 것이다.
2차 차분
가끔 차분(difference)을 구한 데이터가 정상성(stationarity)이 없다고 보일 수도 있다. 정상성을 나타내는 시계열을 얻기 위해 데이터에서 다음과 같이 한 번 더 차분을 구하는 작업이 필요할 수 있다.
이 경우에는, y(t)''는 T-2개의 값을 가질 것이다. 그러면, 원본 데이터의 '변화에서 나타나는 변화'를 모델링하게 되는 셈이다. 실제 상황에서는, 2차 차분 이상으로 구해야 하는 경우는 거의 일어나지 않는다.
계절성 차분
계절성 차분(seasonal differencing)은 관측치와, 같은 계절의 이전 관측값과의 차이를 말한다. 따라서
여기에서 m은 계절의 개수이다. m 주기 시차 뒤의 관측을 빼기 때문에 시차 m 차분이라고 부르기도 한다.
계절성으로 차분을 구한 데이터가 white nois로 보이면, 원본 데이터에 대해 적절한 모델은 다음과 같다.
이 모델에서 낸 예측값은 관련 있느 계절의 마지막 관측값과 같다. 즉, 이 모델은 계절성 단순(seasonal naive) 예측값을 나타낸다.
위 그림의 아래쪽 패널은 호주에서 팔린 A10 약물(당뇨병 약)의 월별 처방전의 수에 로그를 취하여 계절성 차분을 구한 결과를 나타낸다. 변환과 차분을 통해 시계열이 정상성을 나타내는 것처럼 보인다.
cbind("판매량 (백만 달러)"= a10,
"월별 로그 판매량"= log(a10),
"로그 눈금에서 연간 변동"=diff(log(a10),12))%>%
autoplot(facets=TRUE)+
xlab("연도")+ylab("")+
ggtitle("당뇨병 약 판매량")
보통의 차분과 계절성 차분을 구분하기 위해, 때때로 보통의 차분을 시차 1에서 차분을 구한다는 의미로 "1차 차분(first difference)"라고 부른다.
위 그림에서 나타낸 것처럼, 계절성을 나타내는 데이터를 얻기 위해 계절성 차분과 1차 차분 둘 다 구하는 것이 필요하기도 한다. 여기서는 데이터를 먼저 로그로 변환하고(두번째 패널), 그 후 계절성 차분을 계산했다(세번째 패널). 데이터에서 여전히 정상성이 보이지 않는 것 같아서, 1차 차분을 더 많이 계산했다.
어떤 차분(difference)을 구할지 정할 때는 주관적인 요소가 어느정도 들어간다. 위 약물 그림의 계절성 차분(seasonal difference)을 구한 데이터는 월별 순 전기 생산량의 계절성 차분을 구한 데이터와는 큰 차이를 나타내지 않는다. 후자의 경우, 계절성 차분을 구한 데이터로 결정했어야 했고, 차분을 더 구하지 않아도 되었다. 이에 반면해 전자의 경우에는 데이터의 정상성(stationarity)이 충분히 나타나지 않아서 차분을 더 구해야 했다. 차분을 구하는 것에 대한 몇가지 형식적인 검정을 아래에서 다루지만, 모델링 과정에서 항상 몇 가지 선택이 존재하고, 분석하는 사람마다 다른 선택을 할 수 있다.
y'(t)=y(t)-y(t-m)가 계절성 차분(seasonal difference)을 구한 시계열을 나타낸다면, 두 번 차분한 시계열은 다음과 같다.
계절성 차분과 1차 차분을 둘 다 적용할 때, 어떤 것을 먼저 적용하더라도 차이는 없다. 결과는 같을 것이다. 하지만, 데이터에 계절성 패턴이 강하게 나타나면, 계절성 차분을 먼저 계산하는 것을 추천한다. 왜냐면, 때때로 결과 시계열에서 정상성이 나타나기도 해서 이런 경우 1차 차분을 구할 필요가 없게 되기 때문이다. 1차 차분을 먼저 계산했다면, 여전히 남아있는 계절성이 나타날 것이다.
차분을 구했다면, 차분 값이 해석 가능할 것이라는 것은 중요하다. 첫 번째 차분값은 한 관측값과 그 다음 관측값 사이의 변화이다. 계절성 차분값은 한 해와 그 다음 해 사이의 변화이다. 다른 시차값(lagged value)은 직관적으로 해석하기가 쉽지 않기 때문에 사용하지 않는 것이 좋다.
단위근검정
단위근검정(unit root tests)은 더 객관적으로 차분을 구하는 것이 필요할 지 결정하기 위해 사용하는 한 가지 방법이다. 차분을 구하는 것이 필요한지 결정하는 상황을 위해 설계된 통계적 가설 검정들이 존재한다. 사용할 수 있는 단위근검정은 다양하고, 서로 다른 가정에 기초하고 있으며, 상반되는 답을 낼 수도 있다. 분석 과정에서 KPSS(Kwiatkowski-Phillips-Schmidt-Shin)검정을 사용한다. 이 검정은 데이터에 정상성이 나타난다는 것이 귀무가설이고, 귀무 가설이 거짓이라는 증거를 찾으려고 한다. 결과적으로 ,작은 p-value는 차이를 구하는 것이 필요하다는 것을 나타낸다. 검정은 다음과 같다.
예로는 구글 주식 가격 데이터에 적용해보자.
library(urca)
goog %>% ur.kpss() %>% summary()
#>
#> #######################
#> # KPSS Unit Root Test #
#> #######################
#>
#> Test is of type: mu with 7 lags.
#>
#> Value of test-statistic is: 10.72
#>
#> Critical value for a significance level of:
#> 10pct 5pct 2.5pct 1pct
#> critical values 0.347 0.463 0.574 0.739
검정 통계량은 1% 임계값보다 훨씬 크다. 이것은 귀무가설이 기각 된다는 것을 의미한다. 즉, 데이터가 정상성을 가지고 있지 않다. 데이터에 차분을 수행할 수 있고 검정을 다시 적용할 수 있다.
goog %>% diff() %>% ur.kpss() %>% summary()
#>
#> #######################
#> # KPSS Unit Root Test #
#> #######################
#>
#> Test is of type: mu with 7 lags.
#>
#> Value of test-statistic is: 0.0324
#>
#> Critical value for a significance level of:
#> 10pct 5pct 2.5pct 1pct
#> critical values 0.347 0.463 0.574 0.739
이번에는 검정 통계가 작고(0.0324), 정상성이 나타나는 데이터에서 볼 수 있는 것처럼 범위 안에 잘 들어간다. 따라서 차분을 구한 데이터가 정상성을 나타낸다고 결론내릴 수 있다.
1차 차분의 적당한 횟수를 결정하기 위해 여러번의 KPSS 검정을 사용하는 이 과정을 ndiffs()로 수행할 수 있다.
ndiffs(goog)
#> [1] 1
위 검정들에서 본 것처럼, google데이터가 정상성을 나타내도록 하려면 한번의 차분(difference)이 필요하다.
계절성 차분(seasonal difference)이 필요한지 결정하기 위한 비슷한 함수는 nsdiffs()이다. 이 함수는 필요한 계절성 차분의 적당한 횟수를 결정하기 위해 계절성 강도 측정량을 사용한다. Fs<0.64이면, 계절성 차분이 필요 없다고 알려주고, 이외의 경우에는 하나의 계절성 차분이 필요하다 알려준다.