CH 09 통계학 기본 · Part 2 표집분포와 추정
부트스트랩
표준오차 공식이 없는 통계량은 어떻게 하나. 표본을 모집단인 척하고 복원추출로 다시 뽑아 흔들림을 직접 만든다. 다섯 통계량에서 부트스트랩 SE 는 참 SE 의 0.950~1.091 배로 옳았고, engel 데이터의 지니계수 0.2548 에도 [0.2253, 0.2844] 라는 CI 가 붙었다. 그런데 최댓값 추정에서는 포함률이 0.0000 — 한 번도 맞히지 못했다.
- 01 직관
- 02 수식 읽는 법
- 03 손으로 풀기
- 04 코드
- 05 시각화
약어 및 기호 정의
- 부트스트랩 (bootstrap)
- 원표본에서 복원추출로 크기 n 의 재표본을 B 번 만들어, 통계량의 흔들림을 표본 자체로부터 추정하는 방법. 이름은 "제 신발끈을 당겨 스스로를 들어 올린다"는 관용구에서 왔다
- 재표본 X*
- resample. 원표본에서 뽑은 크기 n 의 새 표본. 같은 값이 여러 번 들어갈 수 있고, 안 뽑히는 값도 있다
- 경험분포 F̂ₙ
- empirical distribution. 관측값 n 개 각각에 확률 1/n 을 몰아 준 이산분포. 부트스트랩은 참분포 F 자리에 F̂ₙ 을 대입한 것이다
- 부트스트랩 SE
- B 개의 재표본 통계량 θ̂* 의 표준편차. 8장의 s/√n 을 대체한다
- percentile CI
- θ̂* 의 2.5% / 97.5% 분위수를 그대로 구간의 끝으로 쓴다. 가장 단순하다
- basic CI
- [2θ̂ − q₉₇.₅, 2θ̂ − q₂.₅]. percentile 을 원점 기준으로 뒤집은 것
- studentized CI
- 재표본마다 t* = (θ̂* − θ̂)/SE* 를 만들어 그 분위수를 쓴다. 계산이 가장 무겁고 성능이 가장 좋다
- 파라메트릭 부트스트랩
- 원표본에서 다시 뽑는 대신, 분포 모형을 가정하고 추정된 모수로 새 자료를 생성한다. 모형이 맞으면 강력하다
- 지니계수 G
- 불평등 지표. 0이면 완전 균등, 1이면 완전 불평등. 표준오차 공식이 널리 쓰이지 않는 통계량의 대표 사례다
직관: 표본을 모집단인 척한다
6장에서 표집분포를, 8장에서 신뢰구간을 만들었다. 두 장 모두 같은 전제 위에 있었다. 통계량의 표준오차를 알려 주는 공식이 있다는 전제다. 표본평균에는 σ/√n 이 있고, 비율에는 √(p(1−p)/n) 이 있다.
그런데 현실에서 우리가 계산하는 값의 대부분은 그런 공식이 없다. 사분위범위, 변동계수, 두 중앙값의 비, 지니계수, 상위 10% 점유율, 회귀계수들의 비. 교과서 뒤편을 아무리 뒤져도 표준오차 표가 없다.
이때 두 갈래가 있다. 하나는 수학을 더 하는 것 — 델타법을 쓰거나 점근분포를 유도한다. 통계량마다 새로 해야 하고 어렵다. 다른 하나가 부트스트랩이다. 수학을 하지 않고 컴퓨터에 반복을 시킨다.
왜 이것이 말이 되는가
표준오차의 정의는 "같은 모집단에서 표본을 반복해 뽑았을 때 통계량이 흔들리는 폭"이다. 그러니 모집단이 있으면 반복해 뽑아 재면 된다. 문제는 모집단이 없다는 것.
부트스트랩의 답은 뻔뻔하다. 표본이 모집단을 닮았을 테니, 표본을 모집단이라고 치자. 표본에서 복원추출로 n 개를 다시 뽑으면 "모집단에서 표본을 뽑는 일"을 한 층 아래에서 흉내 낸 것이 된다.
복원추출인 것이 핵심이다. 비복원으로 n 개를 뽑으면 원표본이 그대로 나온다 — 아무 흔들림도 없다.

