아노바(ANOVA)검정이란?

|

개인공부 후 자료를 남기기 위한 목적임으로 내용 상에 오류가 있을 수 있습니다.


데이터에 따른 분류

입력(x) - 출력(y)

  • 수치 - 수치: 회귀모델
  • 수치 - 범주(이진): 로지스틱회귀모델
  • 범주 - 수치: t test, ANOVA
  • 범주 - 범주: 카이제곱검정

    모수적/비모수적

  • 모수적: 데이터가 정규분포를 따름
  • 비모수적: 정규분포에 대한 가정이 거의 없음 > 설명력이 떨어짐

ANOVA

구분 설명
목적 세 그룹 이상에서 평균 차이가 통계적으로 유의한지 검정
핵심 아이디어 그룹 간 분산(변동)과 그룹 내 분산(오차)을 비교해 F-통계량 도출
대표 예시 ① 세 가지 학습법의 시험 점수 비교
② 여러 공정 라인의 제품 수율 비교

두 그룹만 비교한다면 t-test가 적합하지만, 여러 그룹을 t-test로 반복하면 1종 오류(α)가 급격히 늘어난다 → ANOVA로 한 번에 검정!

기본 가정

  1. 독립성 : 각 관측치는 서로 독립
  2. 정규성 : 각 그룹의 데이터 분포가 정규
  3. 등분산성 : 각 그룹의 분산이 동일

가정이 심하게 깨질 때는 Kruskal-Wallis 같은 비모수 검정이나 Welch-ANOVA(등분산 가정 완화)를 고려한다.

예시

# 데이터 로딩 
ris_data = load_iris()
df = pd.DataFrame(iris_data.data, columns=iris_data.feature_names)
df['species'] = iris_data.target_names[iris_data.target]

print("데이터셋 기본 정보:")
print(f"샘플 수: {len(df)}")
print(f"변수 수: {len(df.columns)-1}")
print(f"품종: {df['species'].unique()}")
# 데이터셋 기본 정보:
# 샘플 수: 150
# 변수 수: 4
# 품종: ['setosa' 'versicolor' 'virginica']

df
# 측정 변수들의 짧은 이름 정의
measurements = ['sepal length (cm)', 'sepal width (cm)',
                'petal length (cm)', 'petal width (cm)']
short_names = ['꽃받침 길이', '꽃받침 폭', '꽃잎 길이', '꽃잎 폭']

# 2x2 서브플롯으로 박스플롯 생성
fig, axes = plt.subplots(2, 2, figsize=(15, 10))
axes = axes.ravel()

for i, (measure, name) in enumerate(zip(measurements, short_names)):
    sns.boxplot(data=df, x='species', y=measure, ax=axes[i])
    axes[i].set_title(f'{name} 품종별 분포')
    axes[i].set_xlabel('품종')
    axes[i].set_ylabel('측정값 (cm)')

plt.tight_layout()
plt.show()

독립성 검정

실제 데이터테이블을 보고 데이터들이 다른 영역에 침법하고 있는지를 확인

즉, 수집된 샘플이 다른 개체에 영향을 주면 안된다.

정규성 검정

  • 샤피로-윌크 검정(Shapiro-Wilk Test)은 주어진 데이터 샘플이 정규분포(Normal Distribution)를 따르는지 확인하기 위한 통계적 검정 방법
print("=== 정규성 검정 (Shapiro-Wilk Test) ===")
print("H0: 데이터가 정규분포를 따른다")
print("p > 0.05이면 정규성 가정 충족\n")

for measure, name in zip(measurements, short_names):
    print(f"[{name}]")
    for species in df['species'].unique():
        data = df[df['species'] == species][measure]
        statistic, p_value = stats.shapiro(data)
        result = "충족" if p_value > 0.05 else "위반"
        print(f"  {species}: p = {p_value:.4f} ({result})")
    print()
=== 정규성 검정 (Shapiro-Wilk Test) ===
H0: 데이터가 정규분포를 따른다
p > 0.05이면 정규성 가정 충족

