분산과 표준편차¶
개요¶
분산과 표준편차는 통계적 흩어짐을 재는 가장 널리 쓰이는 측도다. 개별 자료점이 평균에서 얼마나 벗어나는지를 정량화하여 자료의 퍼짐과 일관성에 관한 핵심 정보를 제공한다.
0. 왜 제곱하는가¶
흩어짐을 재려면 "각 점이 중심에서 얼마나 떨어져 있는가"를 평균 내면 될 것 같다. 그런데 편차 \(x_i - \bar{x}\)를 그대로 평균 내면 언제나 정확히 0이 된다.
자료가 어떻게 생겼든 그렇다. 양의 편차와 음의 편차가 정확히 상쇄되기 때문이며, 이는 평균이 균형점이라는 사실의 다른 표현이다. 그러므로 편차를 그대로 평균 내는 것은 흩어짐의 측도가 될 수 없다.
부호를 없애는 방법은 두 가지다.
(1) 절댓값을 취한다 — 평균절대편차
(2) 제곱한다 — 분산
보기 1. 평균절대편차와 표준편차 가운데 어느 것이 더 큰가. 자료 \(2, 4, 4, 4, 5, 5, 7, 9\) 에서 평균절대편차는 \(1.5\), 표준편차는 \(2.0\) 이다.
(1) 어떤 자료에서나 \(\text{MeanAD} \le \sigma\) 임을 보이시오. 등호는 언제 성립하는가.
(2) 두 값의 비 \(\text{MeanAD}/\sigma\) 가 이 자료에서 얼마인지 구하고, 정규분포에서의 이론값 \(\sqrt{2/\pi}\) 와 견주시오.
풀이
(1) 해석적으로. 편차를 \(d_i = x_i - \bar x\) 라 하면 두 측도는
이다. 코시–슈바르츠 부등식을 벡터 \((\lvert d_1\rvert, \ldots, \lvert d_n\rvert)\) 과 \((1, \ldots, 1)\) 에 쓰면
이고, 양변을 \(n^2\) 으로 나누면
가 된다. 코시–슈바르츠의 등호는 두 벡터가 평행할 때만 성립하므로, 모든 \(\lvert d_i\rvert\) 가 같을 때에만 두 측도가 일치한다. 예컨대 \(\{0, 2\}\) 나 \(\{1, 1, 3, 3\}\) 처럼 모든 점이 평균에서 똑같이 떨어져 있는 자료다.
같은 결론을 옌센 부등식으로도 얻는다. \(\sqrt{\cdot}\) 이 오목하므로 \(E[\sqrt{Y}] \le \sqrt{E[Y]}\) 이고 \(Y = d^2\) 을 넣으면 된다.
그러므로 "표준편차는 평균에서 평균적으로 얼마나 떨어져 있는가"가 아니다. 그 문장이 가리키는 수는 평균절대편차이고, 표준편차는 언제나 그보다 크다. 제곱이 큰 편차를 부풀려 담기 때문이다.
(2) 정규분포에서는 비가 정해져 있다. \(X \sim N(\mu, \sigma^2)\) 이면 \(\lvert X - \mu\rvert/\sigma\) 가 반정규분포를 따르고
이다. 곧 정규자료에서는 \(\text{MeanAD}/\sigma \approx 0.798\) 이어야 한다. 이 자료는 \(1.5/2.0 = 0.75\) 다.
(3) 수치적으로.
import numpy as np
x = np.array([2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0])
# 편차는 각 값이 평균에서 얼마나 떨어졌는지다. 부호가 남아 있다.
dev = x - x.mean()
print("편차 :", dev)
print("편차의 합 :", dev.sum()) # 항상 0
print("평균절대편차 :", np.abs(dev).mean())
print("분산(모집단) :", (dev ** 2).mean())
print("표준편차 :", np.sqrt((dev ** 2).mean()))
# (1) MeanAD <= sigma 인가. 비를 재고 등호가 되는 자료도 확인한다.
mad_, sd_ = np.abs(dev).mean(), np.sqrt((dev ** 2).mean())
print(f"\n비 MeanAD/sigma = {mad_ / sd_:.4f} (정규 이론값 sqrt(2/pi) = {np.sqrt(2 / np.pi):.4f})")
print("등호가 되는 자료 (|편차| 가 모두 같은 경우):")
for arr in ([0.0, 2.0], [1.0, 1.0, 3.0, 3.0], [0.0, 1.0, 2.0]):
a = np.array(arr)
dv = a - a.mean()
print(f" {arr}: MeanAD {np.abs(dv).mean():.4f}, sigma {np.sqrt((dv ** 2).mean()):.4f},"
f" 비 {np.abs(dv).mean() / np.sqrt((dv ** 2).mean()):.4f}")
# (2) 정규자료에서 비가 정말 0.798 인가.
rng = np.random.default_rng(0)
z = rng.normal(0, 1, 2000)
dz = z - z.mean()
print(f"\n정규 n=2000: 비 {np.abs(dz).mean() / np.sqrt((dz ** 2).mean()):.4f}")
출력:
편차 : [-3. -1. -1. -1. 0. 0. 2. 4.]
편차의 합 : 0.0
평균절대편차 : 1.5
분산(모집단) : 4.0
표준편차 : 2.0
비 MeanAD/sigma = 0.7500 (정규 이론값 sqrt(2/pi) = 0.7979)
등호가 되는 자료 (|편차| 가 모두 같은 경우):
[0.0, 2.0]: MeanAD 1.0000, sigma 1.0000, 비 1.0000
[1.0, 1.0, 3.0, 3.0]: MeanAD 1.0000, sigma 1.0000, 비 1.0000
[0.0, 1.0, 2.0]: MeanAD 0.6667, sigma 0.8165, 비 0.8165
정규 n=2000: 비 0.8003
편차의 합이 정확히 \(0\) 이다. 절댓값이나 제곱을 거친 뒤에야 뜻이 있는 수가 나온다.
(1)이 확인된다. 이 자료에서 \(1.5 < 2.0\) 이고, \(\lvert d_i\rvert\) 가 모두 같은 두 자료 \(\{0,2\}\) 와 \(\{1,1,3,3\}\) 에서는 비가 정확히 \(1.0000\) 이다. \(\{0,1,2\}\) 는 \(\lvert d\rvert\) 가 \(1, 0, 1\) 로 같지 않으므로 비가 \(0.8165 < 1\) 이다.
(2)에서 이 자료의 비 \(0.7500\) 은 정규 이론값 \(0.7979\) 보다 작다. 비가 작다는 것은 제곱이 특정 점들에 더 몰려 있다는 뜻이고, 이 자료에서는 혼자 떨어진 \(x = 9\) 가 그 역할을 한다. 편차 \(4\) 하나가 제곱합 \(32\) 가운데 \(16\), 곧 절반을 차지하는데 절대편차 합 \(12\) 에서는 \(4\), 곧 삼분의 일만 차지한다.
그리고 정규자료 \(2000\) 개에서 잰 비는 \(0.8003\) 으로 이론값 \(0.7979\) 와 \(0.3\%\) 차이다. 표본이 유한하므로 정확히 맞지는 않지만, 이 비가 정규성에서 얼마나 멀리 있는지를 재는 쓸모 있는 눈금이 된다는 것은 보인다. 이 자료의 \(0.75\) 는 정규보다 꼬리가 무거운 쪽, 곧 큰 편차 하나가 두드러지는 쪽이다.
그런데 왜 절댓값이 아니라 제곱을 쓰는가¶
평균절대편차가 더 직관적인데도 실무에서 거의 쓰이지 않는 데에는 이유가 있다.
- 미분 가능하다. \(|x|\)는 \(0\)에서 미분할 수 없지만 \(x^2\)는 어디서나 매끄럽다. 최소제곱법, 회귀, 최대가능도 같은 최적화 기반 방법이 모두 이 성질에 기댄다.
- 대수적으로 분해된다. 독립인 두 변수의 분산은 더해진다(\(\operatorname{Var}(X+Y) = \operatorname{Var}(X) + \operatorname{Var}(Y)\)). 절대편차에는 이런 성질이 없다.
- 정규분포와 직접 연결된다. 정규분포의 밀도에 \(\sigma^2\)이 그대로 들어가 있어, 이후의 거의 모든 추론이 분산 위에 세워진다.

