El siguiente es desarrollo para el séptimo cuaderno. Abordar los Bloques Incompletos es un salto cualitativo importante para tus estudiantes de Ingeniería Agrícola y Agroindustrial, ya que los prepara para las limitaciones reales del mundo laboral.
A menudo, en la agroindustria, el tamaño de una máquina (como una secadora o un autoclave) o en el campo, el tamaño de una terraza o invernadero, no permite probar todos los tratamientos a la vez. Este diseño resuelve elegantemente ese problema matemático.
Un Diseño de Bloques Incompletos se utiliza cuando el tamaño del bloque físico o temporal es menor que el número total de tratamientos a evaluar (\(k < t\)). Por ejemplo, si quieres probar 5 variedades de maíz (tratamientos), pero en cada estante del cuarto de crecimiento (bloque) solo caben 3 macetas.
Dado que no todos los tratamientos aparecen en todos los bloques, las diferencias simples entre los promedios de los tratamientos están “contaminadas” o confundidas por las diferencias entre los bloques. Por lo tanto, el análisis estadístico debe calcular medias ajustadas.
En easyanova, el diseño tipo 7 maneja los bloques
incompletos clásicos (como los Balanceados o Parcialmente Balanceados),
estimando la recuperación de la información intra-bloque.
El Modelo Estadístico Lineal: \[y_{ij} = \mu + \tau_i + \beta_j + \epsilon_{ij}\]
Donde: * \(y_{ij}\): Respuesta del \(i\)-ésimo tratamiento en el \(j\)-ésimo bloque. * \(\mu\): Media general. * \(\tau_i\): Efecto del tratamiento (que se estimará ajustado por el bloque). * \(\beta_j\): Efecto del bloque. * \(\epsilon_{ij}\): Error experimental.
Tabla ANOVA (Análisis Intra-bloque con Tratamientos Ajustados):
| Fuente de Variación | Grados de Libertad (GL) | Suma de Cuadrados (SC) | Cuadrado Medio (CM) | F calculado |
|---|---|---|---|---|
| Bloques (No ajustados) | \(b - 1\) | \(SCB\) | \(CMB = \frac{SCB}{b-1}\) | |
| Tratamientos (Ajustados) | \(t - 1\) | \(SCTr_{adj}\) | \(CMTr_{adj} = \frac{SCTr_{adj}}{t-1}\) | \(\frac{CMTr_{adj}}{CME}\) |
| Error | \(N - t - b + 1\) | \(SCE\) | \(CME = \frac{SCE}{GL_E}\) | |
| Total | \(N - 1\) | \(SCT\) |
(Nota: \(t\) = tratamientos, \(b\) = bloques, \(k\) = tamaño del bloque, \(r\) = repeticiones, \(N = t \times r = b \times k\)).
El cálculo manual de los Bloques Incompletos Balanceados (BIB) requiere calcular un factor de ajuste (\(Q_i\)) para limpiar el efecto del bloque de cada tratamiento:
Para este diseño, usaremos el data7 incluido en
easyanova. Una parte crucial del EDA aquí es demostrarle al
estudiante, mediante una tabla de contingencia, que el diseño
efectivamente está “incompleto”.
library(easyanova)
# Cargar el dataset de ejemplo para Bloques Incompletos
data(data7)
# Inspeccionar los datos: Col 1 = Tratamiento, Col 2 = Bloque, Col 3 = Respuesta
head(data7)
# EDA 1: Demostrar visualmente la "incompletitud" del diseño
# Una tabla de doble entrada mostrará ceros (0) donde un tratamiento no fue aplicado en un bloque
tabla_presencia <- table(Tratamiento = data7[,1], Bloque = data7[,2])
print("Estructura de presencia de Tratamientos en los Bloques:")
print(tabla_presencia)
# EDA 2: Visualización de los datos brutos
library(ggplot2)
ggplot(data7, aes(x = factor(data7[,1]), y = data7[,3], color = factor(data7[,2]))) +
geom_point(size = 4, alpha = 0.7) +
labs(title = "Dispersión de Respuestas por Tratamiento",
subtitle = "Los colores representan distintos bloques (Nótese que faltan colores en cada tratamiento)",
x = "Tratamiento",
y = "Respuesta Observada",
color = "Bloque") +
theme_minimal()
Propósito pedagógico: Cuando los alumnos vean la matriz llena de “ceros” y “unos” (o las repeticiones), comprenderán inmediatamente por qué no podemos promediar los datos de frente: un tratamiento que tuvo la “mala suerte” de caer siempre en bloques de baja fertilidad o máquinas menos eficientes, tendrá un promedio aritmético artificialmente bajo.
easyanovaLa ejecución es directa con design = 7. El paquete
calculará automáticamente los factores \(Q_i\) y entregará las medias ya ajustadas
para la toma de decisiones.
# Ejecutar Diseño de Bloques Incompletos (design = 7)
# Orden requerido: Col 1 = Tratamiento, Col 2 = Bloque, Col 3 = Respuesta
resultado_bi <- ea1(data7, design = 7)
# Imprimir los resultados, prestando especial atención a las "Medias Ajustadas"
print(resultado_bi)
Indica a los alumnos que analicen la salida del bloque anterior guiados por estas preguntas:
En Python (Usando statsmodels): En
Python, el orden en el que se declaran los factores en la fórmula es
vital cuando los datos están desbalanceados o incompletos. Al usar
typ=2 o poner el Bloque primero y luego el Tratamiento en
Tipo I, aseguramos que la suma de cuadrados de los tratamientos esté
ajustada por el bloque.
import pandas as pd
import statsmodels.api as sm
from statsmodels.formula.api import ols
# Dataset simulado (Ej: 4 Tratamientos, Bloques de tamaño 2)
data = {
'Tratamiento': ['T1','T2', 'T3','T4', 'T1','T3', 'T2','T4', 'T1','T4', 'T2','T3'],
'Bloque': ['B1','B1', 'B2','B2', 'B3','B3', 'B4','B4', 'B5','B5', 'B6','B6'],
'Respuesta': [12, 15, 18, 14, 11, 19, 16, 13, 10, 15, 17, 20]
}
df = pd.DataFrame(data)
# Ajuste del modelo lineal. Extraemos SC ajustando Tratamientos por Bloques
# Usamos ANOVA Tipo II para tratar diseños desbalanceados/incompletos
modelo_bi = ols('Respuesta ~ C(Bloque) + C(Tratamiento)', data=df).fit()
tabla_anova = sm.stats.anova_lm(modelo_bi, typ=2)
print("\n--- Tabla ANOVA (Bloques Incompletos) ---")
print(tabla_anova)
# Para obtener medias ajustadas (LS Means) habitualmente se requeriría una librería extra como `emmeans`
En Julia (Usando DataFrames, GLM y
Effects): En Julia, ajustamos el modelo y luego
usamos paquetes modernos para predecir las medias marginales estimadas
(medias ajustadas).
using DataFrames, GLM, Effects
# DataFrame simulado
df = DataFrame(
Tratamiento = ["T1","T2", "T3","T4", "T1","T3", "T2","T4", "T1","T4", "T2","T3"],
Bloque = ["B1","B1", "B2","B2", "B3","B3", "B4","B4", "B5","B5", "B6","B6"],
Respuesta = [12.0, 15.0, 18.0, 14.0, 11.0, 19.0, 16.0, 13.0, 10.0, 15.0, 17.0, 20.0]
)
df.Tratamiento = categorical(df.Tratamiento)
df.Bloque = categorical(df.Bloque)
# Ajuste del modelo
modelo_bi = lm(@formula(Respuesta ~ Bloque + Tratamiento), df)
# Calcular las medias ajustadas (medias marginales) aislando el efecto del bloque
medias_ajustadas = effects(Dict(:Tratamiento => unique(df.Tratamiento)), modelo_bi)
println("--- Medias Ajustadas de Tratamiento ---")
println(medias_ajustadas)