[기초통계] 회귀분석(Regression Analysis)
회귀분석이란?
: 연속형 변수들에 대해 두 변수 간의 관계를 수식으로 나타내는 분석 방법
쉽게 말해서 x라는 독립변수와 y라는 종속변수가 존재할 때 이 두 변수 간의 관계를 y=ax+b와 같은 형태의 수식으로 나타낼 수 있는 방법입니다. 이 데이터 간의 관계를 가장 적합한 하나의 직선으로 표현하는 방법이 회귀분석이며, 이를 위해 직선과 개별 값들 간의 오차를 최소화하는 직선을 찾는 것을 목표로 합니다. 종속변수가 1개, 독립변수가 2개 이상이면 다중 회귀분석이라고 하고, 종속변수와 독립변수 간의 관계가 log나 거듭제곱과 같은 비선형 관계일 경우에는 비선형 회귀분석이라고 합니다.
단순 선형 회귀분석
단순 선형 회귀분석은 종속변수(y)와 독립변수(x)가 각각 하나씩 존재하며 서로 션형적인 관계를 가질 때 사용하는 방법으로 회귀모델은 y=ax+b 형태의 수식으로 나타냅니다.
- 회귀모델이 통계적으로 유의한 지 확인해야 합니다.
- F 통계량의 P-value가 0.05보다 작으면 유의 수준 5%(신뢰 수준 95%) 하에서 추정된 회귀모델이 통계적으로 유의한 것으로 판단합니다.
- 개별 독립변수가 통계적으로 유의한지 확인해야 합니다.
- 개별 독립변수의 p값이 0.05보다 작으면 유의 수준 5% 하에서 통계적으로 유의한 것으로 판단합니다
- 결정계수(R-squared)가 높은지 확인해야 합니다.
- R^2은 1에 가까울수록 회귀모델의 성능이 뛰어나다고 판단합니다. 상황에 따라 다르지만 일반적으로 R^2이 0.7보다 크면 꽤 우수한 회귀모델이라고 판단할 수 있습니다. 물론, 독립변수와 종속변수의 절대적인 수치 크기로 인해 R^2이 0.7보다 낮더라도 효용 있는 회귀모델일 수 있습니다.
- 회귀모델은 coef(coefficient)값으로 구할 수 있습니다. Intercept는 y절편을 뜻하며 각 독립변수에 해당되는 coef 값은 해당 독립변수의 계수(기울기)를 나타냅니다.
Kaggle의 Salary_data를 토대로 나이, 경력, 그리고 급여의 상관관계에 대해 간단하게 분석해보고자 합니다.
import numpy as np # linear algebra
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
import statsmodels.formula.api as smf
from statsmodels.stats.outliers_influence import variance_inflation_factor
import matplotlib.pyplot as plt
import seaborn as sns
우선, 해당 데이터의 정보는 아래와 같습니다.
df.info()
회귀분석의 경우, 연속형 변수(숫자로 구성된 변수)만을 분석할 수 있기 때문에 Dtype이 float64인 'Age', 'Years of Experience', 'Salary'를 활용하여 분석을 진행하도록 하겠습니다.
df.isnull().sum()
분석을 진행하기 앞서, 데이터에 결측치가 있는지 확인하는 과정이 필요합니다. NULL 값이 있는 경우 오류가 나거나 부정확한 결괏값이 도출될 우려가 있기 때문에 확인하여 제거해야 합니다. 위 데이터의 경우에는 NULL 값을 갖는 데이터가 있기 때문에 제거해 주는 과정이 필요합니다.
df = df.dropna(how='any')
df.isnull().sum()
dropna 함수를 활용하여 간단하게 결측치를 제거해주고 다시 데이터를 확인해 보면 결측치가 제거된 것을 확인할 수 있습니다.
data 변수에 'Age', 'Years of Experience', 'Salary' 열의 값들을 저장한 후, ols 함수를 활용하여 'Age'와 'Years_of_Experience' 간의 회귀분석을 진행합니다. 다른 변수들을 토대로 회귀분석을 진행해봤지만, 식을 만들 수 없는 형태로 결괏값이 도출되어 조금은 뻔하지만, 나이와 경력 변수 간의 회귀분석을 진행하도록 하겠습니다.
data = df.loc[:,['Age', 'Years of Experience', 'Salary']]
reg_model = smf.ols(formula = 'Years_of_Experience ~ Age', data = data)
reg_result = reg_model.fit()
reg_result.summary()
plt.figure(figsize = (10,6))
plt.scatter(data.Age, data.Years_of_Experience, alpha = .5)
plt.plot(data.Age, data.Age * 0.7462 - 16.9943 , color = 'red')
plt.title('Scatter Plot')
plt.xlabel('Age')
plt.ylabel('Years_of_Experience')
plt.show()
회귀분석을 통해 도출된 식이 데이터의 값과 가장 작은 차이를 갖는 것이 좋은 모델입니다. 따라서 잔차(회귀분석 식으로 도출한 값과 실제 데이터 사이의 값 차이)를 보는 것은 모델의 성능을 파악하는 데에 필요합니다
plt.figure(figsize = (10,6))
plt.hist(reg_result.resid, bins = 7)
plt.show()
위와 같이 잔차가 0 근처에 주로 분포해 세로가 긴 종 모양의 히스토그램이 도출되면 좋은 모델이라고 평가할 수 있다.
다중 회귀분석
다중 회귀분석은 독립변수가 2개 이상일 경우에 사용하며 수식으로 표현하면 y = ax1 + bx2 + c 형태로 나타낼 수 있습니다. 단순 선형 회귀분석 결과를 해석하는 것과 동일하나 다중 회귀분석에서는 개별 독립변수의 p값을 더 유심히 봐야 하고, Adj. (Adjusted) R-squared로 모델이 계산을 통해 얼마나 종속변수를 잘 설명하는지 알아야 합니다.
- 다중 회귀분석의 변수 선택 방법
- 전진선택법 : y절편만 있는 상수모형부터 시작해 독립변수를 추가해 나감
- 후진소거법 : 독립변수를 모두 포함한 상태에서 시작해 가장 적은 영향을 주는 변수를 하나씩 제거해 나감
- 단계적 방법 : y절편만 있는 상수모형부터 시작해 독립변수를 추가해 나가지만 추가한 독립변수가 중요하지 않으면(p값이 높으면) 제거하고, 다른 독립변수를 추가해 나감
Age와 Salary를 다중 종속변수로 활용하여 회귀분석을 진행해보겠습니다.
mreg_model = smf.ols(formula = 'Years_of_Experience ~ Age + Salary', data = data)
mreg_result = mreg_model.fit()
mreg_result.summary()
R-squared 값이 1에 가까워진 것을 볼 수 있습니다. 이는 해당 회귀분석 모델의 성능이 뛰어남을 의미합니다.
- 다중공선성
- 다중 회귀분석의 경우 단순 선형 회귀분석과 달리 독립변수가 많기 때문에 예상치 못한 독립변수들 간의 강한 상관관계로 인해 제대로 된 회귀분석이 안될 수도 있습니다.
- 다중공선성 문제는 분산팽창요인(VIF, Variance Inflation Factor)을 계산해 구할 수 있는데, 일반적으로 10 이상이면 다중공선성 문제가 있다고 판단하고, 30을 초과하면 심각한 다중공선성 문제가 있다고 판단합니다.
- 비선형 회귀분석
- 비선형 회귀분석은 독립변수와 종속변수가 선형관계가 아닌 비션형 관계일 때 사용하는 분석 방법입니다. 독립변수와 종속변수가 직선이 아닌 곡선형태의 관계를 가질 수도 있기 때문에 이런 때에는 독립변수에 로그나 거듭제곱 등을 취해 보면서 적합한 비선형 모델을 찾아야 한다.
mreg_model.exog_names
>>> ['Intercept', 'Age', 'Salary']
vif1 = variance_inflation_factor(mreg_model.exog, 1)
vif2 = variance_inflation_factor(mreg_model.exog, 2)
print(vif1, vif2)
>>> 2.127986833610833 2.127986833610834
모델에 다중공선성 문제가 있는지 확인하기 위해 분산팽창요인을 계산합니다. 위의 분산팽창요인 계산 결과 두 가지 독립변수 모두 그 결괏값이 10보다 작기 때문에 다중공선성 문제가 없는 것으로 판단됩니다.
plt.figure(figsize = (10,6))
plt.hist(mreg_result.resid, bins= 7)
plt.show()