- 공유 링크 만들기
- 이메일
- 기타 앱
단순회귀분석(Simple regression)
전형적인 회귀 모형은 y = ax + b와 같은 형태이며 변수 x를 y로 선형변환하는 것으로 정의할 수 있습니다.
선형변환은 x의 변화 정도에 따라 y의 변화 정도가 같습니다.
x를 설명변수 또는 독립변수, y를 반응변수 또는 종속변수라고 하며 x는 1개 이상일 수 있으며 단변수일 경우를 단순회귀 모형이라고 합니다. 위 식과 같이 x를 y로 이동시키는 과정에서 가장 적합한 a와 b를 결정하는 것이 회귀분석의 목적이 됩니다.
예 1)
kospi 지수의 일일 주가 자료중 시가(Open)을 설명변수로 하여 종가(Close)를 추정하는 회귀모델을 작성합니다.
Open | Close | |
---|---|---|
0 | 2874.50 | 2944.45 |
1 | 2943.67 | 2990.57 |
2 | 2993.34 | 2968.21 |
⋮ | ⋮ | ⋮ |
다음 코드는 분석을 위한 자료를 호출하기 위한 것입니다.
st=pd.Timestamp(2021,1, 1) et=pd.Timestamp(2024, 5, 10) kos=fdr.DataReader('KS11',st, et)[["Open","Close"]] kos.index=range(len(kos)) kos.head(3).round(2)
Open | Close | ||
---|---|---|---|
0 | 2201.21 | 2175.17 | |
1 | 2192.58 | 2176.46 | |
2 | 2154.97 | 2155.07 |
통계분석을 위해 다음의 이유로 자료의 표준화(Standardization)가 필요합니다.
- 데이터의 규모(scale) 축소
- 여러변수가 사용될 경우 변수간 발생되는 데이터의 규모를 일정하게 조정
- 반응변수의 표준화는 필수적이지 않습니다. 그러나 b와 동일한 이유로 실행됩니다.
반응변수를 포함한 모든 자료가 표준화된 상태에서 구축된 회귀모델에 의한 추정치는 최종적으로 원래의 스케일로 환원되어야 합니다. 이 경우 표준화 단계에서 산출된 평균과 표준편차의 통계량이 필요합니다. 파이썬의 sklearn.processing.StandardScale(x) 클래스는 이와 같은 조건들을 충족시킵니다. 이 클래스에 전달되는 인수 x는 2차원이어야 합니다.
X=kos.values[:,0].reshape(-1,1) y=kos.values[:,1].reshape(-1,1) from sklearn.preprocessing import StandardScaler #독립변수 정규화(표준화) xScaler=StandardScaler().fit(X) X_n=xScaler.transform(X) print(X_n[:3].round(2))
[[0.56] [0.79] [0.96]]
#반응변수 정규화(표준화) yScaler=StandardScaler().fit(y) y_n=yScaler.transform(y) print(y_n[:3].round(2))
[[0.8 ] [0.95] [0.88]]
이 자료에서 설명변수 Open(indNor)과 반응변수 Close(deNor)의 관계는 그림 1과 같습니다. 이 그림에서 회귀선은 아래 코드에서 계산한 회귀모형입니다.
plt.figure(figsize=(4,2)) plt.scatter(X_n, y_n, label="Data") plt.plot(X_n, 0.998*X_n, color="red", label="Regression line") plt.legend(loc="best", frameon=False) plt.xlabel("Open", weight="bold") plt.ylabel('Close', weight="bold") plt.show()
그림 1에서 Open의 증가에 따라 Close의 증가는 명확해 보입니다. 회귀선(regression line)은 회귀분석에 의해 생성되는 모델이며 식 1과 같이 설정할 수 있습니다.
y = β0 + β1x + ε | (식 1) |
x: 설명변수 | |
y: 반응변수 | |
β0 : 편차 | |
β1 : 회귀계수(가중치) | |
ε : 오차 |
식 1을 회귀모델(regression model)이라고 합니다. 회귀모델은 파이썬의 다양한 패키지에서 제공하는 클래스 또는 함수등을 적용하여 생성할 수 있습니다. 다음은 statsmodel.api.OLS()와 sklearn.linear_model.LinearRegression() 클래스를 적용한 결과입니다. 두 클래스 모두 설명변수와 반응변수 모두 numpy array객체로 전달하여야 합니다.
numpy array 객체는 벡터, 행렬등의 배열 구조의 데이터 생성, 조작등에 특화된 파이썬 대표적인 패키지 numpy의 array() 함수에 의해 생성됩니다.
위 LinearRegression()의 클래스는 전달하는 인수에 의해 편차항을 선택할 수 있지만 OLS() 클래스의 경우 식 2와 같이 설명변수 편차항을 인위적으로 첨가하여야 합니다. 설명변수에 편차항 즉, β0을 첨가하기 위해 add_constant() 함수를 적용합니다.
$$X=\begin{bmatrix}x_1\\x_2\\\vdots\\x_n\end{bmatrix} \Rightarrow X_0=\begin{bmatrix} 1& x_1\\1& x_2\\\vdots& \vdots\\1& x_n\end{bmatrix}$$ | (식 2) |
import statsmodels.api as sm X_n0=sm.add_constant(X_n) X_n0.shape, y_n.shape
((824, 2), (824, 1))
다음코드는 statsmodel.api.OLS()
클래스를 적용한 결과입니다. 이 클래스의 인스턴스 즉, 생성된 모델을 실제로 실행하기 위해 .fit()
메서드를 실행합니다. 또한 생성한 모델의 회귀계수는 .params
속성, 회귀 모델의 결정계수는 .rsuared
속성을 사용하여 나타냅니다.
statsmodels.formula.api.ols('반응변수 ~ 설명변수', data) 클래스도 OLS() 클래스와 같습니다. 그러나 ols()의 경우 설명변수에 편차항이 자동으로 생성되므로 .add_constant(설명변수) 메소드를 사용할 필요가 없습니다. 이 클래스에 편차항을 제거하기 위해서는 "반응변수 ~ 설명변수 -1"와 같이 작성합니다.
reg=sm.OLS(y_n, X_n0).fit() print(f'회귀계수(b0, b1) :{np.around(reg.params,3)}\nR2:{np.around(reg.rsquared,3)}')
회귀계수(b0, b1) :[-0. 0.997] R2:0.994
ols()
클래스를 적용하기 위해서는 각 변수의 변수이름이 존재하는 구조이어야 하므로 다음과 같이 표준화한 자료의 구조를 변환하여 사용합니다.
da=np.c_[X_n, y_n] da1=pd.DataFrame(da, columns=["open", "close"]) da1.head(3).round(3)
open | close | |
---|---|---|
0 | 0.561 | 0.800 |
1 | 0.791 | 0.953 |
2 | 0.956 | 0.879 |
m=statsmodels.formula.api.ols("close ~ open", data=da1).fit() print(f"회귀계수\n {m.params.round(3)}")
회귀계수 Intercept -0.000 open 0.997 dtype: float64
print("R2: %.3f" %m.rsquared)
R2: 0.994
회귀분석 결과의 요약은 .summay()
메서드로 확인할 수 있습니다. 이 결과는 회귀 모형, 회귀계수, 그리고 설명변수의 분포에 대한 표들로 구성되어 있습니다. 각 표에 수록되어 있는 결과들의 의미는 이어지는 절에서 소개할 것입니다.
reg.summary()
위 코드의 결과는 표 1과 같이 3개의 표로 반환됩니다. 첫번째 표는 모델의 주요통계량을 나타내고 회귀모델의 각 계수에 대한 분석결과는 두 번째 표에서 확인할 수 있습니다. 마지막 3번째 표는 모델의 전제 조건에 대한 분석 결과입니다.
Dep. Variable: | y | R-squared: | 0.994 |
---|---|---|---|
Model: | OLS | Adj. R-squared: | 0.994 |
Method: | Least Squares | F-statistic: | 1.407e+05 |
Date: | Tue, 04 Jun 2024 | Prob (F-statistic): | 0.00 |
Time: | 18:42:07 | Log-Likelihood: | 952.04 |
No. Observations: | 824 | AIC: | -1900. |
Df Residuals: | 822 | BIC: | -1891. |
Df Model: | 1 | ||
Covariance Type: | nonrobust |
coef | std err | t | P>|t| | [0.025 | 0.975] | |
---|---|---|---|---|---|---|
const | -1.691e-16 | 0.003 | -6.36e-14 | 1.000 | -0.005 | 0.005 |
x1 | 0.9971 | 0.003 | 375.137 | 0.000 | 0.992 | 1.002 |
Omnibus: | 54.231 | Durbin-Watson: | 2.039 |
---|---|---|---|
Prob(Omnibus): | 0.000 | Jarque-Bera (JB): | 226.114 |
Skew: | 0.016 | Prob(JB): | 7.94e-50 |
Kurtosis: | 5.566 | Cond. No. | 1.00 |
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
생성된 모델인 reg를 사용하여 설명변수의 추정 결과는 .predict(설명변수)
메소드로 확인할 수 있습니다. 이 경우 전달하는 인수인 설명변수는 모델 구축시 사용된 변수와 같은 구조이어야 합니다. 모델 생성에 참여하지 않은 새로운 변수를 모델에 적용하기 위해 새로운 자료를 호출합니다.
trgDay=pd.Timestamp(2023, 5, 11) newD=fdr.DataReader('KS11',trgDay, trgDay)[["Open","Close"]] newD.round(2)
Open | Close | |
---|---|---|
Date | ||
2023-05-11 | 2505.57 | 2491.0 |
위 자료의 Open은 새로운 설명변수가 되므로 표준화합니다. 또한 편차항을 첨가합니다.
x_new=newD.values[0,0].reshape(-1,1) xN_new=xScaler.transform(x_new) print(xN_new.round(2))
[[-0.66]]
xN_new0=np.c_[1, xN_new] print(xN_new0.round(2))
[[ 1. -0.66]]
pre=reg.predict(xN_new0) print(pre.round(2))
[-0.66]
위 결과는 표준화된 값입니다. 이 값을 원 스케일로 환원하기 위해서는 다음과 같이 StandardScaler.inverse_transform()
메소드를 적용합니다.
pre_re=yScaler.inverse_transform(pre.reshape(-1,1)) print(pre_re.round(2))
[[2504.29]]
회귀모델은 sklearn 패키지의 liniear_model.linearRegression(fit_intercept=True)
클래스를 사용하여 구축할 수 있습니다. 이 클래스의 인수인 fit_intercept는 편차항의 존재를 의미하는 것으로서 기본값이 True이므로 편차항이 자동 생성됩니다. 이 클래스에 의해 자료에 모델을 적합시키기 위해 위에서 사용한 OLS 클래스와 같이 .fit(설명변수, 반응변수)
메서드를 사용합니다. 생성된 모델의 계수와 편차는 다음의 속성을 이용하여 나타낼 수 있습니다.
- 모형클래스이름.coef_: model의 계수를 반환
- 모형클래스이름.intercept_: model의 절편을 반환
- 모델.score(설명변수, 반응변수): 결정계수(R2)을 반환
from sklearn.linear_model import LinearRegression mod = LinearRegression() mod.fit(X_n, y_n) print(f'계수:{np.around(mod.coef_,3)}, \n편차 : {np.around(mod.intercept_,3)}\nR2:{np.around(mod.score(X_n, y_n),3)}')
계수:[[0.997]], 편차 : [-0.] R2:0.994
.predict()
메서드를 사용하여 예측값을 계산합니다.
pre=mod.predict(xN_new) print(pre.round(2))
[[-0.66]]
pre_re=yScaler.inverse_transform(pre) print(pre_re.round(2))
[[2504.29]]
댓글
댓글 쓰기