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.


📘 Cuaderno 7: Diseño de Bloques Incompletos (Tipos I y II)

a) Introducción Teórica a los Bloques Incompletos

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\)).


b) Miniguía: Solución Manual (Papel y Lápiz)

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:

  1. Totales Simples: Calcula los totales de cada tratamiento (\(Y_{i.}\)), totales de bloque (\(Y_{.j}\)), y el Gran Total (\(Y_{..}\)).
  2. Suma de Cuadrados Total (SCT): Igual que en diseños anteriores (Suma de los cuadrados de todos los datos menos el Factor de Corrección).
  3. Suma de Cuadrados de Bloques No Ajustados (SCB): \[\sum \frac{Y_{.j}^2}{k} - FC\]
  4. Cálculo de los Totales Ajustados de Tratamiento (\(Q_i\)): Por cada tratamiento, toma su total simple (\(Y_{i.}\)) y réstale la suma de los promedios de los bloques en los que ese tratamiento estuvo presente. \[Q_i = Y_{i.} - \frac{1}{k} \sum (Y_{.j} \text{ de los bloques que contienen el tratamiento } i)\] (La suma de todos los \(Q_i\) debe dar cero).
  5. Suma de Cuadrados de Tratamientos Ajustados (\(SCTr_{adj}\)): \[SCTr_{adj} = \frac{k \sum Q_i^2}{t \lambda}\] (Donde \(\lambda\) es el número de veces que dos tratamientos aparecen juntos en el mismo bloque).
  6. Suma de Cuadrados del Error (SCE): \[SCE = SCT - SCB - SCTr_{adj}\]
  7. Medias Ajustadas: El promedio real con el que se deben tomar decisiones no es el promedio aritmético, sino: \[\bar{Y}_{i(adj)} = \bar{Y}_{..} + \frac{k \cdot Q_i}{t \cdot \lambda}\]

c) Exploración de Datos (EDA) en R

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.


d) Plantilla de Código R: Solución con easyanova

La 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)

e) Prompts Sugeridos para tus Estudiantes

Indica a los alumnos que analicen la salida del bloque anterior guiados por estas preguntas:

  1. “Hola. Estoy analizando la salida de un diseño de Bloques Incompletos en R. Al ver las medias de los tratamientos, el software me arroja unas ‘Medias Ajustadas’. ¿Podrías explicarme de forma práctica por qué mi jefe en la planta agroindustrial tomaría una mala decisión si se fijara solo en los promedios matemáticos simples de mis datos?”
  2. “Observando el valor p de los Tratamientos Ajustados en la tabla ANOVA, ¿existe evidencia suficiente para recomendar un cambio en el proceso de producción?”
  3. “En términos de ‘Eficiencia del Diseño’, ¿es preferible tratar de forzar un diseño completo usando unidades experimentales más heterogéneas, o es mejor usar este diseño de bloques incompletos para mantener las condiciones de prueba muy controladas?”

f) Plantillas Alternativas: Python y Julia

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)