# Cargar librerías necesarias
library(simpleboot)
library(ggplot2)
library(dplyr)
library(boot)
library(tidyr)
# Configurar tema para gráficos
theme_set(theme_minimal())Bootstrap
¿Qué es el método Bootstrap?
El método Bootstrap es una técnica de remuestreo desarrollada por Bradley Efron en 1979. Su idea central es simple pero poderosa: si tenemos una muestra de datos, podemos generar muchas “nuevas muestras” extrayendo con reemplazo de la original. A partir de estas muestras, calculamos el estadístico de interés (media, mediana, pendiente, etc.) y obtenemos su distribución empírica Davison y Hinkley (1997).
En Química Analítica, donde los tamaños muestrales suelen ser pequeños y los supuestos clásicos (como normalidad) no siempre se cumplen, el Bootstrap se convierte en una herramienta muy útil para estimar incertidumbres(González y Herrador 2010)
¿Cómo funciona?
- Se parte de una muestra original de tamaño ( n ).
- Se generan ( R ) muestras bootstrap, cada una de tamaño ( n ), extrayendo con reemplazo.
- Se calcula el estadístico de interés en cada muestra.
- Se obtiene la distribución empírica del estadístico.
- Se estiman intervalos de confianza, sesgo, varianza, etc.
Ventajas del Bootstrap
- No requiere supuestos de distribución: Ideal cuando no se puede asumir normalidad.
- Flexible: Se puede aplicar a medias, medianas, pendientes, coeficientes de regresión, etc.
- Intuitivo: Fácil de explicar y visualizar.
- Compatible con software moderno: En R, basta una línea de código para implementarlo.
Desventajas del Bootstrap
- Dependencia de la muestra original: Si la muestra es muy pequeña o no representativa, el Bootstrap puede amplificar sesgos.
- No siempre funciona bien con extremos: Por ejemplo, estimar percentiles muy altos o bajos puede ser problemático.
- Computacionalmente intensivo: Aunque hoy en día esto no es un gran problema.
¿Cuándo no usar Bootstrap?
- Cuando la muestra es demasiado pequeña (por ejemplo, menos de 5 datos), el remuestreo puede generar resultados poco confiables.
- Cuando el estadístico de interés es no continuo o tiene una distribución muy sesgada.
- Cuando se dispone de un modelo teórico bien validado y se cumplen sus supuestos (por ejemplo, regresión lineal con errores normales y homocedásticos).
Implementación en R
Paquetes Necesarios
Ejemplo 1: Análisis de Concentración de Cobre en Muestras de Agua
Generación de Datos Simulados
# Datos simulados de concentración de Cu en mg/L
set.seed(123)
concentraciones_cu <- c(2.45, 2.52, 2.38, 2.41, 2.49, 2.44, 2.47, 2.43, 2.50, 2.46)
# Estadísticas básicas
summary(concentraciones_cu) Min. 1st Qu. Median Mean 3rd Qu. Max.
2.380 2.433 2.455 2.455 2.485 2.520
Bootstrap de la Media
# Bootstrap de la media usando simpleboot
bootstrap_media <- one.boot(concentraciones_cu, mean, R = 10000)
# Intervalo de confianza 95%
ic_media <- boot.ci(bootstrap_media, type = "perc", conf = 0.95)
print(ic_media)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 10000 bootstrap replicates
CALL :
boot.ci(boot.out = bootstrap_media, conf = 0.95, type = "perc")
Intervals :
Level Percentile
95% ( 2.43, 2.48 )
Calculations and Intervals on Original Scale
Visualización de Resultados
# Crear dataframe para ggplot2
bootstrap_df <- data.frame(
media_bootstrap = bootstrap_media$t
)
# Histograma de la distribución bootstrap
p1 <- ggplot(bootstrap_df, aes(x = media_bootstrap)) +
geom_histogram(bins = 50, fill = "lightblue", color = "black", alpha = 0.7) +
geom_vline(aes(xintercept = mean(concentraciones_cu)),
color = "red", linetype = "dashed", size = 1) +
labs(title = "Distribución Bootstrap de la Media",
subtitle = "Concentración de Cu en agua (mg/L)",
x = "Media Bootstrap (mg/L)",
y = "Frecuencia") +
theme_minimal()
# Q-Q plot para evaluar normalidad
p2 <- ggplot(bootstrap_df, aes(sample = media_bootstrap)) +
stat_qq() +
stat_qq_line(color = "red") +
labs(title = "Q-Q Plot - Distribución Bootstrap",
x = "Cuantiles Teóricos",
y = "Cuantiles Bootstrap") +
theme_minimal()
# Mostrar gráficos lado a lado
gridExtra::grid.arrange(p1, p2, ncol = 2)
Ejemplo 2: Curva de Calibración con Incertidumbre Bootstrap
Datos de Calibración
# Datos de calibración para espectrofotometría UV-Vis
concentraciones_std <- c(0, 5, 10, 15, 20, 25, 30)
absorbancias <- c(0.002, 0.125, 0.248, 0.371, 0.496, 0.618, 0.741)
# Agregar ruido experimental realista
set.seed(456)
absorbancias <- absorbancias + rnorm(length(absorbancias), 0, 0.005)
# Crear dataframe
calibracion_data <- data.frame(
concentracion = concentraciones_std,
absorbancia = absorbancias
)
print(calibracion_data) concentracion absorbancia
1 0 -0.004717607
2 5 0.128108878
3 10 0.252004373
4 15 0.364055538
5 20 0.492428216
6 25 0.616379695
7 30 0.744453215
Bootstrap de Parámetros de Regresión
# Función para obtener coeficientes de regresión lineal
coef_regression <- function(data, indices) {
sample_data <- data[indices, ]
model <- lm(absorbancia ~ concentracion, data = sample_data)
return(c(model$coefficients[1], model$coefficients[2], summary(model)$r.squared))
}
# Bootstrap de los coeficientes
bootstrap_coef <- boot(data = calibracion_data,
statistic = coef_regression,
R = 5000)
# Intervalos de confianza para intercepto, pendiente y R²
ic_intercepto <- boot.ci(bootstrap_coef, type = "perc", conf = 0.95, index = 1)
ic_pendiente <- boot.ci(bootstrap_coef, type = "perc", conf = 0.95, index = 2)
ic_r2 <- boot.ci(bootstrap_coef, type = "perc", conf = 0.95, index = 3)
print("Intervalo de confianza - Intercepto:")[1] "Intervalo de confianza - Intercepto:"
print(ic_intercepto)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates
CALL :
boot.ci(boot.out = bootstrap_coef, conf = 0.95, type = "perc",
index = 1)
Intervals :
Level Percentile
95% (-0.0081, 0.0071 )
Calculations and Intervals on Original Scale
print("Intervalo de confianza - Pendiente:")[1] "Intervalo de confianza - Pendiente:"
print(ic_pendiente)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates
CALL :
boot.ci(boot.out = bootstrap_coef, conf = 0.95, type = "perc",
index = 2)
Intervals :
Level Percentile
95% ( 0.0243, 0.0250 )
Calculations and Intervals on Original Scale
print("Intervalo de confianza - R²:")[1] "Intervalo de confianza - R²:"
print(ic_r2)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 5000 bootstrap replicates
CALL :
boot.ci(boot.out = bootstrap_coef, conf = 0.95, type = "perc",
index = 3)
Intervals :
Level Percentile
95% ( 0.999, 1.000 )
Calculations and Intervals on Original Scale
Visualización de la Curva de Calibración
# Modelo original
modelo_original <- lm(absorbancia ~ concentracion, data = calibracion_data)
# Crear dataframe con resultados bootstrap
bootstrap_params <- data.frame(
intercepto = bootstrap_coef$t[, 1],
pendiente = bootstrap_coef$t[, 2],
r_squared = bootstrap_coef$t[, 3]
)
# Gráfico de la curva de calibración con bandas de confianza
p3 <- ggplot(calibracion_data, aes(x = concentracion, y = absorbancia)) +
geom_point(size = 3, color = "blue") +
geom_smooth(method = "lm", se = TRUE, color = "red", alpha = 0.3) +
labs(title = "Curva de Calibración con Intervalos de Confianza",
subtitle = "Espectrofotometría UV-Vis",
x = "Concentración (mg/L)",
y = "Absorbancia") +
theme_minimal() +
theme(plot.title = element_text(hjust = 0.5),
plot.subtitle = element_text(hjust = 0.5))
# Distribución de parámetros bootstrap
p4 <- bootstrap_params %>%
pivot_longer(everything(), names_to = "parametro", values_to = "valor") %>%
ggplot(aes(x = valor)) +
geom_histogram(bins = 30, fill = "lightgreen", alpha = 0.7) +
facet_wrap(~parametro, scales = "free", ncol = 1,
labeller = as_labeller(c(intercepto = "Intercepto",
pendiente = "Pendiente",
r_squared = "R²"))) +
labs(title = "Distribución Bootstrap de Parámetros de Regresión",
x = "Valor del Parámetro",
y = "Frecuencia") +
theme_minimal()
# Mostrar gráficos
print(p3)
print(p4)
Ejemplo 3: Límite de Detección con Bootstrap
Análisis de Blancos
# Datos de mediciones de blancos (señal de fondo)
set.seed(789)
blancos <- rnorm(20, mean = 0.005, sd = 0.002)
# Bootstrap para estimar la desviación estándar de los blancos
bootstrap_sd_blancos <- one.boot(blancos, sd, R = 10000)
# Límite de detección (LOD = 3 * SD_blancos)
lod_bootstrap <- 3 * bootstrap_sd_blancos$t
# Estadísticas del LOD
lod_stats <- data.frame(
LOD_bootstrap = lod_bootstrap
)
summary(lod_stats) LOD_bootstrap
Min. :0.001844
1st Qu.:0.003533
Median :0.004168
Mean :0.004160
3rd Qu.:0.004729
Max. :0.007037
Visualización del LOD
p5 <- ggplot(lod_stats, aes(x = LOD_bootstrap)) +
geom_histogram(bins = 50, fill = "orange", alpha = 0.7) +
geom_vline(aes(xintercept = mean(LOD_bootstrap)),
color = "red", linetype = "dashed", size = 1) +
geom_vline(aes(xintercept = quantile(LOD_bootstrap, 0.025)),
color = "blue", linetype = "dotted") +
geom_vline(aes(xintercept = quantile(LOD_bootstrap, 0.975)),
color = "blue", linetype = "dotted") +
labs(title = "Distribución Bootstrap del Límite de Detección",
subtitle = "LOD = 3 × SD(blancos)",
x = "Límite de Detección",
y = "Frecuencia") +
theme_minimal()
print(p5)
# Intervalo de confianza del LOD
ic_lod <- quantile(lod_bootstrap, c(0.025, 0.975))
cat("Intervalo de confianza 95% para LOD:", ic_lod[1], "-", ic_lod[2], "\n")Intervalo de confianza 95% para LOD: 0.002683614 - 0.005776629
Ejemplo 4: Comparación de Métodos Analíticos
Datos de Comparación
# Resultados de dos métodos analíticos para la misma muestra
metodo_A <- c(15.2, 15.4, 15.1, 15.3, 15.5, 15.0, 15.2, 15.4)
metodo_B <- c(15.8, 15.6, 15.9, 15.7, 15.5, 15.8, 15.6, 15.7)
# Bootstrap de dos muestras
bootstrap_diff <- two.boot(metodo_A, metodo_B, mean, R = 10000)
# Intervalo de confianza para la diferencia
ic_diff <- boot.ci(bootstrap_diff, type = "perc", conf = 0.95)
print(ic_diff)BOOTSTRAP CONFIDENCE INTERVAL CALCULATIONS
Based on 10000 bootstrap replicates
CALL :
boot.ci(boot.out = bootstrap_diff, conf = 0.95, type = "perc")
Intervals :
Level Percentile
95% (-0.575, -0.300 )
Calculations and Intervals on Original Scale
Visualización de Comparación
# Crear dataframe para comparación
metodos_df <- data.frame(
valor = c(metodo_A, metodo_B),
metodo = rep(c("Método A", "Método B"), each = 8)
)
# Boxplot comparativo
p6 <- ggplot(metodos_df, aes(x = metodo, y = valor, fill = metodo)) +
geom_boxplot(alpha = 0.7) +
geom_jitter(width = 0.2, alpha = 0.6, size = 2) +
labs(title = "Comparación de Métodos Analíticos",
x = "Método",
y = "Resultado (mg/L)") +
theme_minimal() +
theme(legend.position = "none")
# Distribución de la diferencia bootstrap
diff_df <- data.frame(diferencia = bootstrap_diff$t)
p7 <- ggplot(diff_df, aes(x = diferencia)) +
geom_histogram(bins = 50, fill = "purple", alpha = 0.7) +
geom_vline(xintercept = 0, color = "red", linetype = "dashed", size = 1) +
labs(title = "Distribución Bootstrap de la Diferencia de Medias",
subtitle = "Método A - Método B",
x = "Diferencia de Medias",
y = "Frecuencia") +
theme_minimal()
# Mostrar gráficos lado a lado
gridExtra::grid.arrange(p6, p7, ncol = 2)
Referencias
Davison, A. C., y D. V. Hinkley. 1997. Bootstrap Methods and Their Application. Cambridge University Press.
Efron, Bradley. 1979. «Bootstrap Methods: Another Look at the Jackknife». The Annals of Statistics 7 (1): 1-26. https://doi.org/10.1214/aos/1176344552.
González, A. G., y M. A. Herrador. 2010. «Application of resampling methods to method validation in analytical chemistry». Analytica Chimica Acta 691 (1-2): 18-25. https://doi.org/10.1016/j.aca.2010.02.012.