이 장은 부트스트랩을 소개하기만 하지 않는다. 채점한다. 공식이 있는 통계량에 부트스트랩을 적용해 공식과 맞는지 보고(실험 1), CI 세 종류의 실제 포함률을 세고(실험 2), 부트스트랩이 완전히 무너지는 예를 찾아낸다(실험 3).
수식 읽는 법: F 자리에 F̂ 을 끼운다
SE(θ̂) = √( Var_F [ θ̂(X₁, …, Xₙ) ] )
SE_boot = √( Var_F̂ₙ [ θ̂(X*₁, …, X*ₙ) ] ) F 를 F̂ₙ 으로 바꾼 것뿐

percentile: [q*₂.₅, q*₉₇.₅] basic: [2θ̂ − q*₉₇.₅, 2θ̂ − q*₂.₅]
studentized: [θ̂ − t*₉₇.₅·SE, θ̂ − t*₂.₅·SE], t* = (θ̂* − θ̂)/SE*
B(재표본 개수)와 n(표본 크기)을 혼동하면 안 된다. B 를 키우면 계산의 몬테카를로 오차만 줄어든다. n 이 작아서 생기는 문제는 B 를 100만으로 올려도 조금도 나아지지 않는다. 실험 3 이 그 극단적인 사례다.
손으로 풀기
손풀이 A — 표본 4개로 부트스트랩 한 바퀴
원표본 x = [3, 5, 8, 12], n = 4. 합 28 이므로 x̄ = 28/4 = 7.00.
이제 이 네 개에서 복원추출로 네 개를 뽑는다. 로그에 남은 다섯 번은 이렇다.
| 재표본 | 평균 |
|---|---|
| [12, 3, 3, 3] | 5.25 |
| [3, 12, 12, 8] | 8.75 |
| [3, 3, 5, 5] | 4.00 |
| [8, 5, 5, 3] | 5.25 |
| [8, 8, 3, 3] | 5.50 |
세 번째 재표본에는 8도 12도 들어가지 않았다. 두 번째에는 12가 두 번 들어갔다. 이 들쭉날쭉함이 곧 "표본이 달랐다면"의 흉내다.
이것을 10,000번 하면 재표본평균 10,000개가 생긴다. 그 표준편차가 부트스트랩 SE = 1.6982 였다.
비교할 공식이 있다. s = 3.9158, √n = 2 이므로 s/√n = 1.9579.
1.6982 대 1.9579. 정확히 같지는 않다. n = 4 라 경험분포가 참분포를 거칠게 닮았기 때문이고, 또 부트스트랩 SE 는 분산을 n 으로 나눈 쪽(n−1 이 아니라)에 가깝게 나오는 경향이 있다. 중요한 것은 자릿수가 같다는 것이다 — 공식을 몰라도 컴퓨터가 대신 흔들어 줬다.

손풀이 B — 한 관측값이 재표본에 안 뽑힐 확률
재표본 하나를 만들 때, 특정 관측값 xᵢ 가 한 번의 추출에서 안 뽑힐 확률은 1 − 1/n. n 번 뽑는 동안 계속 안 뽑힐 확률은 (1 − 1/n)ⁿ.
n = 50 이면 (1 − 0.02)⁵⁰ = 0.3642. 뒤집으면 적어도 한 번은 뽑힐 확률이 1 − (1−1/n)ⁿ = 0.6358 이다.
n 이 커지면 (1−1/n)ⁿ → e⁻¹ = 0.3679 로 수렴한다. 즉 재표본 하나에는 원표본의 약 63%만 들어간다. 나머지 37%는 그 재표본에 없다.
이 숫자가 실험 3 에서 그대로 다시 나온다. 재표본의 최댓값이 원표본의 최댓값과 정확히 같을 확률 = 원표본 최댓값이 뽑힐 확률 = 0.6358. 실측은 0.6336 이었다. 손계산과 시뮬레이션이 소수 둘째 자리까지 맞는다.
손풀이 C — 왜 최댓값에서는 반드시 실패하는가
재표본은 원표본의 값들로만 이루어진다. 그러므로 언제나 max(X*) ≤ max(X) 다. 부등호에 예외가 없다.
따라서 θ̂* 의 97.5% 분위수도 max(X) 이하다. percentile CI 의 상한이 표본최댓값을 절대 넘지 못한다.
그런데 U(0, θ) 에서 표본최댓값은 언제나 θ 보다 작다(확률 1로). 즉 CI 전체가 참값의 왼쪽에 놓인다. 포함률은 이론적으로 0 이다.
실측 0.0000 은 반올림된 값이 아니라 2,000번 중 0번 이라는 뜻이다.

