Data/통계

분산분석(ANOVA) / 수치형 데이터 비교 분석 / 카이제곱 검정과의 차이점

민 채 2025. 4. 7. 15:25
 
 
 

 

LS 빅데이터 스쿨에서 분산분석에 대해 배웠습니다.

저번 시간에는 범주형 변수 분석에 유용한 카이제곱 검정에 대해 배웠으니, 이번 시간에는 수치형 데이터를 분석에 사용하는 분산분석에 대해 알아보겠습니다.

 

분산분석(ANOVA) 개념

ANOVA (Analysis of Variance), 우리말로는 분산분석이라고 합니다.
한마디로 말하면:

“3개 이상의 그룹 간 평균 차이가 유의미한가?”
한 번의 검정으로 확인하는 통계 기법입니다.

 

왜 ANOVA를 사용하는가?

보통 평균 비교는 두 그룹이라면 t-test로 충분합니다.
하지만 세 그룹 이상이 되면?

  • 그룹 A, B, C의 평균이 같은지 확인하려면
    • A vs B
    • B vs C
    • A vs C
      총 3번의 t검정 필요
      → 유의수준이 깨져서 오류 가능성 증가

그래서 이럴 땐 ANOVA로 한 번에 판단하는 게 안전하고 효율적입니다.
결국, 평균이 같은지 확인하는 것이 분산분석입니다.

 

평균 차이를 ‘분산’의 관점에서 검정하기 때문에 이름이 ‘분산분석(ANOVA)’인 것이지 분산의 차이를 분석하는 것이 아닙니다.

 


평균 차이를 어떻게 분산으로 계산할까?

 

그룹 간 평균이 다르다면?

- 각 그룹의 평균과 전체 평균 사이에 차이(=분산)가 클 것이다.

- 집단 간 분산 / 집단 내 분산을 비교해서 분석한다.

 

$$ F = \frac{\text{집단 내 분산}}{\text{집단 간 분산}} $$
종류 설명
집단 간 분산 (Between-group variance) 그룹 평균들끼리 얼마나 다른가
집단 내 분산 (Within-group variance) 같은 그룹 안에서 얼마나 퍼져있는가
  • 집단 간 평균 차이가 크면? 분자가 커진다.
  • 집단 내 데이터가 일정하면? 분모가 작아진다.
  • 즉, 집단 간 평균 차이가 크고, 집단 내에서는 차이가 적다면 F값이 커진다.
  • F값이 커지면? 평균 차이가 유의미하다고 판단한다.

 

비유:

  • 그룹의 평균이 서로 비슷하면 → 전체 데이터는 고르게 퍼져 있어 → “분산 작음”
  • 그룹의 평균이 서로 멀면 → 전체 데이터는 그룹끼리 다르게 퍼져 있어 → “분산 큼”

👉 그래서 평균 차이를 분산 분석으로 해석하는 거라고 할 수 있습니다.

 

전체 과정 요약

1. 각 집단의 평균 계산

예: 서울, 부산, 제주의 커피 소비량 데이터가 있다고 가정하자.

seoul.mean(), busan.mean(), jeju.mean()

각 그룹의 평균을 구한다. (그룹 간 평균 차이가 존재하는 지 확인해야 함)

 

2. 전체 평균 계산

모든 데이터를 합쳐서 전체 평균을 구한다.

grand_mean = np.mean(np.concatenate([seoul, busan, jeju]))

이 전체 평균은 기준점 역할을 한다.

 

3. 각 집단 평균과 전체 평균의 차이 계산

(집단 간 분산을 계산하기 위함)

즉, 각 집단 평균이 전체 평균에서 얼마나 떨어져 있는지를 확인한다.

$$ \text{SSB (Between-group Sum of Squares)} = \sum n_i (\bar{x}_i - \bar{x})^2 $$

  • ni: 그룹 i의 샘플 수
  • ̄xi: 그룹 i의 평균
  • ̄x: 전체 평균

 

4. 각 집단의 분산 계산

