콘텐츠로 이동

등분산에 대한 F-검정

이 주제를 다루는 다른 곳

여기서는 분산분석의 등분산성 가정을 확인하는 용도로 짧게 다룬다. 자유도, 정규성 민감도, 검정력까지 갖춘 논의는 15.3 두 분산 비교를 위한 F 검정에 있다.

개요

등분산 F-검정은 표본분산의 비를 만들어 정규분포를 따르는 두 모집단의 분산을 비교한다. 등분산성에 대한 고전적인 이표본 검정이며 분산분석 F-검정을 비롯한 여러 절차의 바탕이 된다. 그러나 정규성 위반에 극도로 민감하여 실무에서는 대체로 Levene 검정이 선호된다. 이 페이지에서는 검정을 유도하고 처음부터 구현하며 여러 분산비 시나리오에서 그 행동을 살펴본다.

가설과 검정통계량

분산이 \(\sigma_1^2\), \(\sigma_2^2\)인 정규 모집단에서 크기 \(n_1\), \(n_2\)의 독립 표본 둘이 주어졌을 때 가설은

\[ H_0: \sigma_1^2 = \sigma_2^2, \qquad H_1: \sigma_1^2 \neq \sigma_2^2 \]

이다. 검정통계량은 표본분산의 비이다:

\[ F = \frac{S_1^2}{S_2^2} \]

\(H_0\) 아래에서 이 비는 F-분포를 따른다:

\[ F \sim F(n_1 - 1,\; n_2 - 1) \]

유의수준 \(\alpha\)의 양측검정에서는

\[ F < F_{\alpha/2}(n_1 - 1,\, n_2 - 1) \quad \text{or} \quad F > F_{1-\alpha/2}(n_1 - 1,\, n_2 - 1) \]

일 때 \(H_0\)을 기각한다. 양측 p-값은

\[ p = 2\min\!\bigl(P(F_{n_1-1,\, n_2-1} \le F_{\text{obs}}),\; P(F_{n_1-1,\, n_2-1} \ge F_{\text{obs}})\bigr) \]

이다.

카이제곱 분포와의 연결

F-분포는 각각 자유도로 나눈 독립인 두 카이제곱 확률변수의 비로 나타난다. 정규 자료에서 \((n_i - 1)S_i^2 / \sigma_i^2 \sim \chi^2(n_i - 1)\)이므로 \(H_0: \sigma_1^2 = \sigma_2^2\) 아래에서

\[ F = \frac{S_1^2}{S_2^2} = \frac{\chi^2(n_1 - 1) / (n_1 - 1)}{\chi^2(n_2 - 1) / (n_2 - 1)} \]

이며, 이것이 \(F(n_1 - 1, n_2 - 1)\) 분포의 정의 형태이다.

다음 함수는 양측 F-검정을 구현한다. 어느 표본을 분자에 두느냐는 분석자의 임의 선택이므로, 그 선택이 결론을 바꾸지 않아야 한다는 점을 먼저 확인해 둘 필요가 있다.

보기 1. 등분산 F 검정 구현. 두 표본의 순서를 바꾸면 통계량은 \(F \mapsto 1/F\)로, 자유도는 \((d_1, d_2) \mapsto (d_2, d_1)\)로 바뀐다.

(1) 그래도 양측 p-값 \(p = 2\min(G_{d_1,d_2}(F),\, 1 - G_{d_1,d_2}(F))\)는 바뀌지 않음을 보이시오. 임계값에 대해서는 \(F_{1-\alpha/2}(d_1, d_2) = 1/F_{\alpha/2}(d_2, d_1)\)이 성립함도 보이시오.

(2) 크기가 서로 다른 두 표본(\(n = 20\)과 \(n = 35\))에 함수를 두 순서로 적용해 (1)을 확인하시오.

풀이

(1) 해석적으로. 바탕이 되는 사실 하나로 모두 따라온다. \(G \sim F(d_2, d_1)\)이면

\[ \frac{1}{G} \sim F(d_1, d_2) \]

이다. \(F\)-분포가 두 카이제곱의 비 \(\dfrac{\chi^2_{d_2}/d_2}{\chi^2_{d_1}/d_1}\)로 정의되므로 역수를 취하면 분자와 분모가 맞바뀌는 것뿐이다.

이제 관측된 비를 \(f = s_1^2/s_2^2\)라 하고 순서를 바꾼 쪽의 분포함수를 계산한다. \(G \sim F(d_2, d_1)\)에 대해

\[ P\!\left(G \le \frac{1}{f}\right) = P\!\left(\frac{1}{G} \ge f\right) = P\bigl(F_{d_1, d_2} \ge f\bigr) = 1 - G_{d_1,d_2}(f) \]

이고 같은 계산으로 \(P(G \ge 1/f) = G_{d_1,d_2}(f)\)다. 곧 두 꼬리 확률이 서로 자리를 맞바꿀 뿐이다. \(\min\)은 두 인수의 순서에 영향받지 않으므로

\[ 2\min\bigl(G_{d_2,d_1}(1/f),\, 1 - G_{d_2,d_1}(1/f)\bigr) = 2\min\bigl(1 - G_{d_1,d_2}(f),\, G_{d_1,d_2}(f)\bigr) = p \]

이다. 양측 p-값은 표본의 순서에 의존하지 않는다.

임계값 관계도 같은 사실에서 나온다. \(G \sim F(d_2, d_1)\)일 때

\[ \alpha/2 = P\bigl(G \le F_{\alpha/2}(d_2,d_1)\bigr) = P\!\left(F_{d_1,d_2} \ge \frac{1}{F_{\alpha/2}(d_2,d_1)}\right) \]

