회귀모델 생성
1개 이상의 설명변수들과 1개의 반응변수를 가진 선형모델 역시 statsmodels.api.OLS() 클래스와 sklearn.linear_model() 클래스를 적용하여 구축할 수 있습니다. 이 모델의 구축과정과 평가방법은 단순회귀분석(Simple regression)과 같습니다.
예 1)
코스피지수(kos), 코스탁지수(kq), kodex 레버리지(kl), kodex 인버스(ki), 그리고 원달러환율(WonDol)의 일일 종가들을 설명변수로 사용하여 삼성전자(sam)의 일일 종가를 추정하는 회귀모델을 구축해 봅니다. (이 자료에서 설명변수는 반응변수보다 1일 앞선 데이터 입니다.)
kos | kq | kl | ki | WonDol | sam | |
---|---|---|---|---|---|---|
Date | ||||||
2023-01-10 | 2351.0 | 696.0 | 14440.0 | 4885.0 | 1239.0 | 60400.0 |
2023-01-11 | 2360.0 | 710.0 | 14525.0 | 4875.0 | 1240.0 | 60500.0 |
2023-01-12 | 2365.0 | 711.0 | 14580.0 | 4860.0 | 1242.0 | 60500.0 |
⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
다음은 위 자료들을 호출하고 분석에 적합하게 조절하기 위한 코드들입니다.
import numpy as np import pandas as pd from sklearn import preprocessing from scipy import stats import matplotlib.pyplot as plt import yfinance as yf from sklearn.linear_model import LinearRegression from statsmodels.regression.linear_model import OLS from statsmodels.formula.api import ols
st=pd.Timestamp(2023,1, 10) et=pd.Timestamp(2024, 5, 30) code=["^KS11", "^KQ11", "122630.KS", "114800.KS","KRW=X","005930.KS"] nme=["kos","kq","kl", "ki", "WonDol","sam" ] da={} for i, j in zip(nme,code): da[i]=yf.download(j,st, et)["Close"] da2=pd.DataFrame(da.values(), index=da.keys()).T da2=da2.ffill() da2.head(3).round(0)
호출한 자료들의 시간들이 동일하지는 않습니다. 즉, WonDol을 제외한 자료는 거래가 중단되는 시간들이 존재합니다. 그러므로 이들 자료를 결합하는 과정에서 결측치가 존재합니다(결측치 조정 참조). 위 코드에서는 객체.ffill()
를 적용하여 결측지 직전 값으로 대체하였습니다.
호출한 데이터들을 설명변수와 반응변수로 분리하고 마지막 행에 존재하는 자료들은 모델 생성에 참여하지 않은 설명변수로서 별도의 객체 final에 저장합니다.
ind=da2.values[:-1,:-1] de=da2.values[1:,-1].reshape(-1,1) final=da2.values[-1, :-1].reshape(1,-1)
각 변수들의 범위 즉, 스케일(규모)은 다양하므로 데이터 표준화를 실시합니다. 이것은 sklearn.preprocessing의 StandardScaler() 클래스를 적용합니다.
indScaler=preprocessing.StandardScaler().fit(ind) deScaler=preprocessing.StandardScaler().fit(de) indNor=indScaler.transform(ind) finalNor=indScaler.transform(final) deNor=deScaler.transform(de) print(finalNor.round(3))
[[ 1.256 -0.133 1.488 -1.515 1.395]]
statsmodels.formula.api.ols()
클래스를 적용하여 회귀모델을 생성합니다. 이 모델의 세부적인 사항은 summay()
메서드로 확인할 수 있습니다. 이 메서드의 결과는 단순회귀분석에서 나타낸 것과 같이 3개의 표들로 구성됩니다. 이 표들은 이 메서드의 속성 tables로 별도로 나타낼 수 있습니다. 먼저 첫 번째 표를 나타내면 다음과 같습니다.
ols() 함수를 사용하기 위해서는 위의 indNor, deNor을 결합하여 DataFrame 구조로 생성하여야 합니다.
da3=pd.DataFrame(np.c_[indNor, deNor.reshape(-1,1)]) da3.columns=da2.columns da3.head(3).round(2)
kos | kq | kl | ki | WonDol | sam | |
---|---|---|---|---|---|---|
0 | -1.87 | -3.01 | -1.47 | 1.58 | -2.36 | -1.72 |
1 | -1.79 | -2.73 | -1.42 | 1.53 | -2.33 | -1.72 |
2 | -1.74 | -2.71 | -1.38 | 1.46 | -2.29 | -1.67 |
form='sam~kos+kq+kl+ki+WonDol' reg=ols(form, data=da3).fit() re=reg.summary() re.tables[0]
위 코드의 결과는 표 1과 같습니다.
Dep. Variable: | sam | R-squared: | 0.863 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.861 |
Method: | Least Squares | F-statistic: | 445.4 |
Date: | Wed, 11 Sep 2024 | Prob (F-statistic): | 1.61e-150 |
Time: | 09:52:12 | Log-Likelihood: | -154.10 |
No. Observations: | 361 | AIC: | 320.2 |
Df Residuals: | 355 | BIC: | 343.5 |
Df Model: | 5 | ||
Covariance Type: | nonrobust |
표 1에서 나타낸 F 검정의 유의확률(p-value)은 매우 작은 값으로 모델의 모든 회귀계수가 0이 된다는 귀무가설을 기각할 수 있습니다. 즉, 이 모델은 설명변수들에 의한 반응변수의 설명이 가능함을 나타냅니다. 이 결과는 작지않은 결정계수(R2)에 의해 부연됩니다.
F 검정의 결과는 각각 유의확률과 검정통계량을 반환하는 모델.f_pvalue, 모델.fvalue
속성, 결정계수는 모델.squared
속성으로 확인할 수 있습니다.
fst, fp, r2=reg.fvalue,reg.f_pvalue, reg.rsquared print('F통계량:%.3f, p-value:%.3f, R2:%.3f' %(fst, fp, r2))
F통계량:445.378, p-value:0.000, R2:0.863
F 검정 결과는 이 모델 전체에 대한 검정으로 각 설명변수가 반응변수에 영향 정도를 확인할 수 없습니다. 모델에의해 발생되는 오차에 대해 각 설명변수들의 영향정도는 anova 분석으로 확인할 수 있습니다.
anovaRe=sm.stats.anova_lm(reg) np.around(anovaRe, 2)
df | sum_sq | mean_sq | F | PR(>F) | |
---|---|---|---|---|---|
kos | 1.0 | 217.12 | 217.12 | 1552.84 | 0.0 |
kq | 1.0 | 4.62 | 4.62 | 33.04 | 0.0 |
kl | 1.0 | 82.49 | 82.49 | 589.98 | 0.0 |
ki | 1.0 | 5.45 | 5.45 | 38.99 | 0.0 |
WonDol | 1.0 | 1.68 | 1.68 | 12.04 | 0.0 |
Residual | 355.0 | 49.64 | 0.14 | NaN | NaN |
위 결과는 잔차의 분산 대비 각 설명변수의 분산의 비를 통계량으로 하는 것으로 유의수준 0.05를 기준으로 모든 변수들의 효과가 없다는 귀무가설을 기각할 수 있습니다. 이 결과는 객체 re의 두번째 표는 회귀계수에 대한 t 검정 결과를 나타냅니다(회귀계수의 평가 참조).
re.tables[1]
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
Intercept | 8.29e-16 | 0.020 | 4.21e-14 | 1.000 | -0.039 | 0.039 |
kos | -2.1508 | 0.144 | -14.942 | 0.000 | -2.434 | -1.868 |
kq | 0.3980 | 0.041 | 9.772 | 0.000 | 0.318 | 0.478 |
kl | 0.9641 | 0.280 | 3.445 | 0.001 | 0.414 | 1.514 |
ki | -1.7233 | 0.269 | -6.416 | 0.000 | -2.251 | -1.195 |
WonDol | 0.0810 | 0.023 | 3.470 | 0.001 | 0.035 | 0.127 |
위 결과는 anova 분석과 같은 결과를 보여줍니다. 편차항을 제외한 모든 계수의 신뢰구간은 0을 포함하지 않으므로 모델에 영향을 준다고 할 수 있습니다. 이 회귀계수의 신뢰구간은 위 표외에 모델.conf_int(alpha)
메소드로 확인할 수 있습니다.
reg.conf_int(0.05).round(3)
0 | 1 | |
---|---|---|
Intercept | -0.039 | 0.039 |
kos | -2.434 | -1.868 |
kq | 0.318 | 0.478 |
kl | 0.414 | 1.514 |
ki | -2.251 | -1.195 |
WonDol | 0.035 | 0.127 |
각각의 설명변수와 반응변수 sam에 대한 단순회귀모형으로 위 분석결과를 확인할 수 있습니다. 예를 들어 WonDol-sam의 단순회귀모형으로 이 설명변수의 효과여부를 판단해 봅니다.
reg_wd=ols("sam~WonDol", data=da3).fit() pd.DataFrame([reg_wd.fvalue, reg_wd.f_pvalue], index=['f-statics', 'p-value']).round(3)
0 | |
---|---|
f-statics | 113.455 |
p-value | 0.000 |
위와 같이 각 설명변수에 대한 단순회귀분석의 결과는 sklearn.feature_selection.f_regression(설명변수, 반응변수) 함수를 사용하여 모든 변수에 대한 결과를 확인할 수 있습니다. 이 함수는 하나의 설명변수과 반응변수로 생성되는 단순회귀모델에 대한 F 검정 결과(통계량과 유의확률)를 반환합니다. 예를 들어 다음 결과의 첫번째 통계량과 유의확률은 y=b0 + b1x[:,0]의 모델에 대한 F 검정의 결과입니다.
from sklearn import feature_selection Ftest=feature_selection.f_regression(indNor, deNor.ravel()) f2=pd.DataFrame([Ftest[0], Ftest[1]], index=["f-value", "p-value"], columns=da2.columns[:-1]) np.around(f2, 1)
kos | kq | kl | ki | WonDol | |
---|---|---|---|---|---|
f-value | 541.7 | 105.1 | 975.8 | 994.2 | 113.5 |
p-value | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
위 결과는 F 통계량과 p-value로서 각 설명변수에 의한 단순회귀모델이 유효함을 의미합니다.
생성된 모델의 회귀계수는 모델.params
속성으로 확인할 수 있습니다. 또한 추정값은 모델.predict()
메소드로 확인할 수 있습니다.
reg.params.round(3)
Intercept 0.000 kos -2.151 kq 0.398 kl 0.964 ki -1.723 WonDol 0.081 dtype: float64
위 ols() 함수 대신에 sklearn.linear_model.LinearRegression()
클래스를 사용하여 회귀모델을 작성할 수 있습니다. 다음은 이 클래스를 적용한 결과입니다.
mod=LinearRegression().fit(indNor, deNor) b0=mod.intercept_ b=mod.coef_ print(f"편차:{np.around(b0, 3)}, 계수: {np.around(b, 3)}")
편차:[-0.], 계수: [[-2.151 0.398 0.964 -1.723 0.081]]
r2=mod.score(indNor, deNor) print(f"R2: {round(r2, 3)}")
R2: 0.863
그림 1은 생성한 모델을 통해 반응변수와 그에 대응하는 추정된 값들을 시각화한 것입니다.
plt.figure(figsize=(4, 3)) plt.plot(deNor, c="g", ls="--", label="data") plt.plot(mod.predict(indNor), c="b", label="regression") plt.legend(loc="best", frameon=False) plt.show()
댓글
댓글 쓰기