콘텐츠로 이동

Bartlett 검정

이 주제를 다루는 다른 곳

여기서는 분산분석의 등분산성 가정을 확인하는 용도로 짧게 다룬다. 유도와 카이제곱 근사, 비정규성 아래의 한계는 15.4 Bartlett 검정에서 다룬다.

개요

Bartlett 검정은 둘 이상의 모집단이 같은 분산을 갖는다는(등분산성) 귀무가설을 평가한다. 자료가 정말로 정규일 때 등분산에 대한 균일최강력 불편검정이지만 정규성 이탈에 매우 민감하다. 이 페이지에서는 검정통계량을 제시하고, scipy.stats.bartlett으로 Python 보기를 보이며, 여러 분산비 시나리오에서 검정의 행동을 비교한다.

가설과 검정통계량

분산이 \(\sigma_1^2, \dots, \sigma_k^2\)인 정규 모집단에서 크기 \(n_1, \dots, n_k\)의 독립 표본 \(k\)개를 뽑았다고 하자. 가설은

\[ H_0: \sigma_1^2 = \sigma_2^2 = \cdots = \sigma_k^2, \qquad H_1: \text{분산이 모두 같지는 않다} \]

이다. \(S_i^2\)을 자유도 \(\nu_i = n_i - 1\)인 집단 \(i\)의 표본분산이라 하고, 합동분산을

\[ S_p^2 = \frac{\sum_{i=1}^{k} \nu_i S_i^2}{\sum_{i=1}^{k} \nu_i} \]

라 하자. Bartlett 검정통계량은

\[ \chi^2_B = \frac{\left(\sum_{i=1}^{k} \nu_i\right) \ln S_p^2 - \sum_{i=1}^{k} \nu_i \ln S_i^2}{1 + \frac{1}{3(k-1)}\left(\sum_{i=1}^{k} \frac{1}{\nu_i} - \frac{1}{\sum_{i=1}^{k} \nu_i}\right)} \]

이다. \(H_0\)과 정규성 아래에서 \(\chi^2_B\)는 근사적으로 \(\chi^2(k-1)\)을 따른다. \(\chi^2_B > \chi^2_{1-\alpha}(k-1)\)이면 \(H_0\)을 기각한다.

함께 제공되는 스크립트는 기준 표본 \(X \sim N(0, 1)\)을 생성하고 \(\sigma_Y \in \{1.00, 1.05, 1.10, 1.15, 1.20\}\)인 표본 \(Y \sim N(1, \sigma_Y)\)과 비교한다. 씨앗을 고정하므로 통계량이 자료에 전혀 의존하지 않는 닫힌 꼴로 적힌다.

보기 1. 분산 차이를 키워 가며 보는 Bartlett 검정. 집단당 \(n = 100\), 씨앗 seed = 1을 고정하고 \(\sigma_Y\)만 바꾼다.

(1) \(k = 2\), \(\nu_1 = \nu_2 = \nu = n - 1\)이고 씨앗이 고정되어 있을 때

\[ \chi_B^2(\sigma_Y) = \frac{2\nu}{1 + \dfrac{1}{2\nu}} \, \ln\!\left(\frac{1 + \sigma_Y^2}{2\sigma_Y}\right) \]

임을 유도하시오. 이 값이 언제나 \(0\) 이상이고 \(\sigma_Y = 1\)에서만 \(0\)임을 보이시오.

(2) 검정을 실행해 이 식이 맞는지 확인하고, \(p = 0.05\)가 되는 \(\sigma_Y\)를 구해 등분산 F-검정의 같은 경계와 견주시오.

풀이

(1) 해석적으로. 같은 씨앗에서 norm(loc, scale).rvs 는 같은 표준정규열 \(z_1, \ldots, z_n\)을 꺼내 쓰므로 \(x_j = z_j\), \(y_j = 1 + \sigma_Y z_j\)다. 표본분산은 위치이동에 둔감하고 배율의 제곱만큼 커지므로

\[ S_1^2 = s_z^2, \qquad S_2^2 = \sigma_Y^2 s_z^2 \]

이다. 합동분산은 두 자유도가 같으므로 단순평균이 된다.

\[ S_p^2 = \frac{\nu s_z^2 + \nu \sigma_Y^2 s_z^2}{2\nu} = \frac{1 + \sigma_Y^2}{2}\, s_z^2 \]

이제 분자를 계산한다.

\[ \begin{aligned} 2\nu \ln S_p^2 - \nu \ln S_1^2 - \nu \ln S_2^2 &= 2\nu\left[\ln s_z^2 + \ln\frac{1+\sigma_Y^2}{2}\right] - \nu \ln s_z^2 - \nu\left[\ln s_z^2 + \ln \sigma_Y^2\right] \\ &= 2\nu \ln\frac{1+\sigma_Y^2}{2} - 2\nu \ln \sigma_Y \\ &= 2\nu \ln\!\left(\frac{1+\sigma_Y^2}{2\sigma_Y}\right) \end{aligned} \]

\(\ln s_z^2\)이 완전히 약분된다. 계수 \(2\nu - \nu - \nu = 0\)이기 때문이고, 바로 이 때문에 통계량이 자료의 흩어짐 크기에 의존하지 않는다.

