Answer

Why does bootstrapping work? A plain-language explanation

Inspired by a question on Cross Validated ·

confidence intervalssamplingbootstrap

The short answer

Your sample is the best picture you have of the population. The bootstrap treats that picture as a stand-in population and redraws samples from it, with replacement, to see how much a statistic wobbles from sample to sample. It does not create new information; it measures uncertainty. It works well when the sample is a fair, reasonably sized snapshot of the population, and poorly when it is not.

The problem the bootstrap solves

Any number you calculate from a sample, such as a mean, a median or a correlation, would have come out a little differently if you had happened to draw a different sample. To judge how much to trust it, you want to know how much it would vary across all the samples you could have drawn. That spread is the standard error, and it is the basis of every confidence interval.

The ideal way to find it would be to go back to the population, collect hundreds of new samples, and watch the statistic bounce around. You cannot do that: you have one sample, and collecting more costs time and money. For a few statistics, like the mean, a formula gives the answer. For many others, like a median, a ratio or a trimmed mean, there is no simple formula.

The key idea: let the sample stand in for the population

Here is the explanation you can give a neighbor. Imagine you want to know about the jelly beans in a huge warehouse, but you only have a jar of 40 that were scooped out at random. If the scoop was fair, the jar looks like a miniature version of the warehouse: roughly the same share of red, green and black beans.

So pretend the jar is the warehouse. Make a big pile out of many copies of your jar, and scoop 40 beans from it again and again. Each scoop is a plausible sample you might have gotten. Watching how the share of red beans varies between these scoops tells you how much it would vary between real scoops from the warehouse. Drawing from many copies of the jar is exactly what sampling with replacement does: after you pick a bean, you put it back, so it can be picked again.

The leap that feels suspicious is this: we are not learning about the population's center from the resamples. The center still comes from the original sample. What the resamples teach us is the size of the wobble, and the wobble depends mainly on the shape and spread of the data and on the sample size, which a decent sample captures well.

Statisticians call this the plug-in principle: whenever a calculation needs the unknown population, plug in the sample instead. Bradley Efron introduced the method in 1979, and the name comes from the phrase about pulling yourself up by your own bootstraps.

See it in R and Python

Because the example below simulates the population, we can do the impossible and compare the bootstrap against the truth. It draws one sample of 40 skewed waiting times (true mean 10), then estimates the standard error of the mean in three ways: by drawing 10,000 fresh samples from the population, by bootstrapping the one sample, and by the usual formula. Finally it repeats the whole study 1,000 times to check how often a 95% bootstrap interval captures the true mean. Both versions use only base tools.

R

set.seed(26088)

# A skewed "population" we normally never get to see: waiting times, mean 10
pop_mean <- 10
n <- 40

# The one sample we actually have
x <- rexp(n, rate = 1 / pop_mean)
round(mean(x), 2)

# God's-eye view: draw 10,000 fresh samples from the population
fresh_means <- replicate(10000, mean(rexp(n, rate = 1 / pop_mean)))

# Bootstrap: resample our one sample, with replacement, 10,000 times
boot_means <- replicate(10000, mean(sample(x, replace = TRUE)))

# Compare the spread of the two sampling distributions
round(c(true_SE = sd(fresh_means), bootstrap_SE = sd(boot_means),
        formula_SE = sd(x) / sqrt(n)), 3)

# 95% percentile bootstrap confidence interval for the mean
round(quantile(boot_means, c(0.025, 0.975)), 2)

# Does the recipe work? Repeat the whole study 1,000 times
covers <- replicate(1000, {
  s  <- rexp(n, rate = 1 / pop_mean)
  bm <- replicate(1000, mean(sample(s, replace = TRUE)))
  ci <- quantile(bm, c(0.025, 0.975))
  ci[1] <= pop_mean && pop_mean <= ci[2]
})
mean(covers)   # share of intervals that caught the true mean

Python

import numpy as np

rng = np.random.default_rng(26088)

# A skewed "population" we normally never get to see: waiting times, mean 10
pop_mean = 10
n = 40

# The one sample we actually have
x = rng.exponential(scale=pop_mean, size=n)
print(round(x.mean(), 2))

# God's-eye view: draw 10,000 fresh samples from the population
fresh_means = rng.exponential(scale=pop_mean, size=(10000, n)).mean(axis=1)

# Bootstrap: resample our one sample, with replacement, 10,000 times
boot_means = rng.choice(x, size=(10000, n), replace=True).mean(axis=1)

# Compare the spread of the two sampling distributions
print({"true_SE": round(fresh_means.std(ddof=1), 3),
       "bootstrap_SE": round(boot_means.std(ddof=1), 3),
       "formula_SE": round(x.std(ddof=1) / np.sqrt(n), 3)})

# 95% percentile bootstrap confidence interval for the mean
# (NumPy's default quantile method is the same as R's default, type 7)
print(np.round(np.quantile(boot_means, [0.025, 0.975]), 2))

# Does the recipe work? Repeat the whole study 1,000 times
covers = []
for _ in range(1000):
    s = rng.exponential(scale=pop_mean, size=n)
    bm = rng.choice(s, size=(1000, n), replace=True).mean(axis=1)
    lo, hi = np.quantile(bm, [0.025, 0.975])
    covers.append(lo <= pop_mean <= hi)
print(np.mean(covers))  # share of intervals that caught the true mean

The figures below come from one seeded run of the R code. Python uses a different random number generator, so its figures differ slightly, but they show the same pattern.

The same code works for a median or any other statistic: replace mean with the function you care about. That flexibility is the real selling point, since for many statistics no textbook formula exists.

When the bootstrap does not work well

The method is only as good as the stand-in. It breaks down when the sample is a poor miniature of the population:

For better small-sample intervals, the bias-corrected and accelerated (BCa) interval, available through boot.ci() in the recommended R package boot, usually gets closer to the nominal coverage than the simple percentile interval shown above. Use at least a few thousand resamples for intervals; the extra computation is cheap.

How to Report (APA 7th)

Name the interval type and the number of resamples, so readers can judge and reproduce the result. Using the sample above, an inline write-up could read:

The mean waiting time was 8.93 minutes, 95% CI [6.43, 11.77], based on a percentile bootstrap with 10,000 resamples.

In the method section, add one sentence such as: Confidence intervals were estimated by nonparametric bootstrapping (10,000 resamples with replacement; percentile method). If you use BCa intervals, say so, and report the random seed or software in supplementary material. For more on what the interval itself means, see our page on what 95% confidence actually means.

Related tools and guides

More answered questions

Working with your own data?

General answers only go so far. Send us your situation and we'll reply by email within two business days.

Ask your question

Written by AskStats with AI assistance. This is general information, not advice for your specific data or study. When the results matter, check your approach with a qualified statistician.