이므로 괄호 안의 값이 바로 \(F_{d_1,d_2}\)의 상위 \(\alpha/2\) 분위수다. 곧

\[ F_{1-\alpha/2}(d_1, d_2) = \frac{1}{F_{\alpha/2}(d_2, d_1)} \]

특히 \(d_1 = d_2 = d\)이면 \(F\)와 \(1/F\)가 같은 분포를 가지므로 \(P(F \le 1) = P(F \ge 1) = 1/2\), 곧 \(F(d,d)\)의 중앙값은 정확히 \(1\)이다. 아래 보기 2에서 \(\sigma_Y = 1\)일 때 p-값이 정확히 \(1.000\)으로 찍히는 까닭이 이것이다.

(2) 수치적으로. 자유도가 실제로 뒤바뀌도록 두 표본의 크기를 다르게 둔다.

import numpy as np
import scipy.stats as stats

def f_test(data_0, data_1):
    """두 분산이 같은지 F 검정한다.

    두 표본분산의 비가 F 분포를 따른다는 사실을 쓴다. 카이제곱과 마찬가지로
    좌우가 대칭이 아니므로 작은 쪽 꼬리를 두 배 해 양측 p-값을 만든다.
    이 검정은 정규성에 매우 민감하다 — 그래서 실제로는 Levene 을 더 쓴다.
    """
    statistic = data_0.var(ddof=1) / data_1.var(ddof=1)
    df1 = data_0.shape[0] - 1
    df2 = data_1.shape[0] - 1
    p_value = 2 * min(
        stats.f(df1, df2).cdf(statistic),
        stats.f(df1, df2).sf(statistic)
    )
    return statistic, p_value

# 표본 크기를 일부러 다르게 두어 자유도가 뒤바뀌는 것까지 보이게 한다.
rng = np.random.default_rng(7)
a = rng.normal(scale=1.0, size=20)
b = rng.normal(scale=1.7, size=35)

Fab, pab = f_test(a, b)
Fba, pba = f_test(b, a)
print(f"f_test(a, b):  F = {Fab:.6f}   p = {pab:.6f}   (자유도 19, 34)")
print(f"f_test(b, a):  F = {Fba:.6f}   p = {pba:.6f}   (자유도 34, 19)")
print(f"F 는 역수인가 ?      {np.isclose(Fab * Fba, 1.0)}")
print(f"p 는 같은가 ?        {np.isclose(pab, pba)}")

# 임계값의 역수 관계. F_{1-a/2}(d1,d2) = 1 / F_{a/2}(d2,d1).
d1, d2 = 19, 34
hi = stats.f(d1, d2).ppf(0.975)
lo_swapped = stats.f(d2, d1).ppf(0.025)
print(f"\nF_0.975(19,34)        = {hi:.6f}")
print(f"1 / F_0.025(34,19)    = {1 / lo_swapped:.6f}")

# 양측 p-값이 1 을 넘지 않는 까닭은 d1 = d2 일 때 가장 또렷하다.
# F(d,d) 는 역수에 대해 대칭이므로 중앙값이 정확히 1 이다.
print(f"\nP(F(99,99) <= 1)      = {stats.f(99, 99).cdf(1):.6f}")

출력:

f_test(a, b):  F = 0.237062   p = 0.001581   (자유도 19, 34)
f_test(b, a):  F = 4.218313   p = 0.001581   (자유도 34, 19)
F 는 역수인가 ?      True
p 는 같은가 ?        True

F_0.975(19,34)        = 2.153050
1 / F_0.025(34,19)    = 2.153050

P(F(99,99) <= 1)      = 0.500000

세 가지가 모두 맞는다. 통계량은 \(0.237062\)와 \(4.218313\)으로 곱이 \(1\)이고, p-값은 \(0.001581\)로 소수점 여섯째 자리까지 같다. 임계값은 \(F_{0.975}(19,34) = 2.153050\)이 \(1/F_{0.025}(34,19)\)와 자리까지 일치하며, \(F(99,99)\)의 중앙값이 \(1\)임도 \(P(F \le 1) = 0.500000\)으로 확인된다.

실무적으로는 이것이 안심거리다. "큰 분산을 분자에 두라"는 흔한 지침은 양측검정에서는 필요하지 않다. 그 지침이 뜻을 갖는 것은 한쪽 꼬리만 보는 단측검정이나, 상위 분위수표만 실린 책에서 손으로 임계값을 찾을 때다. 다만 구현을 바꿔 p = 2 * sf(statistic)처럼 한쪽 꼬리만 두 배 하면 이 대칭이 깨져 순서에 따라 결론이 달라진다. 위 함수가 min을 쓰는 이유다.

보기는 \(X \sim N(0, 1)\)과 여러 \(\sigma_Y\) 값에 대한 \(Y \sim N(1, \sigma_Y)\)을 생성한다. 씨앗을 고정하므로 두 표본이 같은 난수열에서 나온다는 점을 눈여겨보라.

보기 2. 검정 실행. 집단당 \(n = 100\), 씨앗 seed = 1을 고정하고 \(\sigma_Y\)만 바꾼다.

(1) 씨앗이 고정되어 있을 때 \(F = S_X^2/S_Y^2\)가 자료에 전혀 의존하지 않고 \(1/\sigma_Y^2\)으로 정확히 정해짐을 보이시오. 또 이 설정에서 \(p = 0.05\)가 되는 \(\sigma_Y\)를 구하시오.

(2) 검정을 실행해 (1)의 두 답을 확인하고, \(\sigma_Y = 1.20\)에서 왜 \(\alpha = 0.05\)에 닿지 못하는지 설명하시오.

풀이

