Se estudia si la fuerza de compresión afecta la dureza de comprimidos farmacéuticos, expresada en newtons (N). Se preparan cuatro lotes independientes de granulado. Cada lote se divide en tres porciones comparables y cada porción se comprime con una fuerza distinta (8, 12 o 16 kN), en orden aleatorio dentro de cada lote.
El diseño es en bloques completos aleatorizados (DBCA), no un diseño completamente aleatorizado sin bloques. Los lotes son los bloques; las fuerzas, los tratamientos. Cada resultado representa la dureza media de una porción procesada independientemente, que constituye una unidad experimental.
Aviso: todos los datos son ficticios y se utilizan exclusivamente para aprender estadística experimental.
Objetivo: determinar si existen diferencias en la dureza media entre tres fuerzas de compresión, controlando la variabilidad entre lotes de granulado.
Nivel de significancia: α = 0.05.
datos <- data.frame(
Lote = factor(rep(paste0("Lote ", 1:4), each=3)),
Fuerza = factor(rep(c("8 kN", "12 kN", "16 kN"), times=4),
levels=c("8 kN", "12 kN", "16 kN")),
Dureza = c(62,81,101, 66,85,105, 59,79,98, 64,83,103)
)
kable(datos, caption="Observaciones simuladas: dureza (N) por lote y fuerza")| Lote | Fuerza | Dureza |
|---|---|---|
| Lote 1 | 8 kN | 62 |
| Lote 1 | 12 kN | 81 |
| Lote 1 | 16 kN | 101 |
| Lote 2 | 8 kN | 66 |
| Lote 2 | 12 kN | 85 |
| Lote 2 | 16 kN | 105 |
| Lote 3 | 8 kN | 59 |
| Lote 3 | 12 kN | 79 |
| Lote 3 | 16 kN | 98 |
| Lote 4 | 8 kN | 64 |
| Lote 4 | 12 kN | 83 |
| Lote 4 | 16 kN | 103 |
descriptivos <- datos %>% group_by(Fuerza) %>%
summarise(n=n(), Media=mean(Dureza), Mediana=median(Dureza),
DE=sd(Dureza), Varianza=var(Dureza),
Minimo=min(Dureza), Maximo=max(Dureza),
CV_porcentaje=100*sd(Dureza)/mean(Dureza), .groups="drop")
kable(descriptivos, digits=2,
caption="Estadísticos descriptivos por fuerza de compresión")| Fuerza | n | Media | Mediana | DE | Varianza | Minimo | Maximo | CV_porcentaje |
|---|---|---|---|---|---|---|---|---|
| 8 kN | 4 | 62.75 | 63 | 2.99 | 8.92 | 59 | 66 | 4.76 |
| 12 kN | 4 | 82.00 | 82 | 2.58 | 6.67 | 79 | 85 | 3.15 |
| 16 kN | 4 | 101.75 | 102 | 2.99 | 8.92 | 98 | 105 | 2.93 |
La mayor dureza media corresponde a 16 kN (101.75 N), mientras que la menor corresponde a 8 kN (62.75 N).
ggplot(datos, aes(Fuerza, Dureza, fill=Fuerza)) +
geom_boxplot(alpha=.65, width=.55, outlier.shape=NA) +
geom_jitter(width=.09, size=3.1, alpha=.85, shape=21, color="#23313F") +
scale_fill_manual(values=color_fuerza) +
labs(title="Distribución de dureza por fuerza de compresión",
subtitle="Cada punto representa una porción experimental",
x="Fuerza de compresión", y="Dureza (N)") +
theme_minimal(base_size=13) + theme(legend.position="none", plot.title=element_text(face="bold"))ggplot(descriptivos, aes(Fuerza, Media, fill=Fuerza)) +
geom_col(width=.62, alpha=.85) +
geom_errorbar(aes(ymin=Media-DE, ymax=Media+DE), width=.15, linewidth=.8) +
geom_text(aes(label=sprintf("%.2f N", Media)), vjust=-1.1, fontface="bold") +
scale_fill_manual(values=color_fuerza) +
scale_y_continuous(limits=c(0, max(descriptivos$Media+descriptivos$DE)+14)) +
labs(title="Dureza media ± desviación estándar",
x="Fuerza de compresión", y="Dureza media (N)") +
theme_minimal(base_size=13) + theme(legend.position="none")ggplot(datos, aes(Fuerza, Dureza, group=Lote, color=Lote)) +
geom_line(linewidth=1.2) + geom_point(size=3.5) +
scale_color_brewer(palette="Dark2") +
labs(title="Respuesta a la compresión dentro de cada lote",
subtitle="Cada línea corresponde a un bloque",
x="Fuerza de compresión", y="Dureza (N)") +
theme_minimal(base_size=13)El modelo aditivo es Dureza = media general + efecto de fuerza + efecto de lote + error.
modelo <- aov(Dureza ~ Fuerza + Lote, data=datos)
tabla_anova <- as.data.frame(summary(modelo)[[1]])
tabla_anova$Fuente <- trimws(rownames(tabla_anova))
tabla_anova <- tabla_anova[, c("Fuente", "Df", "Sum Sq", "Mean Sq", "F value", "Pr(>F)")]
colnames(tabla_anova) <- c("Fuente", "GL", "SC", "CM", "F", "p")
kable(tabla_anova, digits=5, caption="ANOVA aditivo del DBCA")| Fuente | GL | SC | CM | F | p | |
|---|---|---|---|---|---|---|
| Fuerza | Fuerza | 2 | 3042.167 | 1521.08333 | 18253 | 0 |
| Lote | Lote | 3 | 73.000 | 24.33333 | 292 | 0 |
| Residuals | Residuals | 6 | 0.500 | 0.08333 | NA | NA |
p_fuerza <- tabla_anova$p[match("Fuerza", tabla_anova$Fuente)]
p_lote <- tabla_anova$p[match("Lote", tabla_anova$Fuente)]Tratamientos: p = <1e-04. Se rechaza H₀: existe evidencia de diferencias en dureza media entre fuerzas de compresión.
Bloques: p = <1e-04. Se detectan diferencias asociadas con los lotes, lo que muestra que el bloqueo captura variación relevante en estos datos.
Precaución: al existir una sola observación por combinación lote × fuerza, no se puede estimar por separado una interacción lote × fuerza. El ANOVA utilizado supone un modelo aditivo.
residuos <- residuals(modelo)
normalidad <- shapiro.test(residuos)
kable(data.frame(Prueba="Shapiro-Wilk", W=unname(normalidad$statistic),
p=normalidad$p.value), digits=5)| Prueba | W | p |
|---|---|---|
| Shapiro-Wilk | 0.82661 | 0.01911 |
Interpretación: p = 0.0191. Se rechaza la hipótesis de normalidad. El ANOVA debe interpretarse con cautela y conviene revisar el gráfico Q-Q y Friedman.
ggplot(data.frame(Residuos=residuos), aes(sample=Residuos)) +
stat_qq(color="#2471A3", size=3) +
stat_qq_line(color="#E74C3C", linewidth=1) +
labs(title="Gráfico Q-Q de residuos", x="Cuantiles teóricos", y="Cuantiles observados") +
theme_minimal(base_size=13)lev <- car::leveneTest(Dureza ~ Fuerza, data=datos, center=median)
kable(as.data.frame(lev), digits=5, caption="Levene por fuerza de compresión")| Df | F value | Pr(>F) | |
|---|---|---|---|
| group | 2 | 0.04286 | 0.95824 |
| 9 | NA | NA |
Interpretación: p = 0.958. No se rechaza la igualdad de varianzas entre fuerzas.
Limitación: esta prueba compara las observaciones por fuerza sin incorporar explícitamente el bloqueo. Es una revisión complementaria, no una validación completa de la homogeneidad de la varianza residual del DBCA.
diagnostico <- data.frame(Ajustados=fitted(modelo), Residuos=residuos,
Fuerza=datos$Fuerza, Lote=datos$Lote)
ggplot(diagnostico, aes(Ajustados, Residuos, color=Fuerza)) +
geom_hline(yintercept=0, linetype="dashed", color="#C0392B") +
geom_point(size=3.5) +
scale_color_manual(values=color_fuerza) +
labs(title="Residuos frente a valores ajustados",
x="Dureza ajustada (N)", y="Residuo (N)") +
theme_minimal(base_size=13)tukey <- TukeyHSD(modelo, which="Fuerza")
tabla_tukey <- data.frame(Comparacion=rownames(tukey$Fuerza),
tukey$Fuerza, row.names=NULL, check.names=FALSE)
kable(tabla_tukey, digits=5, caption="Comparaciones de Tukey ajustadas por multiplicidad")| Comparacion | diff | lwr | upr | p adj |
|---|---|---|---|---|
| 12 kN-8 kN | 19.25 | 18.62369 | 19.87631 | 0 |
| 16 kN-8 kN | 39.00 | 38.37369 | 39.62631 | 0 |
| 16 kN-12 kN | 19.75 | 19.12369 | 20.37631 | 0 |
Una comparación se considera significativa cuando su valor p ajustado es inferior a 0.05 o su intervalo de confianza no incluye cero.
kw <- kruskal.test(Dureza ~ Fuerza, data=datos)
kable(data.frame(Prueba="Kruskal-Wallis", Estadistico=unname(kw$statistic),
GL=unname(kw$parameter), p=kw$p.value), digits=5)| Prueba | Estadistico | GL | p |
|---|---|---|---|
| Kruskal-Wallis | 9.84615 | 2 | 0.00728 |
Interpretación: p = 0.00728. La prueba detecta diferencias entre los grupos.
Advertencia: Kruskal-Wallis no incorpora los lotes como bloques y supone observaciones independientes entre grupos; por eso no debe reemplazar al análisis bloqueado de este experimento.
fr <- friedman.test(Dureza ~ Fuerza | Lote, data=datos)
kable(data.frame(Prueba="Friedman", Estadistico=unname(fr$statistic),
GL=unname(fr$parameter), p=fr$p.value), digits=5)| Prueba | Estadistico | GL | p |
|---|---|---|---|
| Friedman | 8 | 2 | 0.01832 |
Interpretación: p = 0.0183. Se rechaza H₀: hay evidencia de diferencias entre fuerzas al considerar los lotes como bloques.
Friedman compara los rangos de los tratamientos dentro de cada bloque. Si se necesitan comparaciones posteriores específicas entre fuerzas, debe utilizarse un procedimiento post hoc adecuado para datos bloqueados.
resumen <- data.frame(
Prueba=c("ANOVA: fuerza", "ANOVA: lote", "Shapiro-Wilk (residuos)",
"Levene (por fuerza)", "Kruskal-Wallis (sin bloques)", "Friedman (con bloques)"),
p=c(p_fuerza, p_lote, normalidad$p.value, p_levene, kw$p.value, fr$p.value)
)
resumen$Decision <- ifelse(resumen$p < alpha, "Rechazar H0", "No rechazar H0")
kable(resumen, digits=5, caption="Resumen de contrastes (α = 0.05)")| Prueba | p | Decision |
|---|---|---|
| ANOVA: fuerza | 0.00000 | Rechazar H0 |
| ANOVA: lote | 0.00000 | Rechazar H0 |
| Shapiro-Wilk (residuos) | 0.01911 | Rechazar H0 |
| Levene (por fuerza) | 0.95824 | No rechazar H0 |
| Kruskal-Wallis (sin bloques) | 0.00728 | Rechazar H0 |
| Friedman (con bloques) | 0.01832 | Rechazar H0 |
Alcance: el ejercicio utiliza únicamente datos simulados. Para una investigación real deben justificarse las condiciones de fabricación, el número de réplicas, la independencia de las porciones experimentales y los criterios farmacotécnicos de aceptación.