iBetter Books
수정

중심극한정리와 표본분포

통계학 전체를 떠받치는 가장 중요한 이론 중 하나를 배울 차례입니다. 중심극한정리(Central Limit Theorem, CLT)는 모집단이 어떤 분포를 가지든, 충분히 큰 표본을 충분히 많이 추출하면 그 표본평균들이 정규분포를 이룬다는 것입니다. R 시뮬레이션으로 직접 눈으로 확인해봅니다.

왜 중심극한정리가 중요한가

우리가 가진 것은 항상 표본입니다. 모집단 전체를 조사하는 것은 불가능하거나 비용이 너무 큽니다. 그런데 표본에서 나온 통계량(예: 평균)이 얼마나 믿을 수 있는지, 오차가 얼마나 될지 알아야 합니다.

중심극한정리가 이것을 가능하게 합니다. 모집단의 형태를 몰라도, 표본이 충분히 크다면 통계적 추론을 할 수 있습니다.

시뮬레이션 1 — 균일분포에서 출발

균일분포(uniform distribution)는 1부터 6까지 모든 값이 동일한 확률을 가집니다. 주사위 같은 분포입니다. 전혀 정규분포처럼 생기지 않았습니다.

library(tidyverse)
set.seed(42)

# 모집단: 균일분포 (1~6)
population <- 1:6

# 모집단 분포 시각화
data.frame(x = population) %>%
  ggplot(aes(x = factor(x), y = 1/6)) +
  geom_col(fill = "steelblue") +
  labs(
    title = "모집단 분포: 균일분포 (주사위)",
    x     = "값",
    y     = "확률"
  ) +
  theme_minimal()

이제 이 모집단에서 표본을 반복해서 추출하고, 각 표본의 평균을 모아봅니다.

# 표본 크기별 표본평균 분포 비교
simulate_sampling <- function(pop, n, reps = 5000) {
  sample_means <- numeric(reps)
  for (i in 1:reps) {
    sample_means[i] <- mean(sample(pop, size = n, replace = TRUE))
  }
  sample_means
}

set.seed(42)
population <- 1:6

means_n1  <- simulate_sampling(population, n = 1)
means_n5  <- simulate_sampling(population, n = 5)
means_n20 <- simulate_sampling(population, n = 20)
means_n50 <- simulate_sampling(population, n = 50)

# 데이터프레임으로 합치기
df_clt <- data.frame(
  mean = c(means_n1, means_n5, means_n20, means_n50),
  n    = rep(c("n=1", "n=5", "n=20", "n=50"),
             each = 5000)
) %>%
  mutate(n = factor(n, levels = c("n=1", "n=5", "n=20", "n=50")))

# 시각화
ggplot(df_clt, aes(x = mean)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 40, fill = "steelblue", color = "white", alpha = 0.7) +
  stat_function(fun = dnorm,
                args = list(mean = mean(population),
                            sd   = sd(population) / sqrt(as.numeric(gsub("n=", "", df_clt$n[1])))),
                color = "red", linewidth = 1) +
  facet_wrap(~ n, scales = "free_y") +
  labs(
    title = "중심극한정리: 표본 크기에 따른 표본평균 분포",
    x     = "표본 평균",
    y     = "밀도"
  ) +
  theme_minimal()

n=1일 때는 모집단과 같은 균일한 모양이지만, n이 커질수록 히스토그램이 종 모양으로 변합니다. n=30 이상이면 사실상 정규분포입니다.

시뮬레이션 2 — 지수분포에서 출발

지수분포는 오른쪽으로 긴 꼬리를 가진 비대칭 분포입니다. 정규분포와는 전혀 다릅니다.

set.seed(42)
n_reps <- 5000

# 모집단: 지수분포 (rate=1)
pop_samples <- rexp(100000, rate = 1)

# 모집단 분포 시각화
ggplot(data.frame(x = pop_samples), aes(x)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 50, fill = "coral", color = "white", alpha = 0.7) +
  labs(
    title = "모집단 분포: 지수분포 (오른쪽 꼬리)",
    x     = "값",
    y     = "밀도"
  ) +
  theme_minimal()
# 표본 크기별 표본평균 분포
ns <- c(2, 10, 30, 100)
results <- list()

for (n in ns) {
  sample_means <- numeric(n_reps)
  for (i in 1:n_reps) {
    sample_means[i] <- mean(rexp(n, rate = 1))
  }
  results[[paste0("n=", n)]] <- sample_means
}

df_exp <- data.frame(
  mean = unlist(results),
  n    = rep(names(results), each = n_reps)
) %>%
  mutate(n = factor(n, levels = paste0("n=", ns)))

ggplot(df_exp, aes(x = mean)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 40, fill = "coral", color = "white", alpha = 0.7) +
  facet_wrap(~ n, scales = "free") +
  labs(
    title = "중심극한정리: 지수분포 모집단에서의 표본평균 분포",
    x     = "표본 평균",
    y     = "밀도"
  ) +
  theme_minimal()