(1) 해석적으로. 같은 씨앗에서 norm(loc, scale).rvs 는 같은 표준정규열 \(z_1, \ldots, z_n\)을 꺼내 \(\text{loc} + \text{scale}\cdot z_i\)로 바꾼다. 그러므로 \(x_i = z_i\)이고 \(y_i = 1 + \sigma_Y z_i\)이며, 표본분산은 위치이동에 둔감하고 배율의 제곱만큼 커진다.

\[ s_X^2 = s_z^2, \qquad s_Y^2 = \sigma_Y^2 s_z^2 \quad\Longrightarrow\quad F = \frac{s_X^2}{s_Y^2} = \frac{s_z^2}{\sigma_Y^2 s_z^2} = \frac{1}{\sigma_Y^2} \]

\(s_z^2\)이 약분되어 사라진다. 자료가 무엇이든 \(F\)는 \(1/\sigma_Y^2\) 하나로 정해지며, 표집 변동이 전혀 들어오지 않는다. 그래서 이 표는 검정력의 표가 아니라 분산비 하나를 F-분포에 비추어 본 표다.

\(p = 0.05\)가 되는 \(\sigma_Y\)를 구해 보자. \(F\)가 아래쪽으로 밀려가므로 걸리는 임계값은 하위 \(2.5\%\) 분위수다. \(F = F_{0.025}(99,99)\)에서

\[ \frac{1}{\sigma_Y^{*2}} = F_{0.025}(99, 99) = 0.67284 \quad\Longrightarrow\quad \sigma_Y^* = \frac{1}{\sqrt{0.67284}} = 1.2191 \]

이다. \(\sigma_Y\)가 \(1.2191\)을 넘어야 기각된다. 격자의 마지막 값 \(1.20\)은 그보다 작으므로 기각되지 않으며, 이것이 \(p = 0.071\)의 정체다.

(2) 수치적으로. 다섯 \(\sigma_Y\)에 대해 돌린다.

# Bartlett 검정과 같은 설정으로 돌려 두 검정의 p-값을 견주어 볼 수 있다.
seed, size = 1, 100
x = stats.norm(loc=0, scale=1).rvs(size, random_state=seed)

for scale in [1.00, 1.05, 1.10, 1.15, 1.20]:
    y = stats.norm(loc=1, scale=scale).rvs(size, random_state=seed)
    stat, pval = f_test(x, y)
    print(f"sigma_y={scale:.2f}: F={stat:.2f}, p={pval:.3f}")

출력:

sigma_y=1.00: F=1.00, p=1.000
sigma_y=1.05: F=0.91, p=0.628
sigma_y=1.10: F=0.83, p=0.345
sigma_y=1.15: F=0.76, p=0.166
sigma_y=1.20: F=0.69, p=0.071

\(F = 1/\sigma_Y^2\)이 다섯 자리에서 모두 맞는다. \(1/1.05^2 = 0.9070\), \(1/1.10^2 = 0.8264\), \(1/1.15^2 = 0.7561\), \(1/1.20^2 = 0.6944\)가 출력의 \(0.91,\ 0.83,\ 0.76,\ 0.69\)와 일치한다. 근사가 아니라 등식이다.

\(\sigma_Y = 1.00\)에서 p-값이 정확히 \(1.000\)인 것도 (1)의 부산물이다. \(F = 1\)이고 보기 1에서 본 대로 \(F(99,99)\)의 중앙값이 \(1\)이므로 \(p = 2 \times 0.5 = 1\)이다. p-값이 꼭 \(1\)로 찍히는 일은 여기서처럼 통계량이 귀무분포의 중앙값에 정확히 앉을 때만 일어난다.

\(\sigma_Y = 1.20\)이 \(\alpha = 0.05\)에 닿지 못하는 까닭은 (1)에서 계산한 \(\sigma_Y^* = 1.2191\)과 비교하면 바로 보인다. \(F = 0.6944\)가 임계값 \(0.67284\)보다 아직 크다. 간발의 차이로 보이지만 그 간격이 곧 \(p = 0.071\)과 \(p = 0.05\)의 차이다. \(F(99,99)\)가 \(F = 1\) 부근에 두툼하게 몰려 있으므로, 분산비 \(1.44\)쯤은 귀무가설과 구별되지 않는다.

주의할 점이 하나 있다. \(\sigma_Y^* = 1.2191\)은 이 설정에서만 성립하는 경계다. 씨앗을 바꾸면 두 표본이 더 이상 같은 난수열이 아니고, \(F\)에 표집 변동이 끼어들어 결론이 표본마다 달라진다. 아래 연습문제의 검정력 식

\[ \text{검정력} = P\!\left(F_{d,d} < \frac{F_{\alpha/2}(d,d)}{r}\right) + P\!\left(F_{d,d} > \frac{F_{1-\alpha/2}(d,d)}{r}\right), \qquad r = \frac{\sigma_X^2}{\sigma_Y^2} \]

에 \(d = 99\), \(r = 1/1.44\)를 넣으면 \(\sigma_Y = 1.20\)에서의 실제 검정력은 \(0.438\)이다. 다섯 줄의 표가 보여 준 "기각하지 못함"은 열 번 중 여섯 번 일어나는 일이지, 이 검정이 늘 못 잡는다는 뜻은 아니다.

p-값이 앞의 Bartlett 검정과 소수점 셋째 자리까지 같다. 우연이 아니다. 집단이 둘이고 자료가 정규일 때 Bartlett 검정은 등분산 \(F\)-검정과 동등하다.

표로 정리하면:

\(\sigma_Y\) \(F = S_X^2/S_Y^2\) p-값
1.00 1.000 1.000
1.05 0.907 0.628
1.10 0.826 0.345
1.15 0.756 0.166
1.20 0.694 0.071

