Data/머신러닝

공분산, 상관계수, 다중선형회귀, 잔차의 등분산성, 잔차의 정규성 (Python)

민 채 2025. 4. 9. 17:56
 
 
 

 

1. 공분산 (Covariance) 이란?

두 변수가 같은 방향으로 움직이는지를 수치로 나타낸 것

 

  • 공분산이 양수: 한 변수가 증가할 때 다른 변수도 증가
  • 공분산이 음수: 한 변수가 증가할 때 다른 변수는 감소
  • 공분산이 0에 가까움: 서로 무관
import numpy as np

# 두 변수 X, Y
X = np.array([1, 2, 3, 4, 5])
Y = np.array([2, 4, 6, 8, 10])

# 공분산 계산
cov_matrix = np.cov(X, Y)
print("공분산 행렬:\n", cov_matrix)
print("공분산 값:", cov_matrix[0, 1])

 

2. 상관계수 ( Correlation Coefficient )

공분산의 크기를 표준화한 값. 두 변수의 관계 정도를 -1에서 1 사이 값으로 표현

 

  • +1에 가까울수록: 양의 선형관계
  • -1에 가까울수록: 음의 선형관계
  • 0에 가까울수록: 선형관계 없음
# 상관계수 계산
corr_matrix = np.corrcoef(X, Y)
print("상관계수 행렬:\n", corr_matrix)

 

 

 

3. 공분산과 상관계수

두 변수의 관계를 살펴볼 때 가장 먼저 확인하는 지표가 바로 공분산상관계수입니다.

import numpy as np
import matplotlib.pyplot as plt

# 공분산 행렬 설정
mean = [3, 4]
cov = [[2**2, 3.6], [3.6, 3**2]]

# 데이터 생성
np.random.seed(42)
data = np.random.multivariate_normal(mean, cov, size=1000)
x1, x2 = data[:, 0], data[:, 1]

# 공분산, 상관계수 계산
cov_val = np.cov(x1, x2)[0, 1]
corr_val = np.corrcoef(x1, x2)[0, 1]

print(f"공분산: {cov_val:.2f}, 상관계수: {corr_val:.2f}")

📌 공분산은 두 변수의 방향성만 알려주고, 상관계수는 [-1, 1] 사이의 값으로 강도까지 알려줍니다.

시각화도 함께 해보면 더 직관적입니다.

X1과 X2는 양의 상관관계를 가지고 있습니다.

 

4. 다중선형회귀(Multiple Linear Regression)

이번에는 독립변수가 2개 이상일 때의 회귀 분석을 진행해보겠습니다. 데이터는 sklearn의 iris 데이터를 사용하겠습니다.

import pandas as pd
from sklearn.datasets import load_iris

# 1. 아이리스 데이터 불러오기
df_iris = load_iris() 

# 2. pandas DataFrame으로 변환
iris = pd.DataFrame(data=df_iris.data, columns=df_iris.feature_names)
iris.columns = ['Sepal_Length','Sepal_Width','Petal_Length','Petal_Width'] #컬럼명 변경시

# 3. 타겟(클래스) 추가
iris["species"] = df_iris.target

# 4. 클래스 라벨을 실제 이름으로 변환 (0: setosa, 1: versicolor, 2: virginica)
iris["species"] = iris["species"].map({0: "setosa", 1: "versicolor", 2: "virginica"})

 

회귀분석 해석 포인트

항목 의미
coef 기울기. 독립변수가 1 증가할 때 종속변수 평균이 얼마나 증가하는지
t H0: 계수 = 0에 대한 검정통계량
R-squared 설명력. 전체 분산 중 회귀 모델이 설명하는 비율
Adj. R-squared 설명력 보정치. 변수 수가 많을수록 불이익을 줌
F-statistic 모델 전체의 유의성을 검정. 크면 모델이 의미 있음

 

회귀계수의 벡터 표현: 선형대수 시점

회귀계수는 수학적으로 다음과 같이 구할 수 있습니다:

$$ \mathbf{B} = (X^T X)^{-1} X^T y $$

import numpy as np

# 독립변수: 상수항 포함
X = np.column_stack([np.ones(len(iris)), iris['Petal_Width'], iris['Sepal_Width']])
y = iris['Petal_Length'].values

# 정규방정식으로 회귀계수 계산
beta = np.linalg.inv(X.T @ X) @ X.T @ y
print("벡터형 계수:", beta)

# 벡터형 계수: [ 2.25816351  2.15561052 -0.35503458]

 

