Capítulo 4
Bootstrap y Remuestreo
4.1 El Principio del Bootstrap
El bootstrap usa remuestreo con reemplazo de los datos originales para estimar la distribución muestral de un estadístico. No asume ninguna distribución teórica.
Repetimos B veces (típicamente 1000-10000) para obtener la distribución bootstrap del estadístico.
4.2 Bootstrap para la Media
python
import numpy as np
np.random.seed(42)
data = np.random.exponential(scale=5, size=100)
print(f"Media muestral: {np.mean(data):.3f}")
B = 10000
means = np.zeros(B)
for i in range(B):
sample = np.random.choice(data, size=len(data), replace=True)
means[i] = np.mean(sample)
ci_low = np.percentile(means, 2.5)
ci_high = np.percentile(means, 97.5)
print(f"IC 95% bootstrap: [{ci_low:.3f}, {ci_high:.3f}]")
print(f"Error estándar bootstrap: {np.std(means):.3f}")4.3 Bootstrap para la Mediana (o cualquier estadístico)
El bootstrap funciona para cualquier estadístico: mediana, correlación, diferencia de medias, ratio, percentiles...
python
# Bootstrap para la mediana
medians = np.zeros(B)
for i in range(B):
sample = np.random.choice(data, size=len(data), replace=True)
medians[i] = np.median(sample)
ci_low = np.percentile(medians, 2.5)
ci_high = np.percentile(medians, 97.5)
print(f"Mediana: {np.median(data):.3f}")
print(f"IC bootstrap mediana: [{ci_low:.3f}, {ci_high:.3f}]")4.4 Bootstrap de dos grupos
python
g1 = np.random.normal(5, 1, 50)
g2 = np.random.normal(5.5, 1, 50)
diffs = np.zeros(B)
for i in range(B):
s1 = np.random.choice(g1, size=len(g1), replace=True)
s2 = np.random.choice(g2, size=len(g2), replace=True)
diffs[i] = np.mean(s1) - np.mean(s2)
ci = np.percentile(diffs, [2.5, 97.5])
print(f"Diferencia observada: {np.mean(g1) - np.mean(g2):.3f}")
print(f"IC 95%: [{ci[0]:.3f}, {ci[1]:.3f}]")
# Si el IC no contiene 0 → diferencia significativa4.5 Tests de Permutación
Los tests de permutación son la versión no paramétrica de un test de hipótesis vía remuestreosin reemplazo. Se mezclan aleatoriamente las etiquetas de grupo.
python
observado = np.mean(g1) - np.mean(g2)
combined = np.concatenate([g1, g2])
n1 = len(g1)
count = 0
N = 10000
for _ in range(N):
np.random.shuffle(combined)
perm_mean = np.mean(combined[:n1]) - np.mean(combined[n1:])
if abs(perm_mean) >= abs(observado):
count += 1
p_valor = count / N
print(f"Diferencia observada: {observado:.3f}")
print(f"p-valor permutación: {p_valor:.4f}")4.6 Jackknife
El jackknife estima sesgo y varianza dejando fuera una observación cada vez:
python
n = len(data)
jack_means = np.zeros(n)
for i in range(n):
mask = np.ones(n, dtype=bool)
mask[i] = False
jack_means[i] = np.mean(data[mask])
jack_mean = np.mean(jack_means)
sesgo = (n - 1) * (jack_mean - np.mean(data))
print(f"Sesgo estimado: {sesgo:.4f}")