해석

  • \(\sigma_Y = 1.00\)이면 참 분산비가 1이고 F-통계량도 1에 가까워 p-값이 크다. 검정이 \(H_0\)을 올바르게 유지한다.
  • \(\sigma_Y\)가 커질수록 \(S_Y^2\)이 \(S_X^2\)에 비해 커져 F-통계량이 1에서 멀어지고 p-값이 작아진다.
  • 검정력은 표본크기에 달려 있다. 집단당 \(n = 100\)에서는 완만한 이탈(예: \(\sigma_Y = 1.15\), \(p = 0.166\))을 탐지하지 못하고, \(\sigma_Y = 1.20\)에서도 \(p = 0.071\)로 \(\alpha = 0.05\)에 미치지 못한다. 표본이 커야 안정적으로 탐지할 수 있다.

이 검정의 결정적인 한계는 비정규성에 극도로 민감하다는 점이다. 정규성에서 약간만 벗어나도(예: 완만한 치우침이나 이상점 몇 개) 제1종 오류율이 크게 부풀 수 있다. 실제 자료에는 Levene 검정을 권한다.

두 주장 — 검정력이 낮다는 것과 정규성에 민감하다는 것 — 을 각각 그림으로 확인해 두자.

다섯 통계량은 채택역 한가운데 있고, 비정규 자료에서는 오류율이 0.52 까지 간다

왼쪽은 귀무분포 \(F(99, 99)\) 위에 본문 표의 다섯 통계량을 얹은 것이다. 양측 \(5\%\)의 임계값이 \(0.673\)과 \(1.486\)이다. \(\sigma_Y\)가 커질수록 \(F = S_X^2/S_Y^2\)이 \(1.000 \to 0.907 \to 0.826 \to 0.756 \to 0.694\)로 왼쪽으로 밀려가지만, 마지막 값 \(0.694\)조차 임계값 \(0.673\)에 닿지 못한다. 간발의 차이로 보이지만 그 간격이 곧 \(p = 0.071\)과 \(p = 0.05\)의 차이다. 분포가 \(F = 1\) 부근에 이렇게 두툼하게 몰려 있으니, 분산비 \(1.44\)쯤은 귀무가설과 구별되지 않는다.

오른쪽이 이 검정을 실무에서 쓰지 않는 진짜 이유다. 두 집단의 모분산이 정확히 같은 자료를 각 \(n = 30\)으로 8,000번 만들어 검정했다. 정규분포에서는 \(0.050\)으로 정확하다. 그런데 \(t_5\)에서 \(0.174\), 지수분포에서 \(0.285\), 대수정규에서 \(0.524\)가 된다. 분산이 한 치도 다르지 않은데 절반 넘게 기각한다. 반대로 꼬리가 얇은 균등분포에서는 \(0.004\)로 검정이 멈춰 선다. 같은 자료에 레빈 검정을 적용하면 다섯 분포 모두 \(0.035\)–\(0.051\)이다.

"약간만 벗어나도"라는 표현이 과장이 아니다. \(t_5\)는 정규분포와 눈으로 구별하기 어려운 분포인데도 오류율이 세 배 넘게 부푼다. 원인은 \(\operatorname{Var}(S^2)\)가 첨도에 직접 의존한다는 데 있다. \(F\)-분포의 꼬리는 \(\gamma_2 = 0\)을 전제로 계산된 것이고, 꼬리가 두꺼운 자료에서는 \(S_X^2/S_Y^2\)가 그보다 훨씬 크게 요동친다. 검정은 그 요동을 "분산이 다르다"는 신호로 읽는다.

집단이 둘이고 자료가 정규일 때 이 검정이 Bartlett 검정과 동등하다는 사실을 떠올리면, 바틀렛이 대수정규에서 \(0.673\)까지 갔던 것과 여기 \(0.524\)가 같은 현상의 두 얼굴임을 알 수 있다(집단 수와 표본크기가 달라 값이 정확히 일치하지는 않는다).

연습문제

연습문제 1. 크기가 \(n_1 = 20\), \(n_2 = 25\)인 두 표본에서 표본분산 \(S_1^2 = 15.3\), \(S_2^2 = 8.7\)을 얻었다. F-통계량을 계산하고 자유도를 진술하라.

풀이

F-통계량은

\[ F = \frac{S_1^2}{S_2^2} = \frac{15.3}{8.7} \approx 1.759 \]

이다. 자유도는 \(df_1 = n_1 - 1 = 19\), \(df_2 = n_2 - 1 = 24\)이다. \(H_0\) 아래에서 \(F \sim F(19, 24)\)이다.

연습문제 2. \(F \sim F(d_1, d_2)\)이면 \(1/F \sim F(d_2, d_1)\)임을 보여라. 이 성질이 양측검정에서 왜 중요한가?

풀이

정의에 의해 \(U \sim \chi^2(d_1)\)과 \(V \sim \chi^2(d_2)\)가 독립이면

\[ F = \frac{U/d_1}{V/d_2} \sim F(d_1, d_2) \]

이다. 역수를 취하면

\[ \frac{1}{F} = \frac{V/d_2}{U/d_1} \sim F(d_2, d_1) \]

이다. 이 성질이 중요한 이유는 큰 쪽 표본분산을 분자에 두어 언제나 \(F \ge 1\)이 되도록 배치할 수 있기 때문이다. 그러면 양측 p-값을 \(2 \cdot P(F_{d_1, d_2} \ge F_{\text{obs}})\)로 계산할 수 있다. 한편 위 구현의 \(\min\) 형태는 이런 규약 없이도 양쪽 꼬리를 직접 처리한다.

