Friedman 검정 (반복측정)¶
Kruskal-Wallis 검정은 독립인 집단들을 비교한다. 같은 피험자를 여러 조건에서 측정하거나(반복측정) 자료가 짝지은 블록으로 수집되면 관측값이 집단 간에 더 이상 독립이 아니다. Friedman 검정은 반복측정 분산분석의 비모수 대응물로, 전체 표본이 아니라 각 블록 안에서 순위를 매겨 블록 구조를 반영한다.
가정¶
- \(b\)개의 블록(예: 피험자)과 \(k\)개의 처리(조건)가 있다.
- 각 블록 안에서 \(k\)개의 관측값에 순위를 매길 수 있다.
- 블록들이 서로 독립이다.
- 각 블록 안에서 자료가 적어도 순서형이다.
가설¶
절차¶
1단계. 각 블록 \(j = 1, \ldots, b\)에서 \(k\)개의 관측값에 1부터 \(k\)까지 순위를 매긴다. 블록 안의 동점에는 중간순위를 배정한다.
2단계. 각 처리 \(i\)의 순위합을 계산한다.
여기서 \(r_{ij}\)는 블록 \(j\)에서 처리 \(i\)의 순위이다.
3단계. Friedman 통계량을 계산한다.
동치로
로 쓸 수 있으며, 이는 \(\chi^2_F\)가 처리 순위합이 \(H_0\) 아래의 공통 기댓값에서 얼마나 벗어나는지를 재는 양임을 보여 준다.
귀무분포¶
\(H_0\) 아래에서 각 블록 안의 순위 \(\{1, 2, \ldots, k\}\)의 모든 순열이 동등하게 가능하다. \(b\)가 크면 Friedman 통계량은 근사적으로
로 분포한다. \(b\)와 \(k\)가 작으면 표나 소프트웨어로 정확 \(p\)값을 얻을 수 있다.
보기 1. \(\chi^2_F\) 가 가질 수 있는 값은 45 개뿐이다. 진통제 네 가지를 환자 5명에게 시험한다. 각 환자가 각 약에 대해 통증 완화 정도를 1--100 척도로 평가한다.
| 환자 | 약 A | 약 B | 약 C | 약 D |
|---|---|---|---|---|
| 1 | 30 | 45 | 60 | 50 |
| 2 | 25 | 40 | 55 | 35 |
| 3 | 35 | 50 | 65 | 45 |
| 4 | 20 | 30 | 50 | 40 |
| 5 | 40 | 55 | 70 | 60 |
(1) 위 절차를 밟아 \(\chi^2_F\) 와 \(p\) 값을 구하시오. 이 설계(\(b = 5\), \(k = 4\))에서 \(\chi^2_F\) 의 최댓값은 얼마이며 그때의 순위합은 무엇인가. 관측값은 도달 가능한 값들 가운데 어디에 있는가.
(2) \(k = 2\) 이면 프리드먼 검정이 부호검정의 정규근사와 똑같아진다는 것을 보이시오.
풀이
(1) 절차와 도달 가능한 값들.
1단계. 각 환자(블록) 안에서 순위를 매긴다.
| 환자 | 약 A | 약 B | 약 C | 약 D |
|---|---|---|---|---|
| 1 | 1 | 2 | 4 | 3 |
| 2 | 1 | 3 | 4 | 2 |
| 3 | 1 | 3 | 4 | 2 |
| 4 | 1 | 2 | 4 | 3 |
| 5 | 1 | 2 | 4 | 3 |
2단계. 순위합: \(R_A = 5\), \(R_B = 12\), \(R_C = 20\), \(R_D = 13\).
검산: \(5 + 12 + 20 + 13 = 50 = 5 \times 4 \times (4+1)/2\). \(\checkmark\)
3단계. \(\chi^2_F\)를 계산한다.
4단계. \(\chi^2_3\)과 비교한다: \(P(\chi^2_3 > 13.56) = 0.00357\).
import numpy as np
from scipy import stats
d = np.array([[30, 45, 60, 50], [25, 40, 55, 35], [35, 50, 65, 45],
[20, 30, 50, 40], [40, 55, 70, 60]])
print(stats.friedmanchisquare(*d.T))
# statistic=13.56, pvalue=0.0035696
출력:
FriedmanchisquareResult(statistic=13.560000000000002, pvalue=0.0035695719978437604)
\(\alpha = 0.05\)에서 \(H_0\)을 기각한다. 약들의 통증 완화 효과가 다르다는 유의한 증거가 있다. 평균순위(약 A: 1.0, 약 B: 2.4, 약 C: 4.0, 약 D: 2.6)를 보면 약 C가 가장 큰 완화를, 약 A가 가장 작은 완화를 준다.
최댓값은 \(15\) 다. \(\chi^2_F\) 가 가장 커지는 것은 다섯 블록이 완전히 한목소리를 낼 때, 곧 각 약이 모든 블록에서 같은 순위를 받을 때다. 그때 순위합은
이고
다. 관측값 \(13.56\) 은 이보다 작다 — 약 B 와 약 D 의 순서가 블록 1, 4, 5 에서는 \((2, 3)\) 이지만 블록 2, 3 에서는 \((3, 2)\) 로 뒤바뀌었기 때문이다.
도달 가능한 값은 45 개뿐이다. \((4!)^5 = 7{,}962{,}624\) 가지 배정에서 \(\chi^2_F\) 가 실제로 갖는 값을 모두 모으면 마흔다섯 개이고, 큰 쪽부터 세면
| \(\chi^2_F\) | 배정 수 | 누적 | 정확 \(p\) |
|---|---|---|---|
| 15.00 | 24 | 24 | 0.0000030 |
| 14.04 | 360 | 384 | 0.0000482 |
| 13.56 | 720 | 1104 | 0.0001386 |
| 13.08 | 600 | 1704 | 0.0002140 |
| 12.84 | 960 | 2664 | 0.0003346 |
이다. 관측값은 도달 가능한 값 가운데 세 번째로 크다. 정확 양측 p-값은
이고, 아래 상자가 말하는 \(\chi^2_3\) 근사의 \(0.00357\) 은 그 \(25.7\) 배다. 또 \(15\) 를 주는 배정이 \(24 = 4!\) 가지(네 약을 완전 순서에 어떻게 짝지을지)뿐이므로 이 설계로 낼 수 있는 가장 작은 p-값은 \(24/7962624 = 1/331776 = 3.0 \times 10^{-6}\) 이다.
(2) \(k = 2\) 이면 \(\chi^2_F = Z^2\) 이다. 각 블록에서 순위가 \(\{1, 2\}\) 뿐이다. 처리 1 이 이기는 블록 수를 \(m\) 이라 하면 그 블록에서 처리 1 이 순위 \(2\) 를, 나머지 \(b - m\) 블록에서 순위 \(1\) 을 받으므로
이다(검산: \(R_1 + R_2 = 3b = bk(k+1)/2\)). 이것을 통계량에 넣으면
가 된다. 한편 부호검정의 표준화 통계량은 \(n = b\), \(n_+ = m\), \(n_- = b - m\) 에서
이므로 \(\chi^2_F = Z^2\) 이다. \(\square\) \(\chi^2_1\) 이 표준정규의 제곱이므로 두 p-값이 같다.
다만 "같다"의 뜻을 정확히 해야 한다. \(k = 2\) 의 프리드먼 검정은 부호검정의 정규근사와 같고, 정확 이항검정과는 다르다. 프리드먼 검정은 \(\chi^2_1\) 을 쓰므로 이산성을 반영하지 못한다. \(b\) 가 작으면 binomtest 로 정확히 계산하는 편이 맞다.
이 관계는 크루스칼--월리스가 \(k = 2\) 에서 만–휘트니가 되는 것과 정확히 짝을 이룬다. 독립 자료에서 \(H = Z^2_{\text{순위합}}\) 이고, 블록 자료에서 \(\chi^2_F = Z^2_{\text{부호}}\) 다. 두 다집단 검정이 각자의 이집단 검정을 품고 있다.
수치적으로.
import itertools
from collections import Counter
from fractions import Fraction
b, k = 5, 4
perms = list(itertools.permutations(range(1, k + 1)))
# 블록을 하나씩 더해 가며 순위합 벡터의 분포를 쌓는다.
# 7962624 가지를 하나씩 돌지 않아도 같은 분포를 얻는다.
dist = Counter({(0,) * k: 1})
for _ in range(b):
nxt = Counter()
for state, c in dist.items():
for perm in perms:
nxt[tuple(s + q for s, q in zip(state, perm))] += c
dist = nxt
total = sum(dist.values())
print(f"총 배정 수 {total} (= 24^5 = {24 ** 5})")
print(f"R = (5, 10, 15, 20) 인 배정 {dist[(5, 10, 15, 20)]} 가지")
chis = Counter()
for R, c in dist.items():
chis[round(12 / (b * k * (k + 1)) * sum(x * x for x in R) - 3 * b * (k + 1), 6)] += c
top = sorted(chis)[::-1]
print(f"\nchi2_F 가 가질 수 있는 값 {len(chis)} 개, 최댓값 {top[0]}")
print(" chi2_F 배정 수 누적 정확 p")
for v in top[:5]:
cum = sum(c for w, c in chis.items() if w >= v - 1e-9)
print(f"{v:>7.2f} {chis[v]:>9} {cum:>9} {cum / total:.7f}")
obs = 13.56
cum = sum(c for w, c in chis.items() if w >= obs - 1e-9)
print(f"\n정확 p = {cum}/{total} = {Fraction(cum, total)} = {cum / total:.7f}")
print(f"chi2_3 p = {stats.chi2.sf(obs, 3):.7f}"
f" -> {stats.chi2.sf(obs, 3) / (cum / total):.1f} 배")
print(f"도달 가능한 가장 작은 p = {chis[top[0]]}/{total}"
f" = {Fraction(chis[top[0]], total)} = {chis[top[0]] / total:.2e}")
print(f"열거한 chi2_F 의 평균 {sum(v * c for v, c in chis.items()) / total}"
f" (chi2_3 의 평균 {k - 1})")
출력:
총 배정 수 7962624 (= 24^5 = 7962624)
R = (5, 10, 15, 20) 인 배정 1 가지
chi2_F 가 가질 수 있는 값 45 개, 최댓값 15.0
chi2_F 배정 수 누적 정확 p
15.00 24 24 0.0000030
14.04 360 384 0.0000482
13.56 720 1104 0.0001386
13.08 600 1704 0.0002140
12.84 960 2664 0.0003346
정확 p = 1104/7962624 = 23/165888 = 0.0001386
chi2_3 p = 0.0035696 -> 25.7 배
도달 가능한 가장 작은 p = 24/7962624 = 1/331776 = 3.01e-06
열거한 chi2_F 의 평균 3.0 (chi2_3 의 평균 3)
import numpy as np
rng = np.random.default_rng(0)
print(" b m R1 R2 chi2_F (b-2m)^2/b Z Z^2")
for _ in range(4):
b2 = 9
x = rng.normal(0, 1, b2)
y = rng.normal(0.3, 1, b2)
m = int((x > y).sum())
R1, R2 = b2 + m, 2 * b2 - m
chi_F = 12 / (b2 * 2 * 3) * (R1 ** 2 + R2 ** 2) - 3 * b2 * 3
z = (2 * m - b2) / np.sqrt(b2)
print(f"{b2:>2} {m:>3} {R1:>5} {R2:>4} {chi_F:>9.6f} {(b2 - 2 * m) ** 2 / b2:>13.6f}"
f" {z:>8.4f} {z ** 2:>10.6f}")
출력:
b m R1 R2 chi2_F (b-2m)^2/b Z Z^2
9 7 16 11 2.777778 2.777778 1.6667 2.777778
9 5 14 13 0.111111 0.111111 0.3333 0.111111
9 3 12 15 1.000000 1.000000 -1.0000 1.000000
9 3 12 15 1.000000 1.000000 -1.0000 1.000000
열거한 \(\chi^2_F\) 의 평균이 정확히 \(3.0\) 으로 \(\chi^2_3\) 의 평균과 같다 — 근사가 몸통에서는 제대로 맞춰져 있다는 뜻이다. 그런데도 관측값 자리에서 \(25.7\) 배 어긋나는 것은 \(\chi^2_F\) 의 최댓값이 15 로 유한하고 값이 마흔다섯 개뿐이기 때문이다. 손으로 구한 \((5,10,15,20)\) 과 \(\chi^2_F = 15\), 세 번째로 큰 값이 \(13.56\) 이라는 것, 정확 p-값 \(23/165888\) 이 모두 맞는다.
둘째 코드가 \(k = 2\) 의 항등식을 확인한다. 네 번 모두 \(\chi^2_F\), \((b-2m)^2/b\), \(Z^2\) 가 소수 여섯째 자리까지 같다.
\(b = 5\)에서 \(\chi^2\) 근사는 매우 보수적이다
\(b = 5\), \(k = 4\)이면 가능한 블록 내 순위 배정이 \((4!)^5 = 24^5 = 7{,}962{,}624\)가지이고, 모두 열거하여 정확 \(p\)값을 구할 수 있다. 결과는 \(p = 0.000139\)로 \(\chi^2\) 근사값 \(0.00357\)의 26분의 1이다.
import numpy as np, itertools
b, k = 5, 4
perms = list(itertools.permutations(range(1, k + 1)))
cnt = tot = 0
for combo in itertools.product(perms, repeat=b):
R = np.sum(np.array(combo), axis=0)
chi = 12 / (b * k * (k + 1)) * np.sum(R ** 2) - 3 * b * (k + 1)
tot += 1
cnt += chi >= 13.56 - 1e-9
print(cnt / tot, tot) # 0.000139 7962624
출력:
0.000138647762345679 7962624
이 자료에서 약 A가 다섯 블록 전부에서 최하위, 약 C가 전부에서 최상위였다. 이런 완벽한 일관성은 귀무가설 아래에서 극히 드물지만, \(\chi^2\) 근사는 \(b\)가 작을 때 그 희소성을 제대로 반영하지 못한다.
다만 근사가 언제나 보수적인 것은 아니다. 연습문제 1에서는 반대 방향으로 틀린다.
사후비교¶
Friedman 검정이 유의하면 쌍별 비교로 어느 처리가 다른지 밝힐 수 있다. 가장 흔한 접근은 모든 평균순위 쌍을 비교하는 Nemenyi 검정이다.
여기서 \(q_\alpha\)는 스튜던트화 범위 분포의 임계값이다. 대안으로 Bonferroni 보정을 적용한 쌍별 Wilcoxon 부호순위검정을 쓸 수도 있다.

