5 분 소요

본 게시물은 통계적 선형 회귀분석에 대해 소개한다.

1. 단순 회귀분석

1.1 Category

  • 문제 풀이시 가장 먼저 확인해야 할 것은 데이터의 타입

1.2 Evaluation

  1. 단순 선형 회귀분석

    • R-square: 총 변동 중에 설명된 변동의 비율 ☞ 값이 높을수록 새로운 값을 추정하여도 믿을 만함

    • RMSE: 분산 분석에서의 SSE를 자유도 n-2로 나눈 것이 MSE이며, 이에 루트를 취한 값

  2. 다중 선형 회귀분석

    • Adjusted R-square: 독립변수가 2개 이상일 때 사용되며, 독립변수가 늘어날 수록 R-square 값이 커지는 것에 대한 패널티를 적용한 것

📍 자유도

  • 보통 빅데이터 분석에서는 n이 커진다면 1, 2 값의 차이는 의미가 없어 자유도로 나누지 않고 n으로 나눔

1.3 Considerations

  1. 모형이 데이터를 잘 적합하고 있는가

    • 모형의 잔차가 특정 패턴을 이루고 있지 않아야 함

  2. 회귀 모형이 통계적으로 유의한가

    • F 통계량을 사용하며, 이를 통해 얻은 p-value가 유의수준보다 작아야 함
      • H0: 회귀 모형이 유의하지 않음
      • H1: 회귀 모형이 유의함
  3. 모형은 데이터를 얼마나 설명할 수 있는가

    • R-square를 통해 설명력을 확인함
  4. 모형 내의 회귀계수는 유의한가

    • 각 독립변수에 대해 회귀 계수를 검정
    • 독립변수의 회귀계수는 t 통계량을 사용하며, 이를 통해 얻은 P-value가 유의수준보다 작아야 함

1.4 Parameters

statsmodels.formula.api.ols(formula, data, subset=None, drop_cols=None, *args, **kwargs)
  • formular: 모델을 지정하는 공식을 작성
  • data: 모델에 적용할 데이터
  • drop_cols: 삭제할 데이터 컬럼

1.5 Methods

  • model.summary(): 모델 적합 결과를 요약하여 제시
  • model.parmas: 변수들의 회귀계수
  • model.predict(): 새로운 데이터에 대한 예측 값

1.6 Implementation

😗 집의 평수와 가격의 선형 회귀 분석

import pandas as pd 
import numpy as np 
house = pd.read_csv("https://raw.githubusercontent.com/ADPclass/ADP_book_ver01/main/data/kc_house_data.csv")
house = house[["price","sqft_living"]]
## 독립변수와 종속변수의 선형 가정 
house.corr()

  • 분석 진행 전, 독립변수와 종속변수 간의 상관계수를 확인
from statsmodels.formula.api import ols
import matplotlib.pyplot as plt
# 변수 할당 
y = house['price']
X = house[['sqft_living']]
# 단순 선형 회귀 모형 적합
lr = ols('price ~ sqft_living',data=house).fit()
y_pred = lr.predict(X)
# 시각화 
plt.scatter(X, y) ## 원 데이터 산포도
plt.plot(X, y_pred, color='red') ## 회귀직선 추가 
plt.xlabel('sqft_living', fontsize=10)
plt.ylabel('price',fontsize=10)
plt.title('Linear Regression Result')
plt.show()

lr.summary()

📍 모형 해석

  1. 모형이 데이터를 잘 적합하는가?

    • 원본 데이터와 회귀선을 시각화 후, 데이터의 분포를 보고 판단

    • 0에서 멀어질 수록 오차가 커짐 ☞ 잔차의 분산이 증가 (=등분산성이 벗어남)

      ☞ 모델이 데이터를 잘 설명하고 있지 않음

  2. 회귀 모형이 통계적으로 유의한가?

    • F 통계량과 P-value를 확인
      • p-value < 0.05 이므로 유의함
  3. 모형은 데이터를 얼마나 설명할 수 있는가?

    • R-square가 0.493으로 모형이 전체 데이터의 49.3%를 설명
      • 통계적으로 유의하다고 모델의 성능이 좋은 것은 아님
      • 지표가 좋고 나쁨은 산업별로 다름
  4. 모형 내의 회귀계수는 유의한가?

    • t 통계량과 p-value를 확인
      • P-value < 0.05 이므로 유의함