연습문제 3. 등분산 F-검정이 평균에 대한 이표본 \(t\)-검정보다 비정규성에 더 민감한 이유를 설명하라.

풀이

\(t\)-검정은 표본평균에 기반하는데, 적률이 존재하기만 하면 바탕 분포와 무관하게 중심극한정리에 의해 표본평균이 정규로 수렴한다. 반면 분산에 대한 F-검정은 표본분산에 기반하고 표본분산은 자료의 4차 적률과 관련된다. \(S^2\)의 표본분포는 바탕 분포의 첨도에 강하게 영향을 받는다. 꼬리가 두꺼운 분포에서는 이따금 나오는 극단값이 \(S^2\)을 크게 부풀려 비 \(S_1^2/S_2^2\)이 F-분포에서 크게 벗어난다. \(t\)-통계량이 \(t\)-분포에서 벗어나는 정도보다 훨씬 심하다. 그래서 분산에 대한 F-검정은 고전적 검정 중 가장 로버스트하지 않은 축에 든다.

연습문제 4. \(n_1 = n_2 = 50\), \(\alpha = 0.05\)에서 근사 임계값 \(F_{0.025}(49, 49)\)와 \(F_{0.975}(49, 49)\)를 구하라. 역수 관계로 하나를 다른 하나로 표현하라.

풀이

F-분포표나 소프트웨어에서 \(F_{0.975}(49, 49) \approx 1.762\)이다. 역수 성질에 의해

\[ F_{0.025}(49, 49) = \frac{1}{F_{0.975}(49, 49)} \approx \frac{1}{1.762} \approx 0.568 \]

이다. \(F < 0.568\)이거나 \(F > 1.762\)이면 \(H_0\)을 기각한다. 자유도가 같으면 기각역이 로그 척도에서 1을 중심으로 대칭임에 유의하라: \(\ln(0.568) \approx -0.567\), \(\ln(1.762) \approx 0.567\).

연습문제 5. 어떤 품질 기술자가 두 공장에서 공정의 분산을 측정하여 \(n_1 = 30\)에서 \(S_1^2 = 2.1\)을, \(n_2 = 30\)에서 \(S_2^2 = 3.8\)을 얻었다. 자료는 완만하게 오른쪽으로 치우쳐 있다. F-검정을 써야 하는가? 더 나은 대안을 제시하고 이유를 설명하라.

풀이

자료가 오른쪽으로 치우쳐 F-검정이 요구하는 정규성 가정을 위반하므로 F-검정을 쓰면 안 된다. 완만한 치우침만으로도 오도하는 p-값이 나올 수 있다.

더 나은 대안은 각 집단의 중앙값으로부터의 절대편차를 계산한 뒤 그 편차에 표준 분산분석을 수행하는 Levene 검정이다. 제곱편차가 아니라 절대편차를 쓰므로 치우침과 이상점의 영향을 훨씬 덜 받는다. Python에서는 scipy.stats.levene(data_1, data_2, center='median')이다. 분산비에 대한 검정이 특별히 필요하다면 (분산비를 재표본추출하는) 붓스트랩 접근이 분포에 의존하지 않는 대안이 된다.

연습문제 6. 연습문제 3의 주장을 같은 자료로 두 검정을 돌려 확인하라. 분산의 \(F\) 검정과 평균의 웰치 \(t\) 검정이 비정규성에 각각 얼마나 민감한가?

풀이
import numpy as np
from scipy import stats

def F_p(x, y):
    F = x.var(ddof=1) / y.var(ddof=1)
    d1, d2 = len(x) - 1, len(y) - 1
    return 2 * min(stats.f.cdf(F, d1, d2), stats.f.sf(F, d1, d2))

rng = np.random.default_rng(606)
B = 15_000
dists = {
    "정규": lambda n: rng.normal(0, 1, n),
    "균등": lambda n: rng.uniform(-np.sqrt(3), np.sqrt(3), n),
    "t(8)": lambda n: rng.standard_t(8, n),
    "t(5)": lambda n: rng.standard_t(5, n),
    "지수": lambda n: rng.exponential(1, n),
    "로그정규": lambda n: np.exp(rng.normal(0, 1, n)),
}
print("두 집단 모두 같은 분포에서 (n=25 씩), 명목 0.05")
print(f"{'분포':>20s} {'F 검정(분산)':>12s} {'웰치 t(평균)':>12s}")
for lab, f in dists.items():
    a = b = 0
    for _ in range(B):
        x, y = f(25), f(25)
        a += F_p(x, y) < 0.05
        b += stats.ttest_ind(x, y, equal_var=False).pvalue < 0.05
    print(f"{lab:>20s} {a / B:12.4f} {b / B:12.4f}")
두 집단 모두 같은 분포에서 (n=25 씩), 명목 0.05
                  분포     F 검정(분산)     웰치 t(평균)
                  정규       0.0529       0.0505
                  균등       0.0043       0.0493
                t(8)       0.1115       0.0481
                t(5)       0.1653       0.0481
                  지수       0.2770       0.0477
                로그정규       0.5049       0.0355

로그정규에서 \(F\) 검정의 오류율이 0.505다. 절반이다. 같은 자료의 \(t\) 검정은 0.036으로 멀쩡하다.

분포 \(F\) 검정 웰치 \(t\)
정규 0.053 0.051
균등 0.004 0.049
\(t(8)\) 0.112 0.048
\(t(5)\) 0.165 0.048
지수 0.277 0.048
로그정규 0.505 0.036

\(t\) 검정은 여섯 분포 모두에서 0.036~0.051을 유지한다. \(F\) 검정은 0.004에서 0.505까지 요동친다.