코드: 공식이 있는 곳에서 먼저 채점한다
새 도구를 믿으려면 정답이 있는 문제로 먼저 재 봐야 한다. 실험 1 은 참 SE 를 시뮬레이션으로 20,000번 재표집해 확정해 놓고, 표본 하나에서 계산한 부트스트랩 SE 가 그것에 얼마나 가까운지 300개 표본에 대해 평균낸다.
def bse_avg(f, R=300, B=800, seed=6):
"""원표본을 R개 뽑아 각각에서 부트스트랩 SE 를 계산하고 평균낸다."""
rr = D.rng(seed); out = []
for _ in range(R):
s_ = rr.normal(MU, SG, n)
idx = rr.integers(0, n, (B, n)) # 복원추출 인덱스
out.append(np.array([f(s_[i]) for i in idx]).std(ddof=1))
return np.mean(out), np.std(out, ddof=1)
rr.integers(0, n, (B, n)) 한 줄이 부트스트랩 전부다. 0부터 n−1 까지의 정수를 B×n 개 무작위로 만들면, 그것이 곧 B 개의 재표본 인덱스다. f 자리에 어떤 함수를 넣든 코드는 그대로다 — 이것이 부트스트랩의 실무적 가치다.
for _ in range(R):
s_ = r.uniform(0, theta, n)
idx = r.integers(0, n, (1000, n)); bm = s_[idx].max(1)
lo, hi = np.percentile(bm, [2.5, 97.5]); hit += (lo <= theta <= hi)
4-1. 결과 읽기
부트스트랩 SE 는 평균적으로 옳다
실험 1 의 비 열이 0.992 / 1.032 / 0.950 / 1.091 / 0.958 이다. 다섯 통계량 전부 0.950~1.091 안에 있다. 표본평균(0.992)처럼 공식이 있는 것뿐 아니라, 사분위범위(1.091)와 변동계수(0.958)처럼 표준오차 공식을 찾기 어려운 것도 같은 절차 한 줄로 처리됐다.
가장 크게 벗어난 사분위범위의 1.091 도 방향이 과대 쪽이다. 불확실성을 실제보다 크게 보고하는 쪽이라 실무적으로는 덜 위험한 오차다.
그런데 부트스트랩 SE 자체가 흔들린다
실험 1 의 마지막 열이 자주 지나쳐지는 대목이다. 중앙값의 부트스트랩 SE 는 평균 1.0007 인데 그 값의 표준편차가 0.2504 다. 상대적으로 25%다. 사분위범위는 1.3075 에 대해 0.2580(약 20%), 변동계수는 0.0662 에 대해 0.0165(약 25%).
즉 "내 표본에서 부트스트랩 SE 가 1.0 나왔다"는 것은 참 SE 가 0.75~1.25 어딘가라는 뜻이다. 평균적으로 옳다는 것과 한 번 재서 정확하다는 것은 다르다. 평균의 경우가 0.7794 에 0.0911(약 12%)로 가장 안정적이었다.
이 흔들림은 B 를 키워도 줄지 않는다. n=40 이라는 정보량이 정한 한계다.
부트스트랩 CI 는 세 종류가 서로 다르다
실험 2 에서 n=10 일 때 percentile 은 0.8695 — 명목 0.95 에서 8.05%p 나 모자란다. basic 은 0.8530 으로 더 나쁘다. studentized 만 0.9465 로 제 값을 지켰다.
n=100 에서도 percentile 0.9345, basic 0.9265 로 여전히 명목에 못 미친다. studentized 는 0.9455. 세 방법의 차이는 n 이 커져도 완전히 사라지지 않았다.
모집단이 지수분포라 오른쪽 꼬리가 긴 것이 원인이다. 8장에서 t구간이 치우친 분포에서 고전한 것과 같은 문제이고, 부트스트랩도 그 문제를 자동으로 해결해 주지는 않는다.
"부트스트랩 CI 를 썼다"는 말만으로는 부족하다. 어느 부트스트랩 CI 인지를 밝혀야 한다.
최댓값 추정에서는 완전히 실패한다 — 0.0000
실험 3 의 포함률 0.0000 은 이 시리즈에서 나온 가장 극단적인 실패 수치다. 2,000번 시도해서 단 한 번도 참값을 담지 못했다.
이유는 손풀이 C 그대로다. 재표본최댓값은 원표본최댓값을 넘을 수 없고, 원표본최댓값은 θ 보다 작다. 로그의 확인 줄이 이것을 못박는다 — 표본최댓값 9.8074, 재표본최댓값의 최댓값 9.8074, 20,000번 뽑았는데 정확히 같다.
게다가 재표집분포가 연속이 아니다. 재표본최댓값이 원표본최댓값과 같을 확률이 0.6336(이론 0.6358)이라, 분포의 63%가 한 점에 뭉쳐 있다. 분위수라는 개념 자체가 흔들린다.
파라메트릭 부트스트랩 — "균등분포다"라는 모형을 넣고 θ̂ = max×(n+1)/n 으로 새 자료를 생성 — 로 바꾸면 0.8655 까지 올라온다. 0 에서 0.87 은 큰 회복이지만 여전히 0.95 가 아니다. 모형을 넣어도 최댓값 추정은 어려운 문제다.
실데이터에서는 이렇게 쓴다
engel 데이터(가계 소득과 식비)에서 피어슨 상관 r = 0.9112, 부트스트랩 CI [0.8898, 0.9475]. 구간이 r 을 중심으로 대칭이 아니다 — 아래로 0.0214, 위로 0.0363. 상관계수는 1 근처에서 분포가 왼쪽으로 치우치는데, 부트스트랩은 이 비대칭을 저절로 반영한다. r ± 1.96·SE 로는 나올 수 없는 구간이다.
지니계수는 G = 0.2548, CI [0.2253, 0.2844]. 여기는 아래로 0.0295, 위로 0.0296 으로 거의 대칭이다. 같은 방법으로 두 통계량을 처리했는데 하나는 비대칭, 하나는 대칭 구간이 나왔다 — 대칭성을 미리 가정하지 않았기 때문이다.
지니계수의 표준오차 공식을 아는 사람은 드물다. 부트스트랩은 그것을 몰라도 된다.
시각화