(각 집단 내부에서 얼마나 퍼져있는 지를 본다.)

즉, 집단 내의 일관성을 확인한다.

$$ \text{SSW (Within-group Sum of Squares)} = \sum \sum (x_{ij} - \bar{x}_i)^2 $$

  • 각 샘플이 자기 그룹 평균과 얼마나 차이나는 지를 제곱해서 더한다.

 

5. F통계량 계산

$$ F = \frac{MSB}{MSW} = \frac{SSB / (k - 1)}{SSW / (N - k)} $$

  • MSB: 그룹 간 평균 제곱
  • MSW: 그룹 내 평균 제곱
  • k: 그룹 수
  • N: 전체 데이터 수

즉, F가 클수록 집단 간 차이가 크다.

 

6. F분포로 p-value 계산

위에서 구한 F통계량이 귀무가설 하에서 나올 확률을 F분포로부터 계산합니다.

from scipy.stats import f

# df1 = k-1 (between), df2 = N-k (within)
p_value = 1 - f.cdf(F, df1, df2)

 

7. 유의수준과 비교하여 가설 판단

if p_value < 0.05:
    print("귀무가설 기각 → 평균 차이 있음")
else:
    print("귀무가설 채택 → 평균 차이 없음")

F통계량인 빨간 선이 기각역에 속하므로 귀무가설을 기각할 수 있다.

 


ANOVA가 쓰이는 상황 예시

 

  • 지역별 하루 평균 카페 이용 시간이 같은가?
  • 전공별 수학 점수 평균이 다른가?
  • 세 가지 마케팅 전략 간 매출 성과 차이가 있는가?

 

⚠️ ANOVA의 전제 조건

조건 설명 확인 방법
독립성 각 그룹의 데이터는 서로 독립적이어야 함 실험 설계 시 고려
잔차의 정규성 데이터의 잔차(오류)가 정규분포를 따름 (정규적으로 퍼져야함) Shapiro-Wilk, Q-Q plot 등
잔차의 등분산성 모든 그룹의 잔차 분산이 동일해야 함 Levene’s test, 시각화

해당 전제 조건이 맞지 않는다면 비모수 방법을 고려해야 합니다.

우선, 해당 전제 조건이 맞는다는 것을 가정하고 분산 분석에 대해 학습해보겠습니다.

 

예제: 세 지역의 하루 평균 커피 소비량

1. 예제 데이터 생성

import numpy as np
import pandas as pd

np.random.seed(42)

# 수치형 평균과 분산을 다르게 생성
A = np.random.normal(loc=2.8, scale=0.3, size=30)
B = np.random.normal(loc=2.9, scale=0.3, size=30)
C = np.random.normal(loc=3.5, scale=0.3, size=30)

df = pd.DataFrame({
    '소비량': np.concatenate([A, B, C]),
    '지역': ['A']*30 + ['B']*30 + ['C']*30
})

 

 

2. 데이터 시각화

import seaborn as sns
import matplotlib.pyplot as plt

plt.figure(figsize=(7, 5))
sns.boxplot(x='지역', y='소비량', data=df, palette='Set2')
plt.title('지역별 하루 커피 소비량')
plt.grid(axis='y', linestyle='--', alpha=0.5)
plt.tight_layout()
plt.show()

  • 시각화 한 모습을 보면 C지역의 평균이 더 높아보이고, A와 B는 비슷해보입니다.
  • 하지만, 이러한 차이가 우연인지, 아니면 통계적으로 유의미한지 확인하기 위해 추가적으로 검정을 수행해봅시다.

 

3. 검정 수행

가설 설정

  • 귀무가설(H0): 세 그룹 간 평균 차이가 없다.
  • 대립가설(H1): 세 그룹 간 평균 차이가 있다.

 

파이썬 코드

from scipy.stats import f_oneway

f_stat, p_value = f_oneway(A, B, C)

print("F 통계량:", round(f_stat, 3))
print("p-value:", round(p_value, 3))

 

출력 결과