왜 이렇게 다른가. 두 검정이 기대는 적률의 차수가 다르다.

검정 통계량의 바탕 표집분포가 의존하는 것
\(t\) \(\bar X\) (1차 적률) 2차 적률(분산)
\(F\) \(S^2\) (2차 적률) 4차 적률(첨도)

중심극한정리는 한 차수만 올려 준다. \(\bar X\)의 분포를 정규로 만드는 데 필요한 것은 유한한 분산뿐이다. 반면 \(S^2\)의 표집분포를 알려면 첨도를 알아야 하는데, 중심극한정리는 그것을 정규값 3으로 만들어 주지 않는다.

\[ \operatorname{Var}(S^2)=\frac{\sigma^4}{n}\left(\gamma_2+\frac{2n}{n-1}\right) \]

\(n\to\infty\)에서도 \(\gamma_2\)가 남는다.

균등분포의 0.0043도 잊지 말자. 가벼운 꼬리(\(\gamma_2=-1.2\))에서는 지나치게 보수적이 된다. 검정력을 잃는다는 뜻이다.

이것이 연습문제 3의 정확한 답이다. "\(F\) 검정이 더 민감하다"가 아니라 "\(t\)는 중심극한정리의 보호를 받고 \(F\)는 받지 못한다"가 이유다.

실무 결론. \(t\) 검정은 큰 표본에서 믿어도 되지만, \(F\) 검정은 표본을 아무리 키워도 정규성이 필요하다.

연습문제 7. 연습문제 2의 역수 성질을 이용해 분산비 \(\sigma_1^2/\sigma_2^2\)의 신뢰구간을 유도하고, 실제 피복확률을 재라.

풀이

유도. \(\dfrac{S_1^2/\sigma_1^2}{S_2^2/\sigma_2^2}\sim F(d_1,d_2)\)이므로

\[ P\!\left(F_{\alpha/2}(d_1,d_2)<\frac{S_1^2/S_2^2}{\sigma_1^2/\sigma_2^2}<F_{1-\alpha/2}(d_1,d_2)\right)=1-\alpha \]

이고, 뒤집으면

\[ \left[\frac{S_1^2/S_2^2}{F_{1-\alpha/2}(d_1,d_2)},\ \frac{S_1^2/S_2^2}{F_{\alpha/2}(d_1,d_2)}\right] \]

연습문제 2의 역수 성질 \(F_{\alpha/2}(d_1,d_2)=1/F_{1-\alpha/2}(d_2,d_1)\) 덕분에 표 하나로 양쪽 분위수를 모두 얻을 수 있다. 표를 손으로 보던 시절의 관례가 여기서 왔다.

import numpy as np
from scipy import stats

def ratio_ci(x, y, a=0.05):
    d1, d2 = len(x) - 1, len(y) - 1
    R = x.var(ddof=1) / y.var(ddof=1)
    return R / stats.f.ppf(1 - a / 2, d1, d2), R / stats.f.ppf(a / 2, d1, d2)

rng = np.random.default_rng(707)
B = 10_000
print("참 분산비 = 1 을 덮는 비율 (명목 95%)")
print(f"{'분포':>12s} {'n=25':>8s} {'n=100':>8s}")
for lab, f in {"정규": lambda n: rng.normal(0, 1, n),
               "t(8)": lambda n: rng.standard_t(8, n),
               "t(5)": lambda n: rng.standard_t(5, n),
               "지수": lambda n: rng.exponential(1, n),
               "균등": lambda n: rng.uniform(-1, 1, n)}.items():
    row = []
    for n in [25, 100]:
        c = 0
        for _ in range(B):
            lo, hi = ratio_ci(f(n), f(n))
            c += lo <= 1.0 <= hi
        row.append(c / B)
    print(f"{lab:>12s} {row[0]:8.4f} {row[1]:8.4f}")
참 분산비 = 1 을 덮는 비율 (명목 95%)
          분포     n=25    n=100
          정규   0.9503   0.9495
        t(8)   0.8971   0.8786
        t(5)   0.8345   0.7848
          지수   0.7272   0.6852
          균등   0.9955   0.9969

정규에서는 0.9503으로 정확하다. 근사가 아니라 정확한 구간이기 때문이다.

비정규에서는 무너지고, \(n\)을 늘리면 더 나빠진다.

분포 \(n=25\) \(n=100\)
정규 0.950 0.950
\(t(8)\) 0.897 0.879
\(t(5)\) 0.835 0.785
지수 0.727 0.685
균등 0.996 0.997

연습문제 6과 같은 이유다. 첨도의 영향이 \(n\)과 함께 사라지지 않는다.

구간이 얼마나 넓은지도 봐야 한다. \(n_1=n_2=25\), \(S_1^2/S_2^2=1\)이면 구간이

\[ \left[\frac{1}{F_{0.975}(24,24)},\ \frac{1}{F_{0.025}(24,24)}\right] =[0.44,\ 2.27] \]

이다. 참 분산비가 2여도 이 구간 안에 들어간다. 표본 25개로는 두 배 차이도 구분하지 못한다.

보고 방법. 분산비는 로그 척도에서 대칭이므로

\[ \ln\frac{\sigma_1^2}{\sigma_2^2}\ \in\ [\ln0.44,\ \ln2.27]=[-0.82,\ 0.82] \]

로 쓰면 대칭이 드러난다. "분산비가 1을 중심으로 ±2.3배 범위"라고 말하는 것이 정확하다.

연습문제 8. 연습문제 5의 품질 기술자에게 권할 로버스트 대안을 두 가지 제시하고, 제1종 오류율을 비교하라.

풀이

두 대안.