이것만 기억하자
- 부트스트랩은 표본을 모집단인 척하고 복원추출로 다시 뽑는 것이다. 공식이 있는 다섯 통계량에서 부트스트랩 SE 는 참 SE 의 0.950~1.091 배였다 — 사분위범위·변동계수처럼 공식이 없는 것도 코드 한 줄로 처리된다.
- 부트스트랩 CI 는 한 종류가 아니다. n=10 에서 percentile 0.8695, basic 0.8530, studentized 0.9465. 작은 표본에서는 studentized 를 쓰고, 무엇을 썼는지 밝힌다.
- 부트스트랩은 분포의 끝에 의존하는 통계량에서 무너진다. 최댓값 추정의 포함률은 0.0000 이었다. 재표본은 원표본 밖을 볼 수 없기 때문이다.
흔한 오해
- "부트스트랩은 데이터를 늘려 준다" — 아니다. 정보량은 원표본 n 개가 전부다. 부트스트랩은 이미 가진 정보의 흔들림을 재는 도구이지 정보를 만들지 않는다. 실험 3 이 그 한계를 극단적으로 보여 준다.
- "B 를 크게 하면 정확해진다" — B 는 몬테카를로 오차만 줄인다. 실험 1 의 마지막 열(중앙값 SE 의 SD 0.2504)은 n=40 이 정한 흔들림이라 B 를 백만으로 올려도 그대로다.
- "부트스트랩이면 분포 가정이 필요 없다" — 경험분포 F̂ₙ 이 참분포를 닮았다는 가정이 여전히 있다. n 이 작으면 이 가정이 약하고, 실험 2 의 n=10 percentile 0.8695 가 그 대가다.
- "부트스트랩 CI 는 자동으로 95%다" — 실험 2 에서 명목 95%를 실제로 지킨 것은 studentized 뿐이었고, percentile 은 n=100 에서도 0.9345 였다. 명목과 실제는 8장에서와 마찬가지로 채점해 봐야 안다.
'통계학 기본' 카테고리의 다른 글
| [통계학 기본 11] 가설검정의 논리와 p값 — H0가 참이면 p값은 균등분포다 (0) | 2026.09.02 |
|---|---|
| [통계학 기본 10] 편향과 분산 — 틀린 추정량이 이기는 순간 (0) | 2026.09.01 |
| [통계학 기본 08] 구간추정 — 95% 신뢰구간은 정말 95%를 담는가 (0) | 2026.09.01 |
| [통계학 기본 07] 점추정: 적률법과 MLE — n−1 의 정체를 실측한다 (0) | 2026.09.01 |
| [통계학 기본 06] 표집분포 — 통계량마다 다른 흔들림의 모양 (0) | 2026.09.01 |
댓글