F 통계량: 62.782
p-value: 0.0
  • p-value 가 0.05 미만이므로 귀무가설을 기각할 수 있습니다.
  • 즉, 세 지역 간 평균 소비량에 유의미한 차이가 있습니다.
  • 하지만, 어떤 그룹 간 차이인지 구체적으로 알 수는 없습니다.
    • 이는 사후 검정을 통해 확인할 수 있습니다.
    • 사후 검정(Post-hoc)에 대해서는 다음 포스팅에서 살펴보겠습니다.
  • 또한, ANOVA 는 수치형 데이터를 살펴보는 반면, 카이제곱 검정은 범주형 데이터를 검정할 수 있습니다.

2025.04.07 - [Data] - LS 빅데이터 스쿨 / 카이제곱(Chi-Square) 검정 / 범주형 데이터 분석

 

LS 빅데이터 스쿨 / 카이제곱(Chi-Square) 검정 / 범주형 데이터 분석

카이제곱(Chi-Square) 검정통계 분석에서 우리가 다루는 데이터는 꼭 숫자만 있는 게 아닙니다.“성별과 제품 선호도는 관련이 있을까?”처럼 범주형 데이터 간의 관계를 분석하고 싶을 때가 있죠.

kiminchae.tistory.com

 

 


ANOVA 조건 체크하기

분산 분석을 수행하기 전에 꼭 확인해야할 조건이 있습니다.

조건 설명 확인 방법
독립성 각 그룹의 데이터는 서로 독립적이어야 함 실험 설계 시 고려
잔차의 정규성 데이터의 잔차(오류)가 정규분포를 따름 (정규적으로 퍼져야함) Shapiro-Wilk, Q-Q plot 등
잔차의 등분산성 모든 그룹의 잔차 분산이 동일해야 함 Levene’s test, 시각화

지금부터 잔차가 정규성과 등분산성을 만족하는 지 여부를 확인하는 방법에 대해 알아보겠습니다.

 

왜 잔차를 확인해야 하는가?

$$ Y_{ij} = \mu + \tau_i + \epsilon_{ij} $$

  • Yij: i번째 그룹의 j번째 관측값
  • μ: 전체 평균
  • τi: 그룹 효과
  • εij: 잔차 (오차) ← 이게 정규성 & 등분산성 가정의 대상

 

관측값은 평균 + 집단효과 + 오차(설명하지 못 한 부분) 로 구성되어 있습니다.

신뢰성이 있으려면 모델이 설명하지 못 한 오차의 특성이 중요합니다.

즉, 모델을 믿을 수 있는지 판단할 수 있는 핵심 기준이 됩니다.

"우리가 보고 있는 평균 차이가,
단순히 우연(=오차)에 의한 것인지,
아니면 실제로 유의미한 차이인지 알려면…
그 오차(잔차)가 제대로 된 놈인지 봐야 한다!"

 

예제: 지역별 커피 소비량

import numpy as np
import pandas as pd
from statsmodels.formula.api import ols

# 데이터 생성
np.random.seed(42)
seoul = np.random.normal(2.8, 0.3, size=30)
busan = np.random.normal(2.9, 0.3, size=30)
jeju  = np.random.normal(3.5, 0.3, size=30)

# DataFrame 만들기
df = pd.DataFrame({
    'consumption': np.concatenate([seoul, busan, jeju]),
    'region': ['Seoul'] * 30 + ['Busan'] * 30 + ['Jeju'] * 30
})

# 확인
df.head()

# 모델 적합
model = ols('consumption ~ C(region)', data=df).fit()

서울, 부산, 제주에 대한 각각 30개의 랜덤 데이터를 생성해보겠습니다.

 

1. 정규성 검정

1-1. Q-Q Plot 으로 확인해보기

import matplotlib.pyplot as plt
import statsmodels.api as sm

# 잔차 추출
residuals = model.resid