분모의 보정항은 \(k = 2\), \(\nu_1 = \nu_2 = \nu\)에서

\[ 1 + \frac{1}{3(k-1)}\left(\sum_i \frac{1}{\nu_i} - \frac{1}{\sum_i \nu_i}\right) = 1 + \frac{1}{3}\left(\frac{2}{\nu} - \frac{1}{2\nu}\right) = 1 + \frac{1}{3}\cdot\frac{3}{2\nu} = 1 + \frac{1}{2\nu} \]

이다. 둘을 합치면 문제의 식이 나온다. \(\nu = 99\)에서 보정항은 \(1 + 1/198 = 1.0050505\)다.

부호는 산술–기하평균 부등식이 정해 준다. \(1\)과 \(\sigma_Y^2\)에 대해

\[ \frac{1 + \sigma_Y^2}{2} \ge \sqrt{1 \cdot \sigma_Y^2} = \sigma_Y \quad\Longrightarrow\quad \frac{1+\sigma_Y^2}{2\sigma_Y} \ge 1 \quad\Longrightarrow\quad \ln\!\left(\frac{1+\sigma_Y^2}{2\sigma_Y}\right) \ge 0 \]

이고 등호는 \(\sigma_Y^2 = 1\), 곧 \(\sigma_Y = 1\)일 때만 성립한다. \(\sigma_Y = 1\)에서 \(\chi_B^2\)은 근삿값이 아니라 정확히 \(0\)이다. 출력의 \(-0.00\)은 이 정확한 \(0\)을 부동소수점으로 계산할 때 \(-10^{-15}\) 수준의 잔여가 남은 것일 뿐이다. 통계량이 음수가 될 수 있다는 뜻이 아니다.

(2) 수치적으로. 다섯 \(\sigma_Y\)에 대해 scipy 값과 닫힌 꼴을 나란히 적는다.

import numpy as np
import scipy.optimize as opt
import scipy.stats as stats

# (1) 에서 유도한 닫힌 꼴: nu = n-1, C = 1 + 1/(2 nu).
seed, size = 1, 100
nu = size - 1
C = 1 + 1 / (2 * nu)
x = stats.norm(loc=0, scale=1).rvs(size, random_state=seed)

print(f"nu = {nu},  보정항 C = 1 + 1/(2nu) = {C:.7f}")
print(f"\n{'sigma_y':>8}{'bartlett':>12}{'closed':>12}{'p':>9}")
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 = stats.bartlett(x, y)
    closed = 2 * nu * np.log((1 + scale ** 2) / (2 * scale)) / C
    print(f"{scale:>8.2f}{stat:>12.8f}{closed:>12.8f}{pval:>9.4f}")

# p = 0.05 경계. 닫힌 꼴을 거꾸로 푼다 — 격자 탐색이 아니다.
crit = stats.chi2(df=1).ppf(0.95)
g = lambda s: 2 * nu * np.log((1 + s ** 2) / (2 * s)) / C - crit
root = opt.brentq(g, 1.0001, 5.0)
print(f"\n임계값 chi2_0.95(1) = {crit:.4f}")
print(f"chi2_B = 임계값이 되는 sigma_y = {root:.4f}")

# 같은 경계를 등분산 F 검정에서 구하면 1/sqrt(F_0.025(99,99)) 다.
print(f"F 검정의 같은 경계             = {1 / np.sqrt(stats.f(nu, nu).ppf(0.025)):.4f}")

출력:

nu = 99,  보정항 C = 1 + 1/(2nu) = 1.0050505

 sigma_y    bartlett      closed        p
    1.00 -0.00000000  0.00000000   1.0000
    1.05  0.23439030  0.23439030   0.6283
    1.10  0.89344836  0.89344836   0.3445
    1.15  1.91785747  1.91785747   0.1661
    1.20  3.25635555  3.25635555   0.0711

임계값 chi2_0.95(1) = 3.8415
chi2_B = 임계값이 되는 sigma_y = 1.2191
F 검정의 같은 경계             = 1.2191

유도한 식과 scipy.stats.bartlett 의 값이 소수점 여덟째 자리까지 같다. 첫 줄만 scipy 쪽이 \(-0.00000000\), 닫힌 꼴이 \(0.00000000\)인데, (1)에서 본 대로 참값은 정확히 \(0\)이고 차이는 부동소수점 잔여뿐이다.

\(p = 0.05\) 경계는 \(\sigma_Y = 1.2191\)이다. 격자의 마지막 값 \(1.20\)이 그에 못 미치므로 기각되지 않으며, 이것이 \(p = 0.071\)의 정체다. 표준편차가 \(20\%\) 차이 나도 집단당 100개로는 \(5\%\) 수준을 넘기지 못한다.

같은 경계가 등분산 F-검정에서도 \(1.2191\)로 똑같이 나오는 것이 이 보기의 가장 쓸모 있는 대목이다. 우연이 아니다. (1)의 유도에서 \(\chi_B^2\)이 \(\sigma_Y\)에만 의존했는데, \(\sigma_Y^2 = S_2^2/S_1^2\)은 F-통계량의 역수다. 일반적으로 \(r = S_1^2/S_2^2\)이라 쓰면

