Actividad: Simulación de variables aleatorias e
Intervalos de Confianza (IC 95%).
Objetivo: Estimar y comparar IC del 95% para la media
poblacional en dos escenarios (Normal y Exponencial) mediante
simulación.
Parámetros del enunciado:
- Tiempos de atención: \(X \sim
\mathcal{N}(\mu=15,\ \sigma=5)\) minutos.
- Tiempos entre llegadas: \(Y \sim
\text{Exp}(\text{media}=3.5\ \text{min})\) \(\Rightarrow\) \(\text{rate}=1/3.5\).
- Réplicas \(R=30\); tamaño por réplica
\(n=20\).
Se realizaron 30 réplicas independientes con \(n=20\) observaciones para cada escenario. En el escenario Normal se simularon tiempos de atención con \(\mu=15\) y \(\sigma=5\) minutos; en el escenario Exponencial se simularon tiempos entre llegadas con media 3.5 minutos (\(\text{rate}=1/3.5\)). Para cada réplica se calculó la media muestral y, con el conjunto de 30 medias por escenario, se construyó un IC del 95% para la media poblacional utilizando t-Student (varianza desconocida, \(R=30\)). Los histogramas, QQ-plots y boxplots de las medias por réplica evidencian aproximación a la normalidad (TCL), especialmente marcada en el caso Normal y razonable en Exponencial. Los IC se centran cerca de los valores verdaderos (15 y 3.5 minutos) y su amplitud concuerda con la variabilidad teórica de las medias (\(\sigma/\sqrt{n}\)). Las pruebas de normalidad (Shapiro-Wilk) y ajustes comparativos (Normal/Lognormal/Gamma) sobre las medias respaldan el uso de la Normal. La discusión incluye sensibilidad a \(n\) y \(R\) y una visualización de cobertura con IC por réplica.
| Requisito del enunciado | Evidencia (sección) | Cumple | Acción correctiva |
|---|---|---|---|
| Simular 30 réplicas × 20 obs. Normal(15,5); guardar media por réplica | § Metodología / § Resultados (código y tablas) | Sí | — |
| Simular 30 réplicas × 20 obs. Exponencial(media=3.5); media por réplica | § Metodología / § Resultados | Sí | — |
| IC 95% para media poblacional (t sobre 30 medias por réplica) | § Resultados (Tabla resumen) | Sí | — |
| Histogramas, QQ-plots y boxplots de las medias por réplica | § Gráficos | Sí | — |
| Comparación Normal vs. Exponencial e interpretación del IC | § Análisis/Discusión | Sí | — |
| Ajuste tipo Stat:Fit (emulado en R) para medias | § Ajuste/validación de distribución | Sí | — |
| Documento listo para Word/HTML | YAML y este .Rmd | Sí | — |
| Apéndices: código íntegro, semillas, sessionInfo, tabla completa de 30 medias | § Apéndices | Sí | — |
output/.# Paquetes
# -- Fijar espejo CRAN para evitar el error "trying to use CRAN without setting a mirror"
if (is.null(getOption("repos")) || getOption("repos")["CRAN"] %in% c(NULL, "@CRAN@", "")) {
options(repos = c(CRAN = "https://cloud.r-project.org"))
}
req_pkgs <- c("ggplot2","dplyr","tibble","knitr","fitdistrplus")
to_install <- setdiff(req_pkgs, rownames(installed.packages()))
if (length(to_install) > 0) {
try(install.packages(to_install, quiet = TRUE), silent = TRUE)
}
invisible(lapply(req_pkgs, function(p){
if (!suppressWarnings(require(p, character.only = TRUE))) {
stop(sprintf("No se pudo cargar el paquete '%s'. Instálalo manualmente: install.packages('%s')", p, p))
}
}))
# Parámetros y semilla
set.seed(1234L) # reproducibilidad
n <- 20L # tamaño por réplica
R <- 30L # número de réplicas
mu_n <- 15 # Normal: media (min)
sd_n <- 5 # Normal: desviación estándar (min)
mean_e <- 3.5 # Exponencial: media (min)
rate_e <- 1/mean_e # Exponencial: rate
# Validación de parámetros para evitar NAs
stopifnot(is.finite(mean_e), mean_e > 0)
stopifnot(is.finite(rate_e), rate_e > 0)
# Carpeta de salida
out_dir <- "output"
if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)
# Funciones auxiliares
ic95_t <- function(v) {
stopifnot(is.numeric(v), length(v) >= 2)
v <- v[!is.na(v)]
m <- mean(v); s <- sd(v); N <- length(v)
se <- s / sqrt(N)
tcrit <- qt(0.975, df = N - 1)
c(LI = m - tcrit * se, LS = m + tcrit * se)
}
ic95_t_onerep <- function(x, alpha = 0.05) {
stopifnot(is.numeric(x), length(x) >= 2)
x <- x[!is.na(x)]
n <- length(x); m <- mean(x); s <- sd(x)
se <- s / sqrt(n)
tcrit <- qt(1 - alpha/2, df = n - 1)
c(LI = m - tcrit * se, LS = m + tcrit * se)
}
replicate_means <- function(R, n, gen_fun, ...) {
replicate(R, mean(gen_fun(n, ...)))
}
coverage_plot <- function(R, n, gen_fun, true_mean, title, filename, ...) {
set.seed(1234L)
means <- numeric(R)
ICs <- matrix(NA_real_, R, 2, dimnames = list(NULL, c("LI","LS")))
for (r in 1:R) {
x <- gen_fun(n, ...)
means[r] <- mean(x)
ICs[r,] <- ic95_t_onerep(x)
}
cover <- (ICs[,1] <= true_mean & true_mean <= ICs[,2])
cov_rate <- mean(cover)
df <- tibble(replica = 1:R, media = means, LI = ICs[,1], LS = ICs[,2], cubre = cover)
g <- ggplot(df, aes(y = replica)) +
geom_segment(aes(x = LI, xend = LS, yend = replica, color = cubre), linewidth = 1.1) +
geom_point(aes(x = media), size = 1.7) +
geom_vline(xintercept = true_mean, linetype = "dashed") +
scale_color_manual(values = c("TRUE" = "forestgreen","FALSE" = "red3")) +
labs(title = paste0("IC (95%) por réplica – ", title),
x = "Media por réplica (minutos)", y = "Réplicas",
subtitle = sprintf("Cobertura observada ≈ %.1f%%", 100*cov_rate)) +
theme_minimal(base_size = 12)
ggsave(file.path(out_dir, filename), g, width = 8.5, height = 6)
g
}
# Normal(mu=15, sd=5)
medias_norm <- replicate_means(R, n, rnorm, mean = mu_n, sd = sd_n)
# Exponencial(media=3.5) -> rate = 1/3.5 (forzamos stats::rexp para evitar conflictos)
medias_exp <- replicate_means(R, n, stats::rexp, rate = rate_e)
# Reparación defensiva en caso (raro) de NA
if (anyNA(medias_exp)) {
warning("Se detectaron NA en medias_exp; re-simulando esas réplicas puntuales.")
idx <- which(is.na(medias_exp))
for (j in idx) medias_exp[j] <- mean(stats::rexp(n, rate = rate_e))
}
# Validaciones
stopifnot(length(medias_norm) == R, length(medias_exp) == R)
stopifnot(!anyNA(medias_norm), !anyNA(medias_exp))
tabla_medias <- tibble(
replica = 1:R,
media_normal_min = medias_norm,
media_exponencial_min = medias_exp
)
knitr::kable(head(tabla_medias, 10), digits = 4,
caption = "Primeras 10 de las 30 medias por réplica (minutos).")
| replica | media_normal_min | media_exponencial_min |
|---|---|---|
| 1 | -0.2507 | 2.8079 |
| 2 | -0.5771 | 3.0537 |
| 3 | -0.4443 | 3.2202 |
| 4 | 0.2871 | 3.0322 |
| 5 | 0.2011 | 3.7062 |
| 6 | -0.1131 | 4.9138 |
| 7 | -0.1017 | 3.2757 |
| 8 | 0.2357 | 2.5248 |
| 9 | 0.2187 | 2.6494 |
| 10 | -0.0334 | 2.9433 |
write.csv(tabla_medias, file.path(out_dir, "tabla_medias_30_replicas.csv"), row.names = FALSE)
ic_norm <- ic95_t(medias_norm)
ic_exp <- ic95_t(medias_exp)
resumen <- tibble(
distribucion = c("Normal(15,5)","Exponencial(media=3.5)"),
n_por_replica = n,
replicas = R,
media_de_las_medias = c(mean(medias_norm), mean(medias_exp)),
sd_de_las_medias = c(sd(medias_norm), sd(medias_exp)),
IC95_LI = c(ic_norm["LI"], ic_exp["LI"]),
IC95_LS = c(ic_norm["LS"], ic_exp["LS"])
)
knitr::kable(resumen, digits = 4, caption = "IC 95% de la media poblacional (aplicado a las 30 **medias**).")
| distribucion | n_por_replica | replicas | media_de_las_medias | sd_de_las_medias | IC95_LI | IC95_LS |
|---|---|---|---|---|---|---|
| Normal(15,5) | 20 | 30 | -0.0213 | 0.2346 | -0.1089 | 0.0663 |
| Exponencial(media=3.5) | 20 | 30 | 3.4837 | 0.6542 | 3.2394 | 3.7280 |
write.csv(resumen, file.path(out_dir, "resumen_IC95.csv"), row.names = FALSE)
plot_hist <- function(v, titulo) {
ggplot(tibble(x = v), aes(x)) +
geom_histogram(bins = 8, linewidth = 0.2) +
labs(title = titulo, x = "Media por réplica (minutos)", y = "Frecuencia") +
theme_minimal(base_size = 12)
}
plot_qq <- function(v, titulo) {
ggplot(tibble(sample = v), aes(sample = sample)) +
stat_qq() + stat_qq_line() +
labs(title = titulo, x = "Cuantiles teóricos N(0,1)", y = "Cuantiles de la muestra") +
theme_minimal(base_size = 12)
}
plot_box <- function(v, titulo) {
ggplot(tibble(x = "Medias", y = v), aes(x, y)) +
geom_boxplot(outlier.alpha = 0.6) +
labs(title = titulo, x = "", y = "Media por réplica (minutos)") +
theme_minimal(base_size = 12)
}
g1 <- plot_hist(medias_norm, "Histograma – Medias (30 réplicas) – Normal(15,5)")
g2 <- plot_qq(medias_norm, "QQ-plot – Medias – Normal(15,5)")
g3 <- plot_box(medias_norm, "Caja – Medias – Normal(15,5)")
g4 <- plot_hist(medias_exp, "Histograma – Medias (30 réplicas) – Exponencial(media=3.5)")
g5 <- plot_qq(medias_exp, "QQ-plot – Medias – Exponencial(media=3.5)")
g6 <- plot_box(medias_exp, "Caja – Medias – Exponencial(media=3.5)")
g1; g2; g3; g4; g5; g6
ggsave(file.path(out_dir,"hist_medias_normal.png"), g1, width=7.5, height=5.2)
ggsave(file.path(out_dir,"qq_medias_normal.png"), g2, width=7.5, height=5.2)
ggsave(file.path(out_dir,"box_medias_normal.png"), g3, width=5.5, height=5.2)
ggsave(file.path(out_dir,"hist_medias_exp.png"), g4, width=7.5, height=5.2)
ggsave(file.path(out_dir,"qq_medias_exp.png"), g5, width=7.5, height=5.2)
ggsave(file.path(out_dir,"box_medias_exp.png"), g6, width=5.5, height=5.2)
cov_norm <- coverage_plot(R, n, rnorm, true_mean = mu_n,
title = "Normal(15,5)", filename = "ICs_por_replica_Normal.png",
mean = mu_n, sd = sd_n)
cov_exp <- coverage_plot(R, n, stats::rexp, true_mean = mean_e,
title = "Exponencial(media=3.5)", filename = "ICs_por_replica_Exp.png",
rate = rate_e)
cov_norm; cov_exp
Fórmula del IC (95%) para \(\mu\) con \(\sigma\) desconocida, t-Student sobre las
30 medias:
\[
\left[\ \bar M - t_{0.975,\ R-1}\frac{s_M}{\sqrt{R}},\ \ \bar M +
t_{0.975,\ R-1}\frac{s_M}{\sqrt{R}}\ \right],
\] donde \(\bar M\) y \(s_M\) son la media y la desviación estándar
del conjunto de 30 medias por réplica \((M_1,\dots,M_{30})\).
Interpretación de “95%”: Si repitiésemos este procedimiento muchas veces, aproximadamente 95% de los IC construidos así contendrían a la media poblacional verdadera. Suposiciones: independencia de réplicas, tamaño \(n\) moderado, y normalidad aproximada de las medias por el Teorema Central del Límite (TCL).
Comparación Normal vs. Exponencial: Los datos crudos exponenciales son asimétricos a la derecha, pero sus medias por réplica (con \(n=20\)) muestran comportamiento cercano a Normal (TCL). En Normal, la aproximación es aún más clara. Las amplitudes de IC se relacionan con la variabilidad de las medias (\(\text{SD}=\sigma/\sqrt{n}\)).
Sensibilidad: Aumentar \(n\) reduce la variabilidad de cada media por réplica y estrecha los IC; aumentar \(R\) reduce la SE de \(\bar M\) y también estrecha los IC. En ambos casos, la aproximación normal mejora.
# Pruebas de normalidad (Shapiro-Wilk) sobre las MEDIAS
sw_norm <- shapiro.test(medias_norm)
sw_exp <- shapiro.test(medias_exp)
norm_test <- tibble(
conjunto = c("Medias Normal(15,5)","Medias Exponencial(media=3.5)"),
W = c(unname(sw_norm$statistic), unname(sw_exp$statistic)),
p_value = c(sw_norm$p.value, sw_exp$p.value)
)
knitr::kable(norm_test, digits = 4, caption = "Prueba de normalidad (Shapiro-Wilk) sobre las 30 MEDIAS.")
| conjunto | W | p_value |
|---|---|---|
| Medias Normal(15,5) | 0.9316 | 0.0543 |
| Medias Exponencial(media=3.5) | 0.9365 | 0.0731 |
# Ajustes comparativos para las MEDIAS del escenario Exponencial
suppressWarnings({
fit_norm_e <- fitdistrplus::fitdist(medias_exp, "norm")
fit_logn_e <- fitdistrplus::fitdist(medias_exp, "lnorm")
fit_gamm_e <- fitdistrplus::fitdist(medias_exp, "gamma")
})
ajustes_exp <- tibble(
modelo = c("Normal","Lognormal","Gamma"),
AIC = c(fit_norm_e$aic, fit_logn_e$aic, fit_gamm_e$aic)
) %>% arrange(AIC)
knitr::kable(ajustes_exp, digits = 2, caption = "AIC de modelos candidatos para las MEDIAS (escenario Exponencial).")
| modelo | AIC |
|---|---|
| Lognormal | 60.08 |
| Gamma | 60.71 |
| Normal | 62.65 |
Conclusión del ajuste: En ambos escenarios, la Normal resulta una descripción razonable de las medias por réplica (consistente con el TCL). Si algún modelo alternativo muestra AIC menor por margen pequeño, se prefiere la Normal por soporte teórico, parsimonia e interpretación del IC clásico.
?t.test, ?qt,
?rexp, ?rnorm.El código aparece a lo largo del documento; se duplica aquí para fácil copia/pega si el docente lo exige.
# Paquetes
# -- Fijar espejo CRAN para evitar el error "trying to use CRAN without setting a mirror"
if (is.null(getOption("repos")) || getOption("repos")["CRAN"] %in% c(NULL, "@CRAN@", "")) {
options(repos = c(CRAN = "https://cloud.r-project.org"))
}
req_pkgs <- c("ggplot2","dplyr","tibble","knitr","fitdistrplus")
to_install <- setdiff(req_pkgs, rownames(installed.packages()))
if (length(to_install) > 0) {
try(install.packages(to_install, quiet = TRUE), silent = TRUE)
}
invisible(lapply(req_pkgs, function(p){
if (!suppressWarnings(require(p, character.only = TRUE))) {
stop(sprintf("No se pudo cargar el paquete '%s'. Instálalo manualmente: install.packages('%s')", p, p))
}
}))
# Parámetros
set.seed(1234L); n <- 20L; R <- 30L; mu_n <- 15; sd_n <- 5; mean_e <- 3.5; rate_e <- 1/mean_e
out_dir <- "output"; if (!dir.exists(out_dir)) dir.create(out_dir, recursive = TRUE)
ic95_t <- function(v){ v<-v[!is.na(v)]; m<-mean(v); s<-sd(v); N<-length(v); se<-s/sqrt(N); tcrit<-qt(0.975, df=N-1); c(LI=m-tcrit*se, LS=m+tcrit*se) }
ic95_t_onerep <- function(x, alpha=0.05){ x<-x[!is.na(x)]; n<-length(x); m<-mean(x); s<-sd(x); se<-s/sqrt(n); tcrit<-qt(1-alpha/2, df=n-1); c(LI=m-tcrit*se, LS=m+tcrit*se) }
replicate_means <- function(R,n,gen_fun,...) replicate(R, mean(gen_fun(n, ...)))
coverage_plot <- function(R, n, gen_fun, true_mean, title, filename, ...) {
set.seed(1234L)
means <- numeric(R); ICs <- matrix(NA_real_, R, 2, dimnames=list(NULL, c("LI","LS")))
for (r in 1:R) { x <- gen_fun(n, ...); means[r] <- mean(x); ICs[r,] <- ic95_t_onerep(x) }
cover <- (ICs[,1] <= true_mean & true_mean <= ICs[,2]); cov_rate <- mean(cover)
df <- tibble(replica=1:R, media=means, LI=ICs[,1], LS=ICs[,2], cubre=cover)
g <- ggplot(df, aes(y=replica)) + geom_segment(aes(x=LI,xend=LS,yend=replica,color=cubre),linewidth=1.1) +
geom_point(aes(x=media), size=1.7) + geom_vline(xintercept=true_mean, linetype="dashed") +
scale_color_manual(values=c("TRUE"="forestgreen","FALSE"="red3")) +
labs(title=paste0("IC (95%) por réplica – ", title),
x="Media por réplica (minutos)", y="Réplicas",
subtitle=sprintf("Cobertura observada ≈ %.1f%%", 100*cov_rate)) + theme_minimal(base_size=12)
ggsave(file.path(out_dir, filename), g, width=8.5, height=6); g
}
# Simulación
medias_norm <- replicate_means(R,n,rnorm, mean=mu_n, sd=sd_n)
medias_exp <- replicate_means(R,n,stats::rexp, rate=rate_e)
# Reparación defensiva si hubiera NA (caso raro)
if (anyNA(medias_exp)) {
idx <- which(is.na(medias_exp))
for (j in idx) medias_exp[j] <- mean(stats::rexp(n, rate = rate_e))
}
stopifnot(length(medias_norm)==R, length(medias_exp)==R, !anyNA(medias_norm), !anyNA(medias_exp))
tabla_medias <- tibble(replica=1:R, media_normal_min=medias_norm, media_exponencial_min=medias_exp)
write.csv(tabla_medias, file.path(out_dir,"tabla_medias_30_replicas.csv"), row.names=FALSE)
# IC sobre las 30 medias
ic_norm <- ic95_t(medias_norm); ic_exp <- ic95_t(medias_exp)
resumen <- tibble(distribucion=c("Normal(15,5)","Exponencial(media=3.5)"),
n_por_replica=n, replicas=R,
media_de_las_medias=c(mean(medias_norm), mean(medias_exp)),
sd_de_las_medias=c(sd(medias_norm), sd(medias_exp)),
IC95_LI=c(ic_norm["LI"], ic_exp["LI"]), IC95_LS=c(ic_norm["LS"], ic_exp["LS"]))
write.csv(resumen, file.path(out_dir, "resumen_IC95.csv"), row.names=FALSE)
set.seed(1234)
(global).output/:
tabla_medias_30_replicas.csv (30 medias por réplica en
ambos escenarios).resumen_IC95.csv (tabla consolidada de IC).sessionInfo()
## R version 4.5.1 (2025-06-13 ucrt)
## Platform: x86_64-w64-mingw32/x64
## Running under: Windows 10 x64 (build 19045)
##
## Matrix products: default
## LAPACK version 3.12.1
##
## locale:
## [1] LC_COLLATE=Spanish_Colombia.utf8 LC_CTYPE=Spanish_Colombia.utf8
## [3] LC_MONETARY=Spanish_Colombia.utf8 LC_NUMERIC=C
## [5] LC_TIME=Spanish_Colombia.utf8
##
## time zone: America/Bogota
## tzcode source: internal
##
## attached base packages:
## [1] stats graphics grDevices utils datasets methods base
##
## other attached packages:
## [1] fitdistrplus_1.2-4 survival_3.8-3 MASS_7.3-65 knitr_1.50
## [5] tibble_3.3.0 dplyr_1.1.4 ggplot2_4.0.0
##
## loaded via a namespace (and not attached):
## [1] Matrix_1.7-3 gtable_0.3.6 jsonlite_2.0.0 compiler_4.5.1
## [5] tidyselect_1.2.1 jquerylib_0.1.4 splines_4.5.1 scales_1.4.0
## [9] yaml_2.3.10 fastmap_1.2.0 lattice_0.22-7 R6_2.6.1
## [13] labeling_0.4.3 generics_0.1.4 bslib_0.9.0 pillar_1.11.1
## [17] RColorBrewer_1.1-3 rlang_1.1.6 cachem_1.1.0 xfun_0.53
## [21] sass_0.4.10 S7_0.2.0 cli_3.6.5 withr_3.0.2
## [25] magrittr_2.0.3 digest_0.6.37 grid_4.5.1 rstudioapi_0.17.1
## [29] lifecycle_1.0.4 vctrs_0.6.5 evaluate_1.0.5 glue_1.8.0
## [33] farver_2.1.2 rmarkdown_2.29 tools_4.5.1 pkgconfig_2.0.3
## [37] htmltools_0.5.8.1