방법 발상
브라운-포사이드 중앙값으로부터의 절대편차에 분산분석
순열검정 중심을 맞춘 뒤 라벨을 섞어 영분포를 만든다
import numpy as np
from scipy import stats

rng = np.random.default_rng(707)

def perm_p(x, y, rng, R=600):
    """로그 분산비의 순열검정. 중앙값으로 중심을 맞춘 뒤 섞는다."""
    obs = abs(np.log(x.var(ddof=1) / y.var(ddof=1)))
    z = np.concatenate([x - np.median(x), y - np.median(y)])
    n = len(x)
    cnt = 0
    for _ in range(R):
        p = rng.permutation(z)
        cnt += abs(np.log(p[:n].var(ddof=1) / p[n:].var(ddof=1))) >= obs
    return (cnt + 1) / (R + 1)

B = 4_000
print("두 집단의 분산이 실제로 같음, n=25 씩, 명목 0.05")
print(f"{'분포':>12s} {'F 검정':>8s} {'브라운-포사이드':>14s} {'순열(로그 분산비)':>16s}")
for lab, f in {"정규": lambda n: rng.normal(0, 1, n),
               "t(5)": lambda n: rng.standard_t(5, n),
               "지수": lambda n: rng.exponential(1, n),
               "로그정규": lambda n: np.exp(rng.normal(0, 1, n))}.items():
    a = b = c = 0
    for _ in range(B):
        x, y = f(25), f(25)
        F = x.var(ddof=1) / y.var(ddof=1)
        a += 2 * min(stats.f.cdf(F, 24, 24), stats.f.sf(F, 24, 24)) < 0.05
        b += stats.levene(x, y, center="median").pvalue < 0.05
        c += perm_p(x, y, rng) < 0.05
    print(f"{lab:>12s} {a / B:8.4f} {b / B:14.4f} {c / B:16.4f}")
두 집단의 분산이 실제로 같음, n=25 씩, 명목 0.05
          분포     F 검정       브라운-포사이드       순열(로그 분산비)
          정규   0.0520         0.0387           0.0462
        t(5)   0.1658         0.0398           0.0488
          지수   0.2732         0.0460           0.0565
        로그정규   0.5020         0.0430           0.0580

\(F\) 검정만 무너진다.

분포 \(F\) 검정 브라운-포사이드 순열
정규 0.052 0.039 0.046
\(t(5)\) 0.166 0.040 0.049
지수 0.273 0.046 0.057
로그정규 0.502 0.043 0.058

두 대안 모두 0.04~0.06 범위에 머문다. 브라운-포사이드가 약간 보수적(0.039~0.046), 순열검정이 약간 자유롭다(0.046~0.058).

순열검정의 요령이 중요하다. 그냥 원자료를 섞으면 안 된다. 중심이 다르면 분산 차이와 평균 차이가 뒤섞이기 때문이다. 위 코드처럼

z = [x - median(x)] ++ [y - median(y)]

로 각 집단의 중심을 먼저 맞춘 뒤 섞어야 "분산만 같다"는 귀무가설을 제대로 검정한다.

왜 로그 분산비를 쓰는가. \(S_1^2/S_2^2\)은 1을 중심으로 비대칭이다(\([0.5,1]\)과 \([1,2]\)가 같은 크기의 차이인데 길이가 다르다). 로그를 취하면 대칭이 되어 양측검정의 절댓값이 의미를 갖는다.

품질 기술자에게 할 답.

자료가 오른쪽으로 치우쳐 있으므로 \(F\) 검정을 쓰면 안 됩니다. 그 상황에서 \(F\) 검정의 거짓 양성률은 명목 5%가 아니라 최대 50%까지 갑니다.

대신 브라운-포사이드 검정(scipy.stats.levene의 기본값)을 쓰거나, 표본이 충분하면 순열검정을 쓰십시오. 분산비 2.1 대 3.8(비 1.81)은 \(n=30\)으로는 검정력이 부족할 수 있으므로, 결과와 함께 분산비의 신뢰구간을 보고하는 것이 좋습니다.

덧붙일 것 — 로그 변환. 공정 자료가 오른쪽으로 치우쳐 있다면 애초에 로그 척도에서 분석하는 것이 자연스러울 수 있다. 그러면 정규성과 등분산이 함께 회복되는 경우가 많다.

연습문제 9. 분산비를 검정력 0.80으로 탐지하려면 집단당 표본이 얼마나 필요한가? 평균 비교와 비교하라.

풀이

검정력 식. \(H_1\) 아래에서 \(\dfrac{S_1^2}{S_2^2}\sim r\cdot F(d,d)\)이므로(\(r=\sigma_1^2/\sigma_2^2\))

\[ \text{검정력}=P\!\left(F_{d,d}<\frac{F_{\alpha/2}(d,d)}{r}\right) +P\!\left(F_{d,d}>\frac{F_{1-\alpha/2}(d,d)}{r}\right) \]
from scipy import stats

def n_var(ratio, power=0.80, alpha=0.05):
    for n in range(4, 100_000):
        d = n - 1
        lo = stats.f.ppf(alpha / 2, d, d)
        hi = stats.f.ppf(1 - alpha / 2, d, d)
        pw = stats.f.cdf(lo / ratio, d, d) + stats.f.sf(hi / ratio, d, d)
        if pw >= power:
            return n

print(f"{'σ1²/σ2²':>8s} {'집단당 n':>9s}")
for r in [1.5, 2.0, 2.5, 3.0, 4.0]:
    print(f"{r:8.2f} {n_var(r):9d}")
 σ1²/σ2²     집단당 n
    1.50       193
    2.00        68
    2.50        40
    3.00        28
    4.00        19