\[ \chi_B^2 = \frac{2\nu}{1 + 1/(2\nu)} \ln\!\left(\frac{1+r}{2\sqrt r}\right) \]

이고 이는 \(|\ln r|\)의 증가함수다. 따라서 \(k = 2\), 균형설계, 정규 자료에서 바틀렛 검정은 F-검정의 단조변환이며 두 검정은 같은 순서로 기각한다. 본문 표의 p-값이 F-검정의 것과 소수점 셋째 자리까지 같았던 까닭도 이것이다. 두 경계가 소수점 넷째 자리까지 일치한 것은 \(\chi^2(1)\) 근사가 \(\nu = 99\)에서 충분히 정확했다는 뜻이기도 하다.

각 호출은 Bartlett \(\chi^2\) 통계량과 그 p-값을 돌려준다. 출력은 다음과 같다:

\(\sigma_Y\) \(\chi^2_B\) p-값
1.00 0.00 1.000
1.05 0.23 0.628
1.10 0.89 0.345
1.15 1.92 0.166
1.20 3.26 0.071

해석

  • \(\sigma_Y = 1.00\)이면 두 집단의 분산이 같고 p-값이 커서 \(H_0\)을 올바르게 기각하지 못한다.
  • \(\sigma_Y\)가 1.05에서 1.20으로 커질수록 검정통계량이 커지고 p-값이 작아져 등분산으로부터의 이탈이 커짐을 반영한다.
  • 다만 집단당 \(n = 100\)에서도 표준편차가 20% 큰 경우(분산비 1.44)의 p-값이 \(0.071\)로 \(\alpha = 0.05\)에 미치지 못한다. 완만한 분산 차이를 탐지하려면 표본이 훨씬 커야 한다.

결정적인 주의사항: 바탕 분포가 정규가 아니면(예: 꼬리가 두껍거나 치우쳐 있으면) Bartlett 검정의 제1종 오류율이 부풀려진다. 이런 경우에는 집단 중앙값으로부터의 절대편차를 쓰는 Levene 검정이 분포 모양에 로버스트하여 선호된다.

"부풀려진다"가 어느 정도인지 재어 보면 이 주의사항의 무게가 달라진다. 아래는 모분산이 정확히 같은 세 집단을 각 \(n = 20\)으로 뽑아 8,000번 검정한 결과다. 분포만 바꾸었고 \(H_0\)은 언제나 참이다.

바틀렛은 정규에서만 제 몫을 하고 비정규에서 무너진다

정규 자료에서 바틀렛의 실제 오류율은 \(0.049\)로 흠잡을 데가 없다. 그런데 꼬리가 조금 두꺼워진 \(t_5\)에서 벌써 \(0.214\), 치우친 지수분포에서 \(0.398\), 대수정규에서는 \(0.673\)이다. 분산이 모두 같은데도 세 번에 두 번 "다르다"고 선언한다. 반대편 극단도 있다. 꼬리가 얇은 균등분포에서는 \(0.003\)으로 검정이 사실상 작동을 멈춘다. 같은 자료에 브라운–포사이스(중앙값 레빈)를 적용하면 다섯 분포 모두에서 \(0.029\)–\(0.047\)을 유지한다.

이 숫자들은 15장의 결과와 정확히 같은 이야기다. 원인은 \(\operatorname{Var}(\ln S^2) \approx (2 + \gamma_2)/\nu\)라는 관계에 있다. 기준분포 \(\chi^2_{k-1}\)은 초과첨도 \(\gamma_2 = 0\)을 가정하고 폭을 정하는데, 대수정규의 \(\gamma_2\)는 \(100\)을 넘으므로 통계량의 실제 분포가 기준분포보다 수십 배 넓어진다. 바틀렛이 재는 것은 "분산이 다른가"가 아니라 "\(\ln S_i^2\)이 정규 이론이 예측하는 것보다 많이 흩어졌는가"이고, 비정규 자료에서는 그 둘이 구별되지 않는다. 자세한 유도는 15.4 Bartlett 검정에 있다.

오른쪽은 정규성이 성립할 때의 이야기, 곧 위 보기가 왜 \(p = 0.071\)에 머물렀는지에 대한 답이다. \(\sigma\) 비가 \(1.2\)(분산비 \(1.44\))인 두 집단에서 바틀렛의 기각률은 \(n = 100\)일 때 \(0.438\)이다. 절반 조금 못 미치게 잡아낸다는 뜻이니, 위 보기의 한 표본이 유독 운이 나빴던 것이 아니라 원래 그 정도 확률의 도박이었던 셈이다. \(0.80\)에 이르려면 집단당 \(200\)개를 훌쩍 넘겨야 한다. 등분산 검정으로 완만한 차이를 가려내는 일이 얼마나 어려운지 보여 주는 숫자다.

연습문제

연습문제 1. \(n_1 = n_2 = 50\)인 두 집단에서 \(S_1^2 = 4.0\), \(S_2^2 = 6.0\)일 때 합동분산 \(S_p^2\)을 계산하라.

