iBetter Books
수정

세 집단 이상을 비교해야 할 때

t-검정은 두 집단을 비교합니다. 세 집단 이상을 비교하려면 어떻게 해야 할까요.

잘못된 방법은 t-검정을 여러 번 반복하는 것입니다. 집단이 A, B, C 세 개라면 A vs B, A vs C, B vs C 세 번의 t-검정을 수행하고 싶어집니다. 하지만 각 검정을 α = 0.05로 수행하면 적어도 하나가 우연히 유의할 확률이 훨씬 높아집니다.

세 번의 독립 검정에서 아무것도 유의하지 않을 확률은 0.95³ = 0.857입니다. 즉, 실제로 효과가 없는데도 1종 오류가 발생할 확률이 1 - 0.857 = 14.3%로 급등합니다.

일원분산분석(One-way ANOVA)은 모든 집단을 한 번에 비교하여 이 문제를 해결합니다.

ANOVA의 핵심 아이디어 — 분산 분해

ANOVA는 전체 변동을 두 부분으로 분해합니다.

SStotal=SSbetween+SSwithinSS_{total} = SS_{between} + SS_{within}

  • SS_total: 전체 데이터의 총 변동. 모든 관측치와 전체 평균의 차이
  • SS_between (집단 간 변동): 집단 평균과 전체 평균의 차이. 집단 효과로 설명되는 변동
  • SS_within (집단 내 변동): 각 관측치와 자기 집단 평균의 차이. 설명되지 않는 오차 변동
전체 변동(SST) = 집단 효과(SSB) + 개인차·오차(SSW)

집단 간 변동이 집단 내 변동보다 훨씬 크면, 집단에 따라 평균이 다를 가능성이 높습니다.

F-통계량

F=MSbetweenMSwithin=SSbetween/(k1)SSwithin/(Nk)F = \frac{MS_{between}}{MS_{within}} = \frac{SS_{between} / (k-1)}{SS_{within} / (N-k)}

여기서 k는 집단 수, N은 전체 관측치 수입니다. MS(Mean Square)는 SS를 자유도로 나눈 값입니다.

F-값이 1보다 훨씬 크면 집단 간 변동이 오차보다 크다는 뜻이므로, H0(모든 집단의 평균이 같다)를 기각합니다.

aov()로 ANOVA 실행

R 내장 데이터 PlantGrowth를 사용합니다. 세 가지 조건(ctrl: 대조군, trt1: 처리 1, trt2: 처리 2)에서 식물 건조 중량을 비교합니다.

data(PlantGrowth)
str(PlantGrowth)
tapply(PlantGrowth$weight, PlantGrowth$group, mean)
tapply(PlantGrowth$weight, PlantGrowth$group, sd)
'data.frame':	30 obs. of  2 variables:
 $ weight: num  4.17 5.58 5.18 6.11 4.50 4.61 5.17 4.53 5.33 5.14 ...
 $ group : Factor w/ 3 levels "ctrl","trt1","trt2": 1 1 1 1 1 ...

    ctrl     trt1     trt2 
5.032000 4.661000 5.526000

    ctrl     trt1     trt2 
0.5830914 0.7936757 0.4425378
# 정규성 가정 확인
by(PlantGrowth$weight, PlantGrowth$group, shapiro.test)
... p-value = 0.4542  (ctrl)
... p-value = 0.2050  (trt1)
... p-value = 0.5643  (trt2)

세 집단 모두 정규성 가정을 만족합니다.

# ANOVA 실행
aov_result <- aov(weight ~ group, data = PlantGrowth)
summary(aov_result)
            Df Sum Sq Mean Sq F value Pr(>F)  
group        2  3.766  1.8832   4.846 0.0159 *
Residuals   27 10.492  0.3886                  
---
Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

ANOVA 결과 해석

summary 테이블을 읽는 방법입니다.

항목 의미
group Df 2 집단 수 - 1 = 3 - 1 = 2
Residuals Df 27 N - k = 30 - 3 = 27
Sum Sq (group) 3.766 SS_between
Sum Sq (Residuals) 10.492 SS_within
Mean Sq (group) 1.883 MS_between = 3.766 / 2
F value 4.846 MS_between / MS_within = 1.883 / 0.389
Pr(>F) 0.0159 p-값

F(2, 27) = 4.85, p = .016으로 적어도 한 쌍의 집단 평균이 유의하게 다릅니다.

에타 제곱(η²) 효과크기

# η² 계산
ss_between <- 3.766
ss_total   <- 3.766 + 10.492
eta_sq     <- ss_between / ss_total

cat("η²:", round(eta_sq, 4), "\n")
cat("해석:", ifelse(eta_sq >= 0.14, "큰 효과",
            ifelse(eta_sq >= 0.06, "중간 효과", "작은 효과")), "\n")
η²: 0.2641 
해석: 큰 효과

η² = 0.26은 식물 중량 변동의 26%가 처리 조건에 의해 설명됨을 의미합니다. 큰 효과입니다.

등분산 가정 확인 — Levene 검정

# car 패키지의 leveneTest 사용
# install.packages("car")
library(car)

leveneTest(weight ~ group, data = PlantGrowth)
Levene's Test for Homogeneity of Variance (center = median)
      Df F value Pr(>F)
group  2  1.1192 0.3412
      27

p-값 = 0.341로 등분산 가정을 위반하지 않습니다.

등분산 가정이 위반될 때 — Welch ANOVA

등분산 가정이 충족되지 않으면 oneway.test()를 사용합니다.

# Welch ANOVA (등분산 가정 불필요)
oneway.test(weight ~ group, data = PlantGrowth, var.equal = FALSE)
	One-way analysis of means (not assuming equal variances)

data:  weight and group
F = 5.1809, num df = 2.000, denom df = 17.128, p-value = 0.01807

결과는 비슷하지만 자유도가 달라졌습니다. 등분산이 의심될 때 안전하게 쓸 수 있는 방법입니다.

시각화

library(ggplot2)

ggplot(PlantGrowth, aes(x = group, y = weight, fill = group)) +
  geom_boxplot(alpha = 0.7) +
  geom_jitter(width = 0.1, alpha = 0.6, size = 2) +
  scale_fill_manual(values = c("ctrl" = "gray70",
                                "trt1" = "steelblue",
                                "trt2" = "tomato")) +
  labs(
    title = "처리 조건별 식물 성장",
    subtitle = "One-way ANOVA: F(2, 27) = 4.85, p = .016, η² = .26",
    x = "처리 조건",
    y = "건조 중량 (g)",
    fill = "조건"
  ) +
  theme_minimal() +
  guides(fill = "none")

ANOVA 결과는 "적어도 하나의 집단이 다르다"는 것만 알려줍니다. 어느 집단 쌍이 다른지는 다음 장의 사후검정으로 알아냅니다.