분산비 2를 탐지하려면 집단당 68개가 필요하다.

평균 비교와 나란히 놓으면.

목표 집단당 \(n\)
평균 차이 \(d=0.5\)(중간) 64
평균 차이 \(d=0.8\)(큼) 26
분산비 2.0 68
분산비 1.5 193

분산비 2가 평균 효과크기 0.5와 비슷한 난이도다. 그런데 분산비 2는 표준편차로 1.41배로, 직관적으로 꽤 큰 차이다.

분산비 1.5(표준편차 1.22배)를 잡으려면 193개가 필요하다. 분산 비교는 평균 비교보다 훨씬 비싸다.

왜 그런가. \(S^2\)의 상대표준오차가

\[ \frac{\operatorname{SD}(S^2)}{\sigma^2}\approx\sqrt{\frac{2}{n-1}} \]

인데, \(\bar X\)의 상대오차와 달리 \(\sqrt2\)라는 상수가 붙고 \(\sigma\)에 무관하다. \(n=68\)이면 11%이므로 두 집단의 비를 볼 때 15% 정도의 오차가 있고, 그 정도면 1.41배 차이를 겨우 구분한다.

실무 함의 셋.

  1. "분산에 차이가 없다"를 보이려면 표본이 수백 개 필요하다.
  2. 등분산 검정이 유의하지 않은 것을 등분산의 근거로 삼지 않는다(검정력 부족).
  3. 애초에 등분산을 가정하지 않는 방법(웰치)을 쓰면 이 문제가 사라진다.

세 번째가 가장 실용적인 결론이다. 등분산을 증명하려 애쓰는 대신 가정하지 않는 것이 표본을 아끼는 길이다.

연습문제 10. 등분산 \(F\) 검정의 사용 지침을 정리하라.

풀이

검정의 구조.

\[ F=\frac{S_1^2}{S_2^2}\ \sim\ \frac{\sigma_1^2}{\sigma_2^2}\,F(n_1-1,\,n_2-1) \qquad\text{(정규성 아래에서 정확)} \]

핵심 수치 넷.

사실 값
로그정규에서 제1종 오류율 0.505
같은 상황 웰치 \(t\) 검정 0.036
분산비 2 탐지에 필요한 집단당 \(n\) 68
\(n=25\)에서 분산비의 95% 구간 폭 \([0.44,\ 2.27]\)

사용 조건 셋.

조건 왜
정규성 첨도가 통계량의 분포를 지배
독립성 설계로 확보
표본이 충분 분산 추론은 비싸다

첫 줄이 절대적이다. \(t\) 검정과 달리 표본을 키워도 구제되지 않는다(연습문제 6).

결정 흐름.

두 집단의 분산을 비교하려 한다
    │
    ├─ 목적이 "분산분석을 써도 되나" 확인이라면
    │     └─→ 하지 말 것. 웰치를 쓴다
    │
    └─ 분산 자체가 연구 질문이라면
          │
          ├─ 정규성이 보장됨 ──→ F 검정 + 분산비 신뢰구간
          │
          └─ 그 외 ──→ 브라운-포사이드  또는  순열검정
                          (중앙값으로 중심을 맞춘 뒤 섞는다)

방법 비교표.

방법 정규에서 비정규에서 비고
\(F\) 검정 0.052 (정확) 0.502 구간을 바로 얻음
브라운-포사이드 0.039 0.040~0.046 약간 보수적
순열검정 0.046 0.049~0.058 계산이 무겁다

자주 하는 실수 넷.

실수 결과
치우친 자료에 그대로 적용 오류율 0.50
단측 \(p\)를 두 배 왼쪽 이탈에서 틀림
유의하지 않음 → "등분산" \(n=30\)의 검정력은 낮다
원자료를 그냥 섞는 순열검정 평균 차이가 섞여 들어감

보고 형식.

공장 A: n = 30, S² = 2.10
공장 B: n = 30, S² = 3.80
분산비 = 0.553

자료가 오른쪽으로 치우쳐 F 검정은 부적절.
브라운-포사이드 검정: W = ..., p = ...
분산비의 95% 부트스트랩 구간: [..., ...]

분산비와 그 구간을 항상 함께 적는다. \(p\)만으로는 "얼마나 다른가"를 알 수 없다.

한 문장. 등분산 \(F\) 검정은 정규성 위에서만 정확하며, 그 조건을 확인할 수 없다면 브라운-포사이드나 순열검정으로 바꾸는 것이 옳다.


정리하며

등분산 \(F\) 검정은 고전적이지만 실무에서는 밀려났다.

\[ F=\frac{s_1^2}{s_2^2}\sim F_{n_1-1,\,n_2-1}\quad(H_0\text{ 아래}) \]
  • 정규성 위반에 극도로 민감하다. 꼬리가 조금만 두꺼워도 제1종 오류율이 명목값을 크게 넘으며, 분산이 같아도 기각한다.
  • 그래서 레빈 검정이 선호된다. 다음 절의 주제이며, 정규성에 훨씬 덜 민감하다.
  • 분산분석 \(F\) 검정과 이름은 같지만 다른 검정이다. 여기서는 두 분산의 비를 보고, 분산분석에서는 집단 간·집단 내 분산의 비를 본다. 같은 분포를 참조할 뿐이다.
  • 정당한 용도는 분산 자체가 관심사일 때다. 두 공정의 변동 비교 같은 경우이며, 그때도 정규성을 먼저 확인해야 한다.
  • 합동 \(t\) 검정이나 분산분석의 사전 확인용으로 쓰지 말 것. 2단계 절차의 문제에 더해 이 검정 자체가 믿을 수 없다.

다음 절 Levene 검정으로 넘어간다.