풀이

표본크기가 같으므로 \(\nu_1 = \nu_2 = 49\)이고

\[ S_p^2 = \frac{49 \cdot 4.0 + 49 \cdot 6.0}{49 + 49} = \frac{196 + 294}{98} = \frac{490}{98} = 5.0 \]

집단 크기가 같으면 합동분산은 단순히 두 표본분산의 산술평균이다.

연습문제 2. Bartlett 검정이 비정규성에 민감한 이유를 설명하라. 로그 분산의 어떤 성질이 검정을 취약하게 만드는가?

풀이

Bartlett 통계량은 표본분산의 로그인 \(\ln S_i^2\)으로 만들어진다. 귀무분포의 카이제곱 근사는 표본분산이 근사적으로 카이제곱 확률변수에 비례한다는 데 기대는데, 이는 바탕 자료가 정규일 때에만 성립한다. 비정규성 아래에서는 (초과 첨도 때문에) \(S_i^2\)의 분포가 더 두꺼운 꼬리를 가질 수 있고, 그러면 \(\ln S_i^2\)이 카이제곱 이론이 예측하는 것보다 더 퍼진다. 이 때문에 \(H_0\) 아래에서 검정통계량이 부풀려져 제1종 오류율이 높아진다.

연습문제 3. 집단이 \(k = 3\)개일 때 \(H_0\) 아래 Bartlett 검정통계량의 자유도를 진술하고 \(\alpha = 0.05\)의 임계값을 구하라.

풀이

\(H_0\) 아래에서 Bartlett 통계량은 \(\chi^2(k - 1) = \chi^2(2)\)를 따른다. 임계값은

\[ \chi^2_{0.95}(2) = 5.991 \]

이다. \(\chi^2_B > 5.991\)이면 \(H_0\)을 기각한다.

연습문제 4. 어떤 동료가 (오른쪽으로 심하게 치우친) 소득 자료 세 집단에 Bartlett 검정을 적용해 \(p = 0.02\)를 얻고 집단 분산이 다르다고 결론지었다. 이 분석을 비평하라.

풀이

Bartlett 검정은 정규성을 가정하는데 소득 자료는 대개 오른쪽으로 치우치고 꼬리가 두꺼우므로 이 결론은 믿을 수 없다. 초과 첨도가 Bartlett 통계량을 부풀리므로 작은 p-값이 진짜 분산 차이가 아니라 비정규성을 반영한 것일 수 있다. 올바른 접근은 비정규성에 로버스트한 (중앙값을 중심으로 하는) Levene 검정을 쓰는 것이다. 정규성 기반 검정을 쓰고 싶다면 검정 전에 소득 자료에 로그 변환을 적용해 분포를 대칭에 가깝게 만드는 방법도 있다.

연습문제 5. Bartlett 통계량 분모의 보정 인자 \(C = 1 + \frac{1}{3(k-1)}\!\left(\sum_{i=1}^{k}\frac{1}{\nu_i} - \frac{1}{\sum \nu_i}\right)\)를 유도하라. 이 보정이 왜 필요한가?

풀이

보정이 없으면 분자 \(M = (\sum \nu_i)\ln S_p^2 - \sum \nu_i \ln S_i^2\)이 유한 표본에서 \(\chi^2(k-1)\)을 정확히 따르지 않는다. 비 \(M / C\)는 추정량으로서 \(\ln S_i^2\)이 갖는 편향을 반영하여 더 나은 카이제곱 근사를 준다. 보정 인자 \(C\)는 Box 근사에서 유도된다. \(M\)의 정확한 분포가 \(C \cdot \chi^2(k-1)\)에 가까우므로 \(C\)로 나누면 근사적인 \(\chi^2(k-1)\) 확률변수를 얻는다.

\(C > 1\)이 언제나 성립함에 유의하라(\(k \ge 2\)일 때 각 \(1/\nu_i > 1/\sum \nu_i\)이다). 따라서 보정은 검정통계량을 보정 전보다 줄여 검정을 조금 더 보수적으로 만들고 카이제곱 근사의 정확도를 높인다. \(\square\)

연습문제 6. 연습문제 2·4가 말하는 비정규성 민감도를 수치로 재라. 레빈 검정, 브라운-포사이드 검정과 함께 제1종 오류율을 비교하라.

풀이
import numpy as np
from scipy import stats

rng = np.random.default_rng(1234)
B = 6_000
dists = {
    "정규": lambda n: rng.normal(0, 1, n),
    "t(5) 두꺼운 꼬리": lambda n: rng.standard_t(5, n),
    "균등 (가벼운 꼬리)": lambda n: rng.uniform(-1, 1, n),
    "지수 (치우침 2)": lambda n: rng.exponential(1, n),
    "로그정규 (치우침 6)": lambda n: np.exp(rng.normal(0, 1, n)),
}
print("k=3, 집단당 n=20, 분산이 실제로 모두 같음, 명목 0.05")
print(f"{'분포':>22s} {'바틀렛':>8s} {'레빈(평균)':>11s} {'브라운-포사이드':>14s}")
for lab, f in dists.items():
    a = b = c = 0
    for _ in range(B):
        gs = [f(20) for _ in range(3)]
        a += stats.bartlett(*gs).pvalue < 0.05
        b += stats.levene(*gs, center="mean").pvalue < 0.05
        c += stats.levene(*gs, center="median").pvalue < 0.05
    print(f"{lab:>22s} {a / B:8.4f} {b / B:11.4f} {c / B:14.4f}")