세 칸이 지금까지의 이야기를 차례로 담고 있다. 왼쪽은 편차를 그냥 더하면 왜 안 되는지다. 평균이 균형점이므로 왼쪽으로 간 만큼 오른쪽으로 가고, 이 자료에서는 \(-7.75\)와 \(+7.75\)가 정확히 상쇄된다. 가운데는 부호를 없애는 두 방법의 생김새다. \(|d|\)는 원점에서 꺾여 있고 \(d^2\)은 매끄러우며, 멀어질수록 두 곡선의 높이 차가 빠르게 벌어진다.
오른쪽이 그 높이 차가 실제로 무엇을 뜻하는지 보여 준다. 자료 여덟 개 가운데 혼자 멀리 떨어진 \(x = 14\) 하나가 평균절대편차에서는 전체의 47%를 차지하지만 분산에서는 78%를 차지한다. 나머지 일곱 점이 함께 가진 몫이 22%뿐이라는 뜻이다. 한 점이 흩어짐의 정의를 거의 혼자 정하는 셈이다.
대신 제곱은 큰 편차에 훨씬 큰 벌점을 준다. 이것이 장점(큰 이탈을 민감하게 잡는다)이자 단점(이상치에 취약하다)이다. 이상치가 걱정되면 중앙값 절대편차나 IQR 같은 강건 측도를 쓴다.
이름이 비슷한 두 가지를 구별하라
| 이름 | 정의 | 성질 |
|---|---|---|
| 평균절대편차 (mean absolute deviation) | \(\frac{1}{n}\sum \lvert x_i - \bar{x} \rvert\) | 평균을 중심으로. 강건하지 않다 |
| 중앙값 절대편차 (median absolute deviation, MAD) | \(\operatorname{median}(\lvert x_i - \operatorname{median}(x) \rvert)\) | 중앙값을 중심으로. 붕괴점 50%로 강건하다 |
영문 약자가 둘 다 MAD여서 혼동이 잦다. 이 책에서 MAD는 언제나 뒤쪽, 곧 중앙값 절대편차를 뜻한다.
1. 분산¶
정의 1. 분산¶
분산은 평균으로부터의 편차를 제곱한 값의 평균을 잰다. 제곱함으로써 모든 편차가 양수로 기여하게 하고 큰 편차에 더 큰 벌점을 준다.
공식¶
모분산:
표본분산(베셀 보정 적용):
분모 \(n - 1\)은 참된 모평균 \(\mu\) 대신 표본평균 \(\bar{x}\)를 씀으로써 생기는 아래쪽 편향을 보정한다.
예¶
평균이 \(\bar{x} = 88\)인 자료 70, 85, 90, 95, 100에 대해:
- 제곱편차: \((70-88)^2 = 324\), \((85-88)^2 = 9\), \((90-88)^2 = 4\), \((95-88)^2 = 49\), \((100-88)^2 = 144\)
- 합: \(324 + 9 + 4 + 49 + 144 = 530\)
- 표본분산: \(s^2 = 530 / 4 = 132.5\)
파이썬에서 분산 계산하기¶
보기 2. ddof 가 실제로 하는 일은 분모를 \(n - \texttt{ddof}\) 로 바꾸는 것 하나뿐이다. 자료 \(1.5,\ 2.5,\ 4,\ 2,\ 1,\ 1\) 에서 ddof=0 이 \(1.0833\ldots\), ddof=1 이 \(1.3\) 을 준다.
(1) 두 값을 분수로 정확히 적으시오. 두 분수의 분자가 같은 수인 까닭은 무엇인가.
(2) ddof 를 \(0\) 부터 \(5\) 까지 바꾸면 어떤 값들이 나오는가. \(\texttt{ddof} = 6\) 에서는 무슨 일이 벌어지는가.
풀이
(1) 해석적으로. 평균이
로 깔끔하게 떨어진다. 편차는 \(-0.5,\ 0.5,\ 2,\ 0,\ -1,\ -1\) 이고 제곱합은
이다. 두 분산은 이 하나의 \(\text{SS}\) 를 각각 \(6\) 과 \(5\) 로 나눈 것이므로
이다. 두 분수의 분자가 \(13\) 으로 같은 것은 우연이 아니다. 분자는 \(\text{SS}\) 에서 온 것이고 ddof 는 분모만 건드리기 때문이다. 비는 \(\frac{13/10}{13/12} = \frac{12}{10} = \frac65\) 로 \(n/(n-1) = 6/5\) 와 같다.
(2) 예측. ddof 는 분모를 \(n - \texttt{ddof}\) 로 바꾼다. 그러므로
이고 \(\texttt{ddof} = 0, \ldots, 5\) 에 대해 \(\frac{13}{12}, \frac{13}{10}, \frac{13}{8}, \frac{13}{6}, \frac{13}{4}, \frac{13}{2}\) 다. 마지막은 \(6.5\), 곧 제곱합 그 자체다. \(\texttt{ddof} = 6\) 이면 분모가 \(0\) 이 되어 나눗셈이 정의되지 않는다.
(3) 수치적으로.
import numpy as np
from fractions import Fraction
sample_data = np.array([1.5, 2.5, 4, 2, 1, 1])
# ddof 는 "자유도에서 빼는 수"다. 기본값 0 이면 n 으로 나눈 모분산이 된다.
population_variance = sample_data.var()
print(f"모분산 (ddof=0): {population_variance}")
# ddof=1 이면 n-1 로 나눈다. 표본에서 모분산을 추정할 때 쓰는 값이다.
sample_variance = sample_data.var(ddof=1)
print(f"표본분산 (ddof=1): {sample_variance}")
# (1) 분수로 정확히 적으면 무엇인가.
n = len(sample_data)
SS = ((sample_data - sample_data.mean()) ** 2).sum()
print(f"\n평균 {sample_data.mean()}, 제곱합 SS = {SS} = {Fraction(SS).limit_denominator()}")
print(f" ddof=0: SS/{n} = {Fraction(SS) / n} = {SS / n}")
print(f" ddof=1: SS/{n-1} = {Fraction(SS) / (n - 1)} = {SS / (n - 1)}")
# (2) ddof 를 0 부터 5 까지 밀어 본다. 분모가 n - ddof 로 줄어들 뿐이다.
print(f"\n{'ddof':>6}{'분모 n-ddof':>12}{'분수':>10}{'값':>22}")
for ddof in range(6):
print(f"{ddof:>6}{n - ddof:>12}{str(Fraction(SS) / (n - ddof)):>10}"
f"{sample_data.var(ddof=ddof):>22.10f}")
print(" ddof=6 이면 분모가 0 이라 나눗셈이 정의되지 않는다"
" (numpy 는 경고와 함께 inf 를 돌려준다)")
출력:
모분산 (ddof=0): 1.0833333333333333
표본분산 (ddof=1): 1.3
평균 2.0, 제곱합 SS = 6.5 = 13/2
ddof=0: SS/6 = 13/12 = 1.0833333333333333
ddof=1: SS/5 = 13/10 = 1.3
ddof 분모 n-ddof 분수 값
0 6 13/12 1.0833333333
1 5 13/10 1.3000000000
2 4 13/8 1.6250000000
3 3 13/6 2.1666666667
4 2 13/4 3.2500000000
5 1 13/2 6.5000000000
ddof=6 이면 분모가 0 이라 나눗셈이 정의되지 않는다 (numpy 는 경고와 함께 inf 를 돌려준다)
(1)과 (2)가 그대로 맞는다. 여섯 줄의 분수가 모두 분자 \(13\) 을 공유하고 분모만 \(12, 10, 8, 6, 4, 2\) 로 줄어든다. \(\texttt{ddof} = 5\) 에서 값이 \(6.5\), 곧 제곱합과 같아지는 것도 예측대로다.
그러므로 ddof 는 통계적 개념이 아니라 산술적 손잡이다. "자유도"라는 이름이 붙어 있지만 함수가 하는 일은 분모를 \(n - \texttt{ddof}\) 로 바꾸는 것 하나뿐이고, 그 가운데 \(\texttt{ddof} = 1\) 만이 불편추정량을 준다는 사실은 함수 밖에서 와야 하는 지식이다. \(\texttt{ddof} = 2\) 를 넣어도 numpy 는 불평하지 않고 \(1.625\) 를 돌려준다.
한편 이 자료는 변수 이름이 sample_data 이므로 보고할 값은 \(\texttt{ddof=1}\) 의 \(1.3\) 이다. 코드가 \(\texttt{ddof=0}\) 의 값을 population_variance 라 부르는 것은 "\(n\) 으로 나눈 양"을 가리키는 관례적 이름일 뿐이고, 이 여섯 개가 모집단 전체라는 뜻이 아니다. 이 혼동이 ddof 를 둘러싼 오류의 대부분을 만든다.
해석¶
분산이 132.5라는 것은 시험 점수가 평균으로부터 평균적으로 132.5 단위의 제곱거리만큼 흩어져 있다는 뜻이다. 분산은 제곱된 단위로 표현되므로 직접 해석하기 어려울 수 있으며, 그래서 표준편차가 흔히 선호된다.
2. 표준편차¶
정의 2. 표준편차¶
표준편차는 분산의 제곱근이다. 퍼짐의 측도를 자료의 원래 단위로 되돌려 직접 해석할 수 있게 만든다.
공식¶
모표준편차:
표본표준편차:
예¶
위의 분산을 쓰면 \(s = \sqrt{132.5} \approx 11.51\)이다.
즉 시험 점수가 평균에서 평균적으로 약 11.51점만큼 벗어난다는 뜻이다.
파이썬에서 표준편차 계산하기¶
보기 3. 같은 자료의 두 표준편차 \(1.0408329997330663\) 과 \(1.140175425099138\) 은 보기 좋지 않은 소수지만 정확한 근호 꼴을 갖는다.
(1) 두 값을 \(\sqrt{\ }\) 꼴로 정확히 적고, 비가 \(\sqrt{6/5}\) 임을 보이시오.
(2) 이 자료의 평균절대편차를 구해 두 표준편차와 견주시오. "표준편차 \(1.14\) 는 자료가 평균에서 평균적으로 \(1.14\) 만큼 벗어난다는 뜻"이라는 말이 맞는가.
풀이
(1) 해석적으로. 보기 2 에서 분산이 \(13/12\) 과 \(13/10\) 이었으므로
이다. 수로 바꾸면 \(\sqrt{39} = 6.2450\) 이라 \(\hat\sigma = 1.0408330\), \(\sqrt{130} = 11.401754\) 라 \(s = 1.1401754\) 다. 비는
이다. 분산의 비 \(6/5\) 가 제곱근을 거쳐 \(\sqrt{6/5}\) 가 된다. 분산에서 \(20\%\) 던 차이가 표준편차에서는 \(9.5\%\) 로 줄어든다.
(2) 해석적으로 — 그 말은 맞지 않다. 평균절대편차는 편차 \(-0.5,\ 0.5,\ 2,\ 0,\ -1,\ -1\) 의 절대값 평균이므로
이다. "평균에서 평균적으로 얼마나 벗어나는가"라는 질문의 답은 \(0.833\) 이고, 표준편차 \(1.0408\)(또는 \(1.1402\))과 다르다. 보기 1 에서 증명했듯 \(\text{MeanAD} \le \hat\sigma\) 는 언제나 성립하므로 표준편차를 "평균 이탈"로 읽으면 늘 과장하게 된다. 이 자료에서는 \(0.833\) 을 \(1.041\) 로, \(25\%\) 부풀린다.
표준편차의 정직한 뜻은 "제곱평균제곱근(RMS) 이탈" 이다. 이탈을 제곱해 평균 내고 제곱근을 씌운 것이지, 이탈을 평균 낸 것이 아니다.
(3) 수치적으로.
import numpy as np
sample_data = np.array([1.5, 2.5, 4, 2, 1, 1])
# 표준편차는 분산의 제곱근이므로 ddof 규칙도 그대로 따라간다.
population_std = sample_data.std()
print(f"모표준편차 (ddof=0): {population_std}")
# n-1 로 나눈 표본분산의 제곱근이다.
sample_std = sample_data.std(ddof=1)
print(f"표본표준편차 (ddof=1): {sample_std}")
# (1) 근호 꼴이 정말 같은 값인가.
print(f"\nsqrt(39)/6 = {np.sqrt(39) / 6!r}")
print(f"sqrt(130)/10 = {np.sqrt(130) / 10!r}")
print(f"비 s/sigma_hat = {sample_std / population_std:.10f}, sqrt(6/5) = {np.sqrt(6 / 5):.10f}")
# (2) "평균에서 평균적으로 벗어난 거리" 는 어느 수인가.
dev = sample_data - sample_data.mean()
print(f"\n평균절대편차 (평균 이탈) = {np.abs(dev).mean():.10f}")
print(f"RMS 이탈 = 표준편차(ddof=0) = {population_std:.10f}")
print(f" 표준편차가 평균 이탈보다 {100 * (population_std / np.abs(dev).mean() - 1):.1f}% 크다")
출력:
모표준편차 (ddof=0): 1.0408329997330663
표본표준편차 (ddof=1): 1.140175425099138
sqrt(39)/6 = 1.0408329997330663
sqrt(130)/10 = 1.1401754250991378
비 s/sigma_hat = 1.0954451150, sqrt(6/5) = 1.0954451150
평균절대편차 (평균 이탈) = 0.8333333333
RMS 이탈 = 표준편차(ddof=0) = 1.0408329997
표준편차가 평균 이탈보다 24.9% 크다
(1)이 맞는다. \(\sqrt{39}/6\) 이 population_std 와 비트 단위로 같고, \(\sqrt{130}/10\) 은 sample_std 와 마지막 자리에서 \(1\) ULP 다르다(\(\ldots 991378\) 대 \(\ldots 99138\) — 같은 양을 다른 연산 순서로 계산해 생기는 부동소수점 반올림이며, 상대오차가 \(\varepsilon\) 한 배라 정상이다). 비도 \(1.0954451150 = \sqrt{6/5}\) 로 열 자리가 같다.
(2)도 맞는다. 평균 이탈은 \(0.8333\) 이고 표준편차는 \(1.0408\) 로 \(24.9\%\) 크다. 그러므로 표준편차를 보고할 때 "평균적으로 이만큼 벗어난다"고 풀어 말하면 과장이 된다. 쓸 수 있는 정직한 문장은 두 가지다.
- "이탈의 제곱평균제곱근이 \(1.04\) 다" — 정확하지만 입에 잘 붙지 않는다.
- "자료가 대체로 평균 \(\pm 1.04\) 안에 있다" — 정규분포라면 그 안에 약 \(68\%\) 가 들어가므로 쓸 만한 어림이다. 다만 정규 가정에 기댄 말이라는 것을 알아야 한다. 가정 없이 말할 수 있는 것은 연습문제 9 의 체비쇼프 한계뿐이다.
응용¶
표준편차는 여러 분야에서 쓰인다. 금융 수익률의 변동성 평가, 과학적 측정의 퍼짐 측정, 제조 일관성 평가 등이다. 정규분포에서는 자료의 약 68%가 평균에서 표준편차 1배 안에, 95%가 2배 안에, 99.7%가 3배 안에 들어간다(경험 규칙).
3. 모집단 대 표본: ddof 매개변수¶
NumPy와 pandas에서 분산과 표준편차를 계산할 때 ddof(delta degrees of freedom) 매개변수가 분모를 결정한다.
| 상황 | 분모 | ddof |
사용할 때 |
|---|---|---|---|
| 모집단 | \(N\) | 0 | 모집단 전체를 가지고 있을 때 |
| 표본 | \(n - 1\) | 1 | 더 큰 모집단에서 뽑은 표본을 가지고 있을 때 |
NumPy의 기본값은 ddof=0(모집단)이고 pandas의 기본값은 ddof=1(표본)이다. 어느 쪽을 계산하는지 언제나 명시하라.
4. 표준편차의 쓰임: Z-점수¶
표준편차는 흩어짐을 재는 데서 끝나지 않는다. 서로 다른 척도의 값을 비교 가능하게 만드는 자로도 쓰인다.
중간고사가 평균 \(60\)점에 \(\sigma = 10\), 기말고사가 평균 \(80\)점에 \(\sigma = 20\)이라 하자. 어떤 학생이 중간에 \(70\)점, 기말에 \(95\)점을 받았다. 어느 쪽이 더 잘한 것인가?
원점수만 보면 기말이 높다. 그러나 두 시험은 난이도도 퍼짐도 다르므로 그대로 비교할 수 없다. 각 점수가 자기 분포의 중심에서 표준편차 몇 배만큼 떨어져 있는지로 바꾸면 비교할 수 있다. 이것이 Z-점수다.
보기 4. \(Z = +1.00\) 이 \(Z = +0.75\) 보다 반드시 좋은 성적인가. 중간고사 \(70\) 점(\(\mu = 60\), \(\sigma = 10\))과 기말고사 \(95\) 점(\(\mu = 80\), \(\sigma = 20\))을 견준다.
(1) 두 분포가 모두 정규라면 두 \(Z\) 가 각각 몇 번째 백분위인가. \(300\) 명 학급에서 등수로는 몇 명 차이인가.
(2) 두 분포의 모양이 다르면 \(Z\) 의 순서와 백분위의 순서가 뒤집힐 수 있는가. 할 수 있으면 반례를 만드시오.
풀이
(1) 해석적으로. 정규라면 백분위가 \(\Phi(z)\) 다.
차이는 \(0.0680\) 이다. \(300\) 명 학급에서 위에 있는 사람 수는 \(300\,(1 - \Phi(z))\) 이므로
이고, 등수로 약 \(48\) 등과 \(68\) 등, 스무 명 차이다. 중간고사 쪽이 낫다.
(2) 그런데 그 결론은 정규 가정 위에 서 있다. \(Z\) 는 평균과 표준편차만 쓰므로 분포의 모양을 보지 않는다. 모양이 다른 두 분포에서 같은 \(Z\) 가 다른 백분위에 대응하고, 심하면 순서가 뒤집힌다.
반례를 만들어 보자. 중간고사 점수가 \([0, 1]\) 에서 균등하고 기말고사 점수가 지수분포(평균 \(1\))를 따른다고 하자.
- 균등\((0,1)\): \(\mu = 0.5\), \(\sigma = 1/\sqrt{12} = 0.28868\). \(Z = +1.00\) 은 \(x = 0.5 + 0.28868 = 0.78868\) 이고, 균등분포에서 백분위는 \(x\) 자신이므로 \(78.87\) 번째 백분위다.
- 지수\((1)\): \(\mu = 1\), \(\sigma = 1\). \(Z = +0.75\) 는 \(x = 1.75\) 이고 백분위는 \(1 - e^{-1.75} = 0.82623\), 곧 \(82.62\) 번째 백분위다.
순서가 뒤집혔다. \(Z\) 가 작은 쪽이 더 많은 사람을 이겼다. 까닭은 분명하다. 균등분포는 꼬리가 없어 \(1\sigma\) 위쪽에도 아직 \(21\%\) 가 남아 있고(\(\sigma\) 가 범위의 \(28.9\%\) 나 되므로), 지수분포는 오른쪽 꼬리가 길어 평균 위로 \(0.75\sigma\) 만 올라가도 이미 대부분을 지나친다.
(3) 수치적으로.
import numpy as np
from scipy import stats
# 두 시험은 평균도 표준편차도 다르다. 원점수만으로는 견줄 수 없다.
midterm = {"score": 70, "mu": 60, "sigma": 10}
final = {"score": 95, "mu": 80, "sigma": 20}
# Z-점수는 "평균에서 표준편차 몇 개만큼 떨어졌나"를 재므로 단위가 사라진다.
for name, e in [("중간고사", midterm), ("기말고사", final)]:
z = (e["score"] - e["mu"]) / e["sigma"]
print(f"{name}: 원점수 {e['score']}, Z = {z:+.2f}")
# (1) 두 분포가 정규라면 백분위와 등수는?
print(f"\n정규 가정 아래 (300명 학급)")
for name, z in (("중간고사", 1.00), ("기말고사", 0.75)):
p = stats.norm.cdf(z)
print(f" {name}: Z={z:+.2f} 백분위 {100 * p:.2f}% 위에 있는 사람 {300 * (1 - p):.1f}명")
print(f" 백분위 차 {100 * (stats.norm.cdf(1.0) - stats.norm.cdf(0.75)):.2f}%p")
# (2) 모양이 다르면 순서가 뒤집힌다.
print(f"\n모양을 바꾸면 (중간=균등(0,1), 기말=지수(1))")
x_u = 0.5 + 1.00 * (1 / np.sqrt(12)) # 균등의 Z=+1.00
x_e = 1 + 0.75 * 1 # 지수의 Z=+0.75
print(f" 중간 Z=+1.00 -> x={x_u:.5f}, 백분위 {100 * stats.uniform.cdf(x_u):.2f}%")
print(f" 기말 Z=+0.75 -> x={x_e:.5f}, 백분위 {100 * stats.expon.cdf(x_e):.2f}%")
print(f" -> Z 는 중간이 크지만 백분위는 기말이 크다. 순서가 뒤집혔다.")
출력:
중간고사: 원점수 70, Z = +1.00
기말고사: 원점수 95, Z = +0.75
정규 가정 아래 (300명 학급)
중간고사: Z=+1.00 백분위 84.13% 위에 있는 사람 47.6명
기말고사: Z=+0.75 백분위 77.34% 위에 있는 사람 68.0명
백분위 차 6.80%p
모양을 바꾸면 (중간=균등(0,1), 기말=지수(1))
중간 Z=+1.00 -> x=0.78868, 백분위 78.87%
기말 Z=+0.75 -> x=1.75000, 백분위 82.62%
-> Z 는 중간이 크지만 백분위는 기말이 크다. 순서가 뒤집혔다.
(1)의 네 수가 그대로 나온다. \(84.13\%\) 와 \(77.34\%\), 등수로 약 \(48\) 등과 \(68\) 등이다.
(2)의 반례도 성립한다. \(Z\) 의 순서는 \(+1.00 > +0.75\) 인데 백분위의 순서는 \(78.87\% < 82.62\%\) 로 반대다.
그러므로 \(Z\) 로 서로 다른 분포를 견줄 때 조심해야 할 것이 하나 있다. \(Z\) 는 같은 분포 안에서는 순서를 그대로 보존한다(\(Z\) 가 원점수의 증가함수이므로). 그러나 다른 두 분포 사이의 비교에서는 "\(Z\) 가 크다"와 "백분위가 높다"가 다른 말이 된다. 본문의 결론("중간고사가 더 좋은 성적이다")은 두 시험 점수가 비슷한 모양으로 분포한다는 가정 아래에서만 안전하다.
등수를 알고 싶으면 \(Z\) 를 거치지 말고 백분위를 직접 보고하는 것이 낫다. 백분위는 순위만 쓰므로 모양에 무관하게 "몇 명을 이겼는가"를 정확히 답한다. 그 대가로 간격 정보를 버리는데, 두 측도를 나란히 보고하는 것이 실무의 답이다.
중간고사의 \(70\)점이 기말의 \(95\)점보다 상대적으로 더 좋은 성적이다. 원점수는 \(25\)점이나 낮지만, 중간은 평균보다 \(1\) 표준편차 위에 있고 기말은 \(0.75\) 표준편차 위에 있기 때문이다.
Z-점수는 단위가 없다. 키(cm)와 몸무게(kg)처럼 단위가 아예 다른 변수도 Z-점수로 바꾸면 같은 자 위에서 견줄 수 있다. 이 성질 때문에 Z-점수는 이후 여러 곳에서 쓰인다.
- 이상치 탐지: \(|Z| > 3\)인 점을 표시한다(이상치와 지렛대점).
- 표준정규분포: \(X \sim N(\mu, \sigma^2)\)이면 \(Z \sim N(0, 1)\)이다(4장).
- 자료 전처리: 거리 기반 방법이나 정칙화에서는 변수의 척도를 맞추는 표준화가 필수다(18장).
Z-점수가 분포의 모양을 바꾸지는 않는다
표준화는 위치와 척도만 옮긴다. 치우친 분포를 표준화해도 여전히 치우쳐 있다. \(Z\)가 \(0\)을 중심으로 표준편차 \(1\)을 갖게 될 뿐, 정규분포가 되는 것이 아니다.
"표준화하면 정규분포가 된다"는 흔한 오해다. 정규분포가 되는 것은 원래 분포가 정규일 때뿐이다.
5. 실무적 고려사항¶
자료의 분포: 정규분포에서 표준편차는 경험 규칙을 통해 깔끔한 해석을 갖는다. 치우친 분포에서는 전형적인 퍼짐을 정확히 반영하지 못할 수 있다.
이상치에 대한 민감성: 제곱이 큰 편차를 증폭하므로 분산과 표준편차 모두 극단값에 민감하다. 치우친 자료나 이상치가 있는 자료에는 IQR이 더 강건한 대안이다.
실제 사례:
- 주식시장 변동성: 수익률의 표준편차가 위험을 잰다. 표준편차가 클수록 가격 변동이 크고 투자 위험이 높다.
- 학생 시험 점수: 표준편차가 낮으면 대부분의 학생이 비슷한 점수를 받았다는 뜻이고, 높으면 성취도의 편차가 크다는 뜻이다.
- 제조 품질: 제품 측정값의 표준편차가 공정의 일관성을 나타낸다. 값이 낮을수록 품질관리가 촘촘하다.
연습문제¶
연습문제 1. 자료 \(\{3, 7, 7, 9, 14\}\)에 대해 (a) \(\bar{x}\)를 계산하라. (b) 베셀 보정을 적용해 \(s^2\)을 계산하라. (c) \(s\)를 계산하라. (d) 모든 관측값을 \(c = 10\)만큼 늘리면 평균, 분산, 표준편차는 어떻게 되는가?
풀이
(a) \(\bar{x} = 40/5 = 8\).
(b) 제곱편차: \(25, 1, 1, 1, 36\), 합 \(= 64\), \(s^2 = 64/4 = 16\).
(c) \(s = \sqrt{16} = 4\).
(d) 상수 \(c\)를 더하면 평균은 \(c\)만큼 이동하지만 분산과 표준편차는 변하지 않는다. \((x_i + c) - (\bar{x} + c) = x_i - \bar{x}\)이므로 모든 제곱편차가 동일하다. 새 평균은 18이고 \(s^2 = 16\), \(s = 4\) 그대로다.
연습문제 2.
같은 자료를 넣어도 라이브러리마다 분산이 다르게 나온다. numpy, pandas, 표준 라이브러리 statistics 로 같은 자료의 분산과 표준편차를 구해 비교하고, 왜 그런지 설명하라. 어떤 경우에 이 차이가 실제로 문제가 되는가?
풀이
import numpy as np
import pandas as pd
import statistics
x = [2, 4, 4, 4, 5, 5, 7, 9]
n = len(x)
print(f"자료 {x}, n = {n}\n")
print(f"{'호출':>26s}{'분산':>12s}{'표준편차':>12s}")
print(f"{'np.var(x)':>26s}{np.var(x):>12.6f}{np.std(x):>12.6f}")
print(f"{'pd.Series(x).var()':>26s}{pd.Series(x).var():>12.6f}"
f"{pd.Series(x).std():>12.6f}")
print(f"{'statistics.pvariance(x)':>26s}{statistics.pvariance(x):>12.6f}"
f"{statistics.pstdev(x):>12.6f}")
print(f"{'statistics.variance(x)':>26s}{statistics.variance(x):>12.6f}"
f"{statistics.stdev(x):>12.6f}")
print(f"\n두 값의 비 = {pd.Series(x).var() / np.var(x):.6f}")
print(f"n / (n-1) = {n / (n - 1):.6f}")
자료 [2, 4, 4, 4, 5, 5, 7, 9], n = 8
호출 분산 표준편차
np.var(x) 4.000000 2.000000
pd.Series(x).var() 4.571429 2.138090
statistics.pvariance(x) 4.000000 2.000000
statistics.variance(x) 4.571429 2.138090
두 값의 비 = 1.142857
n / (n-1) = 1.142857
numpy 와 pandas 가 같은 자료에서 14% 다른 분산을 준다.
| 호출 | 분모 | 값 |
|---|---|---|
np.var, np.std |
\(n\) (ddof 기본값 0) | 4.000 |
pd.Series.var, .std |
\(n-1\) (ddof 기본값 1) | 4.571 |
statistics.pvariance |
\(n\) | 4.000 |
statistics.variance |
\(n-1\) | 4.571 |
두 값의 비가 정확히 \(n/(n-1)=8/7=1.1429\)다. 우연이 아니라 분모만 다르기 때문이다.
차이가 문제가 되는 경우.
- \(n\)이 작을 때. \(n=8\)에서 분산이 14%, 표준편차가 6.9% 차이다. \(n=3\)이면 분산이 50% 차이다. \(n=100\)이면 1%라 대개 무시할 만하다.
- 두 도구의 결과를 견줄 때.
numpy로 계산한 표준편차와pandas로 계산한 것을 나란히 놓으면 자료가 다른 것처럼 보인다. - 표준편차를 곱해 쓸 때. 변동성을 연율화하거나 \(z\) 점수를 만들 때 이 차이가 그대로 따라간다.
처방. ddof 를 언제나 명시한다. np.std(x, ddof=1) 처럼 적어 두면 기본값을 기억할 필요가 없고, 코드를 읽는 사람도 어느 쪽인지 안다.
어느 쪽을 써야 하는가. 가진 자료가 모집단 전체이면 \(n\), 표본이면 \(n-1\)이다. 실무에서는 거의 언제나 표본이므로 \(n-1\) 이 기본이 되어야 한다.
왜 \(n-1\) 이 표본에 맞는 분모인지는 기댓값을 다루는 도구가 필요하다. 베셀 보정 절에서 \(\mathbb{E}[s^2]=\sigma^2\) 을 증명한다. \(\square\)
연습문제 3. 척도 변환과 평행이동. \(X\)의 분산이 \(\sigma^2\)이고 \(Y = aX + b\)일 때 \(\mathrm{Var}(Y)\)를 유도하라. 이것이 \(\sigma\)의 단위에 대해 무엇을 뜻하는가?
풀이
\(\mathbb{E}[Y] = a\mu + b\)이므로
이다. 따라서 \(\mathrm{Var}(Y) = a^2 \sigma^2\)이며 분산은 곱하는 상수의 제곱만큼 변한다. 더하는 상수 \(b\)는 아무 영향이 없다.
단위: \(X\)가 달러 단위라면 \(\sigma^2\)은 달러 제곱 단위여서 "전형적인 편차"로 해석할 수 없고, \(\sigma\)는 달러 단위다. 표준편차가 원래 단위를 회복시키므로 "전형적인 퍼짐"을 보고할 때는 표준편차가 선호되고, 대수적 조작(예: 독립 변수들의 분산을 더하기)에는 분산이 선호된다.
연습문제 4. 합의 분산. 독립인 확률변수 \(X_1, \ldots, X_n\)에 대해 \(\mathrm{Var}(\sum_i X_i) = \sum_i \mathrm{Var}(X_i)\)임을 보여라. 독립성은 어디에서 쓰이며, 변수들이 상관되어 있을 때의 공식은 무엇인가?
풀이
\(\mu_i = \mathbb{E}[X_i]\)라 하자. 그러면
이다. 첫 항은 \(\sum_i \sigma_i^2\)이다. 둘째 항 \(\sum_{i \ne j} \mathrm{Cov}(X_i, X_j)\)는 독립성 아래에서 0이다(교차항이 \(\mathbb{E}[X_i - \mu_i] \mathbb{E}[X_j - \mu_j] = 0\)으로 분해되므로).
상관된 변수의 경우:
양의 상관은 합의 분산을 부풀리고 음의 상관은 줄인다. 포트폴리오 분산투자가 이를 활용한다. 상관이 낮거나 음인 자산을 결합하면 같은 기대수익에 대해 총분산을 줄일 수 있다.
연습문제 5. 표본평균의 표준오차는 \(\mathrm{SE}(\bar{X}) = \sigma/\sqrt{n}\)이다. 분모가 왜 \(n\)이 아니라 \(\sqrt{n}\)인가? 이를 이용해 표본 크기를 네 배로 늘려야 표준오차가 절반이 되는 이유를 설명하라.
풀이
i.i.d. 자료에서 표본평균의 분산은
이다. 표준오차는 그 제곱근인 \(\mathrm{SE}(\bar{X}) = \sigma/\sqrt{n}\)이다. \(1/n\)로 줄어드는 분산의 제곱근을 취하기 때문에 \(\sqrt{n}\)이 나타난다.
네 배 규칙: \(n\)이 \(n\)에서 \(4n\)이 되면 \(\sqrt{n} \to 2\sqrt{n}\)이므로 표준오차가 절반이 된다. 표준오차를 다시 절반으로 줄이려면 표본 크기를 또 4배, 즉 \(16n\)으로 만들어야 한다. 정밀도는 \(\sqrt{n}\)으로만 좋아지며, 이 가차 없는 속도가 고정밀 조사의 비용을 좌우한다.
연습문제 6. 변동계수 \(\mathrm{CV} = \sigma/\mu\)는 상대적 퍼짐을 재는 단위 없는 측도다. CV가 \(\sigma\)만 보는 것보다 유용한 때는 언제인가? \(\sigma\)는 같지만 CV는 크게 다른 두 분포의 예를 들어라.
풀이
CV는 곱셈적 척도 변환에 불변이다. 모든 자료값을 두 배로 하면 \(\sigma\)와 \(\mu\)가 모두 두 배가 되어 CV는 그대로다. 따라서 CV는 "척도에서 자유로운" 퍼짐 측도로서 단위나 크기가 다른 분포들 사이에서 변동성을 비교하는 데 유용하다.
예:
- 주식 A의 연간 수익률: \(\mu = 5\%\), \(\sigma = 5\%\)이므로 CV \(= 1.0\).
- 주식 B의 연간 수익률: \(\mu = 50\%\), \(\sigma = 5\%\)이므로 CV \(= 0.1\).
둘의 절대 표준편차는 같다(5%포인트). 그러나 A의 수익률은 전형적인 값에 비해 훨씬 변동이 크다. 표준편차 1배만큼 하락하면 한 해 수익이 통째로 사라질 수 있다. B의 수익률은 높은 수준 주위에 촘촘히 몰려 있다. CV가 이 차이를 포착한다.
단서: \(\mu\)가 0에 가깝거나 부호가 바뀔 수 있으면(예: 손실을 포함하는 투자 수익률) CV는 정의되지 않거나 불안정해진다. 그런 자료에는 샤프 지수(\(\mu\)가 양수인 비교에서의 \(\mu/\sigma\))나 사분위범위 / 중앙값(강건한 대응물) 같은 대안 측도가 선호된다.
연습문제 7. 총분산의 법칙은 전체 분산을 집단내 성분과 집단간 성분으로 쪼갠다.
이를 수치로 확인하고 무엇에 쓰이는지 설명하라.
풀이
import numpy as np
rng = np.random.default_rng(0)
g = np.repeat([0, 1, 2], [500, 300, 200]) # 세 집단
mu = np.array([10., 14., 22.]) # 집단 평균이 다르다
y = rng.normal(mu[g], 2.0) # 집단내 표준편차는 같다
w = np.array([np.mean(g == k) for k in range(3)])
group_mean = np.array([y[g == k].mean() for k in range(3)])
group_var = np.array([y[g == k].var(ddof=0) for k in range(3)])
within = (w * group_var).sum()
between = (w * (group_mean - y.mean()) ** 2).sum()
print(f"전체 분산 {y.var(ddof=0):.4f}")
print(f"집단내 성분 {within:.4f}")
print(f"집단간 성분 {between:.4f}")
print(f"합 {within + between:.4f}")
print(f"\n집단간 비율(급내상관 ICC) {between / y.var(ddof=0):.4f}")
출력:
전체 분산 23.7283
집단내 성분 3.8086
집단간 성분 19.9198
합 23.7283
집단간 비율(급내상관 ICC) 0.8395
분해가 정확히 맞는다(\(3.809 + 19.920 = 23.728\)).
어디에 쓰이는가. 이 하나의 항등식이 여러 곳에서 되풀이된다.
| 맥락 | 집단내 | 집단간 |
|---|---|---|
| 분산분석(ANOVA) | 오차제곱합 | 처리제곱합 |
| 층화표집 | 층화가 남기는 분산 | 층화가 제거하는 분산 |
| 군집표집 | 군집 안의 변동 | 군집 사이의 변동 |
| 혼합모형 | 잔차 분산 | 임의효과 분산 |
1장 표본조사 연습문제 8에서 층화가 집단간 성분을 제거하기 때문에 정밀도가 높아진다고 했는데, 그 성분이 바로 이 식의 둘째 항이다. 여기서는 그것이 전체의 \(84\%\)를 차지하므로 층화의 이득이 클 것이다.
급내상관(ICC) 은 집단간 비율로 정의되며, 1장 연습문제 9의 설계효과 \(1+(m-1)\rho\)에 들어가는 \(\rho\)가 이것이다.
해석상의 주의. 집단간 성분이 크다고 집단이 결과를 유발한다는 뜻은 아니다. 이 분해는 순전히 기술적이며, 인과 해석에는 1장에서 본 식별 조건이 따로 필요하다.
한 가지 더. 분해는 분산에서만 이렇게 깔끔하다. 표준편차는 \(\sqrt{\text{집단내}+\text{집단간}} \ne \sqrt{\text{집단내}}+\sqrt{\text{집단간}}\)이므로 더해지지 않는다. 이 절 앞부분에서 "제곱을 쓰는 이유"로 든 성질이 여기서 다시 쓰인다. \(\square\)
연습문제 8. \(s\)는 \(\sigma\)의 추정값이며 그 자체가 확률변수다. 표본이 얼마나 있어야 \(\sigma\)를 믿을 만하게 알 수 있는가?
풀이
정규모집단에서 \((n-1)s^2/\sigma^2 \sim \chi^2_{n-1}\)이므로
이다. 뒤의 식은 델타 방법(\(\sqrt{\cdot}\)의 미분)에서 나온다.
import numpy as np
rng = np.random.default_rng(0)
sigma = 1.0
print(f"{'n':>6}{'SD(s^2) 모의':>14}{'이론':>10}{'SD(s) 모의':>13}{'이론':>10}{'s 의 상대오차':>15}")
for n in (5, 10, 30, 100, 1000):
s = rng.normal(0, sigma, (200_000, n)).std(axis=1, ddof=1)
print(f"{n:>6}{(s ** 2).std():>14.5f}{sigma ** 2 * np.sqrt(2 / (n - 1)):>10.5f}"
f"{s.std():>13.5f}{sigma / np.sqrt(2 * (n - 1)):>10.5f}{s.std() / sigma:>15.4f}")
출력:
n SD(s^2) 모의 이론 SD(s) 모의 이론 s 의 상대오차
5 0.71078 0.70711 0.34201 0.35355 0.3420
10 0.47168 0.47140 0.23245 0.23570 0.2325
30 0.26317 0.26261 0.13108 0.13131 0.1311
100 0.14182 0.14213 0.07083 0.07107 0.0708
1000 0.04486 0.04474 0.02242 0.02237 0.0224
표준편차를 아는 것은 평균을 아는 것보다 훨씬 어렵다.
| \(n\) | \(s\)의 상대오차 | 참고: \(\bar{x}\)의 상대오차 |
|---|---|---|
| \(5\) | \(34.2\%\) | \(44.7\%\) |
| \(30\) | \(13.1\%\) | \(18.3\%\) |
| \(1000\) | \(2.2\%\) | \(3.2\%\) |
두 상대오차가 비슷해 보이지만, \(\operatorname{SD}(s) \approx \sigma/\sqrt{2n}\)이므로 \(s\)의 표준오차는 \(\bar{x}\)의 표준오차보다 \(\sqrt{2}\)배 작을 뿐이다. 즉 \(\sigma\)를 알아내는 일도 \(\mu\)를 알아내는 일과 같은 차수의 어려움이다.
실무적 함의.
- \(n = 5\)에서 \(s\)는 거의 정보가 없다. 상대오차가 \(34\%\)이므로 참 \(\sigma\)가 \(1\)일 때 \(s\)가 \(0.5\)나 \(1.5\)로 나오는 일이 흔하다. 소표본에서 "분산이 두 배 차이 난다"는 관찰은 대개 잡음이다.
- \(t\) 검정이 \(s\)를 쓰는 대가가 여기 있다. \(\sigma\)를 모르고 \(s\)로 대체하기 때문에 \(t\) 분포의 꼬리가 두꺼워지며, \(n\)이 작을수록 심하다.
- 표본 크기 계획에 \(\sigma\)의 추정값을 쓸 때 조심하라. 예비 연구의 \(n = 20\)짜리 \(s\)는 상대오차가 \(16\%\)이므로, 그것으로 계산한 표본 크기도 그만큼 불확실하다. 보수적으로 \(\sigma\)를 크게 잡는 것이 안전하다.
- 분산비 검정(\(F\) 검정)이 정규성에 민감한 이유도 같은 뿌리다. \(s^2\)은 \(4\)차 적률에 의존하므로 분포 가정이 조금만 어긋나도 크게 흔들린다. \(\square\)
연습문제 9. 표준편차의 해석으로 흔히 쓰는 "\(68\)–\(95\)–\(99.7\) 규칙"은 정규분포에서만 성립한다. 어떤 분포에서든 성립하는 보장은 무엇인가? 체비셰프 부등식과 실제 값을 비교하라.
풀이
체비셰프 부등식. 분산이 유한한 어떤 분포에서든
이다. 마르코프 부등식을 \((X-\mu)^2\)에 적용하면 바로 나온다.
import numpy as np
rng = np.random.default_rng(0)
n = 2_000_000
dists = {"정규": rng.normal(0, 1, n),
"균등": rng.uniform(-np.sqrt(3), np.sqrt(3), n),
"지수": rng.exponential(1, n),
"t(3)": rng.standard_t(3, n)}
print(f"{'k':>5}{'체비셰프 하한':>14}" + "".join(f"{name:>10}" for name in dists))
for k in (1, 1.5, 2, 3):
row = f"{k:>5}{max(0, 1 - 1 / k ** 2):>14.4f}"
for x in dists.values():
row += f"{np.mean(np.abs(x - x.mean()) < k * x.std()):>10.4f}"
print(row)
출력:
k 체비셰프 하한 정규 균등 지수 t(3)
1 0.0000 0.6830 0.5775 0.8647 0.8191
1.5 0.5556 0.8665 0.8657 0.9180 0.9202
2 0.7500 0.9543 1.0000 0.9505 0.9598
3 0.8889 0.9973 1.0000 0.9817 0.9863
어떤 분포도 하한을 어기지 않는다. 그러나 하한이 매우 느슨하다.
| \(k\) | 체비셰프 | 정규 실제 |
|---|---|---|
| \(1\) | \(0\) (무의미) | \(0.683\) |
| \(2\) | \(0.750\) | \(0.955\) |
| \(3\) | \(0.889\) | \(0.997\) |
\(k = 3\)에서 체비셰프는 "적어도 \(88.9\%\)"라고만 말하는데 정규분포의 실제는 \(99.73\%\)다. 정규분포에서 \(3\sigma\) 밖은 \(0.27\%\)인데 체비셰프는 \(11.1\%\)까지 허용한다. \(40\)배 차이다.
왜 이렇게 느슨한가. 체비셰프는 분산만 아는 상태에서 최악의 분포를 가정한다. 그 최악의 경우는 질량이 \(\mu\)와 \(\mu \pm k\sigma\) 세 점에만 놓인 이산분포이며, 실제 자료가 그런 모습인 경우는 거의 없다.
그래도 쓸모가 있는 곳.
- 분포를 전혀 모를 때의 안전한 진술. "적어도 \(75\%\)가 \(\pm 2\sigma\) 안에 있다"는 언제나 참이다.
- 이론적 도구. 큰수의 약법칙 증명이 체비셰프에서 나온다. \(\operatorname{Var}(\bar{X}) = \sigma^2/n \to 0\)이므로 \(P(\lvert\bar{X}-\mu\rvert>\varepsilon) \le \sigma^2/(n\varepsilon^2) \to 0\)이다.
- 더 강한 가정이 있으면 더 좋은 부등식이 있다. 단봉이면 비소찬스키–페투닌 부등식(\(k=3\)에서 \(95.1\%\)), 유계이면 회프딩, 정규이면 정확한 값을 쓸 수 있다.
표에서 지수분포를 눈여겨보라. \(k=1\)에서 \(0.865\)로 정규(\(0.683\))보다 높은데, 이는 치우친 분포에서 \(\pm k\sigma\) 구간이 비대칭 분포에 잘 맞지 않기 때문이다. 치우친 자료에 \(\mu \pm k\sigma\)를 쓰는 것 자체가 부적절하며, 그런 경우에는 분위수를 보고하는 것이 옳다. \(\square\)
연습문제 10. 이 절의 \(Z\)-점수를 더 밀고 나가라. 표준화가 보존하는 것과 바꾸는 것은 각각 무엇인가?
풀이
import numpy as np
from scipy import stats
rng = np.random.default_rng(4)
n = 500
x1 = rng.normal(50, 2, n)
x2 = 0.8 * (x1 - 50) / 2 * 30 + 300 + rng.normal(0, 18, n) # 단위가 전혀 다르다
X = np.column_stack([x1, x2])
Z = (X - X.mean(0)) / X.std(0, ddof=1)
print(f"원자료 평균 {X.mean(0).round(3)} 표준편차 {X.std(0, ddof=1).round(3)}")
print(f"표준화 평균 {Z.mean(0).round(6)} 표준편차 {Z.std(0, ddof=1).round(6)}")
print(f"\n피어슨 상관: 원자료 {np.corrcoef(X.T)[0, 1]:.6f} 표준화 {np.corrcoef(Z.T)[0, 1]:.6f}")
print(f"스피어만 상관: 원자료 {stats.spearmanr(X)[0]:.6f} 표준화 {stats.spearmanr(Z)[0]:.6f}")
print(f"왜도 (첫 변수): 원자료 {stats.skew(X[:, 0]):.6f} 표준화 {stats.skew(Z[:, 0]):.6f}")
d_raw = np.linalg.norm(X[0] - X[1])
d_std = np.linalg.norm(Z[0] - Z[1])
print(f"\n두 점 사이 유클리드 거리: 원자료 {d_raw:.4f} 표준화 {d_std:.4f}")
출력:
원자료 평균 [ 50.007 299.67 ] 표준편차 [ 2.019 29.279]
표준화 평균 [ 0. -0.] 표준편차 [1. 1.]
피어슨 상관: 원자료 0.779248 표준화 0.779248
스피어만 상관: 원자료 0.766642 표준화 0.766642
왜도 (첫 변수): 원자료 -0.082544 표준화 -0.082544
두 점 사이 유클리드 거리: 원자료 27.1591 표준화 1.0405
보존되는 것.
- 상관계수. 피어슨도 스피어만도 소수점 여섯째 자리까지 같다. \(Z = (X-\mu)/\sigma\)는 양의 기울기를 갖는 선형변환이라 상관을 바꾸지 않는다.
- 분포의 모양. 왜도와 첨도가 그대로다. 표준화는 위치와 척도만 바꾸므로 표준화된 적률은 불변이다(왜도 문서 연습문제 3).
- 순위와 상대적 순서. 단조 변환이므로 분위수의 순위가 유지된다.
바뀌는 것.
- 거리. 두 점 사이의 유클리드 거리가 완전히 달라진다. 단위가 큰 변수가 지배하던 것이 사라지고 모든 변수가 같은 무게를 갖는다.
- 단위와 해석. \(Z = 1.5\)는 "평균보다 \(1.5\) 표준편차 위"이지 원래 단위의 값이 아니다.
함의가 갈린다.
| 방법 | 표준화의 영향 |
|---|---|
| 상관·회귀(\(R^2\)) | 없다 (계수의 척도만 바뀐다) |
| k-평균, k-NN, 계층 군집화 | 크다 — 앞 절에서 본 대로 결과가 뒤바뀔 수 있다 |
| PCA | 크다 — 공분산 PCA와 상관 PCA가 다르다 |
| 능형·라소 | 크다 — 벌점이 계수 크기에 걸리므로 척도에 의존한다 |
| 의사결정나무 | 없다 — 각 변수를 따로 분할한다 |
표준화가 언제나 옳은 것은 아니다. 모든 변수를 표준편차 \(1\)로 맞추는 것은 "모든 변수가 똑같이 중요하다"고 선언하는 것이다. 변수들이 같은 단위이고 분산 차이가 실제 정보를 담고 있다면 그 정보를 지우게 된다.
한 가지 함정. 학습·시험 자료를 나눌 때는 학습 자료의 평균과 표준편차로 양쪽을 모두 변환해야 한다. 시험 자료의 통계량을 쓰면 1장에서 본 전처리 누출이 된다. sklearn 의 StandardScaler 를 fit 은 학습 자료에만, transform 은 양쪽에 적용하는 이유가 이것이다. \(\square\)
정리하며¶
분산과 표준편차는 모든 관측값의 평균으로부터의 거리를 고려하여 자료 변동성의 완전한 그림을 제공한다. 표준편차는 원래 단위를 쓰므로 해석하기 쉽고 널리 쓰인다. 모집단 공식과 표본 공식의 구분을 이해하고 ddof를 올바르게 설정하는 것이 정확한 통계 분석에 필수적이다.