이원 ANOVA에서 상호작용과 Tukey HSD를 해석하는 방법
이원 ANOVA의 주효과와 상호작용 효과를 해석하고, 제곱합 선택·가정 진단·Tukey HSD 사후검정을 실험 설계에 적용하는 방법
2026-08-14 · 최초 발행 2024-04-29
요인 효과를 한 모형에서 분리하기
이원 분산분석(Two-way ANOVA)은 두 요인(Factor)이 연속형 종속변수에 미치는 영향을 함께 평가하는 방법이다. 요인 A와 B 각각의 주효과(Main effect)뿐 아니라, A의 효과가 B의 수준에 따라 달라지는지 나타내는 A×B 상호작용 효과(Interaction)도 같은 모형에서 검정한다. 각 요인은 2수준 이상을 가정한다.
모형은 다음과 같이 표현할 수 있다.
Y = μ + A_i + B_j + (A×B)_ij + ε_ijk, ε ~ N(0, σ²)
주효과는 각 요인 수준별 평균 차이를 뜻한다. 상호작용은 한 요인의 차이가 다른 요인의 수준에 따라 일정하지 않은 경우를 가리킨다. 적용 전에는 실험 단위의 독립성, 잔차의 정규성, 집단 간 등분산성을 확인해야 한다. 집단 크기가 서로 다른 불균형 설계에서는 제곱합 Type I·II·III 선택도 해석 결과에 영향을 준다.
분석 전에 확인할 가정과 제곱합
독립성은 무작위 배정과 재현 가능한 프로토콜에서 출발한다. 시계열이나 공간적 상관이 개입할 가능성도 함께 점검한다. 정규성은 잔차 Q-Q 플롯과 Shapiro-Wilk 검정으로 확인할 수 있으며, 큰 표본에서는 정규성의 경미한 위반 영향이 경미하다. 등분산성은 Levene 또는 Brown–Forsythe 검정을 사용한다. 위반이 확인되면 Box–Cox 변환이나 견고 방법을 검토한다.
제곱합 체계는 설계와 모형 구성에 맞춰 선택한다.
- Type I은 모형에 항을 넣은 순서에 의존한다. 균형 설계 또는 위계적 모형에서 탐색 목적으로 유리하다.
- Type II는 다른 요인의 주효과를 보정한다. 상호작용이 유의하지 않거나 균형 설계일 때 주효과 추정에 권장된다.
- Type III는 상호작용을 포함한 불균형 설계에서 널리 사용한다. 효과 코딩(sum-to-zero)이 필요하며, 중심화된 평균 대비 관점에서 해석한다.
상호작용이 해석의 순서를 바꾼다
상호작용이 유의하면 주효과를 단순 평균으로 비교하는 해석은 피한다. 요인 수준별로 단순 주효과(Simple main effects) 또는 단순 대비(Simple contrasts)를 검토해 차이가 어디에서 발생하는지 추적한다.
상호작용이 유의하지 않은 경우에는 주효과를 중심으로 해석할 수 있다. 이때 Tukey HSD 같은 다중 비교를 사용해 각 수준 사이의 차이를 확인한다.
Tukey HSD의 목적은 모든 쌍대 비교에서 가족별 오류율(FWER)을 제어하는 데 있다. 주효과가 유의하면 요인 수준 간 쌍대 비교에 적용하고, 상호작용이 유의하면 결합수준(A×B) 또는 단순 주효과 안에서 비교한다. Bonferroni/Holm은 보수적이고, Scheffé는 유연하지만 보수적이며, Dunnett은 대조군 대비에 사용한다. 등분산성이 위반되고 표본수가 다를 때는 Games–Howell을 고려할 수 있다.
보고에는 부분 η²(partial eta squared) 또는 ω²(omega squared)와 신뢰구간을 포함한다. F(df1, df2) = 값, p, η²_p 또는 ω², 가정 진단 요약, 사후검정의 차이·CI·조정 p, EMMs와 상호작용 플롯을 함께 제시한다.
실험 설계에 연결하는 방식
제조 공정에서는 가열 온도(A: 저/중/고)와 촉매 유형(B: X/Y/Z)이 수율(Y)에 미치는 영향을 검정할 수 있다. 무작위 배치와 잔차 진단 뒤, 불균형 가능성이 있다면 Type III ANOVA를 적용한다. 상호작용이 유의하면 각 온도 수준에서 촉매를 비교하는 Tukey HSD를 수행해 설정값 추천에 활용한다.
교육 효과 평가에서는 교수법(A: 전통/혼합/온라인)과 학습자 유형(B: 초·중·고숙련)을 시험 점수(Y)와 연결한다. 상호작용 플롯에서 비평행성을 확인하고, 상호작용이 유의하면 숙련도별 교수법의 단순 주효과를 분석해 자원 배분 근거를 만든다.
가격전략(A: 3수준)과 채널(B: 검색/소셜/디스플레이)의 조합을 전환율(Y)로 평가하는 경우도 같은 틀을 쓴다. 등분산성이 위반되면 변환 또는 Games–Howell을 고려하고, 채널별 최적 가격 포인트를 제시한다.
진단부터 보고까지의 흐름
가정이 맞지 않을 때의 선택지
등분산성이 깨졌다면 log 또는 Box–Cox 변환을 적용할 수 있다. 분산 안정화 효과를 기대할 수 있지만 해석은 더 복잡해진다.
표본이 불균형하고 이분산성이 있는 경우 단일 요인에서는 Welch의 ANOVA를 사용한다. 이원 설계에서는 WLS 또는 헤테로스케다스틱 로버스트(White) 분산 추정을 적용할 수 있으며, 검정력은 다소 감소한다.
비정규 자료나 순서척도에는 정렬-랭크 변환(ART) ANOVA 또는 Scheirer–Ray–Hare 비모수 이원 분석을 적용할 수 있다. 다만 효과크기 해석에는 제약이 있다.
R과 Python으로 실행하기
전제: 재현 가능한 예제 데이터 df(y: numeric, A/B: factor), 유의수준 α=0.05.
R(>= 4.3) 예시: car, emmeans, effectsize
# install.packages(c("car","emmeans","effectsize"))
set.seed(1)
df <- expand.grid(A = factor(c("Low","Mid","High")),
B = factor(c("X","Y","Z")),
rep = 1:15)
df$y <- with(df, rnorm(nrow(df),
mean = as.numeric(A)*2 + as.numeric(B)*1.5 +
ifelse(A=="High" & B=="Z", 4, 0), sd = 3))
# 가정 진단
m <- lm(y ~ A * B, data = df)
par(mfrow=c(1,2)); plot(m, which=1); qqnorm(residuals(m)); qqline(residuals(m))
car::leveneTest(y ~ A * B, data = df) # 등분산성
# Type III ANOVA (상호작용 포함·불균형 대비)
op <- options(contrasts = c("contr.sum","contr.poly"))
car::Anova(m, type = 3)
# 상호작용 유의 시: 단순 주효과 및 Tukey HSD
library(emmeans)
emm <- emmeans(m, ~ A | B) # B 수준별 A의 EMM
pairs(emm, adjust = "tukey") # Tukey HSD
# 주효과만 유의 시: 각 요인별 Tukey
pairs(emmeans(m, ~ A), adjust = "tukey")
pairs(emmeans(m, ~ B), adjust = "tukey")
# 효과크기
library(effectsize)
eta_squared(car::Anova(m, type=3), partial = TRUE)
options(op) # contrasts 복원
Python(>= 3.11) 예시: statsmodels, pandas, scipy
# pip install pandas statsmodels scipy
import numpy as np, pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
from statsmodels.stats.anova import anova_lm
from statsmodels.stats.multicomp import pairwise_tukeyhsd
np.random.seed(1)
A = np.repeat(["Low","Mid","High"], 45)
B = np.tile(np.repeat(["X","Y","Z"], 15), 3)
df = pd.DataFrame({"A":A, "B":B})
df["y"] = (df["A"].map({"Low":2,"Mid":4,"High":6}).astype(float) +
df["B"].map({"X":1,"Y":2.5,"Z":4}).astype(float) +
np.where((df["A"]=="High") & (df["B"]=="Z"), 4, 0) +
np.random.normal(0,3,size=len(df)))
# 모형 적합
model = ols('y ~ C(A)*C(B)', data=df).fit()
# Type II 또는 III 지정
anova_res = anova_lm(model, typ=2) # typ=3도 가능
print(anova_res)
# 상호작용 유의 시: 결합수준에 대한 Tukey (A:B)
df["AB"] = df["A"] + ":" + df["B"]
tukey_ab = pairwise_tukeyhsd(endog=df["y"], groups=df["AB"], alpha=0.05)
print(tukey_ab)
# 주효과만 검정: A 또는 B에 대해 Tukey
tukey_A = pairwise_tukeyhsd(endog=df["y"], groups=df["A"], alpha=0.05)
tukey_B = pairwise_tukeyhsd(endog=df["y"], groups=df["B"], alpha=0.05)
print(tukey_A); print(tukey_B)
# 간단한 부분 η² 계산(주의: 근사)
# partial eta^2 = SS_effect / (SS_effect + SS_error)
ss = anova_res
ss_error = ss.loc["Residual", "sum_sq"]
for eff in ["C(A)", "C(B)", "C(A):C(B)"]:
val = ss.loc[eff, "sum_sq"] / (ss.loc[eff, "sum_sq"] + ss_error)
print(eff, "partial eta^2 =", round(val, 3))
statsmodels에서 Type III를 사용할 때 합계 제약(sum contrasts)을 엄밀히 반영하려면 Patsy의 Sum 코딩 사용을 권장한다: y ~ C(A, Sum)*C(B, Sum).
불균형 설계에서 결측이나 반복수 차이가 있으면 Type II와 Type III의 결과 해석이 달라질 수 있다.
결과 문장을 읽는 기준
상호작용이 유의한 결과는 다음처럼 보고할 수 있다.
“A×B 상호작용 유의(F(4, 360)=5.21, p<.001, η²_p=.055). B=Z에서 A의 단순 주효과 유의, High 수준이 Low·Mid 대비 높음(Tukey, 조정 p<.01).”
상호작용이 유의하지 않은 경우에는 주효과와 수준 간 차이를 중심으로 쓴다.
“A 주효과 유의(F(2, 360)=18.4, p<.001, η²_p=.093), B 주효과 유의. Tukey HSD에서 High>Mid>Low, Z>Y>X 순으로 차이 확인.”
가정 진단은 분석 결과와 분리하지 않고 함께 남긴다.
“잔차 정규성 양호(Q-Q), 등분산성 비유의(Levene p=.18). 영향점 없음(Cook’s D<0.5).”
불필요한 실험군을 축소하고 원인 요인을 식별하면 비용 1030% 절감이 가능하며, 반복 측정 최적화는 검정력 515% 향상으로 이어질 수 있다. 요인 간 상호작용을 기준으로 운영 의사결정을 정밀화하고, 보고 형식을 표준화해 커뮤니케이션 효율과 재현성을 높일 수 있다.