회귀분석의 전제조건
회귀분석의 전제조건, 선형성, 독립성, 등분산성, 정규성
선형회귀는 분석 데이터가 선형성, 독립성, 등분산성, 정규성의 성질을 갖는다고 가정하기 때문에 좋은 선형 회귀분석 모델을 만들기 위해서는 네개의 기본가정을 모두 만족하는지 확인해야 한다.
iris
데이터를 통해 네 가지 기본가정이 선형회귀 모델에 미치는 영향을 확인해보자.
0. 예제 데이터¶
import pydataset as pds
import pandas as pd
df = pds.data('iris')
df.reset_index(drop=True, inplace=True)
df.columns = [i.replace('.', '_') for i in df.columns]
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 150 entries, 0 to 149
Data columns (total 5 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 Sepal_Length 150 non-null float64
1 Sepal_Width 150 non-null float64
2 Petal_Length 150 non-null float64
3 Petal_Width 150 non-null float64
4 Species 150 non-null object
dtypes: float64(4), object(1)
memory usage: 6.0+ KB
1. 선형성¶
iris
데이터의 분포는 다음과 같다.
import matplotlib.pyplot as plt
import seaborn as sns
sns.pairplot(
data=df,
hue='Species',
)
plt.show()
만약 Sepal_Length를 예측하려고 하는 종속변수라고 한다면, 위 그래프를 보았을 때 Sepal_Length와 대략적인 선형관계를 이루고 있는 변수는 Petal_Length와 Petal_Width이고, 선형성을 만족하지 않는 것은 Sepal_Width인 것으로 보인다.
이 상황에서 선형 회귀모델을 만들어 보자.
import statsmodels.formula.api as smf
formula = 'Sepal_Length ~ Sepal_Width + Petal_Length + Petal_Width'
model = smf.ols(formula=formula, data=df)
result = model.fit()
print(result.summary())
OLS Regression Results
==============================================================================
Dep. Variable: Sepal_Length R-squared: 0.859
Model: OLS Adj. R-squared: 0.856
Method: Least Squares F-statistic: 295.5
Date: Mon, 31 Jan 2022 Prob (F-statistic): 8.59e-62
Time: 18:26:28 Log-Likelihood: -37.321
No. Observations: 150 AIC: 82.64
Df Residuals: 146 BIC: 94.69
Df Model: 3
Covariance Type: nonrobust
================================================================================
coef std err t P>|t| [0.025 0.975]
--------------------------------------------------------------------------------
Intercept 1.8560 0.251 7.401 0.000 1.360 2.352
Sepal_Width 0.6508 0.067 9.765 0.000 0.519 0.783
Petal_Length 0.7091 0.057 12.502 0.000 0.597 0.821
Petal_Width -0.5565 0.128 -4.363 0.000 -0.809 -0.304
==============================================================================
Omnibus: 0.345 Durbin-Watson: 2.060
Prob(Omnibus): 0.842 Jarque-Bera (JB): 0.504
Skew: 0.007 Prob(JB): 0.777
Kurtosis: 2.716 Cond. No. 54.7
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
모든 변수의 유의확률(p-value)이 0.05 미만으로 나와 유의하다고 나온다. 그 이유는 가장 선형성을 강하게 만족하는 Petal_Length와 Petal_Width의 영향도를 뺀 나머지 값들이 종속변수인 Sepal_Width와 선형성을 이루기 때문인데, 시각화로 확인하면 다음과 같다.
df['Rest_Sepal_Width'] = (
df['Sepal_Length']
- result.params.Petal_Length * df['Petal_Length']
- result.params.Petal_Width * df['Petal_Width']
)
sns.pairplot(
data=df,
hue='Species',
)
plt.show()
Petal_Length와 Petal_Width의 영향도를 제거한 Rest_Sepal_Width를 Sepal_Width와 비교해보면 선형성이 아주 약간 생긴 것을 확인할 수 있다.
Petal_Length와 Petal_Width의 영향도를 뺀 나머지 값을 위와 같이 계산하는 이유는 Sepal_Length를 \(y\), Sepal_Width를 \(x_{0}\), Petal_Length를 \(x_{1}\), Petal_Width를 \(x_{2}\)라고 할 때 회귀식은 아래와 같이 정리되고,
따라서 종속변수 \(y\)에서 Petal_Length와 Petal_Width의 영향도를 뺀 나머지 값인 Rest_Sepal_Width(\(\beta_{0}x_{0} + \varepsilon\))는 아래와 같이 정리되기 때문이다.
Sepal_Width의 영향력(결정계수)을 확인하기 위해 Sepal_Width와 Sepal_Length를 단변량 회귀분석을 통해 확인해보자.
import statsmodels.formula.api as smf
formula = 'Sepal_Length ~ Sepal_Width'
model = smf.ols(formula=formula, data=df)
result = model.fit()
print(result.summary())
OLS Regression Results
==============================================================================
Dep. Variable: Sepal_Length R-squared: 0.014
Model: OLS Adj. R-squared: 0.007
Method: Least Squares F-statistic: 2.074
Date: Mon, 31 Jan 2022 Prob (F-statistic): 0.152
Time: 19:05:50 Log-Likelihood: -183.00
No. Observations: 150 AIC: 370.0
Df Residuals: 148 BIC: 376.0
Df Model: 1
Covariance Type: nonrobust
===============================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------
Intercept 6.5262 0.479 13.628 0.000 5.580 7.473
Sepal_Width -0.2234 0.155 -1.440 0.152 -0.530 0.083
==============================================================================
Omnibus: 4.389 Durbin-Watson: 0.952
Prob(Omnibus): 0.111 Jarque-Bera (JB): 4.237
Skew: 0.360 Prob(JB): 0.120
Kurtosis: 2.600 Cond. No. 24.2
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
Sepal_Width의 유의확률(p-value)이 0.152로 0.05보다 크기 때문에 Sepal_Width 단독으로는 Sepal_Length에 영향력이 없다는 귀무가설을 기각할 수 없다. 즉, Sepal_Width가 Sepal_Length에 영향력이 없다고 해석된다.
2. 독립성(다중공선성)¶
독립성이란 독립변수 간에 상관관계가 없이 독립성을 만족하는 특성을 의미하며, 다중회귀분석에서 중요하게 다뤄지는 가정이다.
iris
데이터에서 변수 간 상관성을 확인해보자.
Sepal_Length Sepal_Width Petal_Length Petal_Width
Sepal_Length 1.000000 -0.117570 0.871754 0.817941
Sepal_Width -0.117570 1.000000 -0.428440 -0.366126
Petal_Length 0.871754 -0.428440 1.000000 0.962865
Petal_Width 0.817941 -0.366126 0.962865 1.000000
시각화해서 보기 좋게 표현하면 아래와 같다.
sns.set(font_scale=1)
fig, ax = plt.subplots()
ax = sns.heatmap(
data=corr,
cmap=plt.cm.coolwarm,
annot=True,
fmt='.4f',
)
plt.show()
Petal_Length와 Petal_Width의 상관성이 0.96으로 매우 높게 나오는데, 독립변수 간의 상관성이 있을 경우 다중공선성(Multicollinearity)이 있다고 표현되며, 분산팽창요인(VIF, Variance Inflation Factors)을 통해 다중공선성을 계산할 수 있다.
VIF를 계산하는 공식은 아래와 같고, \({R^{2}_{i}}\)은 \(i\) 번째 독립변수에 대해 다른 독립변수들로 회귀분석을 시행한 선형 모델의 \(R^{2}\)라는 뜻이다.
Tip
VIF가 10이 넘으면 다중공선성이 있으며 5가 넘으면 주의할 필요가 있다고 보는데, 독립변수 a와 b가 서로 상관관계가 있다고 했을 때 두 변수 모두 VIF가 높고, 어느 하나만 VIF가 높은 경우는 없다. 서로 연관 있는 변수끼리 VIF가 높다.
Python에서는 statsmodels 패키지에서 제공하는 함수를 통해 직접 확인해보자.
import statsmodels.formula.api as smf
from statsmodels.stats.outliers_influence import variance_inflation_factor
formula = 'Sepal_Length ~ Sepal_Width + Petal_Length + Petal_Width'
model = smf.ols(formula=formula, data=df)
vif = pd.DataFrame(
{'VIF': variance_inflation_factor(model.exog, i), 'columns': column}
for i, column in enumerate(model.exog_names)
)
vif.sort_values(by='VIF', ascending=False, inplace=True)
vif.reset_index(drop=True, inplace=True)
print(vif)
VIF columns
0 95.343302 Intercept
1 15.097572 Petal_Length
2 14.234335 Petal_Width
3 1.270815 Sepal_Width
Petal_Length와 Petal_Width가 모두 10 이상이 나와 다중공선성이 있는 것으로 나타났다. 이러면 회귀분석 결과에 왜곡을 줘서 변수들의 결정계수가 틀리게 나오게 된다. 좀 더 정확한 회귀분석 결과를 위해 둘 중 하나를 제외하고 회귀분석을 시행해보자.
우선 Petal_Width를 제외하고 회귀분석을 시행한 결과와 VIF는 아래와 같다.
import statsmodels.formula.api as smf
from statsmodels.stats.outliers_influence import variance_inflation_factor
formula = 'Sepal_Length ~ Sepal_Width + Petal_Length'
model = smf.ols(formula=formula, data=df)
result = model.fit()
print(result.summary(), end="\n\n")
vif = pd.DataFrame(
{'VIF': variance_inflation_factor(model.exog, i), 'columns': column}
for i, column in enumerate(model.exog_names)
)
vif.sort_values(by='VIF', ascending=False, inplace=True)
vif.reset_index(drop=True, inplace=True)
print(vif)
OLS Regression Results
==============================================================================
Dep. Variable: Sepal_Length R-squared: 0.840
Model: OLS Adj. R-squared: 0.838
Method: Least Squares F-statistic: 386.4
Date: Sun, 13 Feb 2022 Prob (F-statistic): 2.93e-59
Time: 18:09:59 Log-Likelihood: -46.513
No. Observations: 150 AIC: 99.03
Df Residuals: 147 BIC: 108.1
Df Model: 2
Covariance Type: nonrobust
================================================================================
coef std err t P>|t| [0.025 0.975]
--------------------------------------------------------------------------------
Intercept 2.2491 0.248 9.070 0.000 1.759 2.739
Sepal_Width 0.5955 0.069 8.590 0.000 0.459 0.733
Petal_Length 0.4719 0.017 27.569 0.000 0.438 0.506
==============================================================================
Omnibus: 0.164 Durbin-Watson: 2.021
Prob(Omnibus): 0.921 Jarque-Bera (JB): 0.319
Skew: -0.044 Prob(JB): 0.853
Kurtosis: 2.792 Cond. No. 48.3
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
VIF columns
0 83.033291 Intercept
1 1.224831 Sepal_Width
2 1.224831 Petal_Length
다음으로 Petal_Length를 제외하고 회귀분석을 시행한 결과는 아래와 같다.
import statsmodels.formula.api as smf
from statsmodels.stats.outliers_influence import variance_inflation_factor
formula = 'Sepal_Length ~ Sepal_Width + Petal_Width'
model = smf.ols(formula=formula, data=df)
result = model.fit()
print(result.summary(), end="\n\n")
vif = pd.DataFrame(
{'VIF': variance_inflation_factor(model.exog, i), 'columns': column}
for i, column in enumerate(model.exog_names)
)
vif.sort_values(by='VIF', ascending=False, inplace=True)
vif.reset_index(drop=True, inplace=True)
print(vif)
OLS Regression Results
==============================================================================
Dep. Variable: Sepal_Length R-squared: 0.707
Model: OLS Adj. R-squared: 0.703
Method: Least Squares F-statistic: 177.6
Date: Sun, 13 Feb 2022 Prob (F-statistic): 6.15e-40
Time: 18:11:02 Log-Likelihood: -91.910
No. Observations: 150 AIC: 189.8
Df Residuals: 147 BIC: 198.9
Df Model: 2
Covariance Type: nonrobust
===============================================================================
coef std err t P>|t| [0.025 0.975]
-------------------------------------------------------------------------------
Intercept 3.4573 0.309 11.182 0.000 2.846 4.068
Sepal_Width 0.3991 0.091 4.380 0.000 0.219 0.579
Petal_Width 0.9721 0.052 18.659 0.000 0.869 1.075
==============================================================================
Omnibus: 2.095 Durbin-Watson: 1.877
Prob(Omnibus): 0.351 Jarque-Bera (JB): 1.677
Skew: 0.239 Prob(JB): 0.432
Kurtosis: 3.198 Cond. No. 30.3
==============================================================================
Notes:
[1] Standard Errors assume that the covariance matrix of the errors is correctly specified.
VIF columns
0 70.472677 Intercept
1 1.154799 Sepal_Width
2 1.154799 Petal_Width
아래와 같이 R-squared는 크게 변하지 않으면서 VIF가 크게 개선된 것을 확인할 수 있다.
지표 | 모든 변수 | Petal_Width 제외 | Petal_Length 제외 |
---|---|---|---|
R-squared | 0.859 | 0.840 | 0.707 |
Adj. R-squared | 0.856 | 0.838 | 0.703 |
coef Sepal_Width | 0.6508 | 0.5955 | 0.3991 |
coef Petal_Length | 0.7091 | 0.4719 | |
coef Petal_Width | -0.5565 | 0.9721 | |
VIF Sepal_Width | 1.270815 | 1.224831 | 1.154799 |
VIF Petal_Length | 15.097572 | 1.224831 | |
VIF Petal_Width | 14.234335 | 1.154799 |
Tip
다중공선성을 해결하는 방법은 위에서 진행한 것과 같이 다중공선성이 높은 변수를 제외하는 방법과, 다중공선성이 높은 변수들을 합쳐서 하나로 치환해주는 방법이 있다.
2-1. 💡다중공선성 계산용 모듈¶
statsmodels의 model
을 거치지 않고 계산 하는 함수는 아래와 같다.
import statsmodels.api as sm
from statsmodels.stats.outliers_influence import variance_inflation_factor
def vif_check(dataset, target=False):
dataset = dataset.select_dtypes(exclude=['object'])
dataset = sm.add_constant(dataset)
if target: dataset.drop([target], axis=1, inplace=True)
else: pass
vif = pd.DataFrame()
vif['VIF'] = [variance_inflation_factor(exog=dataset.values, exog_idx=i) for i in range(dataset.shape[1])]
vif['features'] = dataset.columns
vif.sort_values(by='VIF', ascending=False, inplace=True)
vif.reset_index(drop=True, inplace=True)
return vif
import pydataset as pds
import pandas as pd
df = pds.data('iris')
vif = vif_check(dataset=df)
print(vif)
VIF features
0 131.113086 const
1 31.261498 Petal.Length
2 16.090175 Petal.Width
3 7.072722 Sepal.Length
4 2.100872 Sepal.Width
3. 등분산성¶
Incomplete
이 글은 미완성입니다.
등분산검정(Equal-variance test)은 두 정규분포로부터 생성된 두 개의 데이터 집합으로부터 두 정규분포의 분산 모수가 같은지 확인하기 위한 검정이다. SciPy 패키지를 통해서 검정할 수 있다.
- scipy
- scipy.stats.bartlett: 바틀렛 검정
- scipy.stats.fligner: 플리그너 검정
- scipy.stats.levene: 레빈 검정
4. 정규성¶
Incomplete
이 글은 미완성입니다.
마지막 정규성은 확률분포가 가우시안 정규분포를 따르는지의 여부를 의미한다. 모델 요약의 Omnibus, Prob(Omnibus), Durbin-Watson, Jarque-Bera (JB), Prob(JB) 등이 정규성을 확인하는 지표이며, SciPy, statsmodels 패키지를 통해서 별도로 확인할 수 있다.
-
scipy
- scipy.stats.ks_2samp: 콜모고로프-스미르노프 검정(Kolmogorov-Smirnov test)
- scipy.stats.shapiro: 샤피로-윌크 검정(Shapiro–Wilk test)
- scipy.stats.anderson: 앤더스-달링 검정(Anderson–Darling test)
- scipy.stats.mstats.normaltest: 다고스티노 K-제곱 검정(D’Agostino’s K-squared test)
-
StatsModels
- statsmodels.stats.diagnostic.kstest_normal: 콜모고로프-스미르노프 검정(Kolmogorov-Smirnov test)
- statsmodels.stats.stattools.omni_normtest: 옴니버스 검정(Omnibus Normality test)
- statsmodels.stats.stattools.jarque_bera: 자크-베라 검정(Jarque–Bera test)
- statsmodels.stats.diagnostic.lillifors: 릴리포스 검정(Lilliefors test)