2. 다중 회귀분석

2.1 다중공선성

선형 회귀분석에서 독리변수들 간에 강한 상관관계가 나타나는 문제로, 정확한 회귀계수 추정과 독립변수의 해석을 어렵게 하고, 모델의 일반화 능력을 저하시킴

  • 독립변수간 상관계수를 구하여 상관성이 0.9 이상일 경우

  • 두 독립변수의 회귀분석을 통해 얻은 허용오차(1-R-square)가 0.1 이하일 경우

  • VIF 값이 10 이상일 경우

1.2 변수선택법

모형 내 설명변수가 증가할 수록 데이터 관리 비용이 필요하기에, 종속변수에 영향을 미치는 유의미한 독립변수만을 선택하여 최적의 회귀 방정식을 도출하는 과정

  • 전진 선택법: 하나씩 변수를 추가하여 지표를 확인
  • 후진 제거법: 모든 변수를 추가한 뒤 하나씩 제거하며 지표를 확인
  • 단계적 선택법: 변수를 추가, 제거하며 모델의 지표를 확인

📍 모델의 지표

  • F-통계량

  • AIC

1.3 Implementation

😗차 가격에 영향을 미치는 변수들로 모형 구축

import pandas as pd
# 데이터 불러오기
Cars = pd.read_csv("https://raw.githubusercontent.com/ADPclass/ADP_book_ver01/main/data/Cars93.csv")
Cars.info()
 #   Column              Non-Null Count  Dtype  
---  ------              --------------  -----  
 4   Price               93 non-null     float64
 6   MPG.city            93 non-null     int64  
 7   MPG.highway         93 non-null     int64  
 11  EngineSize          93 non-null     float64
 13  RPM                 93 non-null     int64  
 18  Length              93 non-null     int64  
 19  Wheelbase           93 non-null     int64  
 24  Weight              93 non-null     int64  
  • 다양한 변수 중 분석을 위해 사용할 변수들
import numpy as np
import statsmodels.api as sm
import statsmodels.formula.api as smf
# ols 모델의 formula을 정의할 때, 일부 특수문자는 쓸 수 없기에, 컬럼 특수문자 제거 
Cars.columns = Cars.columns.str.replace(".","")
model = smf.ols(formula ="Price ~ EngineSize + RPM + Weight+ Length + MPGcity + MPGhighway", data = Cars)
result = model.fit()
result.summary()

  • 검정 결과 p-value < 0.05임으로 모델은 유의함
  • Adj R-square가 0.542로 낮은 수치
Cars[['EngineSize','RPM', 'Weight','Length','MPGcity','MPGhighway']].corr()

  • 다중 공선성을 파악하기 위한 상관관계 분석
    • MPGcity와 MPGhighway 중 어느것을 제거해야할지 확인
from patsy import dmatrices
from statsmodels.stats.outliers_influence import variance_inflation_factor
# 독립변수와 종속변수를 데이터프레임으로 나누어 저장하는 함수 
y,X = dmatrices("Price ~ EngineSize + RPM + Weight+ Length + MPGcity + MPGhighway",
 data = Cars,return_type ="dataframe")
# 독립변수끼리의 VIF 값을 계산하여 데이터프레임으로 만드는 과정 
vif_list = []
for i in range(1,len(X.columns)): 
 vif_list.append([variance_inflation_factor(X.values,i), X.columns[i]])
pd.DataFrame(vif_list,columns=['vif','variable'])

  • VIF 결과 MPGcity < MPGhighway 이므로 MPGcity 제거
model = smf.ols(formula ="Price ~ EngineSize + RPM + Weight + MPGhighway", data = Cars)
result = model.fit()
result.summary()

  • 공선성을 제거하였음에도 AdjR-square 값이 변화 없음
    • 무분별한 변수 사용이 문제
  • MPGcity를 제거했을때, MPGhighway의 p-value가 낮아짐
    • 유의한 변수임에도 다중 공선성으로 유의하지 않게 표현될 수 있음