k=3, 집단당 n=20, 분산이 실제로 모두 같음, 명목 0.05
                    분포      바틀렛      레빈(평균)       브라운-포사이드
                    정규   0.0508      0.0547         0.0367
           t(5) 두꺼운 꼬리   0.2153      0.0632         0.0418
           균등 (가벼운 꼬리)   0.0027      0.0617         0.0293
            지수 (치우침 2)   0.3795      0.1817         0.0462
          로그정규 (치우침 6)   0.6742      0.2510         0.0370

로그정규에서 바틀렛의 오류율이 0.674다. 분산이 실제로 모두 같은데 세 번 중 두 번 "다르다"고 한다.

분포 바틀렛 레빈(평균) 브라운-포사이드
정규 0.051 0.055 0.037
\(t(5)\) 0.215 0.063 0.042
균등 0.003 0.062 0.029
지수 0.380 0.182 0.046
로그정규 0.674 0.251 0.037

바틀렛은 정규에서만 정확하다(0.0508). 나머지 넷에서 모두 실패한다.

균등분포에서 0.0027이라는 극단적 보수성도 주목할 만하다. 꼬리가 가벼우면 반대 방향으로 어긋난다. 바틀렛은 꼬리의 두께를 분산의 차이로 착각한다.

왜 그런가. 바틀렛 통계량은 \(\ln S_i^2\)의 흩어짐을 잰다. \(S_i^2\)의 분산은

\[ \operatorname{Var}(S_i^2)=\frac{\sigma^4}{n}\left(\beta_2-1+\frac{2n}{n-1}\right) \quad\text{(}\beta_2\text{는 첨도)} \]

로 첨도에 직접 의존한다. 바틀렛은 \(\beta_2=3\)(정규)을 전제로 기준분포를 잡으므로, 첨도가 다르면 기준 자체가 틀린다.

첨도 결과
\(\beta_2>3\)(두꺼운 꼬리) \(S_i^2\)이 더 흩어짐 → 자유롭게
\(\beta_2=3\)(정규) 정확
\(\beta_2<3\)(가벼운 꼬리) \(S_i^2\)이 덜 흩어짐 → 보수적

브라운-포사이드가 압도적이다(0.029~0.046). 중앙값 중심의 절대편차를 쓰기 때문에 치우침과 두꺼운 꼬리에 모두 견딘다.

레빈(평균 중심)은 중간이다(0.055~0.251). 치우친 분포에서 무너진다. scipy의 기본값 center="median"이 곧 브라운-포사이드이며, 이 기본값이 옳다.

연습문제 4의 동료에게 할 말. 소득 자료는 로그정규에 가깝다. \(p=0.02\)는 분산 차이의 증거가 아니라 치우침의 증거일 가능성이 크다. 브라운-포사이드로 다시 하거나, 로그 변환 후 다시 하라.

연습문제 7. "바틀렛은 정규일 때 최강력"이라는 주장의 값을 재라. 정규 자료에서 레빈·브라운-포사이드보다 얼마나 더 잡는가?

풀이
import numpy as np
from scipy import stats

rng = np.random.default_rng(1234)
B = 6_000
print("k=3, n=20, 정규, σ=(1, 1, r), 명목 0.05")
print(f"{'r':>5s} {'바틀렛':>8s} {'레빈(평균)':>11s} {'브라운-포사이드':>14s}")
for r in [1.5, 2.0, 3.0]:
    a = b = c = 0
    for _ in range(B):
        gs = [rng.normal(0, 1, 20), rng.normal(0, 1, 20), rng.normal(0, r, 20)]
        a += stats.bartlett(*gs).pvalue < 0.05
        b += stats.levene(*gs, center="mean").pvalue < 0.05
        c += stats.levene(*gs, center="median").pvalue < 0.05
    print(f"{r:5.1f} {a / B:8.4f} {b / B:11.4f} {c / B:14.4f}")
k=3, n=20, 정규, σ=(1, 1, r), 명목 0.05
    r      바틀렛      레빈(평균)       브라운-포사이드
  1.5   0.4300      0.3942         0.3338
  2.0   0.8777      0.8167         0.7665
  3.0   0.9983      0.9943         0.9903

바틀렛이 가장 강력하지만 차이가 크지 않다.

\(\sigma\) 비 바틀렛 브라운-포사이드 손실
1.5 0.430 0.334 \(-22\%\)
2.0 0.878 0.767 \(-13\%\)
3.0 0.998 0.990 \(-1\%\)

차이가 가장 큰 곳은 검정력이 낮은 구간(\(r=1.5\))이다. 그런데 거기서는 두 검정 모두 쓸모가 없다(0.33이든 0.43이든 절반도 못 잡는다).