지수분포에서 출발해도 n=30 이상이면 표본평균이 정규분포에 수렴합니다.

표본분포와 표준오차

표본평균의 분포를 표본분포(sampling distribution)라 합니다. 중심극한정리에 따르면 표본평균의 분포는 이렇습니다.

  • 평균: μ (모집단 평균과 동일)
  • 표준편차: σ / √n (표준오차, Standard Error)
# 표준오차 확인 시뮬레이션
set.seed(42)
population_mean <- 50
population_sd   <- 10

sample_sizes <- c(10, 30, 100, 500)

for (n in sample_sizes) {
  means <- numeric(10000)
  for (i in 1:10000) {
    means[i] <- mean(rnorm(n, mean = population_mean, sd = population_sd))
  }
  cat(sprintf("n=%3d | 표본평균의 평균=%.2f | 표본평균의 SD=%.3f | 이론 SE=%.3f\n",
              n, mean(means), sd(means), population_sd / sqrt(n)))
}

결과를 보면 표본평균의 SD이론 SE(σ/√n)가 거의 일치합니다. 표본 크기가 커질수록 표준오차가 작아지고, 표본평균이 모집단 평균에 더 가까워집니다.

표본 크기와 표준오차의 관계

ns  <- 1:200
ses <- 10 / sqrt(ns)   # σ=10 가정

ggplot(data.frame(n = ns, se = ses), aes(x = n, y = se)) +
  geom_line(color = "steelblue", linewidth = 1.2) +
  geom_vline(xintercept = 30, color = "red", linetype = "dashed") +
  annotate("text", x = 35, y = 4, label = "n=30", color = "red") +
  labs(
    title = "표본 크기와 표준오차의 관계 (σ=10)",
    x     = "표본 크기 (n)",
    y     = "표준오차 (σ/√n)"
  ) +
  theme_minimal()

표본 크기가 작을 때는 n을 조금만 늘려도 표준오차가 크게 줄지만, 어느 정도 커지면 추가 효과가 감소합니다. n=4일 때 n=1보다 표준오차가 절반이 되지만, n=100에서 n=400으로 늘려야 같은 효과가 납니다.

신뢰구간 미리보기

중심극한정리와 표준오차를 알면 신뢰구간을 만들 수 있습니다.

set.seed(42)

# 표본 추출
sample_data <- rnorm(50, mean = 70, sd = 10)

n   <- length(sample_data)
m   <- mean(sample_data)
se  <- sd(sample_data) / sqrt(n)

# 95% 신뢰구간 (t분포 이용)
t_crit <- qt(0.975, df = n - 1)
lower  <- m - t_crit * se
upper  <- m + t_crit * se

cat(sprintf("표본 평균: %.2f\n", m))
cat(sprintf("95%% 신뢰구간: [%.2f, %.2f]\n", lower, upper))

"이 표본에서 구한 신뢰구간 [lower, upper]이 모집단 평균을 포함할 확률이 95%"라는 뜻입니다. 자세한 내용은 PART 03에서 다룹니다.

핵심 정리

중심극한정리를 세 줄로 요약하면 이렇습니다.

첫째, 모집단의 분포가 무엇이든 상관없습니다. 둘째, 표본 크기가 충분히 크면 (보통 n≥30), 표본평균의 분포는 정규분포에 수렴합니다. 셋째, 이 덕분에 t검정, ANOVA, 회귀분석 같은 모든 통계적 추론이 가능합니다.

# 최종 확인 — 다양한 분포에서 CLT 시뮬레이션
set.seed(42)
n      <- 50    # 표본 크기
n_reps <- 5000  # 반복 횟수

# 세 가지 비정규 분포에서 표본평균 추출
means_uniform <- replicate(n_reps, mean(runif(n, 0, 1)))       # 균일분포
means_exp     <- replicate(n_reps, mean(rexp(n, rate = 1)))    # 지수분포
means_binom   <- replicate(n_reps, mean(rbinom(n, 10, 0.3)))   # 이항분포

df_final <- data.frame(
  mean = c(means_uniform, means_exp, means_binom),
  dist = rep(c("균일분포", "지수분포", "이항분포"), each = n_reps)
)

ggplot(df_final, aes(x = mean)) +
  geom_histogram(aes(y = after_stat(density)),
                 bins = 40, fill = "steelblue", color = "white", alpha = 0.7) +
  facet_wrap(~ dist, scales = "free") +
  labs(
    title    = "중심극한정리: 다양한 모집단에서의 표본평균 분포 (n=50)",
    subtitle = "어떤 분포에서 시작해도 표본평균은 정규분포에 수렴합니다",
    x        = "표본 평균",
    y        = "밀도"
  ) +
  theme_minimal()

세 분포 모두 n=50인 표본의 평균들이 종 모양을 이룹니다. 이것이 통계적 추론의 출발점입니다. 다음 PART에서는 이 기반 위에 가설검정을 쌓아 올립니다.