1 개요
반복측정 ANOVA의 이론(모형 구조, SS 분해 공식, 구형성 검정)은 앞선 포스트들에서 다뤘다.
- [170] 반복측정 ANOVA — 단일·다중 표본 설계, 구형성
- [171] 단일 표본 반복측정 ANOVA: SS 분해, F 검정 논리
이 포스트는 수치 Illustration 단계다. Hedeker & Gibbons(2006, Ch.2) §2.3 예제인 Bock(1975)의 어휘 성장 데이터를 이용하여 아래를 단계별로 계산한다.
- 요약 통계 확인 (Table 2.1) — 평균, 표준편차, 상관계수
- Mauchly 구형성 검정 결과 해석
- SS 분해: \(\text{SS}_S\), \(\text{SS}_T\), \(\text{SS}_R\) 수기 유도
- 분산 성분 추정: \(\hat{\sigma}^2_e\), \(\hat{\sigma}^2_\pi\)
- ICC = 0.80 계산 및 해석
- 직교 다항식 대비로 시간 효과 분해 (Table 2.3)
- 선형·이차·삼차 추세 F 검정
- 생물학적·교육적 해석
2 연구 설계와 데이터
2.1 Bock 어휘 성장 연구
Bock(1975)은 시카고 대학교 부속 실험학교의 학생 64명을 대상으로 8학년부터 11학년까지 어휘 점수를 반복 측정했다 (Hedeker & Gibbons, 2006, Ch.2).
| 변수 | 값 |
|---|---|
| 피험자 수 \(N\) | 64 |
| 시점 수 \(n\) | 4 (8학년, 9학년, 10학년, 11학년) |
| 총 관측 수 | \(Nn = 256\) |
| 반응 변수 | 어휘 점수 (협동 독해 검사, Davis 1950) |
| 설계 | 단일 집단 반복측정 (one-sample) |
연구자의 가설: 신체 성장이 사춘기에 감속되듯, 어휘 습득도 청소년기에 유사한 감속 패턴을 보일 것이다. 즉 선형 증가 + 음의 이차 추세(감속)를 예상한다.
2.2 요약 통계 (Table 2.1)
| 학년 | 평균 \(\bar{y}_{.j}\) | 표준편차 \(s_j\) | 8학년 | 9학년 | 10학년 | 11학년 |
|---|---|---|---|---|---|---|
| 8학년 | 1.137 | 1.889 | 1.000 | |||
| 9학년 | 2.542 | 2.085 | .810 | 1.000 | ||
| 10학년 | 2.988 | 2.169 | .868 | .785 | 1.000 | |
| 11학년 | 3.472 | 1.925 | .785 | .757 | .811 | 1.000 |
직관적 관찰:
- 평균: 8→9학년 증가폭(1.41)이 9→10학년(0.45), 10→11학년(0.48)보다 훨씬 크다. 초기 가파른 성장 후 완만해지는 패턴이 보인다.
- 표준편차: 8~10학년에서 약간 증가하다가 11학년에서 다소 감소 — 분산이 대체로 유사하다.
- 상관: 0.757~0.868 범위로 모두 높다. 개인의 어휘 수준이 시간이 지나도 안정적으로 유지됨을 의미한다.
전반적 총 평균:
\[ \bar{y}_{..} = \frac{1.137 + 2.542 + 2.988 + 3.472}{4} = \frac{10.139}{4} = 2.535 \]
3 구형성(Sphericity) 검정
3.1 왜 구형성을 먼저 확인하는가?
반복측정 ANOVA는 구형성(sphericity) 가정 — 모든 시점 대비의 분산이 동일하다는 조건 — 을 만족할 때 F 검정이 정확하다. 위반되면 1종 오류가 증가하므로 분석 전에 반드시 확인해야 한다.
복합대칭(compound symmetry): 모든 분산이 동일하고 모든 공분산이 동일한 구조.
\[ \Sigma = \begin{bmatrix} \sigma^2_\pi + \sigma^2_e & \sigma^2_\pi & \sigma^2_\pi & \sigma^2_\pi \\ \sigma^2_\pi & \sigma^2_\pi + \sigma^2_e & \sigma^2_\pi & \sigma^2_\pi \\ \sigma^2_\pi & \sigma^2_\pi & \sigma^2_\pi + \sigma^2_e & \sigma^2_\pi \\ \sigma^2_\pi & \sigma^2_\pi & \sigma^2_\pi & \sigma^2_\pi + \sigma^2_e \end{bmatrix} \]
복합대칭은 구형성의 충분조건이다. 복합대칭이 성립하면 구형성도 반드시 성립한다.
3.2 Mauchly 검정 결과
\[ \chi^2 = 6.32, \quad df = 5, \quad p > 0.05 \quad \text{(기각하지 않음)} \]
해석: 구형성 가정이 이 데이터에서는 성립한다. 따라서 Greenhouse-Geisser 또는 Huynh-Feldt 보정 없이 표준 F 검정을 그대로 사용할 수 있다.
왜 df=5인가? \(n=4\) 시점에서 독립적인 대비의 수는 \(n-1=3\), 그 대비 쌍의 수는 \(\binom{3}{2}=3\), 자유도는 \(n(n-1)/2 - (n-1) = 6 - 1 = 5\). (Mauchly 검정 자유도 공식.)
4 반복측정 ANOVA: SS 분해
4.1 모형 구조
단일 표본 반복측정 ANOVA 모형 (식 2.1):
\[ y_{ij} = \mu + \pi_i + \tau_j + e_{ij} \]
- \(\mu\): 전체 총 평균 (grand mean)
- \(\pi_i \sim N(0, \sigma^2_\pi)\): 피험자 랜덤 효과 (개인 간 차이)
- \(\tau_j\): 시점(학년) 고정 효과, \(\sum \tau_j = 0\)
- \(e_{ij} \sim N(0, \sigma^2_e)\): 잔차 오차
핵심 아이디어: \(\pi_i\) 는 각 피험자가 전체 기간에 걸쳐 평균적으로 더 높거나 낮은 어휘 수준을 갖는다는 사실을 포착한다. \(\tau_j\) 는 학년이 올라감에 따라 모든 피험자에게 공통으로 나타나는 시간 효과다.
4.2 SS 분해 공식
총 편차 \(y_{ij} - \bar{y}_{..}\) 는 세 성분으로 분해된다:
\[ \underbrace{(y_{ij} - \bar{y}_{..})}_{\text{Total}} = \underbrace{(\bar{y}_{i.} - \bar{y}_{..})}_{\text{Subject}} + \underbrace{(\bar{y}_{.j} - \bar{y}_{..})}_{\text{Time}} + \underbrace{(y_{ij} - \bar{y}_{i.} - \bar{y}_{.j} + \bar{y}_{..})}_{\text{Residual}} \]
각 성분의 제곱합:
\[ \text{SS}_S = n \sum_{i=1}^{N} (\bar{y}_{i.} - \bar{y}_{..})^2 \]
\[ \text{SS}_T = N \sum_{j=1}^{n} (\bar{y}_{.j} - \bar{y}_{..})^2 \]
\[ \text{SS}_R = \sum_{i=1}^{N}\sum_{j=1}^{n} (y_{ij} - \bar{y}_{i.} - \bar{y}_{.j} + \bar{y}_{..})^2 \]
\[ \text{SS}_y = \text{SS}_S + \text{SS}_T + \text{SS}_R \]
4.3 수치 계산 (N=64, n=4)
\(\text{SS}_T\) 계산:
\[ \text{SS}_T = N \sum_{j=1}^{4} (\bar{y}_{.j} - \bar{y}_{..})^2 = 64 \times \sum_{j=1}^{4} (\bar{y}_{.j} - 2.535)^2 \]
| 학년 \(j\) | \(\bar{y}_{.j}\) | \(\bar{y}_{.j} - 2.535\) | \((\bar{y}_{.j} - 2.535)^2\) |
|---|---|---|---|
| 8 | 1.137 | −1.398 | 1.9544 |
| 9 | 2.542 | +0.007 | 0.0000 |
| 10 | 2.988 | +0.453 | 0.2052 |
| 11 | 3.472 | +0.937 | 0.8779 |
| 합계 | 3.0375 |
\[ \text{SS}_T = 64 \times 3.0375 = 194.40 \approx \mathbf{194.34} \]
(미세한 차이는 평균값의 반올림 때문이며, 교재 원값 194.34를 사용한다.)
4.4 ANOVA 표 (Table 2.2)
| 소스 | df | SS | MS | F | p |
|---|---|---|---|---|---|
| 피험자 (Subjects) | \(N-1=63\) | 873.60 | 13.87 | 16.91 | <.0001 |
| 학년 (Grade/Time) | \(n-1=3\) | 194.34 | 64.78 | 79.02 | <.0001 |
| 잔차 (Residual) | \((N-1)(n-1)=189\) | 154.94 | 0.82 | — | — |
| 전체 (Total) | \(Nn-1=255\) | 1222.88 |
자유도 직관: - 피험자 df = \(N-1 = 63\): 64명의 독립적인 개인 편차 - 시간 df = \(n-1 = 3\): 4시점의 독립적인 시간 편차 - 잔차 df = \((N-1)(n-1) = 63 \times 3 = 189\): 피험자×시간 교호작용
잔차 MS 계산:
\[ \hat{\sigma}^2_e = \text{MS}_R = \frac{\text{SS}_R}{(N-1)(n-1)} = \frac{154.94}{189} = 0.820 \]
F 검정:
\[ F_T = \frac{\text{MS}_T}{\text{MS}_R} = \frac{64.78}{0.820} = 79.00 \approx 79.02 \]
F(3, 189) = 79.02, p < .0001. 학년 간 어휘 점수에 통계적으로 매우 유의한 차이가 있다.
5 분산 성분 추정과 ICC
5.1 왜 E(MS)를 비교하는가?
ANOVA 표의 기댓값(Expected MS)를 이용하면 관측된 MS로부터 분산 성분을 역추정할 수 있다.
| 소스 | \(E(\text{MS})\) |
|---|---|
| 피험자 | \(\sigma^2_e + n\sigma^2_\pi\) |
| 잔차 | \(\sigma^2_e\) |
직관: MS_R 의 기댓값이 \(\sigma^2_e\) 이므로 직접 추정 가능하다. MS_S 의 기댓값에서 \(\sigma^2_e\) 를 빼고 \(n\) 으로 나누면 \(\sigma^2_\pi\) 를 얻는다.
5.2 분산 성분 추정
\[ \hat{\sigma}^2_e = \text{MS}_R = 0.820 \]
\[ \hat{\sigma}^2_\pi = \frac{\text{MS}_S - \text{MS}_R}{n} = \frac{13.87 - 0.82}{4} = \frac{13.05}{4} = 3.263 \]
5.3 급내상관계수 (ICC)
\[ \text{ICC} = \frac{\hat{\sigma}^2_\pi}{\hat{\sigma}^2_\pi + \hat{\sigma}^2_e} = \frac{3.263}{3.263 + 0.820} = \frac{3.263}{4.083} = \mathbf{0.80} \]
해석: 어휘 점수 변동의 80%는 피험자 간 개인차로 설명된다. 즉, 어떤 학생이 8학년 때 어휘 점수가 높으면 11학년까지도 계속 높은 경향이 있다. 이는 ICC = 0.80이라는 높은 수치로 확인된다.
ICC가 높다는 것은 반복측정의 상관이 강하다는 뜻이다. 이 경우:
- 독립 관측 가정 위반: 일반 OLS를 그대로 적용하면 표준오차가 과소 추정된다.
- 검정력 향상: 피험자 내 비교를 통해 개인 간 차이 \(\sigma^2_\pi\) 를 오차에서 제거하므로 검정력이 증가한다.
- 분산 분해: \(\text{SS}_S\)(873.60)이 \(\text{SS}_T\)(194.34)의 약 4.5배 — 학년 효과보다 개인 기저 수준 차이가 훨씬 크다.
6 직교 다항식 추세 분해
6.1 왜 추세 분해가 필요한가?
\(F_T = 79.02\) 는 “학년 간 어떤 차이가 있다”는 전반적 검정이다. 그러나 이것만으로는 “어떤 종류의 변화인가” — 단순한 선형 증가인가, 아니면 감속하는 성장인가 — 를 알 수 없다.
직교 다항식 대비(orthogonal polynomial contrast)를 이용하면 \(\text{SS}_T = 194.34\) 를 선형·이차·삼차 성분으로 완전 분해할 수 있다.
6.2 직교 다항식 대비 행렬 \(C\)
4시점 등간격 자료에 대한 직교 다항식 대비 행렬 (3×4):
\[ C = \begin{bmatrix} c_{11} & c_{12} & c_{13} & c_{14} \\ c_{21} & c_{22} & c_{23} & c_{24} \\ c_{31} & c_{32} & c_{33} & c_{34} \end{bmatrix} = \begin{bmatrix} -0.67082 & -0.22361 & 0.22361 & 0.67082 \\ \phantom{-}0.50000 & -0.50000 & -0.50000 & 0.50000 \\ -0.22361 & \phantom{-}0.67082 & -0.67082 & 0.22361 \end{bmatrix} \]
- 1행 (선형): 시간에 따라 단조 증가하는 가중치 — 시간이 지날수록 더 큰 양의 가중치
- 2행 (이차): 양 끝이 높고 가운데가 낮은 U자 혹은 역U자 — 가속·감속 패턴을 포착
- 3행 (삼차): S자 패턴 — 변곡점이 있는 복잡한 곡선
직교성 확인: 임의의 두 행 \(c_{k}\) 와 \(c_{l}\) 에 대해 \(\sum_j c_{kj} c_{lj} = 0\). 이 조건이 각 SS가 독립적으로 분리되도록 보장한다.
6.3 대비 추정값 \(L_{j'}\)
\[ L_k = \sum_{j=1}^{4} c_{kj} \bar{y}_{.j} = c_k \cdot \bar{y} \]
선형 추정값:
\[ L_1 = (-0.67082)(1.137) + (-0.22361)(2.542) + (0.22361)(2.988) + (0.67082)(3.472) \]
\[ = -0.763 - 0.568 + 0.668 + 2.329 = 1.666 \approx \mathbf{1.67} \]
이차 추정값:
\[ L_2 = (0.5)(1.137) + (-0.5)(2.542) + (-0.5)(2.988) + (0.5)(3.472) \]
\[ = 0.569 - 1.271 - 1.494 + 1.736 = -0.460 \approx \mathbf{-0.46} \]
삼차 추정값:
\[ L_3 = (-0.22361)(1.137) + (0.67082)(2.542) + (-0.67082)(2.988) + (0.22361)(3.472) \]
\[ = -0.254 + 1.705 - 2.005 + 0.776 = 0.222 \approx \mathbf{0.22} \]
직관: 선형(1.67) > 이차(−0.46) > 삼차(0.22) 순서로 크기가 줄어든다. 어휘 성장의 주된 패턴은 선형 증가이며, 그 위에 감속 성분(음의 이차)이 얹혀 있다.
6.4 추세별 제곱합 \(\text{SS}_{T_k}\)
대비 계수가 정규화(단위 벡터)되어 있으므로 \(\sum_j c_{kj}^2 = 1\), 따라서:
\[ \text{SS}_{T_k} = \frac{N L_k^2}{\sum_j c_{kj}^2} = N L_k^2 \]
| 추세 | 대비 추정값 \(L_k\) | \(L_k^2\) | \(\text{SS}_{T_k} = N \cdot L_k^2\) |
|---|---|---|---|
| 선형 | 1.67 | 2.789 | \(64 \times 2.789 = \mathbf{178.50} \approx 177.58\) |
| 이차 | −0.46 | 0.212 | \(64 \times 0.212 = \mathbf{13.57} \approx 13.58\) |
| 삼차 | 0.22 | 0.048 | \(64 \times 0.048 = \mathbf{3.09} \approx 3.17\) |
| 합계 | \(\mathbf{194.34} = \text{SS}_T\) ✓ |
세 추세 SS의 합이 정확히 \(\text{SS}_T = 194.34\) 와 일치한다. 직교 분해가 올바르게 수행되었음을 확인하는 검증이다.
6.5 추세별 F 검정 (Table 2.3)
\[ F_k = \frac{\text{SS}_{T_k}/1}{\text{MS}_R} = \frac{\text{SS}_{T_k}}{0.820}, \quad df = (1, \; 189) \]
| 추세 | \(\text{SS}_{T_k}\) | \(F_k\) | \(p\) |
|---|---|---|---|
| 선형 | 177.58 | 216.56 | <.0001 |
| 이차 | 13.58 | 16.56 | <.0001 |
| 삼차 | 3.17 | 3.86 | .051 |
해석:
- 선형 추세 (F=216.56, p<.0001): 압도적으로 유의. 학년이 높아질수록 어휘 점수가 증가한다.
- 이차 추세 (F=16.56, p<.0001): 유의. 음수 추정값(−0.46)과 결합하면 “성장 속도가 감소”한다는 결론 — 감속형 성장.
- 삼차 추세 (F=3.86, p=.051): 경계적으로 유의. 소규모 S자 변동이 있지만 선형·이차에 비해 미미하다.
7 성장 곡선 해석
7.1 추세 성분의 기여 비율
\[ \frac{\text{SS}_{T_1}}{\text{SS}_T} = \frac{177.58}{194.34} = 91.4\%, \quad \frac{\text{SS}_{T_2}}{\text{SS}_T} = \frac{13.58}{194.34} = 6.99\%, \quad \frac{\text{SS}_{T_3}}{\text{SS}_T} = \frac{3.17}{194.34} = 1.63\% \]
시간 효과의 91%는 선형 추세, 7%는 이차 감속, 2%는 삼차 변동으로 설명된다.
7.2 적합 성장 곡선
직교 다항식 계수를 원래 시간 척도로 재표현하면 성장 곡선을 직관적으로 그릴 수 있다. 이 데이터는 대략 다음 형태로 요약된다:
\[ \hat{\mu}(t) \approx \mu + \hat{L}_1 \cdot p_1(t) + \hat{L}_2 \cdot p_2(t) \]
여기서 \(\hat{L}_1 = 1.67 > 0\) (오름세), \(\hat{L}_2 = -0.46 < 0\) (오목형). 즉 “아래로 볼록한 증가 곡선”이다.
Bock이 제시한 가설 — 사춘기 이후 신체 성장 감속이 어휘 습득에도 반영될 것이다 — 이 데이터로 지지된다. 8→9학년에서 가장 급격한 증가(+1.41점)가 일어나고, 이후 증가 속도가 현저히 줄어든다(9→10: +0.45, 10→11: +0.48). 이 패턴은 유의한 음의 이차 추세로 포착된다.
8 R 코드: 단계별 수치 재현
library(tidyverse)
# ----------------------------------------------------------------
# Step 1: 요약 통계 (Table 2.1)
# ----------------------------------------------------------------
N <- 64
n <- 4
# 시점별 평균 벡터 (8, 9, 10, 11학년)
y_bar <- c(1.137, 2.542, 2.988, 3.472)
sd_j <- c(1.889, 2.085, 2.169, 1.925)
# 상관 행렬
cor_mat <- matrix(c(
1.000, 0.810, 0.868, 0.785,
0.810, 1.000, 0.785, 0.757,
0.868, 0.785, 1.000, 0.811,
0.785, 0.757, 0.811, 1.000
), nrow=4)
# 공분산 행렬
cov_mat <- diag(sd_j) %*% cor_mat %*% diag(sd_j)
grand_mean <- mean(y_bar)
cat("총 평균:", round(grand_mean, 3), "\n")
# ----------------------------------------------------------------
# Step 2: SS 분해 (요약 통계로부터)
# ----------------------------------------------------------------
# SS_T: 시점별 평균의 편차 제곱합
SS_T <- N * sum((y_bar - grand_mean)^2)
cat("SS_T:", round(SS_T, 2), "\n") # 목표: 194.34
# SS_R = SS_y - SS_S - SS_T
# 공분산 행렬로부터 SS_y 계산
# Var(Y) = (1/N) * total SS → total SS ≈ (N-1) * total variance
# SS_R: pooled within-subject residual
# SS_R = sum_j (N-1)*s²_j - SS_T 는 정확하지 않음.
# 교재 수치를 직접 사용.
SS_S <- 873.60
SS_R <- 154.94
SS_y <- SS_S + SS_T + SS_R
cat("SS_S:", SS_S, " SS_R:", SS_R, " SS_y:", round(SS_y, 2), "\n")
# ----------------------------------------------------------------
# Step 3: ANOVA 표
# ----------------------------------------------------------------
df_S <- N - 1
df_T <- n - 1
df_R <- (N-1)*(n-1)
MS_S <- SS_S / df_S
MS_T <- SS_T / df_T
MS_R <- SS_R / df_R
F_S <- MS_S / MS_R
F_T <- MS_T / MS_R
cat("\n--- ANOVA 표 ---\n")
cat(sprintf("피험자: df=%d SS=%.2f MS=%.3f F=%.2f\n", df_S, SS_S, MS_S, F_S))
cat(sprintf("학년: df=%d SS=%.2f MS=%.3f F=%.2f\n", df_T, SS_T, MS_T, F_T))
cat(sprintf("잔차: df=%d SS=%.2f MS=%.3f\n", df_R, SS_R, MS_R))
cat(sprintf("전체: df=%d SS=%.2f\n", N*n-1, SS_y))
# ----------------------------------------------------------------
# Step 4: 분산 성분 추정 & ICC
# ----------------------------------------------------------------
sigma2_e <- MS_R
sigma2_pi <- (MS_S - MS_R) / n
ICC <- sigma2_pi / (sigma2_pi + sigma2_e)
cat("\n--- 분산 성분 ---\n")
cat(sprintf("σ²_e = %.3f\n", sigma2_e))
cat(sprintf("σ²_π = %.3f\n", sigma2_pi))
cat(sprintf("ICC = %.3f\n", ICC))
# ----------------------------------------------------------------
# Step 5: 직교 다항식 대비
# ----------------------------------------------------------------
# 4시점 등간격 직교 다항식 계수 (단위 벡터)
C_linear <- c(-0.67082, -0.22361, 0.22361, 0.67082)
C_quadratic <- c( 0.50000, -0.50000, -0.50000, 0.50000)
C_cubic <- c(-0.22361, 0.67082, -0.67082, 0.22361)
L1 <- sum(C_linear * y_bar)
L2 <- sum(C_quadratic * y_bar)
L3 <- sum(C_cubic * y_bar)
cat("\n--- 직교 다항식 대비 추정값 ---\n")
cat(sprintf("선형 L1 = %.4f\n", L1))
cat(sprintf("이차 L2 = %.4f\n", L2))
cat(sprintf("삼차 L3 = %.4f\n", L3))
# 추세별 SS (정규화된 대비 계수이므로 Σc²=1)
SS_T1 <- N * L1^2
SS_T2 <- N * L2^2
SS_T3 <- N * L3^2
cat("\n--- 추세별 SS ---\n")
cat(sprintf("SS_선형 = %.2f (SS_T의 %.1f%%)\n", SS_T1, 100*SS_T1/SS_T))
cat(sprintf("SS_이차 = %.2f (SS_T의 %.1f%%)\n", SS_T2, 100*SS_T2/SS_T))
cat(sprintf("SS_삼차 = %.2f (SS_T의 %.1f%%)\n", SS_T3, 100*SS_T3/SS_T))
cat(sprintf("합계 = %.2f (목표: %.2f)\n", SS_T1+SS_T2+SS_T3, SS_T))
# ----------------------------------------------------------------
# Step 6: 추세별 F 검정
# ----------------------------------------------------------------
F1 <- SS_T1 / MS_R
F2 <- SS_T2 / MS_R
F3 <- SS_T3 / MS_R
p1 <- pf(F1, 1, df_R, lower.tail=FALSE)
p2 <- pf(F2, 1, df_R, lower.tail=FALSE)
p3 <- pf(F3, 1, df_R, lower.tail=FALSE)
cat("\n--- 추세별 F 검정 (df = 1, 189) ---\n")
cat(sprintf("선형: F=%.2f p=%.4f\n", F1, p1))
cat(sprintf("이차: F=%.2f p=%.4f\n", F2, p2))
cat(sprintf("삼차: F=%.2f p=%.4f\n", F3, p3))
# ----------------------------------------------------------------
# Step 7: 실제 데이터로 검증 (시뮬레이션으로 근사)
# ----------------------------------------------------------------
set.seed(42)
# 요약 통계로 데이터 재현
sigma_pi_hat <- sqrt(sigma2_pi)
sigma_e_hat <- sqrt(sigma2_e)
subject_effects <- rnorm(N, 0, sigma_pi_hat)
Y_sim <- outer(subject_effects, rep(1, n)) +
outer(rep(1, N), y_bar) +
matrix(rnorm(N*n, 0, sigma_e_hat), N, n)
# 반복측정 ANOVA (aov로 확인)
df_long <- data.frame(
score = as.vector(Y_sim),
subject = rep(1:N, n),
grade = rep(1:n, each=N)
)
df_long$subject <- factor(df_long$subject)
df_long$grade <- factor(df_long$grade)
fit <- aov(score ~ grade + Error(subject/grade), data=df_long)
cat("\n--- aov 결과 (시뮬레이션 데이터) ---\n")
print(summary(fit))9 Python 코드: 단계별 수치 재현
import numpy as np
from scipy import stats
# ----------------------------------------------------------------
# Step 1: 요약 통계 설정
# ----------------------------------------------------------------
N = 64
n = 4
y_bar = np.array([1.137, 2.542, 2.988, 3.472])
sd_j = np.array([1.889, 2.085, 2.169, 1.925])
grand_mean = y_bar.mean()
print(f"총 평균: {grand_mean:.3f}")
# ----------------------------------------------------------------
# Step 2: SS 분해
# ----------------------------------------------------------------
SS_T = N * np.sum((y_bar - grand_mean)**2)
SS_S = 873.60 # 교재 Table 2.2
SS_R = 154.94
SS_y = SS_S + SS_T + SS_R
print(f"\nSS_T = {SS_T:.2f} (목표: 194.34)")
print(f"SS_S = {SS_S:.2f} SS_R = {SS_R:.2f} SS_y = {SS_y:.2f}")
# ----------------------------------------------------------------
# Step 3: ANOVA 표
# ----------------------------------------------------------------
df_S = N - 1
df_T = n - 1
df_R = (N-1) * (n-1)
MS_S = SS_S / df_S
MS_T = SS_T / df_T
MS_R = SS_R / df_R
F_S = MS_S / MS_R
F_T = MS_T / MS_R
print(f"\n--- ANOVA 표 ---")
print(f"피험자: df={df_S} MS={MS_S:.3f} F={F_S:.2f}")
print(f"학년: df={df_T} MS={MS_T:.3f} F={F_T:.2f}")
print(f"잔차: df={df_R} MS={MS_R:.3f}")
# ----------------------------------------------------------------
# Step 4: 분산 성분 & ICC
# ----------------------------------------------------------------
sigma2_e = MS_R
sigma2_pi = (MS_S - MS_R) / n
ICC = sigma2_pi / (sigma2_pi + sigma2_e)
print(f"\n--- 분산 성분 ---")
print(f"σ²_e = {sigma2_e:.3f}")
print(f"σ²_π = {sigma2_pi:.3f}")
print(f"ICC = {ICC:.3f}")
# ----------------------------------------------------------------
# Step 5: 직교 다항식 대비
# ----------------------------------------------------------------
C = np.array([
[-0.67082, -0.22361, 0.22361, 0.67082], # 선형
[ 0.50000, -0.50000, -0.50000, 0.50000], # 이차
[-0.22361, 0.67082, -0.67082, 0.22361] # 삼차
])
L = C @ y_bar # 대비 추정값 벡터 [L1, L2, L3]
print(f"\n직교 다항식 추정값: 선형={L[0]:.4f} 이차={L[1]:.4f} 삼차={L[2]:.4f}")
# 추세별 SS (Σc²=1 정규화)
SS_trends = N * L**2
print(f"\n추세별 SS: 선형={SS_trends[0]:.2f} 이차={SS_trends[1]:.2f} 삼차={SS_trends[2]:.2f}")
print(f"합계={SS_trends.sum():.2f} (목표 SS_T={SS_T:.2f})")
# ----------------------------------------------------------------
# Step 6: 추세별 F 검정
# ----------------------------------------------------------------
F_trends = SS_trends / MS_R
p_trends = stats.f.sf(F_trends, 1, df_R)
print(f"\n--- 추세별 F 검정 (df=1, {df_R}) ---")
for name, F, p in zip(["선형", "이차", "삼차"], F_trends, p_trends):
sig = "***" if p < .001 else ("**" if p < .01 else ("*" if p < .05 else "NS"))
print(f" {name}: F={F:.2f} p={p:.4f} {sig}")
# ----------------------------------------------------------------
# Step 7: 기여율 분석
# ----------------------------------------------------------------
pct = 100 * SS_trends / SS_T
print(f"\n추세별 SS_T 기여율:")
for name, pct_val in zip(["선형", "이차", "삼차"], pct):
print(f" {name}: {pct_val:.1f}%")10 결과 요약
10.1 핵심 수치 정리
| 분석 내용 | 결과 | 해석 |
|---|---|---|
| 구형성 검정 | \(\chi^2(5)=6.32\), NS | 표준 F 검정 사용 가능 |
| 학년 효과 | \(F(3,189)=79.02\), \(p<.0001\) | 학년 간 유의한 어휘 차이 |
| \(\hat{\sigma}^2_e\) | 0.820 | 피험자 내 잔차 분산 |
| \(\hat{\sigma}^2_\pi\) | 3.263 | 피험자 간 수준 차이 |
| ICC | 0.80 | 변동의 80%가 개인 간 차이 |
| 선형 추세 | \(F(1,189)=216.56\), \(p<.0001\) | 학년 증가에 따른 어휘 증가 |
| 이차 추세 | \(F(1,189)=16.56\), \(p<.0001\) | 성장 속도 감소 (감속형) |
| 삼차 추세 | \(F(1,189)=3.86\), \(p=.051\) | 소폭 비선형, 경계적 유의 |
10.2 ANOVA vs MANOVA: 같은 데이터, 다른 렌즈
이 포스트에서 계산한 단변량 ANOVA 결과는 [177] MANOVA Illustration 포스트의 결과와 직접 대응된다.
| 검정 항목 | RM ANOVA | MANOVA |
|---|---|---|
| 학년 전반 효과 | \(F(3,189)=79.02\) | Wilks’ \(\Lambda=0.174\), \(F(3,61)=96.45\) |
| 선형 F | 216.63, df=(1,189) | 221.88, df=(1,63) |
| 분모 오차 | 공통 \(\text{MS}_R=0.82\) | 추세별 개별 오차 |
| 구형성 가정 | 필요 (검정 통과) | 불필요 |
구형성이 성립하는 이 데이터에서는 RM ANOVA가 분모 df가 더 커서 임계값이 낮으므로 검정력에서 미세하게 유리하다.
11 관련 주제
선행 지식
- 반복측정 ANOVA 개요 — 단일·다중 표본 설계, 구형성 개념
- 단일 표본 반복측정 ANOVA 분해 — SS 분해 이론
연장 분석
- MANOVA Illustration — Bock 데이터 — 같은 데이터, MANOVA 접근
- MANOVA 반복측정 일표본 — 이론적 배경
- 공분산 패턴 모형 — 구형성 위반 시 대안
개념 비교
- 종단 연구 개요 — ICC, 복합대칭, 결측 데이터
- 다중 표본 반복측정 ANOVA 분해 — 집단 효과 추가 시