[꽃받침 길이]
  setosa: p = 0.4595 (충족)
  versicolor: p = 0.4647 (충족)
  virginica: p = 0.2583 (충족)

[꽃받침 폭]
  setosa: p = 0.2715 (충족)
  versicolor: p = 0.3380 (충족)
  virginica: p = 0.1809 (충족)

[꽃잎 길이]
  setosa: p = 0.0548 (충족)
  versicolor: p = 0.1585 (충족)
  virginica: p = 0.1098 (충족)

[꽃잎 폭]
  setosa: p = 0.0000 (위반)
  versicolor: p = 0.0273 (위반)
  virginica: p = 0.0870 (충족)

등분산성 가정 검토

  • 레빈 검정(Levene Test)은 두 개 이상의 집단(group)들의 분산이 서로 같은지 확인하는 통계적 방법으로 이를 분산의 동질성(homogeneity of variance)이라 한다.
print("=== 등분산성 검정 (Levene Test) ===")
print("H0: 모든 집단의 분산이 같다")
print("p > 0.05이면 등분산성 가정 충족\n")

for measure, name in zip(measurements, short_names):
    # 각 품종별 데이터 분리
    setosa_data = df[df['species'] == 'setosa'][measure]
    versicolor_data = df[df['species'] == 'versicolor'][measure]
    virginica_data = df[df['species'] == 'virginica'][measure]

    # Levene 검정
    statistic, p_value = stats.levene(setosa_data, versicolor_data, virginica_data)
    result = "충족" if p_value > 0.05 else "위반"
    print(f"{name}: F = {statistic:.4f}, p = {p_value:.4f} ({result})")
=== 등분산성 검정 (Levene Test) ===
H0: 모든 집단의 분산이 같다
p > 0.05이면 등분산성 가정 충족

꽃받침 길이: F = 6.3527, p = 0.0023 (위반)
꽃받침 폭: F = 0.5902, p = 0.5555 (충족)
꽃잎 길이: F = 19.4803, p = 0.0000 (위반)
꽃잎 폭: F = 19.8924, p = 0.0000 (위반)

Anova 분석 > 변수 3개

f_stat, p_val = stats.f_oneway(setosa, versicolor, virginica)
print(f"F-통계량: {f_stat:.4f}")
print(f"p-value: {p_val}")
# F-통계량: 119.2645
# p-value: 1.6696691907693826e-31

일원분산 분석(One-way ANOVA)

import statsmodels.api as sm
from statsmodels.formula.api import ols

# 선형회귀 분석 ols
# Q("sepal length (cm)" 변수 하나를 넣고, 모델 훈련시키고
model = ols('Q("sepal length (cm)") ~ C(species)', data=df).fit()

# residual > 잔차를 계산한것, F > F 통계량 값
anova_table = sm.stats.anova_lm(model, typ=2)
anova_table

사후검정(Post-hoc test)

사후 검정은 분산분석(ANOVA)에서 “집단 간 차이가 있다”는 결과를 얻은 후, 구체적으로 어떤 집단들 사이에 유의미한 차이가 있는지 알아보기 위해 수행하는 추가 분석

from statsmodels.stats.multicomp import pairwise_tukeyhsd
turkey = pairwise_tukeyhsd(endog=df["sepal length (cm)"], groups=df["species"], alpha=0.05)
print(turkey)
   Multiple Comparison of Means - Tukey HSD, FWER=0.05   
=========================================================
  group1     group2   meandiff p-adj lower  upper  reject
---------------------------------------------------------
    setosa versicolor     0.93   0.0 0.6862 1.1738   True
    setosa  virginica    1.582   0.0 1.3382 1.8258   True
versicolor  virginica    0.652   0.0 0.4082 0.8958   True
---------------------------------------------------------
  • setosa > versicolor > virginica 순서대로 sepal length 차이가 크다
  • reject: 귀무가설 기각 여부 / true면 기각
  • 좀 더 객관적으로 파악하고 싶을때 사용