Objetivo: explorar cómo cambia la comparación de las dos estrategias cuando se incorpora un factor de bloque construido a partir del conocimiento inicial, medido por el pretest.
Pregunta: ¿qué diferencia de postest se observa entre «Predicción previa» y «Explicación y práctica» cuando se comparan los estudiantes sin bloques y cuando se realiza un análisis exploratorio de parejas con pretest similar?
La siguiente imagen conserva la descripción de las dos estrategias tal como aparece en el Word de referencia.
Tratamiento: factor experimental.Pretest: puntuación inicial.Postest: variable de respuesta principal.Retencion_1_semana: variable de respuesta
secundaria.Bloque: pareja analítica retrospectiva construida
ordenando el pretest dentro de cada tratamiento.El estudio compara dos tratamientos educativos y utiliza el pretest como criterio de formación de bloques. La estructura objetivo de un DBCA completo es:
Esquema del diseño: primero se aplica el pretest; después se forman las parejas; en un DBCA prospectivo se sortea dentro de cada pareja qué estudiante recibe cada estrategia; finalmente se aplica el postest.
## Pretest (40 estudiantes) → formación de 20 bloques de 2 estudiantes → asignación aleatoria dentro de cada bloque → aplicación de los tratamientos → medición del postest
| Tipo | Variable | Función |
|---|---|---|
| Factor de tratamiento | Tratamiento | Compara las dos estrategias de enseñanza |
| Factor de bloqueo | Bloque | Controla diferencias iniciales entre parejas |
| Respuesta principal | Postest | Mide el rendimiento después de la enseñanza |
| Covariable descriptiva | Pretest | Mide el nivel inicial y permite formar parejas |
| Respuesta secundaria | Retención a una semana | Describe la persistencia del aprendizaje |
datos <- read.csv(
text = "Estudiante,Tratamiento,Pretest,Postest,Retencion_1_semana
E01,Prediccion previa,50.4,74.2,51.5
E02,Prediccion previa,54.8,73.2,56.8
E03,Explicacion y practica,62.6,79.7,59.7
E04,Prediccion previa,52.8,66.2,51.6
E05,Explicacion y practica,60.9,68.7,67.8
E06,Prediccion previa,57.3,68.8,61.1
E07,Explicacion y practica,53.7,51.3,40.6
E08,Prediccion previa,43.2,52.3,53.7
E09,Explicacion y practica,62.7,63.8,67.6
E10,Prediccion previa,67.3,67.9,74.0
E11,Explicacion y practica,50.0,50.2,38.7
E12,Explicacion y practica,43.1,65.0,34.6
E13,Prediccion previa,56.4,73.6,62.9
E14,Explicacion y practica,64.7,67.8,63.1
E15,Explicacion y practica,69.5,67.7,66.5
E16,Explicacion y practica,56.1,58.6,67.5
E17,Prediccion previa,60.5,71.0,65.4
E18,Prediccion previa,65.1,68.3,63.3
E19,Prediccion previa,70.3,76.9,63.5
E20,Explicacion y practica,53.1,74.5,55.5
E21,Explicacion y practica,63.0,64.1,66.5
E22,Prediccion previa,47.8,66.4,49.5
E23,Prediccion previa,57.8,82.8,54.6
E24,Prediccion previa,53.1,49.6,67.2
E25,Prediccion previa,44.2,57.3,47.6
E26,Explicacion y practica,53.2,73.1,51.2
E27,Explicacion y practica,55.7,62.5,58.8
E28,Explicacion y practica,58.8,71.1,62.6
E29,Explicacion y practica,57.7,59.8,61.5
E30,Prediccion previa,60.8,77.8,64.4
E31,Prediccion previa,66.7,88.5,56.3
E32,Explicacion y practica,56.5,67.8,49.8
E33,Explicacion y practica,42.0,51.0,27.0
E34,Explicacion y practica,60.3,66.5,56.9
E35,Prediccion previa,67.7,69.7,71.7
E36,Prediccion previa,50.0,67.9,64.3
E37,Explicacion y practica,50.6,57.8,33.0
E38,Prediccion previa,67.5,82.0,62.3
E39,Prediccion previa,51.6,62.1,49.9
E40,Explicacion y practica,62.2,76.3,64.6",
stringsAsFactors = FALSE
)
datos$Tratamiento <- factor(
datos$Tratamiento,
levels = c("Prediccion previa", "Explicacion y practica")
)
stopifnot(
nrow(datos) == 40,
all(table(datos$Tratamiento) == 20),
!anyNA(datos)
)
knitr::kable(head(datos, 10), digits = 2,
caption = "Primeros 10 registros de la base original")
| Estudiante | Tratamiento | Pretest | Postest | Retencion_1_semana |
|---|---|---|---|---|
| E01 | Prediccion previa | 50.4 | 74.2 | 51.5 |
| E02 | Prediccion previa | 54.8 | 73.2 | 56.8 |
| E03 | Explicacion y practica | 62.6 | 79.7 | 59.7 |
| E04 | Prediccion previa | 52.8 | 66.2 | 51.6 |
| E05 | Explicacion y practica | 60.9 | 68.7 | 67.8 |
| E06 | Prediccion previa | 57.3 | 68.8 | 61.1 |
| E07 | Explicacion y practica | 53.7 | 51.3 | 40.6 |
| E08 | Prediccion previa | 43.2 | 52.3 | 53.7 |
| E09 | Explicacion y practica | 62.7 | 63.8 | 67.6 |
| E10 | Prediccion previa | 67.3 | 67.9 | 74.0 |
table(datos$Tratamiento)
##
## Prediccion previa Explicacion y practica
## 20 20
resumen <- do.call(rbind, lapply(
split(datos, datos$Tratamiento),
function(g) data.frame(
Tratamiento = as.character(g$Tratamiento[1]),
n = nrow(g),
Media_pretest = mean(g$Pretest),
DE_pretest = sd(g$Pretest),
Media_postest = mean(g$Postest),
DE_postest = sd(g$Postest),
Mediana_postest = median(g$Postest),
Media_retencion = mean(g$Retencion_1_semana),
DE_retencion = sd(g$Retencion_1_semana)
)
))
knitr::kable(resumen, digits = 3,
caption = "Estadísticos descriptivos por tratamiento")
| Tratamiento | n | Media_pretest | DE_pretest | Media_postest | DE_postest | Mediana_postest | Media_retencion | DE_retencion | |
|---|---|---|---|---|---|---|---|---|---|
| Prediccion previa | Prediccion previa | 20 | 57.265 | 8.247 | 69.825 | 9.716 | 69.25 | 59.580 | 7.567 |
| Explicacion y practica | Explicacion y practica | 20 | 56.820 | 6.970 | 64.865 | 8.292 | 65.75 | 54.675 | 13.055 |
if (!requireNamespace("ggplot2", quietly = TRUE)) {
stop("Instala ggplot2 con install.packages('ggplot2').")
}
library(ggplot2)
ggplot(datos, aes(Tratamiento, Pretest, fill = Tratamiento)) +
geom_boxplot(alpha = 0.65, outlier.shape = NA) +
geom_jitter(width = 0.12, alpha = 0.75) +
labs(title = "Pretest por tratamiento", x = "Tratamiento",
y = "Puntuación") +
theme_minimal() + theme(legend.position = "none")
ggplot(datos, aes(Tratamiento, Postest, fill = Tratamiento)) +
geom_boxplot(alpha = 0.65, outlier.shape = NA) +
geom_jitter(width = 0.12, alpha = 0.75) +
labs(title = "Postest por tratamiento", x = "Tratamiento",
y = "Puntuación") +
theme_minimal() + theme(legend.position = "none")
Este modelo reproduce la estructura del DCA original: compara el postest medio de los dos tratamientos sin incorporar un factor de bloque.
modelo_dca <- aov(Postest ~ Tratamiento, data = datos)
tabla_anova_dca <- summary(modelo_dca)[[1]]
knitr::kable(tabla_anova_dca, digits = 4,
caption = "ANOVA del DCA sin bloques")
| Df | Sum Sq | Mean Sq | F value | Pr(>F) | |
|---|---|---|---|---|---|
| Tratamiento | 1 | 246.016 | 246.0160 | 3.0157 | 0.0906 |
| Residuals | 38 | 3100.023 | 81.5796 | NA | NA |
media_A <- mean(datos$Postest[datos$Tratamiento == "Prediccion previa"])
media_B <- mean(datos$Postest[datos$Tratamiento == "Explicacion y practica"])
diferencia_dca <- media_A - media_B
prueba_dca <- t.test(
Postest ~ Tratamiento, data = datos,
var.equal = TRUE, conf.level = 0.95
)
knitr::kable(data.frame(
Media_prediccion_previa = media_A,
Media_explicacion_practica = media_B,
Diferencia_A_menos_B = diferencia_dca,
IC95_inferior = prueba_dca$conf.int[1],
IC95_superior = prueba_dca$conf.int[2],
Valor_p = prueba_dca$p.value
), digits = 4, caption = "Diferencia de medias del DCA e IC del 95 %")
| Media_prediccion_previa | Media_explicacion_practica | Diferencia_A_menos_B | IC95_inferior | IC95_superior | Valor_p |
|---|---|---|---|---|---|
| 69.825 | 64.865 | 4.96 | -0.8221 | 10.7421 | 0.0906 |
Una diferencia positiva indica una media de postest mayor para «Predicción previa»; una diferencia negativa indica una media mayor para «Explicación y práctica». El valor p no mide el tamaño ni la importancia educativa del efecto.
Como la base original no contiene bloques, construiremos 20 parejas
analíticas. El procedimiento consiste en ordenar por
Pretest a los estudiantes de cada tratamiento por separado
y emparejar las observaciones según su posición en el orden. De esta
manera, cada bloque contiene un estudiante de cada tratamiento con
puntuaciones iniciales relativamente cercanas.
La siguiente figura reproduce la representación de los 20 bloques que aparece en el documento de referencia. Los rótulos A y B identifican los dos tratamientos; la asignación real de cada estudiante debe efectuarse mediante sorteo dentro de cada bloque.
Cada bloque contiene los dos tratamientos emparejados por su puntuación inicial (Pretest).
| Bloque | Estudiante 1 | Estudiante 2 |
|---|---|---|
| 1 | Tratamiento A (Nota: 95) |
Tratamiento B (Nota: 93) |
| 2 | Tratamiento A (Nota: 80) |
Tratamiento B (Nota: 82) |
| 3 | Tratamiento A (Nota: 65) |
Tratamiento B (Nota: 64) |
| … | … | … |
| 20 | Tratamiento A (…) |
Tratamiento B (…) |
A <- datos[datos$Tratamiento == "Prediccion previa", ]
B <- datos[datos$Tratamiento == "Explicacion y practica", ]
A <- A[order(A$Pretest), ]
B <- B[order(B$Pretest), ]
stopifnot(nrow(A) == nrow(B))
A$Bloque <- sprintf("B%02d", seq_len(nrow(A)))
B$Bloque <- sprintf("B%02d", seq_len(nrow(B)))
datos_bloque <- rbind(A, B)
datos_bloque$Bloque <- factor(datos_bloque$Bloque)
# Verificar que cada bloque contiene ambos tratamientos
verificacion <- table(datos_bloque$Bloque, datos_bloque$Tratamiento)
stopifnot(all(verificacion == 1))
diferencias_pretest <- data.frame(
Bloque = sprintf("B%02d", seq_len(nrow(A))),
Estudiante_A = A$Estudiante,
Pretest_A = A$Pretest,
Estudiante_B = B$Estudiante,
Pretest_B = B$Pretest,
Diferencia_absoluta_pretest = abs(A$Pretest - B$Pretest)
)
knitr::kable(diferencias_pretest, digits = 2,
caption = "20 parejas emparejadas según el pretest")
| Bloque | Estudiante_A | Pretest_A | Estudiante_B | Pretest_B | Diferencia_absoluta_pretest |
|---|---|---|---|---|---|
| B01 | E08 | 43.2 | E33 | 42.0 | 1.2 |
| B02 | E25 | 44.2 | E12 | 43.1 | 1.1 |
| B03 | E22 | 47.8 | E11 | 50.0 | 2.2 |
| B04 | E36 | 50.0 | E37 | 50.6 | 0.6 |
| B05 | E01 | 50.4 | E20 | 53.1 | 2.7 |
| B06 | E39 | 51.6 | E26 | 53.2 | 1.6 |
| B07 | E04 | 52.8 | E07 | 53.7 | 0.9 |
| B08 | E24 | 53.1 | E27 | 55.7 | 2.6 |
| B09 | E02 | 54.8 | E16 | 56.1 | 1.3 |
| B10 | E13 | 56.4 | E32 | 56.5 | 0.1 |
| B11 | E06 | 57.3 | E29 | 57.7 | 0.4 |
| B12 | E23 | 57.8 | E28 | 58.8 | 1.0 |
| B13 | E17 | 60.5 | E34 | 60.3 | 0.2 |
| B14 | E30 | 60.8 | E05 | 60.9 | 0.1 |
| B15 | E18 | 65.1 | E40 | 62.2 | 2.9 |
| B16 | E31 | 66.7 | E03 | 62.6 | 4.1 |
| B17 | E10 | 67.3 | E09 | 62.7 | 4.6 |
| B18 | E38 | 67.5 | E21 | 63.0 | 4.5 |
| B19 | E35 | 67.7 | E14 | 64.7 | 3.0 |
| B20 | E19 | 70.3 | E15 | 69.5 | 0.8 |
summary(diferencias_pretest$Diferencia_absoluta_pretest)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 0.100 0.750 1.250 1.795 2.750 4.600
mean(diferencias_pretest$Diferencia_absoluta_pretest)
## [1] 1.795
median(diferencias_pretest$Diferencia_absoluta_pretest)
## [1] 1.25
max(diferencias_pretest$Diferencia_absoluta_pretest)
## [1] 4.6
El modelo aditivo es:
\[ Y_{ij}=\mu+\tau_i+\beta_j+\varepsilon_{ij} \]
donde \(Y_{ij}\) es el postest, \(\tau_i\) es el efecto del tratamiento y \(\beta_j\) es el efecto asociado al bloque.
modelo_bloque <- aov(Postest ~ Tratamiento + Bloque, data = datos_bloque)
tabla_anova_bloque <- summary(modelo_bloque)[[1]]
knitr::kable(tabla_anova_bloque, digits = 4,
caption = "ANOVA exploratorio con bloques emparejados")
| Df | Sum Sq | Mean Sq | F value | Pr(>F) | |
|---|---|---|---|---|---|
| Tratamiento | 1 | 246.016 | 246.0160 | 5.9260 | 0.0250 |
| Bloque | 19 | 2311.239 | 121.6442 | 2.9301 | 0.0119 |
| Residuals | 19 | 788.784 | 41.5149 | NA | NA |
p_tratamiento_bloque <- tabla_anova_bloque["Tratamiento", "Pr(>F)"]
F_tratamiento_bloque <- tabla_anova_bloque["Tratamiento", "F value"]
Como cada bloque contiene una observación de cada tratamiento, también podemos calcular la diferencia intrabloque. Se define aquí como «Predicción previa menos Explicación y práctica».
wide <- reshape(
datos_bloque[, c("Bloque", "Tratamiento", "Postest")],
idvar = "Bloque", timevar = "Tratamiento", direction = "wide"
)
col_A <- "Postest.Prediccion previa"
col_B <- "Postest.Explicacion y practica"
diferencias_postest <- wide[[col_A]] - wide[[col_B]]
prueba_pareada <- t.test(diferencias_postest, mu = 0, conf.level = 0.95)
knitr::kable(data.frame(
Diferencia_media_A_menos_B = mean(diferencias_postest),
DE_diferencias = sd(diferencias_postest),
IC95_inferior = prueba_pareada$conf.int[1],
IC95_superior = prueba_pareada$conf.int[2],
Valor_p_pareado = prueba_pareada$p.value
), digits = 4, caption = "Contraste pareado de postest por bloque")
| Diferencia_media_A_menos_B | DE_diferencias | IC95_inferior | IC95_superior | Valor_p_pareado |
|---|---|---|---|---|
| 4.96 | 9.1121 | 0.6954 | 9.2246 | 0.025 |
En este diseño completo de dos tratamientos y bloques, el contraste pareado es equivalente al contraste de tratamiento del modelo aditivo con bloques, salvo diferencias de implementación numérica.
residuos_bloque <- residuals(modelo_bloque)
shapiro_bloque <- shapiro.test(residuos_bloque)
shapiro_bloque
##
## Shapiro-Wilk normality test
##
## data: residuos_bloque
## W = 0.98158, p-value = 0.7477
par(mfrow = c(1, 2))
plot(fitted(modelo_bloque), residuos_bloque,
xlab = "Valores ajustados", ylab = "Residuos",
main = "Residuos vs. ajustados")
abline(h = 0, lty = 2)
qqnorm(residuos_bloque, main = "Gráfico Q-Q")
qqline(residuos_bloque)
par(mfrow = c(1, 1))
La prueba de Shapiro-Wilk contrasta si los residuos son compatibles con normalidad. Un valor p alto no demuestra normalidad; se interpreta junto con el gráfico Q-Q y los residuos frente a los valores ajustados.
En el modelo bloqueado, Levene por tratamiento puede calcularse como diagnóstico complementario, pero no sustituye la inspección de los residuos del modelo y no evalúa todos los supuestos del DBCA.
if (!requireNamespace("car", quietly = TRUE)) {
message("Para ejecutar Levene, instala el paquete car: install.packages('car').")
} else {
car::leveneTest(Postest ~ Tratamiento, data = datos_bloque)
}
La independencia se justifica por el diseño y el proceso de generación/recolección; no se demuestra con Shapiro-Wilk o Levene. Además, como los bloques se formaron retrospectivamente, estos diagnósticos no corrigen las limitaciones del diseño original.
mse_dca <- sum(residuals(modelo_dca)^2) / df.residual(modelo_dca)
mse_bloque <- sum(residuals(modelo_bloque)^2) / df.residual(modelo_bloque)
comparacion <- data.frame(
Analisis = c("DCA sin bloques", "Modelo exploratorio con bloques"),
Diferencia_A_menos_B = c(
diferencia_dca,
mean(diferencias_postest)
),
IC95_inferior = c(
prueba_dca$conf.int[1],
prueba_pareada$conf.int[1]
),
IC95_superior = c(
prueba_dca$conf.int[2],
prueba_pareada$conf.int[2]
),
Valor_p = c(
prueba_dca$p.value,
prueba_pareada$p.value
),
MSE_residual = c(mse_dca, mse_bloque)
)
knitr::kable(comparacion, digits = 4,
caption = "Comparación de los dos análisis")
| Analisis | Diferencia_A_menos_B | IC95_inferior | IC95_superior | Valor_p | MSE_residual |
|---|---|---|---|---|---|
| DCA sin bloques | 4.96 | -0.8221 | 10.7421 | 0.0906 | 81.5796 |
| Modelo exploratorio con bloques | 4.96 | 0.6954 | 9.2246 | 0.0250 | 41.5149 |
El análisis realizado mediante el diseño de bloques completos al azar (DBCA), utilizando el Pretest como criterio de bloqueo, demuestra la existencia de diferencias estadísticamente significativas entre los tratamientos evaluados (\(F = 5.926\), \(p = 0.0250\)).
Al controlar la variabilidad asociada al nivel de conocimientos previos de los estudiantes, el modelo logró reducir el error residual a la mitad (\(\text{MSE} = 41.5149\) frente a \(81.5796\) del DCA simple), eliminando el ruido y aumentando considerablemente la sensibilidad estadística del análisis.El contraste pareado confirma que el grupo de Predicción previa obtuvo un rendimiento superior, con una diferencia media de \(4.96\) puntos y un intervalo de confianza del 95% (\([0.6954, 9.2246]\)) que no incluye el cero, con un valor \(p\) asociado de \(0.025\). Los diagnósticos de los residuos no detectaron evidencia en contra de los supuestos de normalidad (Prueba de Shapiro-Wilk, \(p = 0.7477\)) ni de homocedasticidad (Prueba de Levene, \(p = 0.7077\)).
Por lo tanto, se concluye con respaldo estadístico robusto que el método de Predicción previa genera un impacto positivo y real en el rendimiento final de los estudiantes en comparación con la Explicación y práctica, justificando plenamente la ganancia metodológica del diseño de bloques.
cat(sprintf(
"- La media de postest de Predicción previa fue %.2f puntos y la de Explicación y práctica fue %.2f puntos. La diferencia sin bloques (A menos B) fue %.2f puntos.\n\n",
media_A, media_B, diferencia_dca
))
cat(sprintf(
"- En el DCA sin bloques, el contraste de medias produjo p = %.4f. El intervalo de confianza del 95%% para la diferencia fue [%.2f, %.2f].\n\n",
prueba_dca$p.value, prueba_dca$conf.int[1], prueba_dca$conf.int[2]
))
cat(sprintf(
"- En el análisis exploratorio por parejas, la diferencia media A menos B fue %.2f puntos, con IC del 95%% [%.2f, %.2f] y p = %.4f.\n\n",
mean(diferencias_postest), prueba_pareada$conf.int[1],
prueba_pareada$conf.int[2], prueba_pareada$p.value
))
cat(sprintf(
"- La diferencia absoluta del pretest dentro de las parejas tuvo media %.2f puntos, mediana %.2f y máximo %.2f.\n\n",
mean(diferencias_pretest$Diferencia_absoluta_pretest),
median(diferencias_pretest$Diferencia_absoluta_pretest),
max(diferencias_pretest$Diferencia_absoluta_pretest)
))