sm.qqplot(residuals, line='s')
plt.title("잔차의 Q-Q Plot (정규성 시각화)")
plt.tight_layout()
plt.show()

  • Q-Q Plot 을 통해 확인해봤습니다.
  • 대체적으로 잔차가 정규성을 따르는 모습을 볼 수 있습니다.
  • 그러면 추가적으로 통계적으로 검정해보겠습니다.

 

1-2. Shapiro-Wilk 검정으로 확인해보기

from scipy.stats import shapiro

stat, p = shapiro(residuals)
print(f'잔차 정규성 검정 (Shapiro-Wilk): p-value = {p:.4f}')

 

출력 결과:

잔차 정규성 검정 (Shapiro-Wilk): p-value = 0.8948
# p-value > 0.05 이므로 귀무가설을 기각할 수 없다.
# 즉, 잔차는 정규성을 만족한다.

p-value 가 상당히 높은 모습을 볼 수 있습니다. 귀무가설을 기각하지 못하므로 정규성을 만족한다고 할 수 있습니다.

 

참고로 Q-Q Plot과 정규성 검정에 대한 자세한 설명은 이 포스팅을 참고하시면 됩니다.

2025.04.04 - [Data] - LS 빅데이터 스쿨 / 정규성 검정 / Shapiro-Wilk / Anderson-Darling / Kolmogorov–Smirnov / Q-Q Plot

 

LS 빅데이터 스쿨 / 정규성 검정 / Shapiro-Wilk / Anderson-Darling / Kolmogorov–Smirnov / Q-Q Plot

정규성 검정(Normality Test) 이란?정규성 검정은 데이터가 정규분포를 따르는지를 검정하는 과정입니다.많은 통계 기법(Z-test, t-test 등)은 정규분포를 전제로 하기 때문에, 분석 전에 정규성을 검정

kiminchae.tistory.com

 


2. 잔차의 등분산성 검정 (Levene Test)

ANOVA(분산분석)는 각 그룹의 오차 분산이 동일하다는 가정을 전제로합니다.

즉, 우리가 만든 모델이 공정하게 그룹을 비교하려면, 잔차들의 분산이 비슷해야합니다.

 

Levene 검정 수행

# 그룹별 잔차 분리
grouped_resid = [residuals[df['region'] == g] for g in df['region'].unique()]

# Levene's Test
stat, p = levene(*grouped_resid)
print(f"Levene 검정 통계량: {stat:.4f}")
print(f"p-value: {p:.4f}")

# 결과 해석
if p > 0.05:
    print("✅ 등분산성 만족 (귀무가설 채택)")
else:
    print("❌ 등분산성 불만족 (귀무가설 기각)")

 

출력 결과:

Levene 검정 통계량: 0.1480
p-value: 0.8627
✅ 등분산성 만족 (귀무가설 채택)
  • 귀무가설(H₀): 각 그룹의 분산은 같다 (등분산이다)
  • 대립가설(H₁): 적어도 하나의 그룹 분산은 다르다 (비등분산이다)

→ p-value가 0.8627유의수준 0.05보다 훨씬 크다

즉, 귀무가설을 기각할 수 없다
→ 각 그룹(지역) 간 잔차의 분산이 통계적으로 유의미하게 다르지 않다
→ 등분산성 가정이 충분히 만족된다고 볼 수 있다

 

시각화

등분산성(Homoscedasticity)을 만족하려면:

모든 그룹(각 예측값 위치)에서 잔차가 비슷한 범위로 고르게 퍼져야 함

위에 그래프를 보면, 잔차가 대체로 ±0.4 정도로 일정하게 퍼져 있음.
시각적으로도 잔차가 등분산성을 만족한다고 볼 수 있다.

 

시각화 Python 코드

# 시각화
import matplotlib.pyplot as plt

fitted = model.fittedvalues  # 예측값
plt.scatter(fitted, residuals)
plt.axhline(0, color='red', linestyle='--')
plt.xlabel("예측값 (Fitted values)")
plt.ylabel("잔차 (Residuals)")
plt.title("잔차 vs 예측값 플롯 (등분산성 확인)")
plt.grid(True)
plt.tight_layout()
plt.show()