1단계의 "각 블록 안에서 순위를 매긴다"가 왜 결정적인지 그림으로 보자. 왼쪽 (가)가 원점수이고 선 하나가 환자 한 명이다.
환자마다 선 전체가 위아래로 통째로 평행이동해 있다. 환자 5의 평균은 \(56.25\)인데 환자 4는 \(35.00\)이라 \(21\)점이나 차이가 난다. 이 기준선 차이 때문에 약별 점수 구간이 크게 겹친다. 예를 들어 환자 4가 약 C에서 받은 \(50\)점은 환자 5가 약 B에서 받은 \(55\)점보다 낮다. 관측값을 전부 한 줄로 세워 순위를 매기는 검정은 이 겹침을 그대로 뒤집어쓴다.
오른쪽 (나)가 블록 내 순위이다. 환자마다 자기 네 점수에만 \(1\)부터 \(4\)까지 매기므로 기준선이 완전히 지워진다. 그러자 다섯 환자가 한목소리를 낸다. 약 A는 다섯 번 모두 \(1\)위, 약 C는 다섯 번 모두 \(4\)위이다. 순위합은 \(R_A = 5\), \(R_B = 12\), \(R_C = 20\), \(R_D = 13\)으로 최솟값 \(5\)와 최댓값 \(20\)이라는 양 극단에 놓인다.
숫자로 확인하면 차이가 분명하다. 이 자료를 독립표본으로 착각해 Kruskal-Wallis를 돌리면 \(H = 12.24\), \(p = 0.0066\)이 나오는데, 블록 구조를 살린 Friedman 검정의 정확 \(p\)값은 \(0.000139\)로 47배 작다. 블록 내 순위 매기기가 하는 일은 반복측정 분산분석이 피험자 간 변동을 오차에서 빼내는 것과 정확히 같으며, 그 대가는 원점수의 크기 정보를 \(1\)--\(k\)의 순위로 압축하는 것뿐이다.
Kruskal-Wallis 검정과의 관계
Kruskal-Wallis 검정은 \(N\)개 관측값 전체에 전역적으로 순위를 매기는 반면, Friedman 검정은 각 블록 안에서 순위를 매긴다. 블록 내 순위 매기기는 블록 간 변동을 제거하며, 이는 반복측정 분산분석이 피험자 간 변동을 제거하는 것과 같다. 블록 구조가 존재할 때 Friedman 검정이 Kruskal-Wallis 검정보다 강력하다.
연습문제¶
연습문제 1. 평가자 4명이 커피 세 브랜드를 1--10 척도로 평가했다.
| 평가자 | 브랜드 A | 브랜드 B | 브랜드 C |
|---|---|---|---|
| 1 | 7 | 5 | 8 |
| 2 | 6 | 8 | 7 |
| 3 | 5 | 4 | 9 |
| 4 | 8 | 6 | 7 |
(a) 각 평가자(블록) 안에서 브랜드에 1부터 3까지 순위를 매겨라.
(b) 모든 평가자에 걸쳐 각 브랜드의 순위합 \(R_j\)를 계산하라.
(c) Friedman 검정통계량
을 계산하라. 여기서 \(b = 4\)(평가자), \(k = 3\)(브랜드)이다.
(d) \(H_0\) 아래에서 \(\chi_F^2 \sim \chi^2(k-1)\)이다. \(\alpha = 0.05\)에서 검정하라.
풀이
(a) 각 평가자 안에서 순위를 매기면(1 = 가장 낮음, 3 = 가장 높음)
| 평가자 | 브랜드 A | 브랜드 B | 브랜드 C |
|---|---|---|---|
| 1 | 2 | 1 | 3 |
| 2 | 1 | 3 | 2 |
| 3 | 2 | 1 | 3 |
| 4 | 3 | 1 | 2 |
(b) 순위합:
검산: \(R_A + R_B + R_C = 24 = bk(k+1)/2 = 4 \times 3 \times 4/2 = 24\).
(c)
(d) \(H_0\) 아래에서 \(\chi_F^2 \sim \chi^2(2)\)이고 \(\alpha = 0.05\)의 임계값은 \(5.991\)이다.
\(\chi_F^2 = 2.0 < 5.991\)이므로 \(H_0\)을 기각하지 못한다. 세 커피 브랜드의 평가에 통계적으로 유의한 차이가 없다. \(p\)값은 \(P(\chi^2(2) > 2.0) = 0.3679\)이다. 평가자가 4명뿐이라 중간 정도의 차이를 탐지할 검정력이 제한적이다.
정확 \(p\)값과의 비교. \((3!)^4 = 1296\)가지 배정을 모두 열거하면 정확 \(p = 0.4306\)이다.
import numpy as np, itertools
b, k = 4, 3
perms = list(itertools.permutations(range(1, k + 1)))
cnt = tot = 0
for combo in itertools.product(perms, repeat=b):
R = np.sum(np.array(combo), axis=0)
chi = 12 / (b * k * (k + 1)) * np.sum(R ** 2) - 3 * b * (k + 1)
tot += 1
cnt += chi >= 2.0 - 1e-9
print(cnt / tot, tot) # 0.4306 1296
출력:
0.4305555555555556 1296
여기서는 \(\chi^2\) 근사(\(0.368\))가 정확값(\(0.431\))보다 작다. 즉 이 영역에서는 근사가 비보수적이다. 본문 보기에서 26배 보수적이었던 것과 방향이 반대이다.
이유는 \(\chi^2_F\)의 귀무분포가 이산적이고 \(b\)가 작을 때 \(\chi^2\) 곡선과 계단이 어긋나기 때문이다. 분포의 중앙부(작은 \(\chi^2_F\))에서는 근사가 \(p\)를 과소평가하고, 극단 꼬리에서는 크게 과대평가한다.
권고: \(b < 10\)이면 정확 \(p\)값이나 몬테카를로 순열 \(p\)값을 쓴다. \(\chi^2\) 근사의 오차 방향을 미리 알 수 없기 때문이다.
연습문제 2. 같은 자료를 Friedman 검정과 Kruskal-Wallis 검정으로 각각 분석하면 어떻게 다른가? 본문의 진통제 자료로 확인하고, 왜 차이가 나는지 설명하라.
풀이
Kruskal-Wallis는 블록 구조를 무시하고 \(20\)개 관측값 전체에 순위를 매긴다.
import numpy as np
from scipy import stats
d = np.array([[30, 45, 60, 50], [25, 40, 55, 35], [35, 50, 65, 45],
[20, 30, 50, 40], [40, 55, 70, 60]])
print(stats.friedmanchisquare(*d.T)) # 13.560, p=0.003570
print(stats.kruskal(*d.T)) # 12.237, p=0.006615
print(np.corrcoef(d.T)[0]) # 블록 간 상관
출력:
FriedmanchisquareResult(statistic=13.560000000000002, pvalue=0.0035695719978437604)
KruskalResult(statistic=12.236750189825356, pvalue=0.006614630461599845)
[1. 0.98639392 1. 0.82199494]
| 검정 | 통계량 | \(p\)값 |
|---|---|---|
| Friedman (블록 반영) | \(13.56\) | \(0.00357\) |
| Kruskal-Wallis (블록 무시) | \(12.24\) | \(0.00661\) |
Friedman이 \(p\)값을 1.9배 작게 준다. 즉 블록 구조를 반영하는 편이 더 강력하다.
차이가 극적이지 않은 것은 이 자료에서 처리 효과가 환자 간 변동보다 크기 때문이다. 그래도 방향은 분명하다. 환자 4는 모든 약에 대해 낮게(20--50) 평가하고 환자 5는 높게(40--70) 평가한다. Kruskal-Wallis에서는 이 개인차가 모두 잡음으로 들어간다. 환자 5가 약 A에 준 \(40\)이 환자 4가 약 D에 준 \(40\)과 같은 순위를 받아 신호가 희석된다.
Friedman은 각 환자 안에서만 순위를 매기므로 개인차가 통째로 제거된다. 실제로 이 자료에서 각 환자의 순위 패턴이 거의 동일하다(약 A가 언제나 1위, 약 C가 언제나 4위).
이는 대응 부호검정 연습문제 4에서 본 것과 같은 원리이다. 블록 간 변동이 처리 효과에 비해 클수록 격차가 벌어진다. 그 보기에서는 개인차가 압도적이어서 \(p\)값이 11배 차이 났다.
연습문제 3. Friedman 검정은 각 블록 안에서 순위만 쓴다. 이것이 어떤 정보를 버리는가? 처리 효과의 크기가 블록마다 크게 다른 자료를 만들어 확인하라.
풀이
두 자료를 비교하자. 둘 다 각 블록에서 순위 패턴은 동일하지만 효과의 크기가 다르다.
import numpy as np
from scipy import stats
# 자료 1: 모든 블록에서 효과가 일정하다
d1 = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32],
[40, 41, 42], [50, 51, 52], [60, 61, 62]])
# 자료 2: 한 블록의 효과만 압도적으로 크다
d2 = np.array([[10, 11, 12], [20, 21, 22], [30, 31, 32],
[40, 41, 42], [50, 51, 52], [60, 160, 260]])
for name, d in (("d1", d1), ("d2", d2)):
print(name, stats.friedmanchisquare(*d.T))
출력:
d1 FriedmanchisquareResult(statistic=12.0, pvalue=0.002478752176666357)
d2 FriedmanchisquareResult(statistic=12.0, pvalue=0.002478752176666357)
두 자료 모두 \(\chi^2_F = 12.0\), \(p = 0.002479\)로 완전히 같은 결과가 나온다.
각 블록에서 세 열의 순위가 언제나 \((1, 2, 3)\)이므로 순위합이 \(R = (6, 12, 18)\)로 동일하기 때문이다. Friedman 검정은 \(10 \to 11 \to 12\)라는 미미한 차이와 \(60 \to 160 \to 260\)이라는 거대한 차이를 구별하지 못한다.
이것은 장점이자 단점이다.
- 장점: 자료가 순서형이거나 척도가 블록마다 다를 때(예: 평가자마다 점수 사용 습관이 다를 때) 이 불변성이 정확히 필요한 성질이다.
- 단점: 효과크기를 재지 못한다. Friedman 검정이 유의하다는 것은 "순서가 일관되게 다르다"는 뜻이지 "차이가 실질적으로 크다"는 뜻이 아니다.
권고: Friedman 검정 결과와 함께 반드시 원자료의 요약(블록별 프로파일 그림, 처리별 중앙값과 사분위범위)을 제시한다. \(p\)값만으로는 \(10 \to 12\)와 \(60 \to 260\)을 구별할 수 없다.
연습문제 4. 블록 안에 동점이 있으면 어떻게 하는가? 중간순위를 쓸 때 \(\chi^2_F\)의 귀무분포가 어떻게 달라지는지 설명하고, 보정된 통계량을 유도하라.
풀이
블록 \(j\) 안에서 동점이 생기면 중간순위를 배정한다. 순위의 합은 여전히 \(k(k+1)/2\)로 유지되지만, 블록 내 순위의 분산이 줄어든다.
동점이 없으면 블록 내 순위의 (모)분산은 \((k^2-1)/12\)이다. 블록 \(j\)에 크기 \(t_{j1}, \ldots, t_{jg_j}\)인 동점 집단이 있으면 분산이
로 감소한다. 이는 순위와 순위변환 연습문제 2에서 본 항등식과 같은 구조이다.
\(\chi^2_F\)는 순위합의 변동을 순위의 이론적 분산으로 표준화한 양이므로, 실제 분산이 줄었는데 이론값으로 나누면 통계량이 과소평가되어 검정이 보수적으로 된다. 보정된 통계량은
이다.
import numpy as np
from scipy import stats
# 블록마다 동점이 하나씩 있는 자료
d = np.array([[5, 5, 8], [6, 6, 9], [4, 4, 7], [3, 3, 6], [7, 7, 10]])
print(stats.friedmanchisquare(*d.T))
출력:
FriedmanchisquareResult(statistic=10.0, pvalue=0.006737946999085468)
이 자료에서 각 블록의 처음 두 열이 동점이라 순위가 \((1.5, 1.5, 3)\)이다. \(R = (7.5, 7.5, 15)\)이고
- 보정 없는 \(\chi^2_F = \frac{12}{5 \cdot 3 \cdot 4}(7.5^2 + 7.5^2 + 15^2) - 3 \cdot 5 \cdot 4 = \frac{12}{60}(337.5) - 60 = 67.5 - 60 = 7.5\)
- 보정계수 \(= 1 - \frac{5 \times (2^3-2)}{5 \times 3 \times (9-1)} = 1 - \frac{30}{120} = 0.75\)
- 보정된 \(\chi^2_F = 7.5 / 0.75 = 10.0\)
SciPy의 friedmanchisquare는 이 보정을 자동으로 적용하여 \(10.0\)을 반환한다. \(p\)값은 \(0.0067\)로 보정 없는 \(7.5\)의 \(p = 0.0235\)보다 훨씬 작다.
동점이 많으면 보정이 결정적이다. 이 예처럼 \(k = 3\)에서 블록마다 동점이 하나씩만 있어도 보정계수가 \(0.75\)로 떨어져 통계량이 33% 커진다. Likert 척도 자료에서는 흔한 상황이다.
정리하며¶
Friedman 검정은 각 블록 안에서 관측값에 순위를 매김으로써 비모수 집단비교를 반복측정 및 확률화블록 설계로 확장한다. 검정통계량 \(\chi^2_F\)는 처리 순위합의 변동을 재며 귀무가설 아래에서 근사적으로 \(\chi^2_{k-1}\) 분포를 따른다. 반복측정 분산분석의 비모수 대응물이며, 블록 안에서 자료에 의미 있게 순위를 매길 수 있으면 순서형이어도 적용할 수 있다. Nemenyi 검정 같은 사후절차가 구체적으로 어느 처리가 다른지 밝혀 준다.