변수 간 상관관계 확인

import seaborn as sns

plt.figure(figsize=(8,6))
sns.heatmap(df.corr(), annot=True, cmap='coolwarm')
plt.title("Iris 변수 간 상관계수 히트맵")
plt.show()

상관계수 값 해석
1.0 완전한 양의 상관관계
0.7 ~ 0.9 강한 양의 상관관계
0.4 ~ 0.6 중간 정도 양의 상관관계
0.1 ~ 0.3 약한 양의 상관관계
0 상관관계 없음
-1.0 ~ -0.7 강한 음의 상관관계
-0.4 ~ -0.6 중간 정도 음의 상관관계
-0.1 ~ -0.3 약한 음의 상관관계

 

5. Iris 데이터 활용

여러 개의 독립변수로 하나의 종속변수를 예측하는 다중선형회귀모델을 만들어봅시다.
예: Petal_Length ~ Petal_Width + Sepal_Width

import statsmodels.formula.api as smf

model = smf.ols("Petal_Length ~ Petal_Width + Sepal_Width", # 종속변수 ~ 독립변수1 + 독립변수2
                data=iris).fit()
print(model.summary())

 

결과 출력

                            OLS Regression Results                            
==============================================================================
Dep. Variable:           Petal_Length   R-squared:                       0.934
Model:                            OLS   Adj. R-squared:                  0.933
Method:                 Least Squares   F-statistic:                     1036.
Date:                Wed, 09 Apr 2025   Prob (F-statistic):           2.24e-87
Time:                        17:40:03   Log-Likelihood:                -93.997
No. Observations:                 150   AIC:                             194.0
Df Residuals:                     147   BIC:                             203.0
Df Model:                           2                                         
Covariance Type:            nonrobust                                         
===============================================================================
                  coef    std err          t      P>|t|      [0.025      0.975]
-------------------------------------------------------------------------------
Intercept       2.2582      0.314      7.203      0.000       1.639       2.878
Petal_Width     2.1556      0.053     40.804      0.000       2.051       2.260
Sepal_Width    -0.3550      0.092     -3.843      0.000      -0.538      -0.172
==============================================================================
Omnibus:                        6.235   Durbin-Watson:                   1.600
Prob(Omnibus):                  0.044   Jarque-Bera (JB):                6.208
Skew:                           0.366   Prob(JB):                       0.0449
Kurtosis:                       3.677   Cond. No.                         30.3
==============================================================================

 

  • coef: 회귀계수 (변수가 1 단위 증가할 때 종속변수가 얼마나 변하는지)
  • p-value: 회귀계수가 통계적으로 유의한지 판단 (보통 0.05 미만이면 유의미)
  • R-squared: 모델의 설명력 (1에 가까울수록 좋음)

 

다중회귀 시각화 (독립변수 1개)

import matplotlib.pyplot as plt

x1 = iris["Petal_Width"]
y = iris["Petal_Length"]

b1 = model.params["Petal_Width"]
b0 = model.params["Intercept"]

# x축 범위에 맞춰 선형 간격
x_line = np.linspace(x1.min(), x1.max(), 100)  

# y = ax + b
y_line = b1 * x_line + b0

x_line1 = np.linspace(x1.min(), x1.max(), 100)
y_line1 = b0 + b1 * x_line1 + b2

plt.figure(figsize=(6, 4))
plt.scatter(x1, y, alpha=0.7)
plt.plot(x_line1, y_line1, color='red', label='회귀직선 (x2 평균 고정)')
plt.xlabel("꽃잎 너비")
plt.ylabel("꽃잎 길이")
plt.title("꽃잎 너비 vs 꽃잎 길이")
plt.legend()
plt.grid(True)
plt.show()

 

위 코드로 시각화 한 그래프입니다. 데이터들이 전체적으로 회귀직선을 따르고 있는 모습을 볼 수 있습니다.

 

다중회귀 평면도 시각화 (독립변수 2개)

# 다중회귀 평면도 그리기
import matplotlib.pyplot as plt
import numpy as np

# 독립변수
x1 = iris["Petal_Width"]
x2 = iris["Sepal_Width"]
y = iris["Petal_Length"]

# 회귀 계수
b0 = model.params["Intercept"]
b1 = model.params["Petal_Width"]
b2 = model.params["Sepal_Width"]

# 3D figure 생성
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')

# 산점도 찍기
ax.scatter(x1, x2, y, color='blue', alpha=0.6, label='관측값')