import time
import itertools
import pandas as pd
import statsmodels.api as sm

def processSubset(X, y, feature_set):
    model = sm.OLS(y, X[list(feature_set)])  # Modeling
    regr = model.fit()  # 모델 학습
    AIC = regr.aic  # 모델의 AIC
    return {"model": regr, "AIC": AIC}

# 전진선택법
def forward(X, y, predictors):
    # 데이터 변수들이 미리 정의된 predictors에 있는지 없는지 확인 및 분류
    remaining_predictors = [p for p in X.columns.difference(['Intercept']) if p not in predictors]
    results = []
    
    for p in remaining_predictors:
        results.append(processSubset(X=X, y=y, feature_set=predictors + [p] + ['Intercept']))
    
    # 데이터프레임으로 변환
    models = pd.DataFrame(results)
    
    # AIC가 가장 낮은 것을 선택
    best_model = models.loc[models['AIC'].argmin()]  # index
    print("Processed ", models.shape[0], "models on", len(predictors) + 1, "predictors in")
    print('Selected predictors:', best_model['model'].model.exog_names, ' AIC:', best_model[0])
    
    return best_model

# 후진소거법
def backward(X, y, predictors):
    tic = time.time()
    results = []

    # 데이터 변수들이 미리 정의된 predictors 조합 확인
    for combo in itertools.combinations(predictors, len(predictors) - 1):
        results.append(processSubset(X=X, y=y, feature_set=list(combo) + ['Intercept']))
    
    models = pd.DataFrame(results)

    # 가장 낮은 AIC를 가진 모델을 선택
    best_model = models.loc[models['AIC'].argmin()]
    toc = time.time()
    print("Processed ", models.shape[0], "models on", len(predictors) - 1, "predictors in", (toc - tic))
    print('Selected predictors:', best_model['model'].model.exog_names, 'AIC:', best_model[0])
    
    return best_model

# 단계적 선택법
def Stepwise_model(X, y):
    Stepmodels = pd.DataFrame(columns=["AIC", "model"])
    tic = time.time()
    predictors = []
    Smodel_before = processSubset(X, y, predictors + ['Intercept'])['AIC']
    
    for i in range(1, len(X.columns.difference(['Intercept'])) + 1):
        Forward_result = forward(X=X, y=y, predictors=predictors)
        print('forward')
        Stepmodels.loc[i] = Forward_result
        predictors = Stepmodels.loc[i]["model"].model.exog_names
        predictors = [k for k in predictors if k != 'Intercept']
        Backward_result = backward(X=X, y=y, predictors=predictors)
        
        if Backward_result['AIC'] < Forward_result['AIC']:
            Stepmodels.loc[i] = Backward_result
            predictors = Stepmodels.loc[i]["model"].model.exog_names
            Smodel_before = Stepmodels.loc[i]["AIC"]
            predictors = [k for k in predictors if k != 'Intercept']
            print('backward')
            
        if Stepmodels.loc[i]['AIC'] > Smodel_before:
            break
        else:
            Smodel_before = Stepmodels.loc[i]["AIC"]
    
    toc = time.time()
    print("Total elapsed time:", (toc - tic), "seconds.")
    
    return Stepmodels['model'][len(Stepmodels['model'])]
Stepwise_best_model = Stepwise_model(X=X, y=y)

Stepwise_best_model.summary()

📍 해석

단계적 선택법을 통해 다중 선형 회귀 모델을 채택하였고, Adj R-square 값이 0.547로 모델이 전체 데이터의 54.7%를 설명할 수 있다. 설명력이 높은 모델은 아니므로 추가적인 변수를 찾아 데이터를 보완해야 할 필요성이 있다. (회귀 계수와 p-value를 통해 영향력 체크)

📍 AIC vs p-value

  • 실무에선 AIC도 중요하지만 분석가가 판단하여 모델을 선정
  • p-value가 높더라도 포함된 모델을 선택할 수 있음

댓글남기기