차이가 실질적으로 중요한 구간이 없다. \(r=2\) 이상이면 둘 다 0.77 이상이고, \(r=1.5\)에서는 둘 다 부족하다.

비용-편익을 나란히 놓으면.

정규일 때 검정력 비정규일 때 오류율
바틀렛 기준 0.68(로그정규)
브라운-포사이드 \(-1\%\sim-22\%\) 0.04

보험료가 검정력 몇 퍼센트, 보험금이 오류율 17배다. 웰치 대 표준 \(F\)와 같은 구조의 거래다.

그럼 바틀렛은 언제 쓰나.

상황 선택
정규성을 설계나 이론으로 보장 바틀렛
측정오차가 정규인 물리·공학 자료 바틀렛
그 외 모든 경우 브라운-포사이드
분산 자체가 관심사이고 표본이 큼 둘 다(일치하면 안심)

역사적 맥락. 바틀렛(1937)은 계산이 손으로 가능하던 시절에 나왔고, 레빈(1960)·브라운-포사이드(1974)는 그 취약성을 고치려 나왔다. 로버스트성을 위해 최적성을 포기한 것이 20세기 후반 통계학의 큰 흐름이며, 이 세 검정이 그 축소판이다.

연습문제 8. 바틀렛 통계량을 직접 구현하여 scipy와 대조하고, 연습문제 5의 보정인자 \(C\)가 실제로 무엇을 고치는지 수치로 보여라.

풀이
import numpy as np
from scipy import stats

def bartlett_manual(groups, correct=True):
    """바틀렛 검정. correct=False 면 보정인자 C 를 쓰지 않는다."""
    nu = np.array([len(g) - 1 for g in groups], float)
    s2 = np.array([g.var(ddof=1) for g in groups])
    N = nu.sum()
    sp2 = (nu * s2).sum() / N
    num = N * np.log(sp2) - (nu * np.log(s2)).sum()
    C = (1 + ((1 / nu).sum() - 1 / N) / (3 * (len(groups) - 1))
         if correct else 1.0)
    chi = num / C
    return chi, C, stats.chi2.sf(chi, len(groups) - 1)

rng = np.random.default_rng(31)
gs = [rng.normal(0, 1, 12), rng.normal(0, 1.6, 9), rng.normal(0, 2.2, 15)]
c, C, p = bartlett_manual(gs)
r = stats.bartlett(*gs)
print(f"자작 구현: chi² = {c:.6f}, p = {p:.6f}  (보정인자 C = {C:.6f})")
print(f"scipy   : chi² = {r.statistic:.6f}, p = {r.pvalue:.6f}")
c0, _, p0 = bartlett_manual(gs, correct=False)
print(f"보정 없이 : chi² = {c0:.6f}, p = {p0:.6f}")

print("\n보정인자 C 가 표본 크기에 따라 얼마나 커지는가 (k=3, 균형)")
print(f"{'집단당 n':>9s} {'C':>9s}")
for n in [3, 5, 10, 20, 50, 100]:
    nu = np.array([n - 1.0] * 3)
    N = nu.sum()
    print(f"{n:9d} {1 + ((1 / nu).sum() - 1 / N) / 6:9.5f}")

print("\n보정의 효과: 등분산인데 기각하는 비율 (k=3, 정규, B=8000)")
rng = np.random.default_rng(77)
B = 8_000
print(f"{'집단당 n':>9s} {'보정 있음':>10s} {'보정 없음':>10s}")
for n in [3, 5, 10, 20]:
    a = b = 0
    for _ in range(B):
        g = [rng.normal(0, 1, n) for _ in range(3)]
        a += bartlett_manual(g)[2] < 0.05
        b += bartlett_manual(g, correct=False)[2] < 0.05
    print(f"{n:9d} {a / B:10.4f} {b / B:10.4f}")
자작 구현: chi² = 5.444673, p = 0.065721  (보정인자 C = 1.042839)
scipy   : chi² = 5.444673, p = 0.065721
보정 없이 : chi² = 5.677918, p = 0.058487

보정인자 C 가 표본 크기에 따라 얼마나 커지는가 (k=3, 균형)
    집단당 n         C
        3   1.22222
        5   1.11111
       10   1.04938
       20   1.02339
       50   1.00907
      100   1.00449

보정의 효과: 등분산인데 기각하는 비율 (k=3, 정규, B=8000)
    집단당 n      보정 있음      보정 없음
        3     0.0459     0.0848
        5     0.0517     0.0685
       10     0.0494     0.0578
       20     0.0501     0.0532

소수점 여섯 자리까지 scipy와 일치한다.

\(C\)가 고치는 것은 카이제곱 근사의 오차다.

집단당 \(n\) \(C\) 보정 없음 보정 있음
3 1.222 0.0848 0.0459
5 1.111 0.0685 0.0517
10 1.049 0.0578 0.0494
20 1.023 0.0532 0.0501

\(n=3\)에서 보정이 오류율을 0.085에서 0.046으로 절반 가까이 낮춘다.

\(C\)는 언제나 1보다 크다. 통계량을 나누므로 값을 줄이고, 따라서 보수적으로 만든다. 보정 없는 통계량은 카이제곱보다 체계적으로 크다.