# 그리드 만들기
x1_grid, x2_grid = np.meshgrid(
    np.linspace(x1.min(), x1.max(), 30),
    np.linspace(x2.min(), x2.max(), 30)
)

# 회귀 평면
y_pred = b0 + b1 * x1_grid + b2 * x2_grid
ax.plot_surface(x1_grid, x2_grid, y_pred, alpha=0.3, color="red", edgecolor='none', label='회귀평면')

# 축 라벨
ax.set_xlabel("꽃잎 너비 (Petal_Width)")
ax.set_ylabel("꽃받침 너비 (Sepal_Width)")
ax.set_zlabel("꽃잎 길이 (Petal_Length)")
ax.set_title("다중 회귀 평면 시각화")

plt.tight_layout()
plt.show()

독립변수가 2개일 경우, 3차원 평면에 나타내야 합니다.

 

성능 비교: F-검정

두 모델의 설명력 차이가 통계적으로 유의한지를 검정하려면 F-검정을 사용합니다.

model1 = smf.ols('Petal_Length ~ Petal_Width + Sepal_Width', data=iris).fit()
model2 = smf.ols('Petal_Length ~ Petal_Width + Sepal_Width + Sepal_Length', data=iris).fit()

import statsmodels.api as sm
anova_table = sm.stats.anova_lm(model1, model2)
print(anova_table)

 

  • 귀무가설: 두 모델 성능 차이가 없다
  • 대립가설: 모델2가 더 좋은 설명력을 가진다
    → p-value < 0.05이면 모델2가 유의미하게 더 좋다는 뜻!

 

6. 잔차의 중요성

아무리 R²가 높더라도 모델이 좋다는 보장은 없습니다. 잔차 패턴에 이상이 없고, 정규성과 등분산성이 충족되는지도 꼭 확인해야 합니다.

잔차의 합은 0

import scipy.stats as stats

# 잔차 뽑아오기
residuals = model2.resid

# 잔차의 합은 0
sum(residuals)

 

  • 선형 회귀 모델은 잔차의 제곱합을 최소화하면서,
  • 동시에 잔차의 합이 0이 되도록 만들어진다.
  • 선형 회귀를 최소제곱법으로 푸는 과정에서, 평균을 통과하도록 되어있기 때문이다.
  • 이건 수학적으로 증명된 특성이며, 회귀분석이 가진 구조적인 결과이다.

 

잔차의 등분산성과 정규성

fitted_values = model2.fittedvalues

plt.figure(figsize=(15,4))
plt.subplot(1,2,1)
plt.scatter(fitted_values, residuals)
plt.xlabel("residuals")
plt.ylabel("fitted value");

plt.subplot(1,2,2)
stats.probplot(residuals, plot=plt);
plt.show()

왼쪽 산점도 그래프

1. 선형성(linearity) 가정 확인
잔차들이 예측값(fitted values)에 대해 특정한 패턴 없이 퍼져 있어야 합니다.

→ 즉, 랜덤하게 흩어진 모양이어야 함 (구름처럼 퍼진 형태).
만약 곡선 형태나 U자 형태로 보인다면, 선형 회귀는 적절하지 않다는 의미가 된다.
비선형 관계를 의심해야 합니다.

2. 등분산성(Homoscedasticity) 가정 확인
잔차와 예측값 사이에 특정한 패턴이 없어야 합니다.

만약 잔차가 깔때기 모양(작다가 커지는 등)을 보인다면, 분산이 일정하지 않다는 뜻 (이분산성)
이러면 회귀 결과의 신뢰성이 떨어집니다.

 

오른쪽 Q-Q Plot

잔차는 정규성을 따라야합니다. 그 이유는 다음과 같습니다.

  • 이 오차가 정규분포처럼 퍼져 있으면: → 평균 근처에 많고, 양쪽으로 갈수록 적음 → 예측 신뢰성 있음!
  • 그런데 엉뚱하게 오차가 치우쳐 있으면? → 평균이 의미 없어짐 → t검정, 신뢰구간 전부 깨짐!

그래서, 회귀분석을 할 때는 잔차가 정규성과 등분산성을 따르는지 검증해야합니다.

 

 

7. 정리

개념 설명
공분산 두 변수의 선형 관계 방향을 알려주는 값
상관계수 관계의 강도와 방향을 동시에 보여줌
다중회귀 여러 독립변수를 사용해 종속변수를 예측
F검정 모델 전체의 유의성을 평가
R², Adj. R² 모델의 설명력
정규방정식 벡터로 회귀계수를 직접 계산