\[ C=1+\frac{1}{3(k-1)}\left(\sum_i\frac{1}{\nu_i}-\frac{1}{\sum_i\nu_i}\right) \]

\(\nu_i\)가 작을수록 \(1/\nu_i\)가 크므로 \(C\)가 커진다. \(n\to\infty\)면 \(C\to1\)이고 보정이 사라진다.

불균형이면 보정이 더 커진다. \(\sum 1/\nu_i\)는 가장 작은 집단에 지배되므로, \(n=(3,3,50)\)이 \(n=(19,19,18)\)보다 \(C\)가 훨씬 크다.

이것이 바틀렛-박스 보정의 일반적 형태다. 같은 발상이 우도비 검정의 박스 보정, 다변량 분산의 박스 M 검정에도 쓰인다. 통계량의 기댓값을 자유도에 맞추는 스케일 조정이다.

주의 — 보정은 정규성을 고치지 못한다. \(C\)는 유한표본 오차만 다룬다. 연습문제 6의 0.674는 \(C\)가 있어도 그대로다.

연습문제 9. \(k=2\)일 때 바틀렛 검정과 분산비 \(F\) 검정의 관계를 확인하라. 두 검정이 같은 약점을 갖는가?

풀이
import numpy as np
from scipy import stats

rng = np.random.default_rng(55)
print("k=2 에서 바틀렛과 F 검정 (분산비) 비교")
print(f"{'':4s} {'n1':>4s} {'n2':>4s} {'s1²/s2²':>9s} "
      f"{'바틀렛 p':>10s} {'F 검정 p':>10s}")
for i in range(5):
    n1, n2 = int(rng.integers(8, 30)), int(rng.integers(8, 30))
    x = rng.normal(0, 1, n1)
    y = rng.normal(0, rng.uniform(1, 2.2), n2)
    F = x.var(ddof=1) / y.var(ddof=1)
    pf = 2 * min(stats.f.cdf(F, n1 - 1, n2 - 1),
                 stats.f.sf(F, n1 - 1, n2 - 1))
    print(f"{i:4d} {n1:4d} {n2:4d} {F:9.4f} "
          f"{stats.bartlett(x, y).pvalue:10.4f} {pf:10.4f}")

B = 20_000
print("\n제1종 오류율 (n1=n2=15, B=20000)")
for lab, seed, f in [("정규", 56, lambda r, n: r.normal(0, 1, n)),
                     ("t(5)", 57, lambda r, n: r.standard_t(5, n))]:
    rng2 = np.random.default_rng(seed)
    a = b = 0
    for _ in range(B):
        x, y = f(rng2, 15), f(rng2, 15)
        a += stats.bartlett(x, y).pvalue < 0.05
        F = x.var(ddof=1) / y.var(ddof=1)
        b += 2 * min(stats.f.cdf(F, 14, 14), stats.f.sf(F, 14, 14)) < 0.05
    print(f"  {lab:6s} 바틀렛 {a / B:.4f}   F 검정 {b / B:.4f}")
k=2 에서 바틀렛과 F 검정 (분산비) 비교
       n1   n2   s1²/s2²      바틀렛 p     F 검정 p
   0   29   26    0.5143     0.0914     0.0900
   1   28   24    0.6797     0.3398     0.3344
   2   29   14    0.3577     0.0258     0.0220
   3    9   14    0.2171     0.0330     0.0369
   4   20   22    0.7977     0.6192     0.6246

제1종 오류율 (n1=n2=15, B=20000)
  정규     바틀렛 0.0515   F 검정 0.0516
  t(5)   바틀렛 0.1484   F 검정 0.1485

두 검정이 사실상 같다. \(p\)-값이 소수점 둘째 자리까지 비슷하고, 오류율은 소수점 넷째 자리까지 같다(0.0515 대 0.0516, 0.1484 대 0.1485).

우연이 아니다. 둘 다 \(\ln(S_1^2/S_2^2)\)의 크기를 재고, 기준분포만 다르게 근사한다.

통계량 기준분포
\(F\) 검정 \(S_1^2/S_2^2\) \(F(\nu_1,\nu_2)\) 정확
바틀렛 \(\propto\) 로그비의 함수 \(\chi^2(1)\) 근사

\(F\) 검정 쪽이 정규 아래에서 정확하고, 바틀렛은 근사다. 그런데 \(C\) 보정 덕분에 차이가 무시할 수준이 된다.

\(t(5)\)에서 둘 다 0.148로 똑같이 무너진다. 같은 약점을 공유한다. 두 검정 모두 정규성에 결정적으로 의존한다.

이것이 중요한 이유. "\(F\) 검정으로 분산을 비교하면 되지 않나"라는 생각은 바틀렛의 문제를 전혀 해결하지 못한다. 두 검정은 같은 것이다.

그럼 분산 자체가 관심사일 때는? 등분산 검정을 가정 점검이 아니라 연구 질문으로 쓰는 경우가 있다.

분야 질문
공정관리 두 라인의 산포가 같은가
금융 두 자산의 변동성이 같은가
측정 두 장비의 정밀도가 같은가
교육 두 교수법의 격차가 같은가

이때도 정규성이 관건이다. 금융 수익률은 \(t(5)\)보다 꼬리가 두꺼워 바틀렛·\(F\) 검정을 그대로 쓰면 오류율이 0.15 이상이다.

권장 대안 셋.

  1. 브라운-포사이드(가정 점검용으로도, 연구 질문용으로도 무난)
  2. 부트스트랩(분산비의 신뢰구간을 직접 만든다)
  3. 순열검정(귀무가설 아래에서 라벨을 섞는다)

분산이 연구 질문이면 \(p\)보다 구간을 보고한다. "분산비가 1.8, 95% 구간 \([1.1,\,3.0]\)"이 "\(p=0.02\)"보다 훨씬 많은 것을 말해 준다.

연습문제 10. 등분산 검정의 선택 지침을 정리하라.

풀이

세 검정의 요약.

검정 통계량의 바탕 정규일 때 비정규일 때
바틀렛 \(\ln S_i^2\)의 흩어짐 최강력 0.68까지 붕괴
레빈(평균) \(\lvert y_{ij}-\bar y_i\rvert\)의 분산분석 약간 약함 0.25(치우침에 취약)
브라운-포사이드 \(\lvert y_{ij}-\text{med}_i\rvert\)의 분산분석 \(-1\%\sim-22\%\) 0.03~0.05 유지

핵심 수치 넷.

사실 값
로그정규에서 바틀렛의 오류율 0.674
같은 상황 브라운-포사이드 0.037
정규·\(r=1.5\)에서 바틀렛의 검정력 우위 \(+29\%\)
\(n=3\)에서 보정인자 \(C\) 1.222

선택 흐름.

등분산 검정이 필요한가?
    │
    ├─ 분산분석의 가정 점검 목적
    │     └─→ 하지 말 것. 처음부터 웰치를 쓴다
    │         (사전검정의 역설 — 가정 페이지 연습문제 7)
    │
    └─ 분산 자체가 연구 질문
          │
          ├─ 정규성이 보장됨 ──→ 바틀렛 (또는 k=2 면 F 검정)
          │
          └─ 그 외 ──→ 브라운-포사이드
                          또는 부트스트랩 구간

왼쪽 갈래가 중요하다. 이 페이지의 검정들은 분산분석의 관문으로 쓰라고 만들어진 것이 아니다. 웰치를 기본으로 쓰면 등분산 검정 자체가 필요 없다.

scipy 사용법.

목적 호출
바틀렛 stats.bartlett(*groups)
레빈(평균) stats.levene(*groups, center="mean")
브라운-포사이드 stats.levene(*groups) (기본값)
절사평균 중심 stats.levene(*groups, center="trimmed")

scipy의 기본값이 center="median", 곧 브라운-포사이드다. 기본값을 바꾸지 않는 것이 안전하다.

흔한 실수 넷.

실수 결과
치우친 자료에 바틀렛 거짓 양성(연습문제 6)
유의 → 웰치 분기 2단계 절차의 역설
비유의 → "분산이 같다" 검정력 부족일 수 있음
\(p\)만 보고 분산비를 안 봄 실질적 크기를 모름

세 번째가 흔하다. \(n=20\), \(\sigma\)비 1.5에서 브라운-포사이드의 검정력이 0.33이다. 셋 중 둘은 놓친다. "유의하지 않음"은 "같음"이 아니다.

보고 형식.

집단별 표준편차: 1.02, 1.11, 2.94  (비 2.9배)
브라운-포사이드 검정: W = ..., p = ...
→ 등분산을 가정하지 않고 웰치 분산분석을 사용

표준편차를 먼저 적고 검정을 나중에 적는다. 독자가 크기를 보고 판단할 수 있어야 한다.

한 문장. 바틀렛은 정규성이라는 값비싼 전제 위에서만 최고이며, 그 전제를 보장할 수 없는 대부분의 상황에서는 브라운-포사이드가 옳은 기본값이다.


정리하며

바틀렛 검정은 정규성 아래에서 최강력이지만 그 조건이 까다롭다.

  • 정규일 때는 최적이다. 등분산에 대한 균일최강력 불편검정이므로, 자료가 정말 정규라면 가장 예민하게 이분산을 잡아낸다.
  • 정규성 이탈에 매우 민감하다. 꼬리가 조금만 두꺼워도 분산이 같은데도 기각한다. 이 검정이 기각했을 때 이분산 때문인지 비정규성 때문인지 구별할 수 없다는 것이 근본적인 약점이다.
  • 그래서 실무에서는 레빈이 선호된다. 중앙값을 쓰는 브라운–포사이드 변형이 특히 강건하다.
  • \(k=2\) 면 \(F\) 검정과 비슷한 역할이다. 둘 다 정규성에 민감하다는 약점을 공유한다.
  • 검정 결과로 분산분석 방법을 고르지 말 것. 2단계 절차가 전체 오류율을 왜곡하며, 웰치를 기본으로 쓰는 편이 낫다.

다음 절 분산에 대한 카이제곱 검정으로 넘어간다.