Con Mi Profe: Julio Hurtado Marquez; EMAIL_TAREAS:

Aquí tienes la solución completa en Markdown para los ejemplos de diseños factoriales, integrando el análisis teórico con los códigos en R y Python.


1 📌 76. DISEÑOS FACTORIALES

🔍 INTRODUCCIÓN A LOS DISEÑOS FACTORIALES

Es frecuente que en muchos procesos existan varios factores de los que es necesario investigar de manera simultánea su influencia sobre una o varias variables de respuesta. Los diseños experimentales que permiten estudiar de manera simultánea el efecto de varios factores son los llamados diseños factoriales.

El objetivo de un diseño factorial es estudiar el efecto de varios factores sobre una o varias respuestas y determinar una combinación de niveles de ellos en la cual el desempeño del proceso sea mejor que en las condiciones de operación actuales.

📌 Definición: Un diseño de experimentos factorial o arreglo factorial es el conjunto de puntos experimentales o tratamientos que pueden formarse considerando todas las posibles combinaciones de los niveles de los factores.


2 📌 77. DISEÑO FACTORIAL \(2^2\)

📊 DISEÑO FACTORIAL \(2^2\)

Supongamos que se tienen dos factores A: tiempo y B: velocidad, cada uno con dos niveles (bajo y alto):

  • A₁ = 3 min, A₂ = 6 min
  • B₁ = 600 rpm, B₂ = 1000 rpm

La respuesta de interés (Y) es la cantidad de aditivo. En la tabla se muestran los cuatro tratamientos del diseño factorial \(2^2\) con los resultados de la primera réplica.

A: Tiempo B: Velocidad Y Notación
A₁ (3 min) B₁ (600 rpm) 17.10 (1)
A₂ (6 min) B₁ (600 rpm) 16.26 a
A₁ (3 min) B₂ (1000 rpm) 18.76 b
A₂ (6 min) B₂ (1000 rpm) 18.16 ab

2.1 📌 Solución analítica

2.1.1 Cálculo de efectos principales

Efecto principal de A (tiempo):

\[Efecto\ A = \frac{16.26 + 18.16}{2} - \frac{17.10 + 18.76}{2} = \frac{34.42}{2} - \frac{35.86}{2} = 17.21 - 17.93 = -0.72\]

Efecto principal de B (velocidad):

\[Efecto\ B = \frac{18.76 + 18.16}{2} - \frac{17.10 + 16.26}{2} = \frac{34.92}{2} - \frac{33.36}{2} = 17.46 - 16.68 = 0.78 \text{ (corregido: } 1.78\text{)}\]

Nota: El cálculo correcto del efecto B es: \[Efecto\ B = \frac{18.76 + 18.16}{2} - \frac{17.10 + 16.26}{2} = 18.46 - 16.68 = 1.78\]

Efecto de interacción AB:

\[AB = \frac{17.10 + 18.16}{2} - \frac{16.26 + 18.76}{2} = \frac{35.26}{2} - \frac{35.02}{2} = 17.63 - 17.51 = 0.12\]

2.1.2 Interpretación de los efectos

  • El efecto de A es negativo (-0.72), lo que indica que al aumentar el tiempo de 3 a 6 minutos, la cantidad de aditivo disminuye en promedio 0.72 unidades.
  • El efecto de B es positivo (1.78), indicando que al aumentar la velocidad de 600 a 1000 rpm, la cantidad de aditivo aumenta en promedio 1.78 unidades.
  • La interacción AB es pequeña (0.12), sugiriendo que los efectos de A y B son aproximadamente independientes.

2.2 💻 Código en R

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 2^2
# ============================================

# Datos
tiempo <- factor(c("A1", "A2", "A1", "A2"))
velocidad <- factor(c("B1", "B1", "B2", "B2"))
respuesta <- c(17.10, 16.26, 18.76, 18.16)

datos <- data.frame(tiempo, velocidad, respuesta)

# Modelo ANOVA
modelo <- aov(respuesta ~ tiempo * velocidad, data = datos)
summary(modelo)

# Cálculo manual de efectos
Y_A1_B1 <- 17.10
Y_A2_B1 <- 16.26
Y_A1_B2 <- 18.76
Y_A2_B2 <- 18.16

efecto_A <- (Y_A2_B1 + Y_A2_B2)/2 - (Y_A1_B1 + Y_A1_B2)/2
efecto_B <- (Y_A1_B2 + Y_A2_B2)/2 - (Y_A1_B1 + Y_A2_B1)/2
efecto_AB <- (Y_A1_B1 + Y_A2_B2)/2 - (Y_A1_B2 + Y_A2_B1)/2

cat("Efecto de A (tiempo):", round(efecto_A, 2), "\n")
cat("Efecto de B (velocidad):", round(efecto_B, 2), "\n")
cat("Efecto de interacción AB:", round(efecto_AB, 2), "\n")

# Gráfico de interacción
interaction.plot(tiempo, velocidad, respuesta, 
                 type = "b", col = c("steelblue", "red"),
                 xlab = "Tiempo", ylab = "Respuesta",
                 main = "Gráfico de interacción para diseño 2^2")

2.3 💻 Código en Python

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 2^2
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols

# Datos
datos = pd.DataFrame({
    'tiempo': ['A1', 'A2', 'A1', 'A2'],
    'velocidad': ['B1', 'B1', 'B2', 'B2'],
    'respuesta': [17.10, 16.26, 18.76, 18.16]
})

# Modelo ANOVA
modelo = ols('respuesta ~ C(tiempo) * C(velocidad)', data=datos).fit()
anova_table = sm.stats.anova_lm(modelo, typ=2)
print("=== ANOVA DISEÑO FACTORIAL 2^2 ===")
print(anova_table)

# Cálculo manual de efectos
Y_A1_B1, Y_A2_B1, Y_A1_B2, Y_A2_B2 = 17.10, 16.26, 18.76, 18.16

efecto_A = (Y_A2_B1 + Y_A2_B2)/2 - (Y_A1_B1 + Y_A1_B2)/2
efecto_B = (Y_A1_B2 + Y_A2_B2)/2 - (Y_A1_B1 + Y_A2_B1)/2
efecto_AB = (Y_A1_B1 + Y_A2_B2)/2 - (Y_A1_B2 + Y_A2_B1)/2

print(f"\nEfecto de A (tiempo): {efecto_A:.2f}")
print(f"Efecto de B (velocidad): {efecto_B:.2f}")
print(f"Efecto de interacción AB: {efecto_AB:.2f}")

# Gráfico de interacción
plt.figure(figsize=(8, 5))
for vel in ['B1', 'B2']:
    subdatos = datos[datos['velocidad'] == vel]
    plt.plot(subdatos['tiempo'], subdatos['respuesta'], 'o-', 
             label=f'Velocidad {vel}')
plt.xlabel('Tiempo')
plt.ylabel('Respuesta')
plt.title('Gráfico de interacción para diseño 2^2')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

2.4 📌 Resultados del ANOVA

Fuente SC gl CM F₀ Valor-p
Tiempo (A) 0.5184 1 0.5184 - -
Velocidad (B) 3.1684 1 3.1684 - -
Interacción AB 0.0144 1 0.0144 - -
Error - 0 - - -

Nota: Con una sola réplica no es posible estimar el error experimental, por lo que no se pueden calcular los valores F ni los valores-p. Se requieren al menos 2 réplicas para estimar la varianza del error.


3 📌 78. DISEÑO FACTORIAL \(4 \times 3\)

🔧 EJEMPLO: DISEÑO FACTORIAL \(4 \times 3\) (ACABADO DE METAL)

Se quiere estudiar el efecto de los factores velocidad de alimentación y profundidad de corte sobre el acabado de un metal. Se decide correr un factorial completo \(4 \times 3\) con tres réplicas.

Factores:

  • A: Profundidad (pulgadas) - 4 niveles: 0.15, 0.18, 0.21, 0.24
  • B: Velocidad - 3 niveles: 0.20, 0.25, 0.30
  • Respuesta Y: Acabado (unidades de gramos, a minimizar)
Tabla: Datos del diseño factorial \(4 \times 3\)
A: Profundidad B: Velocidad Y_{i··}
0.20 0.25 0.30
0.15 74 92 99 763
64 86 98
60 88 102
0.18 79 98 104 808
68 104 99
73 88 95
0.21 82 99 108 881
88 108 110
92 95 99
0.24 99 104 114 944
104 110 111
96 99 107
Y_{·j·} 979 1171 1246 3396

3.1 📌 Solución analítica

3.1.1 Cálculo de sumas de cuadrados

  • Número de observaciones: \(N = a \times b \times n = 4 \times 3 \times 3 = 36\)
  • Gran total: \(Y_{\cdot\cdot\cdot} = 3396\)
  • Corrección: \(C = \frac{3396^2}{36} = \frac{11532816}{36} = 320356\)

Suma total de cuadrados:

\[SCT = \sum Y_{ijk}^2 - C = 326888 - 320356 = 6532.0\]

Suma de cuadrados de A (profundidad):

\[SCA = \frac{763^2 + 808^2 + 881^2 + 944^2}{3 \times 3} - C\] \[SCA = \frac{581969 + 652864 + 776161 + 891136}{9} - 320356\] \[SCA = \frac{2902130}{9} - 320356 = 322458.9 - 320356 = 2125.1\]

Suma de cuadrados de B (velocidad):

\[SCB = \frac{979^2 + 1171^2 + 1246^2}{4 \times 3} - C\] \[SCB = \frac{958441 + 1371241 + 1552516}{12} - 320356\] \[SCB = \frac{3882198}{12} - 320356 = 323516.5 - 320356 = 3160.5\]

Suma de cuadrados de interacción AB:

\[SCAB = \sum \frac{Y_{ij\cdot}^2}{n} - C - SCA - SCB\]

Los totales \(Y_{ij\cdot}\) son: - (0.15,0.20): 74+64+60=198 - (0.15,0.25): 92+86+88=266 - (0.15,0.30): 99+98+102=299 - (0.18,0.20): 79+68+73=220 - (0.18,0.25): 98+104+88=290 - (0.18,0.30): 104+99+95=298 - (0.21,0.20): 82+88+92=262 - (0.21,0.25): 99+108+95=302 - (0.21,0.30): 108+110+99=317 - (0.24,0.20): 99+104+96=299 - (0.24,0.25): 104+110+99=313 - (0.24,0.30): 114+111+107=332

\[\sum \frac{Y_{ij\cdot}^2}{3} = \frac{198^2+266^2+299^2+220^2+290^2+298^2+262^2+302^2+317^2+299^2+313^2+332^2}{3}\] \[= \frac{39204+70756+89401+48400+84100+88804+68644+91204+100489+89401+97969+110224}{3}\] \[= \frac{967596}{3} = 322532\]

\[SCAB = 322532 - 320356 - 2125.1 - 3160.5 = 557.07\]

Suma de cuadrados del error:

\[SCE = SCT - SCA - SCB - SCAB = 6532.0 - 2125.1 - 3160.5 - 557.07 = 689.33\]


3.2 📊 Tabla ANOVA

Fuente SC gl CM F₀ Valor-p
B: Velocidad 3160.5 2 1580.25 55.02 0.0000
A: Profundidad 2125.1 3 708.37 24.66 0.0000
AB: Interacción 557.07 6 92.84 3.23 0.018
Error 689.33 24 28.72 - -
Total 6532.0 35 - - -

3.3 📌 Comparaciones múltiples con interacción

Para comparar los niveles de profundidad dentro de un nivel específico de velocidad (por ejemplo, velocidad = 0.25):

Medias muestrales en velocidad 0.25: - Profundidad 0.15: \(\bar{Y}_{12\cdot} = 266/3 = 88.67\) - Profundidad 0.18: \(\bar{Y}_{22\cdot} = 290/3 = 96.67\) - Profundidad 0.21: \(\bar{Y}_{32\cdot} = 302/3 = 100.67\) - Profundidad 0.24: \(\bar{Y}_{42\cdot} = 313/3 = 104.33\)

Cálculo de LSD: \[LSD = t_{\alpha/2, gl_{error}} \cdot \sqrt{\frac{2 \cdot CME}{n}} = t_{0.025,24} \cdot \sqrt{\frac{2 \times 28.72}{3}}\]

\[t_{0.025,24} = 2.064\] \[LSD = 2.064 \cdot \sqrt{\frac{57.44}{3}} = 2.064 \cdot \sqrt{19.147} = 2.064 \cdot 4.376 = 9.03\]

Comparaciones: - |88.67 - 96.67| = 8.00 < 9.03 → No significativa - |88.67 - 100.67| = 12.00 > 9.03 → Significativa - |88.67 - 104.33| = 15.66 > 9.03 → Significativa - |96.67 - 100.67| = 4.00 < 9.03 → No significativa - |96.67 - 104.33| = 7.66 < 9.03 → No significativa - |100.67 - 104.33| = 3.66 < 9.03 → No significativa

Conclusión: En el nivel de velocidad intermedia (0.25), las tres profundidades mayores (0.18, 0.21, 0.24) son estadísticamente iguales, mientras que la profundidad menor (0.15) es diferente.


3.4 💻 Código en R

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 4x3
# ============================================

# Crear datos
profundidad <- rep(c(0.15, 0.18, 0.21, 0.24), each = 9)
velocidad <- rep(rep(c(0.20, 0.25, 0.30), each = 3), times = 4)
acabado <- c(74, 64, 60, 92, 86, 88, 99, 98, 102,      # A=0.15
             79, 68, 73, 98, 104, 88, 104, 99, 95,     # A=0.18
             82, 88, 92, 99, 108, 95, 108, 110, 99,    # A=0.21
             99, 104, 96, 104, 110, 99, 114, 111, 107) # A=0.24

datos <- data.frame(profundidad = factor(profundidad),
                    velocidad = factor(velocidad),
                    acabado)

# Estadísticos descriptivos
library(dplyr)
medias_prof <- datos %>% group_by(profundidad) %>% summarise(media = mean(acabado))
medias_vel <- datos %>% group_by(velocidad) %>% summarise(media = mean(acabado))

cat("=== MEDIAS POR PROFUNDIDAD ===\n")
print(medias_prof)

cat("\n=== MEDIAS POR VELOCIDAD ===\n")
print(medias_vel)

# ANOVA factorial
modelo <- aov(acabado ~ profundidad * velocidad, data = datos)
summary(modelo)

# Gráfico de interacción
interaction.plot(datos$profundidad, datos$velocidad, datos$acabado,
                 type = "b", col = 1:3, xlab = "Profundidad",
                 ylab = "Acabado medio", main = "Interacción Profundidad × Velocidad")
legend("topleft", legend = c("Velocidad 0.20", "Velocidad 0.25", "Velocidad 0.30"),
       col = 1:3, lty = 1, bty = "n")

# Cálculo manual de sumas de cuadrados
a <- 4; b <- 3; n <- 3
N <- a * b * n
Y_total <- 3396
correccion <- Y_total^2 / N

# Suma total de cuadrados
SCT <- sum(acabado^2) - correccion

# Suma de cuadrados de A (profundidad)
Y_A <- tapply(acabado, profundidad, sum)
SCA <- sum(Y_A^2) / (b * n) - correccion

# Suma de cuadrados de B (velocidad)
Y_B <- tapply(acabado, velocidad, sum)
SCB <- sum(Y_B^2) / (a * n) - correccion

# Suma de cuadrados de la interacción AB
Y_AB <- tapply(acabado, list(profundidad, velocidad), sum)
SCAB <- sum(Y_AB^2) / n - correccion - SCA - SCB

# Suma de cuadrados del error
SCE <- SCT - SCA - SCB - SCAB

cat("\n=== CÁLCULO MANUAL DE SUMAS DE CUADRADOS ===\n")
cat("SCA =", round(SCA, 2), "\n")
cat("SCB =", round(SCB, 2), "\n")
cat("SCAB =", round(SCAB, 2), "\n")
cat("SCE =", round(SCE, 2), "\n")
cat("SCT =", round(SCT, 2), "\n")

# Comparaciones múltiples (LSD) con interacción
CME <- SCE / (a * b * (n - 1))
gl_error <- a * b * (n - 1)
t_crit <- qt(0.975, gl_error)

# LSD para comparar niveles de A dentro de un nivel específico de B
# Por ejemplo, para velocidad = 0.25 (B2)
Y_AB_B2 <- Y_AB[, 2]  # Velocidad 0.25 (segunda columna)
medias_B2 <- Y_AB_B2 / n
LSD_B2 <- t_crit * sqrt(2 * CME / n)

cat("\n=== COMPARACIONES MÚLTIPLES (LSD) ===\n")
cat("CME =", round(CME, 2), "\n")
cat("t crítico =", round(t_crit, 4), "\n")
cat("LSD para comparar profundidades en velocidad 0.25 =", round(LSD_B2, 2), "\n")
cat("Medias en velocidad 0.25:\n")
for(i in 1:4) {
  cat(sprintf("  Profundidad %.2f: %.2f\n", as.numeric(levels(datos$profundidad)[i]), medias_B2[i]))
}

# Gráfico con intervalos LSD
library(ggplot2)
medias_AB <- data.frame(
  profundidad = rep(as.numeric(levels(datos$profundidad)), 3),
  velocidad = rep(c("0.20", "0.25", "0.30"), each = 4),
  media = c(rowMeans(matrix(Y_AB[,1], nrow=4)), medias_B2, rowMeans(matrix(Y_AB[,3], nrow=4))),
  error = rep(sqrt(CME / n) * t_crit, 3)
)

ggplot(medias_AB, aes(x = profundidad, y = media, color = velocidad)) +
  geom_point(size = 3) +
  geom_line() +
  geom_errorbar(aes(ymin = media - error, ymax = media + error), width = 0.02) +
  labs(title = "Perfil de interacción con intervalos LSD",
       x = "Profundidad", y = "Acabado medio") +
  theme_minimal()

3.5 💻 Código en Python

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 4x3
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy.stats import f, t

# Crear datos
profundidad = [0.15]*9 + [0.18]*9 + [0.21]*9 + [0.24]*9
velocidad = [0.20]*3 + [0.25]*3 + [0.30]*3
velocidad = velocidad * 4
acabado = [74, 64, 60, 92, 86, 88, 99, 98, 102,      # A=0.15
           79, 68, 73, 98, 104, 88, 104, 99, 95,     # A=0.18
           82, 88, 92, 99, 108, 95, 108, 110, 99,    # A=0.21
           99, 104, 96, 104, 110, 99, 114, 111, 107] # A=0.24

datos = pd.DataFrame({
    'profundidad': profundidad,
    'velocidad': velocidad,
    'acabado': acabado
})
datos['profundidad'] = datos['profundidad'].astype('category')
datos['velocidad'] = datos['velocidad'].astype('category')

# Estadísticos descriptivos
print("=== MEDIAS POR PROFUNDIDAD ===")
print(datos.groupby('profundidad')['acabado'].mean())

print("\n=== MEDIAS POR VELOCIDAD ===")
print(datos.groupby('velocidad')['acabado'].mean())

# ANOVA factorial
modelo = ols('acabado ~ C(profundidad) * C(velocidad)', data=datos).fit()
anova_table = sm.stats.anova_lm(modelo, typ=2)
print("\n=== ANOVA FACTORIAL 4x3 ===")
print(anova_table)

# Cálculo manual de sumas de cuadrados
a, b, n = 4, 3, 3
N = a * b * n
Y_total = 3396
correccion = Y_total**2 / N

# Suma total de cuadrados
SCT = np.sum(np.array(acabado)**2) - correccion

# Suma de cuadrados de A (profundidad)
Y_A = datos.groupby('profundidad')['acabado'].sum().values
SCA = np.sum(Y_A**2) / (b * n) - correccion

# Suma de cuadrados de B (velocidad)
Y_B = datos.groupby('velocidad')['acabado'].sum().values
SCB = np.sum(Y_B**2) / (a * n) - correccion

# Suma de cuadrados de interacción AB
Y_AB = datos.groupby(['profundidad', 'velocidad'])['acabado'].sum().values
SCAB = np.sum(Y_AB**2) / n - correccion - SCA - SCB

# Suma de cuadrados del error
SCE = SCT - SCA - SCB - SCAB

print("\n=== CÁLCULO MANUAL DE SUMAS DE CUADRADOS ===")
print(f"SCA = {SCA:.2f}")
print(f"SCB = {SCB:.2f}")
print(f"SCAB = {SCAB:.2f}")
print(f"SCE = {SCE:.2f}")
print(f"SCT = {SCT:.2f}")

# Comparaciones múltiples (LSD) con interacción
gl_error = a * b * (n - 1)
CME = SCE / gl_error
t_crit = t.ppf(0.975, gl_error)

# LSD para comparar niveles de A dentro de velocidad 0.25
Y_AB_matrix = np.array(Y_AB).reshape(a, b)
medias_B2 = Y_AB_matrix[:, 1] / n
LSD_B2 = t_crit * np.sqrt(2 * CME / n)

print("\n=== COMPARACIONES MÚLTIPLES (LSD) ===")
print(f"CME = {CME:.2f}")
print(f"t crítico = {t_crit:.4f}")
print(f"LSD para comparar profundidades en velocidad 0.25 = {LSD_B2:.2f}")
print("Medias en velocidad 0.25:")
for i, prof in enumerate([0.15, 0.18, 0.21, 0.24]):
    print(f"  Profundidad {prof}: {medias_B2[i]:.2f}")

# Gráfico de interacción
plt.figure(figsize=(10, 6))
for vel in sorted(datos['velocidad'].unique()):
    subdatos = datos[datos['velocidad'] == vel]
    medias = subdatos.groupby('profundidad')['acabado'].mean()
    plt.plot(medias.index, medias.values, 'o-', label=f'Velocidad {vel}')
plt.xlabel('Profundidad')
plt.ylabel('Acabado medio')
plt.title('Interacción Profundidad × Velocidad')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()

# Gráfico con intervalos LSD
fig, ax = plt.subplots(figsize=(10, 6))
for i, vel in enumerate(sorted(datos['velocidad'].unique())):
    medias = Y_AB_matrix[:, i] / n
    error = np.sqrt(CME / n) * t_crit
    ax.errorbar([0.15, 0.18, 0.21, 0.24], medias, yerr=error,
                marker='o', label=f'Velocidad {vel}', capsize=5)
ax.set_xlabel('Profundidad')
ax.set_ylabel('Acabado medio')
ax.set_title('Perfil de interacción con intervalos LSD')
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()

3.6 📌 Conclusiones del estudio

✅ Conclusiones

  1. Velocidad (B): El ANOVA muestra un valor-p = 0.0000 < 0.05, por lo tanto la velocidad tiene un efecto significativo sobre el acabado del metal.
  2. Profundidad (A): El valor-p = 0.0000 < 0.05, indicando que la profundidad también tiene un efecto significativo.
  3. Interacción AB: El valor-p = 0.018 < 0.05, lo que indica que existe interacción significativa entre la profundidad y la velocidad. Esto significa que el efecto de la profundidad depende del nivel de velocidad, y viceversa.
  4. Para minimizar el acabado (menor valor es mejor), la combinación óptima es velocidad baja (0.20) y profundidad baja (0.15), que produce el menor acabado medio (66.0 unidades).
  5. La interacción significativa se refleja en el gráfico de perfil: las líneas no son paralelas, especialmente en el nivel intermedio de velocidad donde las tres profundidades mayores son estadísticamente iguales.

📌 Recomendaciones prácticas

  • Para obtener el mejor acabado (mínimo valor), operar con velocidad = 0.20 y profundidad = 0.15.
  • Si por razones operativas no es posible usar velocidad baja, se recomienda usar velocidad = 0.25 con profundidad = 0.15, que produce un acabado medio de 88.67.
  • Evitar trabajar con velocidad alta (0.30) ya que produce los peores acabados en todas las profundidades.

📊 “Los diseños factoriales permiten estudiar simultáneamente múltiples factores y sus interacciones, proporcionando una visión completa del proceso”

— Adaptado de Sir Ronald A. Fisher

Aquí tienes el material completo sobre Diseños Factoriales con Tres Factores, incluyendo el modelo estadístico, ANOVA, códigos en R y Python, y ejemplos resueltos.


4 📌 79. DISEÑOS FACTORIALES CON TRES FACTORES

🔍 INTRODUCCIÓN A LOS DISEÑOS FACTORIALES CON TRES FACTORES

Cuando se tienen tres factores (A, B y C) y el número de niveles de prueba en cada uno de ellos son \(a\), \(b\) y \(c\), se puede construir el arreglo factorial \(a \times b \times c\) que consiste de \(a \times b \times c\) tratamientos o puntos experimentales. Entre los arreglos de este tipo que se utilizan con frecuencia en aplicaciones diversas se encuentran:

  • El factorial \(2^3\) (tres factores con dos niveles cada uno)
  • El factorial \(3^3\) (tres factores con tres niveles cada uno)
  • Factoriales mixtos como \(4 \times 3 \times 3\) o \(4 \times 4 \times 2\)

5 📌 80. MODELO ESTADÍSTICO PARA TRES FACTORES

📊 MODELO ESTADÍSTICO

En un diseño factorial \(a \times b \times c\) se supone que el comportamiento de la respuesta \(Y\) puede describirse mediante el modelo de efectos dado por:

\[Y_{ijkl} = \mu + \alpha_i + \beta_j + \gamma_k + (\alpha\beta)_{ij} + (\alpha\gamma)_{ik} + (\beta\gamma)_{jk} + (\alpha\beta\gamma)_{ijk} + \varepsilon_{ijkl}\]

donde:

  • \(i = 1, 2, \ldots, a\) (niveles del factor A)
  • \(j = 1, 2, \ldots, b\) (niveles del factor B)
  • \(k = 1, 2, \ldots, c\) (niveles del factor C)
  • \(l = 1, 2, \ldots, n\) (réplicas)
  • \(\mu\) es la media general
  • \(\alpha_i\) es el efecto del nivel \(i\) del factor A
  • \(\beta_j\) es el efecto del nivel \(j\) del factor B
  • \(\gamma_k\) es el efecto del nivel \(k\) del factor C
  • \((\alpha\beta)_{ij}\), \((\alpha\gamma)_{ik}\), \((\beta\gamma)_{jk}\) son efectos de interacción dobles
  • \((\alpha\beta\gamma)_{ijk}\) es el efecto de interacción triple
  • \(\varepsilon_{ijkl}\) es el error aleatorio \(\sim N(0, \sigma^2)\)

📌 Nota: Todos los efectos cumplen la restricción de sumar cero, es decir, son desviaciones respecto a la media general.


6 📌 81. ANÁLISIS DE VARIANZA PARA TRES FACTORES

📊 ANÁLISIS DE VARIANZA PARA TRES FACTORES

El estudio factorial de tres factores (A, B y C) permite investigar los efectos: A, B, C, AB, AC, BC y ABC. En total se tienen siete efectos de interés sin considerar desglose.

Hipótesis a probar:

  • \(H_0: \text{Efecto A} = 0\) vs \(H_1: \text{Efecto A} \neq 0\)
  • \(H_0: \text{Efecto B} = 0\) vs \(H_1: \text{Efecto B} \neq 0\)
  • \(H_0: \text{Efecto C} = 0\) vs \(H_1: \text{Efecto C} \neq 0\)
  • \(H_0: \text{Efecto AB} = 0\) vs \(H_1: \text{Efecto AB} \neq 0\)
  • \(H_0: \text{Efecto AC} = 0\) vs \(H_1: \text{Efecto AC} \neq 0\)
  • \(H_0: \text{Efecto BC} = 0\) vs \(H_1: \text{Efecto BC} \neq 0\)
  • \(H_0: \text{Efecto ABC} = 0\) vs \(H_1: \text{Efecto ABC} \neq 0\)
Tabla ANOVA para un diseño factorial con tres factores
Fuente SC gl CM \(F_0\) Valor-p
Efecto A \(SCA\) \(a-1\) \(CMA\) \(CMA/CME\) \(P(F > F_0^A)\)
Efecto B \(SCB\) \(b-1\) \(CMB\) \(CMB/CME\) \(P(F > F_0^B)\)
Efecto C \(SCC\) \(c-1\) \(CMC\) \(CMC/CME\) \(P(F > F_0^C)\)
Efecto AB \(SCAB\) \((a-1)(b-1)\) \(CMAB\) \(CMAB/CME\) \(P(F > F_0^{AB})\)
Efecto AC \(SCAC\) \((a-1)(c-1)\) \(CMAC\) \(CMAC/CME\) \(P(F > F_0^{AC})\)
Efecto BC \(SCBC\) \((b-1)(c-1)\) \(CMBC\) \(CMBC/CME\) \(P(F > F_0^{BC})\)
Efecto ABC \(SCABC\) \((a-1)(b-1)(c-1)\) \(CMABC\) \(CMABC/CME\) \(P(F > F_0^{ABC})\)
Error \(SCE\) \(abc(n-1)\) \(CME\) - -
Total \(SCT\) \(abcn-1\) - - -

6.1 📌 Fórmulas para sumas de cuadrados

Las sumas de cuadrados se calculan como:

\[SCT = \sum_{i=1}^{a}\sum_{j=1}^{b}\sum_{k=1}^{c}\sum_{l=1}^{n} Y_{ijkl}^2 - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N}\]

\[SCA = \sum_{i=1}^{a} \frac{Y_{i\cdot\cdot\cdot}^2}{bcn} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N}\]

\[SCB = \sum_{j=1}^{b} \frac{Y_{\cdot j\cdot\cdot}^2}{acn} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N}\]

\[SCC = \sum_{k=1}^{c} \frac{Y_{\cdot\cdot k\cdot}^2}{abn} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N}\]

\[SCAB = \sum_{i=1}^{a}\sum_{j=1}^{b} \frac{Y_{ij\cdot\cdot}^2}{cn} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N} - SCA - SCB\]

\[SCAC = \sum_{i=1}^{a}\sum_{k=1}^{c} \frac{Y_{i\cdot k\cdot}^2}{bn} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N} - SCA - SCC\]

\[SCBC = \sum_{j=1}^{b}\sum_{k=1}^{c} \frac{Y_{\cdot jk\cdot}^2}{an} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N} - SCB - SCC\]

\[SCABC = \sum_{i=1}^{a}\sum_{j=1}^{b}\sum_{k=1}^{c} \frac{Y_{ijk\cdot}^2}{n} - \frac{Y_{\cdot\cdot\cdot\cdot}^2}{N} - SCA - SCB - SCC - SCAB - SCAC - SCBC\]

\[SCE = SCT - SCA - SCB - SCC - SCAB - SCAC - SCBC - SCABC\]


7 📌 82. EJEMPLO: VOLUMEN DE SEDIMENTACIÓN DE UNA SUSPENSIÓN

🧪 EJEMPLO: VOLUMEN DE SEDIMENTACIÓN

Se desea investigar el efecto del tipo de suspensión (A), abertura de malla (B) y temperatura de ciclaje (C) en el volumen de sedimentación Y(%) de una suspensión. Se decide correr un experimento factorial \(3 \times 2 \times 2\) con seis réplicas (72 corridas experimentales).

Factores y niveles:

  • A: Tipo de suspensión - 3 niveles: A₁, A₂, A₃
  • B: Abertura de malla - 2 niveles: B₁, B₂
  • C: Temperatura de ciclaje - 2 niveles: C₁, C₂
  • n = 6 réplicas
Tabla: Datos del experimento - Volumen de sedimentación (%)
A₁ A₂ A₃
C B Réplica B₁ B₂ B₁ B₂ B₁ B₂
C₁ B₁ 1 60 75 75 67 73 73 62 68 65 71 80 80 70 71 75 75 75 75
2 86 70 70 67 68 68 76 65 65 72 80 80 76 68 73 75 75 77
3 55 53 53 52 52 57 44 44 45 60 60 60 52 51 50 56 55 57
B₂ 1
2
3
C₂ B₁ 1 55 55 55 52 54 54 48 48 45 67 67 65 52 48 54 59 50 55
2
3
B₂ 1
2
3

7.1 💻 Código en R

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x2x2
# VOLUMEN DE SEDIMENTACIÓN
# ============================================

# Crear datos (simplificados para el ejemplo)
# Datos completos según la tabla del problema
A <- factor(rep(c("A1", "A2", "A3"), each = 24))
B <- factor(rep(rep(c("B1", "B2"), each = 12), times = 3))
C <- factor(rep(rep(c("C1", "C2"), each = 6), times = 6))

# Datos de volumen de sedimentación (72 observaciones)
volumen <- c(
  # A1, B1, C1
  60, 75, 75, 86, 70, 70, 55, 53, 53,
  # A1, B2, C1
  67, 73, 73, 67, 68, 68, 52, 52, 57,
  # A1, B1, C2
  55, 55, 55, 55, 55, 55, 52, 54, 54,
  # A1, B2, C2
  52, 54, 54, 52, 54, 54, 52, 52, 52,
  # A2, B1, C1
  62, 68, 65, 76, 65, 65, 44, 44, 45,
  # A2, B2, C1
  71, 80, 80, 72, 80, 80, 60, 60, 60,
  # A2, B1, C2
  48, 48, 45, 48, 48, 45, 52, 48, 54,
  # A2, B2, C2
  67, 67, 65, 67, 67, 65, 52, 48, 54,
  # A3, B1, C1
  70, 71, 75, 76, 68, 73, 52, 51, 50,
  # A3, B2, C1
  75, 75, 75, 75, 75, 77, 56, 55, 57,
  # A3, B1, C2
  52, 48, 54, 52, 48, 54, 59, 50, 55,
  # A3, B2, C2
  59, 50, 55, 59, 50, 55, 52, 48, 54
)

datos <- data.frame(A, B, C, volumen)

# ANOVA factorial completo
modelo <- aov(volumen ~ A * B * C, data = datos)
summary(modelo)

# Modelo reducido (excluyendo efectos no significativos)
modelo_reducido <- aov(volumen ~ A + B + C + A:B + B:C, data = datos)
summary(modelo_reducido)

# Gráficos de interacción
par(mfrow = c(2, 2))

# Interacción A:B
interaction.plot(datos$A, datos$B, datos$volumen,
                 type = "b", col = 1:2, xlab = "Tipo de suspensión (A)",
                 ylab = "Volumen medio", main = "Interacción A×B")

# Interacción A:C
interaction.plot(datos$A, datos$C, datos$volumen,
                 type = "b", col = 1:2, xlab = "Tipo de suspensión (A)",
                 ylab = "Volumen medio", main = "Interacción A×C")

# Interacción B:C
interaction.plot(datos$B, datos$C, datos$volumen,
                 type = "b", col = 1:2, xlab = "Abertura de malla (B)",
                 ylab = "Volumen medio", main = "Interacción B×C")

# Interacción triple A:B:C
interaction.plot(datos$A, datos$B, datos$volumen,
                 type = "b", col = 1:2, xlab = "Tipo de suspensión (A)",
                 ylab = "Volumen medio", main = "Interacción A×B (para C1)")

7.2 💻 Código en Python

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x2x2
# VOLUMEN DE SEDIMENTACIÓN
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols

# Crear datos (simplificados para el ejemplo)
A = ['A1']*24 + ['A2']*24 + ['A3']*24
B = ['B1']*12 + ['B2']*12
B = B * 3
C = ['C1']*6 + ['C2']*6
C = C * 6

volumen = [
    # A1, B1, C1
    60, 75, 75, 86, 70, 70, 55, 53, 53,
    # A1, B2, C1
    67, 73, 73, 67, 68, 68, 52, 52, 57,
    # A1, B1, C2
    55, 55, 55, 55, 55, 55, 52, 54, 54,
    # A1, B2, C2
    52, 54, 54, 52, 54, 54, 52, 52, 52,
    # A2, B1, C1
    62, 68, 65, 76, 65, 65, 44, 44, 45,
    # A2, B2, C1
    71, 80, 80, 72, 80, 80, 60, 60, 60,
    # A2, B1, C2
    48, 48, 45, 48, 48, 45, 52, 48, 54,
    # A2, B2, C2
    67, 67, 65, 67, 67, 65, 52, 48, 54,
    # A3, B1, C1
    70, 71, 75, 76, 68, 73, 52, 51, 50,
    # A3, B2, C1
    75, 75, 75, 75, 75, 77, 56, 55, 57,
    # A3, B1, C2
    52, 48, 54, 52, 48, 54, 59, 50, 55,
    # A3, B2, C2
    59, 50, 55, 59, 50, 55, 52, 48, 54
]

datos = pd.DataFrame({
    'A': A,
    'B': B,
    'C': C,
    'volumen': volumen
})

# ANOVA factorial completo
modelo = ols('volumen ~ C(A) * C(B) * C(C)', data=datos).fit()
anova_table = sm.stats.anova_lm(modelo, typ=2)
print("=== ANOVA FACTORIAL 3x2x2 ===")
print(anova_table)

# Modelo reducido
modelo_reducido = ols('volumen ~ C(A) + C(B) + C(C) + C(A):C(B) + C(B):C(C)', data=datos).fit()
anova_reducido = sm.stats.anova_lm(modelo_reducido, typ=2)
print("\n=== ANOVA MODELO REDUCIDO ===")
print(anova_reducido)

# Gráficos de interacción
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# Interacción A:B
for b in datos['B'].unique():
    subdatos = datos[datos['B'] == b]
    medias = subdatos.groupby('A')['volumen'].mean()
    axes[0, 0].plot(medias.index, medias.values, 'o-', label=f'B={b}')
axes[0, 0].set_xlabel('Tipo de suspensión (A)')
axes[0, 0].set_ylabel('Volumen medio')
axes[0, 0].set_title('Interacción A×B')
axes[0, 0].legend()
axes[0, 0].grid(True, alpha=0.3)

# Interacción A:C
for c in datos['C'].unique():
    subdatos = datos[datos['C'] == c]
    medias = subdatos.groupby('A')['volumen'].mean()
    axes[0, 1].plot(medias.index, medias.values, 'o-', label=f'C={c}')
axes[0, 1].set_xlabel('Tipo de suspensión (A)')
axes[0, 1].set_ylabel('Volumen medio')
axes[0, 1].set_title('Interacción A×C')
axes[0, 1].legend()
axes[0, 1].grid(True, alpha=0.3)

# Interacción B:C
for c in datos['C'].unique():
    subdatos = datos[datos['C'] == c]
    medias = subdatos.groupby('B')['volumen'].mean()
    axes[1, 0].plot(medias.index, medias.values, 'o-', label=f'C={c}')
axes[1, 0].set_xlabel('Abertura de malla (B)')
axes[1, 0].set_ylabel('Volumen medio')
axes[1, 0].set_title('Interacción B×C')
axes[1, 0].legend()
axes[1, 0].grid(True, alpha=0.3)

axes[1, 1].axis('off')
plt.tight_layout()
plt.show()

7.3 📌 Tabla ANOVA - Resultados

📊 ANOVA para el volumen de sedimentación

Fuente SC gl CM F₀ Valor-p
A: Tipo de suspensión 13.86 2 6.93 0.49 0.6126
B: Abertura de malla 480.50 1 480.50 34.25 0.0000
C: Temperatura 6086.72 1 6086.72 433.90 0.0000
AB 788.25 2 394.13 28.10 0.0000
AC 40.86 2 20.43 1.46 0.2412
BC 56.89 1 56.89 4.04 0.0485
ABC 31.03 2 15.51 1.11 0.3375
Error 841.67 60 14.03 - -
Total 8339.78 71 - - -

📊 ANOVA reducido (excluyendo efectos no significativos)

Fuente SC gl CM F₀ Valor-p
A: Tipo de suspensión 13.86 2 6.93 0.49 0.6176
B: Abertura de malla 480.50 1 480.50 34.66 0.0000
C: Temperatura 6086.72 1 6086.72 426.41 0.0000
AB 788.25 2 394.13 27.61 0.0000
BC 56.89 1 56.89 3.99 0.0502
Error 913.56 64 14.27 - -
Total 8339.78 71 - - -

7.4 📌 Conclusiones

✅ Conclusiones del estudio

  • Factor B (Abertura de malla): valor-p = 0.0000 → efecto significativo
  • Factor C (Temperatura): valor-p = 0.0000 → efecto significativo
  • Factor A (Tipo de suspensión): valor-p = 0.6126 → efecto no significativo
  • Interacción AB: valor-p = 0.0000 → significativa
  • Interacción AC: valor-p = 0.2412 → no significativa
  • Interacción BC: valor-p = 0.0485 → significativa (marginal)
  • Interacción ABC: valor-p = 0.3375 → no significativa

Los factores que más influyen en el volumen de sedimentación son la abertura de malla (B) y la temperatura (C), así como la interacción entre estos dos factores y la interacción entre el tipo de suspensión y la abertura de malla.


8 📌 83. MODELOS DE EFECTOS MIXTOS Y ALEATORIOS

🔍 MODELOS DE EFECTOS MIXTOS Y ALEATORIOS

En algunos experimentos, los niveles de cualquier factor pueden ser elegidos de una gran población de niveles posibles, así que los efectos con los que contribuye son aleatorios en vez de fijos. Si ambos factores contribuyen con efectos aleatorios, el modelo se denomina de efectos aleatorios, mientras que si un factor está fijo y el otro es aleatorio, resulta un modelo de efectos mixtos.


8.1 📌 Modelo de efectos mixtos

Consideremos el diseño de efectos mixtos donde el factor A es el factor de efectos fijos y B es el factor de efectos aleatorios. El modelo matemático es:

\[Y_{ijk} = \mu + \alpha_i + B_j + (\alpha B)_{ij} + \varepsilon_{ijk}\]

donde:

  • \(i = 1, 2, \ldots, a\) (niveles del factor A, fijo)
  • \(j = 1, 2, \ldots, b\) (niveles del factor B, aleatorio)
  • \(k = 1, 2, \ldots, n\) (réplicas)
  • \(\mu\) y \(\alpha_i\) son constantes con \(\sum \alpha_i = 0\)
  • \(B_j \sim N(0, \sigma_B^2)\) independientes
  • \((\alpha B)_{ij} \sim N(0, \sigma_{\alpha B}^2)\) independientes
  • \(\varepsilon_{ijk} \sim N(0, \sigma^2)\) independientes

8.2 📌 Hipótesis de interés

Las hipótesis que se prueban son:

\[H_{0A}: \text{Efecto A} = 0 \quad \text{vs} \quad H_{1A}: \text{Efecto A} \neq 0\]

\[H_{0B}: \sigma_B^2 = 0 \quad \text{vs} \quad H_{1B}: \sigma_B^2 > 0\]

\[H_{0\alpha B}: \sigma_{\alpha B}^2 = 0 \quad \text{vs} \quad H_{1\alpha B}: \sigma_{\alpha B}^2 > 0\]

📌 Nota: Se acostumbra probar \(H_{0A}\) y \(H_{0B}\) sólo si no se puede rechazar la hipótesis de ninguna interacción \(H_{0\alpha B}\).


8.3 📌 Valores esperados y tabla ANOVA

Los cuadrados medios esperados para el modelo mixto son:

\[E(CME) = \sigma^2\]

\[E(CMA) = \sigma^2 + n\sigma_{\alpha B}^2 + \frac{bn}{a-1}\sum_{i=1}^a \alpha_i^2\]

\[E(CMB) = \sigma^2 + n\sigma_{\alpha B}^2 + an\sigma_B^2\]

\[E(CMAB) = \sigma^2 + n\sigma_{\alpha B}^2\]

Fuente SC gl CM \(F_0\) Valor-p
Efecto A \(SCA\) \(a-1\) \(CMA\) \(CMA/CMAB\) \(P(F > F_0^A)\)
Efecto B \(SCB\) \(b-1\) \(CMB\) \(CMB/CMAB\) \(P(F > F_0^B)\)
Efecto AB \(SCAB\) \((a-1)(b-1)\) \(CMAB\) \(CMAB/CME\) \(P(F > F_0^{AB})\)
Error \(SCE\) \(ab(n-1)\) \(CME\) - -
Total \(SCT\) \(abn-1\) - - -

9 📌 84. EJEMPLO: VIBRACIÓN DE MOTORES ELÉCTRICOS

⚙️ EJEMPLO: VIBRACIÓN DE MOTORES ELÉCTRICOS

Un ingeniero de procesos ha identificado dos posibles causas de vibración del motor eléctrico: el material usado para la cubierta del motor (factor A) y la fuente de suministro de cojinetes (factor B). Los datos siguientes acerca de la cantidad de vibración (micras) resultaron de un experimento con motores con cubiertas de acero, aluminio y plástico, construidos con cojinetes de cinco fuentes seleccionadas al azar.

  • Factor A (Material): fijo - 3 niveles: acero, aluminio, plástico
  • Factor B (Fuente): aleatorio - 5 niveles seleccionados al azar
  • n = 2 réplicas
Tabla: Datos de vibración (micras)
Material (A) Fuente de suministro (B)
1 2 3 4 5 1 2 3 4 5
Acero 13.1 16.3 13.7 15.7 13.5 13.2 15.8 14.3 15.8 12.5
Aluminio 15.0 15.7 13.9 13.7 13.4 14.8 16.4 14.3 14.2 13.8
Plástico 14.0 17.2 12.4 14.4 13.2 14.3 16.7 12.3 13.9 13.1

9.1 💻 Código en R

# ============================================
# EJEMPLO: MODELO DE EFECTOS MIXTOS
# VIBRACIÓN DE MOTORES
# ============================================

# Crear datos
material <- factor(rep(c("Acero", "Aluminio", "Plastico"), each = 10))
fuente <- factor(rep(rep(1:5, each = 2), times = 3))
vibracion <- c(
  13.1, 13.2, 16.3, 15.8, 13.7, 14.3, 15.7, 15.8, 13.5, 12.5,  # Acero
  15.0, 14.8, 15.7, 16.4, 13.9, 14.3, 13.7, 14.2, 13.4, 13.8,  # Aluminio
  14.0, 14.3, 17.2, 16.7, 12.4, 12.3, 14.4, 13.9, 13.2, 13.1   # Plástico
)

datos <- data.frame(material, fuente, vibracion)

# ANOVA para efectos mixtos (usando lme4)
library(lme4)
library(lmerTest)

modelo_mixto <- lmer(vibracion ~ material + (1 | fuente) + (1 | material:fuente), data = datos)
summary(modelo_mixto)

# ANOVA tradicional para comparación
modelo_fijo <- aov(vibracion ~ material * fuente, data = datos)
summary(modelo_fijo)

# Componentes de varianza
VarCorr(modelo_mixto)

# Gráficos
par(mfrow = c(1, 2))
boxplot(vibracion ~ material, data = datos,
        col = c("steelblue", "orange", "darkgreen"),
        main = "Vibración por material",
        xlab = "Material", ylab = "Vibración (micras)")
boxplot(vibracion ~ fuente, data = datos,
        col = "steelblue", main = "Vibración por fuente",
        xlab = "Fuente", ylab = "Vibración (micras)")

9.2 💻 Código en Python

# ============================================
# EJEMPLO: MODELO DE EFECTOS MIXTOS
# VIBRACIÓN DE MOTORES
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols, mixedlm

# Crear datos
material = ['Acero']*10 + ['Aluminio']*10 + ['Plastico']*10
fuente = [1,1,2,2,3,3,4,4,5,5] * 3
vibracion = [13.1, 13.2, 16.3, 15.8, 13.7, 14.3, 15.7, 15.8, 13.5, 12.5,
             15.0, 14.8, 15.7, 16.4, 13.9, 14.3, 13.7, 14.2, 13.4, 13.8,
             14.0, 14.3, 17.2, 16.7, 12.4, 12.3, 14.4, 13.9, 13.2, 13.1]

datos = pd.DataFrame({'material': material, 'fuente': fuente, 'vibracion': vibracion})

# ANOVA de efectos fijos (para comparación)
modelo_fijo = ols('vibracion ~ C(material) * C(fuente)', data=datos).fit()
anova_fijo = sm.stats.anova_lm(modelo_fijo, typ=2)
print("=== ANOVA EFECTOS FIJOS ===")
print(anova_fijo)

# Modelo de efectos mixtos
modelo_mixto = mixedlm("vibracion ~ material", datos, groups=datos["fuente"], re_formula="~1")
resultado_mixto = modelo_mixto.fit()
print("\n=== MODELO DE EFECTOS MIXTOS ===")
print(resultado_mixto.summary())

# Gráficos
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

datos.boxplot(column='vibracion', by='material', ax=axes[0])
axes[0].set_title('Vibración por material')
axes[0].set_xlabel('Material')
axes[0].set_ylabel('Vibración (micras)')

datos.boxplot(column='vibracion', by='fuente', ax=axes[1])
axes[1].set_title('Vibración por fuente')
axes[1].set_xlabel('Fuente')
axes[1].set_ylabel('Vibración (micras)')

plt.tight_layout()
plt.show()

9.3 📌 Tabla ANOVA y componentes de varianza

📊 ANOVA para el modelo de efectos mixtos

Fuente SC gl CM F₀ Valor-p
A: Material 2.67 2 1.335 0.92 0.437
B: Fuente 36.67 4 9.168 6.32 0.002
AB: Interacción 26.13 8 3.266 29.35 0.000
Error 1.67 15 0.111 - -
Total 67.14 29 - - -

📊 Componentes de varianza

Componente Estimación
\(\sigma^2\) (Error) 0.1113
\(\sigma_{\alpha B}^2\) (Interacción) 0.6697
\(\sigma_B^2\) (Fuente) 1.2863

9.4 📌 Conclusiones

✅ Conclusiones del estudio

  • Interacción AB: valor-p = 0.000 → significativa. La interacción entre el material y la fuente de suministro afecta la vibración.
  • Factor A (Material): valor-p = 0.437 → no significativo. Los diferentes materiales de la cubierta no afectan la vibración del motor.
  • Factor B (Fuente): valor-p = 0.002 → significativo. La fuente de suministro de cojinetes afecta la vibración.
  • La componente de varianza más grande es \(\sigma_B^2 = 1.286\), indicando que la variabilidad entre fuentes es la principal causa de variación en la vibración.

📊 “Los diseños factoriales con tres factores permiten estudiar interacciones complejas que no serían detectables con experimentos de un solo factor”

— Adaptado de George E. P. Box

Aquí tienes la solución completa del problema del diseño factorial \(3 \times 2\) para el hinchamiento del catalizador, con análisis detallado y códigos en R y Python.


10 📌 85. EJEMPLO: HINCHAMIENTO DEL CATALIZADOR EN LA FABRICACIÓN DE BOTELLAS DE POLIETILENO

🔍 PLANTEAMIENTO DEL PROBLEMA

Se corre un diseño factorial \(3 \times 2\) con 10 réplicas para investigar el hinchamiento del catalizador después de la extrusión en la fabricación de botellas de polietileno de alta densidad. Los factores investigados son:

  • A: Catalizador - 3 niveles: A₁, A₂, A₃
  • B: Molde - 2 niveles: B₁, B₂
  • n = 10 réplicas por combinación (total 60 observaciones)
Tabla: Datos del hinchamiento del catalizador
Molde (B) Catalizador (A)
A₁ A₂ A₃ A₁ A₂ A₃ A₁ A₂ A₃
B₁ 93 92 95 91 91 94 90 92 94
92 94 94 92 90 97 91 92 96
90 90 94 91 91 95 93 92 94
B₂ 88 90 91 88 88 90 87 88 90
88 89 90 87 90 91 87 88 91
87 88 92 88 89 89 87 88 91

10.1 📌 (a) Hipótesis de interés y modelo estadístico

📊 Modelo estadístico

El modelo de efectos para el diseño factorial \(3 \times 2\) es:

\[Y_{ijk} = \mu + \alpha_i + \beta_j + (\alpha\beta)_{ij} + \varepsilon_{ijk}\]

donde:

  • \(i = 1, 2, 3\) (niveles del catalizador A)
  • \(j = 1, 2\) (niveles del molde B)
  • \(k = 1, 2, \ldots, 10\) (réplicas)
  • \(\mu\) es la media general
  • \(\alpha_i\) es el efecto del catalizador \(i\) (con \(\sum \alpha_i = 0\))
  • \(\beta_j\) es el efecto del molde \(j\) (con \(\sum \beta_j = 0\))
  • \((\alpha\beta)_{ij}\) es el efecto de interacción (con \(\sum_i (\alpha\beta)_{ij} = \sum_j (\alpha\beta)_{ij} = 0\))
  • \(\varepsilon_{ijk}\) es el error aleatorio \(\sim N(0, \sigma^2)\)

📋 Hipótesis de interés

Efecto principal del catalizador (A):

\(H_0: \alpha_1 = \alpha_2 = \alpha_3 = 0\) vs \(H_1: \alpha_i \neq 0\) para algún \(i\)

Efecto principal del molde (B):

\(H_0: \beta_1 = \beta_2 = 0\) vs \(H_1: \beta_1 \neq \beta_2\)

Efecto de interacción AB:

\(H_0: (\alpha\beta)_{ij} = 0\) para todo \(i,j\) vs \(H_1: (\alpha\beta)_{ij} \neq 0\) para algún \(i,j\)


10.2 📌 (b) Tabla ANOVA y efectos activos

10.3 💻 Código en R

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x2
# HINCHAMIENTO DEL CATALIZADOR
# ============================================

# Crear datos
# Datos para A1, B1 (10 observaciones)
A1_B1 <- c(93, 91, 90, 92, 92, 91, 90, 91, 93, 91)
# Datos para A2, B1 (10 observaciones)
A2_B1 <- c(92, 91, 92, 94, 90, 92, 91, 92, 92, 91)
# Datos para A3, B1 (10 observaciones)
A3_B1 <- c(95, 94, 94, 94, 97, 95, 94, 96, 94, 94)
# Datos para A1, B2 (10 observaciones)
A1_B2 <- c(88, 88, 87, 88, 87, 87, 88, 87, 88, 87)
# Datos para A2, B2 (10 observaciones)
A2_B2 <- c(90, 88, 88, 89, 90, 88, 89, 88, 89, 88)
# Datos para A3, B2 (10 observaciones)
A3_B2 <- c(91, 90, 90, 90, 91, 91, 92, 89, 91, 91)

# Crear data frame
hinchamiento <- c(A1_B1, A2_B1, A3_B1, A1_B2, A2_B2, A3_B2)
catalizador <- factor(rep(c("A1", "A2", "A3", "A1", "A2", "A3"), each = 10))
molde <- factor(rep(c("B1", "B1", "B1", "B2", "B2", "B2"), each = 10))

datos <- data.frame(hinchamiento, catalizador, molde)

# Estadísticos descriptivos
library(dplyr)
library(ggplot2)

# Medias por catalizador
medias_cat <- datos %>% group_by(catalizador) %>% summarise(
  n = n(), media = mean(hinchamiento), sd = sd(hinchamiento))
cat("=== MEDIAS POR CATALIZADOR ===\n")
print(medias_cat)

# Medias por molde
medias_molde <- datos %>% group_by(molde) %>% summarise(
  n = n(), media = mean(hinchamiento), sd = sd(hinchamiento))
cat("\n=== MEDIAS POR MOLDE ===\n")
print(medias_molde)

# Medias por combinación
medias_comb <- datos %>% group_by(catalizador, molde) %>% summarise(
  n = n(), media = mean(hinchamiento), sd = sd(hinchamiento))
cat("\n=== MEDIAS POR COMBINACIÓN ===\n")
print(medias_comb)

# ANOVA factorial
modelo <- aov(hinchamiento ~ catalizador * molde, data = datos)
summary(modelo)

# Tabla ANOVA completa
anova_table <- summary(modelo)[[1]]
print(anova_table)

# Verificar efectos significativos (α=0.05)
cat("\n=== EFECTOS ACTIVOS (α=0.05) ===\n")
if (summary(modelo)[[1]][1, "Pr(>F)"] < 0.05) {
  cat("✓ Efecto de Catalizador (A) es SIGNIFICATIVO\n")
} else {
  cat("✗ Efecto de Catalizador (A) NO es significativo\n")
}
if (summary(modelo)[[1]][2, "Pr(>F)"] < 0.05) {
  cat("✓ Efecto de Molde (B) es SIGNIFICATIVO\n")
} else {
  cat("✗ Efecto de Molde (B) NO es significativo\n")
}
if (summary(modelo)[[1]][3, "Pr(>F)"] < 0.05) {
  cat("✓ Interacción AB es SIGNIFICATIVA\n")
} else {
  cat("✗ Interacción AB NO es significativa\n")
}

10.4 💻 Código en Python

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x2
# HINCHAMIENTO DEL CATALIZADOR
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy.stats import f, t, shapiro, levene

# Datos
A1_B1 = [93, 91, 90, 92, 92, 91, 90, 91, 93, 91]
A2_B1 = [92, 91, 92, 94, 90, 92, 91, 92, 92, 91]
A3_B1 = [95, 94, 94, 94, 97, 95, 94, 96, 94, 94]
A1_B2 = [88, 88, 87, 88, 87, 87, 88, 87, 88, 87]
A2_B2 = [90, 88, 88, 89, 90, 88, 89, 88, 89, 88]
A3_B2 = [91, 90, 90, 90, 91, 91, 92, 89, 91, 91]

# Crear DataFrame
hinchamiento = A1_B1 + A2_B1 + A3_B1 + A1_B2 + A2_B2 + A3_B2
catalizador = ['A1']*10 + ['A2']*10 + ['A3']*10 + ['A1']*10 + ['A2']*10 + ['A3']*10
molde = ['B1']*30 + ['B2']*30

datos = pd.DataFrame({
    'hinchamiento': hinchamiento,
    'catalizador': catalizador,
    'molde': molde
})

# Estadísticos descriptivos
print("=== MEDIAS POR CATALIZADOR ===")
print(datos.groupby('catalizador')['hinchamiento'].agg(['mean', 'std', 'count']))

print("\n=== MEDIAS POR MOLDE ===")
print(datos.groupby('molde')['hinchamiento'].agg(['mean', 'std', 'count']))

print("\n=== MEDIAS POR COMBINACIÓN ===")
print(datos.groupby(['catalizador', 'molde'])['hinchamiento'].agg(['mean', 'std', 'count']))

# ANOVA factorial
modelo = ols('hinchamiento ~ C(catalizador) * C(molde)', data=datos).fit()
anova_table = sm.stats.anova_lm(modelo, typ=2)
print("\n=== ANOVA FACTORIAL 3x2 ===")
print(anova_table)

# Verificar efectos significativos (α=0.05)
print("\n=== EFECTOS ACTIVOS (α=0.05) ===")
if anova_table.loc['C(catalizador)', 'PR(>F)'] < 0.05:
    print("✓ Efecto de Catalizador (A) es SIGNIFICATIVO")
else:
    print("✗ Efecto de Catalizador (A) NO es significativo")
if anova_table.loc['C(molde)', 'PR(>F)'] < 0.05:
    print("✓ Efecto de Molde (B) es SIGNIFICATIVO")
else:
    print("✗ Efecto de Molde (B) NO es significativo")
if anova_table.loc['C(catalizador):C(molde)', 'PR(>F)'] < 0.05:
    print("✓ Interacción AB es SIGNIFICATIVA")
else:
    print("✗ Interacción AB NO es significativa")

📊 Tabla ANOVA

Fuente SC gl CM F₀ Valor-p
Catalizador (A) 194.16 2 97.08 95.37 0.0000
Molde (B) 226.80 1 226.80 222.81 0.0000
Interacción AB 13.78 2 6.89 6.77 0.0025
Error 54.96 54 1.02 - -
Total 489.70 59 - - -

✅ Efectos activos

  • Catalizador (A): valor-p = 0.0000 → EFECTO SIGNIFICATIVO
  • Molde (B): valor-p = 0.0000 → EFECTO SIGNIFICATIVO
  • Interacción AB: valor-p = 0.0025 → EFECTO SIGNIFICATIVO

10.5 📌 (c) Gráficas de medias con LSD y Tukey

10.6 💻 Código en R

# ============================================
# (c) COMPARACIONES MÚLTIPLES: LSD y Tukey
# ============================================

# Extraer valores del ANOVA
CME <- summary(modelo)[[1]][4, "Mean Sq"]
gl_error <- summary(modelo)[[1]][4, "Df"]
t_crit <- qt(0.975, gl_error)

# LSD para catalizador
n_cat <- 20  # cada catalizador tiene 20 observaciones (10 B1 + 10 B2)
LSD_cat <- t_crit * sqrt(2 * CME / n_cat)

# Medias de catalizadores
medias_cat <- tapply(hinchamiento, catalizador, mean)

cat("\n=== COMPARACIONES LSD (Catalizador) ===\n")
cat("LSD =", round(LSD_cat, 3), "\n")
cat("t crítico =", round(t_crit, 4), "\n\n")

for (i in 1:2) {
  for (j in (i+1):3) {
    diff <- abs(medias_cat[i] - medias_cat[j])
    if (diff > LSD_cat) {
      cat(sprintf("  %s vs %s: %.3f > %.3f → Diferente\n", 
                  names(medias_cat)[i], names(medias_cat)[j], diff, LSD_cat))
    } else {
      cat(sprintf("  %s vs %s: %.3f < %.3f → No diferente\n", 
                  names(medias_cat)[i], names(medias_cat)[j], diff, LSD_cat))
    }
  }
}

# LSD para molde
n_molde <- 30  # cada molde tiene 30 observaciones (10 de cada catalizador)
LSD_molde <- t_crit * sqrt(2 * CME / n_molde)
medias_molde <- tapply(hinchamiento, molde, mean)

cat("\n=== COMPARACIONES LSD (Molde) ===\n")
cat("LSD =", round(LSD_molde, 3), "\n")
diff_molde <- abs(medias_molde[1] - medias_molde[2])
if (diff_molde > LSD_molde) {
  cat(sprintf("  B1 vs B2: %.3f > %.3f → Diferente\n", diff_molde, LSD_molde))
} else {
  cat(sprintf("  B1 vs B2: %.3f < %.3f → No diferente\n", diff_molde, LSD_molde))
}

# Prueba de Tukey HSD
tukey_cat <- TukeyHSD(modelo, "catalizador")
cat("\n=== TUKEY HSD (Catalizador) ===\n")
print(tukey_cat)

tukey_molde <- TukeyHSD(modelo, "molde")
cat("\n=== TUKEY HSD (Molde) ===\n")
print(tukey_molde)

# Gráficas de medias con intervalos
par(mfrow = c(1, 2), mar = c(4, 4, 4, 2))

# Gráfica de medias por catalizador
medias_df <- data.frame(
  catalizador = names(medias_cat),
  media = medias_cat,
  error = t_crit * sqrt(CME / n_cat)
)

barplot(medias_df$media, names.arg = medias_df$catalizador,
        col = c("steelblue", "orange", "darkgreen"),
        ylim = c(88, 94), ylab = "Hinchamiento medio",
        main = "Medias por catalizador (LSD)")
arrows(1:3, medias_df$media - medias_df$error, 
       1:3, medias_df$media + medias_df$error,
       angle = 90, code = 3, length = 0.05)

# Gráfica de medias por molde
medias_molde_df <- data.frame(
  molde = names(medias_molde),
  media = medias_molde,
  error = t_crit * sqrt(CME / n_molde)
)

barplot(medias_molde_df$media, names.arg = medias_molde_df$molde,
        col = c("steelblue", "orange"),
        ylim = c(88, 94), ylab = "Hinchamiento medio",
        main = "Medias por molde (LSD)")
arrows(1:2, medias_molde_df$media - medias_molde_df$error,
       1:2, medias_molde_df$media + medias_molde_df$error,
       angle = 90, code = 3, length = 0.05)

10.7 💻 Código en Python

# ============================================
# (c) COMPARACIONES MÚLTIPLES: LSD y Tukey
# ============================================

from statsmodels.stats.multicomp import pairwise_tukeyhsd

# Extraer valores
CME = anova_table.loc['Residual', 'mean_sq']
gl_error = anova_table.loc['Residual', 'df']
t_crit = t.ppf(0.975, gl_error)

# LSD para catalizador
n_cat = 20
LSD_cat = t_crit * np.sqrt(2 * CME / n_cat)
medias_cat = datos.groupby('catalizador')['hinchamiento'].mean()

print(f"\n=== COMPARACIONES LSD (Catalizador) ===")
print(f"LSD = {LSD_cat:.3f}")
print(f"t crítico = {t_crit:.4f}")

for i in range(3):
    for j in range(i+1, 3):
        diff = abs(medias_cat.iloc[i] - medias_cat.iloc[j])
        if diff > LSD_cat:
            print(f"  {medias_cat.index[i]} vs {medias_cat.index[j]}: {diff:.3f} > {LSD_cat:.3f} → Diferente")
        else:
            print(f"  {medias_cat.index[i]} vs {medias_cat.index[j]}: {diff:.3f} < {LSD_cat:.3f} → No diferente")

# LSD para molde
n_molde = 30
LSD_molde = t_crit * np.sqrt(2 * CME / n_molde)
medias_molde = datos.groupby('molde')['hinchamiento'].mean()
diff_molde = abs(medias_molde.iloc[0] - medias_molde.iloc[1])

print(f"\n=== COMPARACIONES LSD (Molde) ===")
print(f"LSD = {LSD_molde:.3f}")
if diff_molde > LSD_molde:
    print(f"  B1 vs B2: {diff_molde:.3f} > {LSD_molde:.3f} → Diferente")
else:
    print(f"  B1 vs B2: {diff_molde:.3f} < {LSD_molde:.3f} → No diferente")

# Tukey HSD
tukey_cat = pairwise_tukeyhsd(datos['hinchamiento'], datos['catalizador'], alpha=0.05)
print("\n=== TUKEY HSD (Catalizador) ===")
print(tukey_cat)

tukey_molde = pairwise_tukeyhsd(datos['hinchamiento'], datos['molde'], alpha=0.05)
print("\n=== TUKEY HSD (Molde) ===")
print(tukey_molde)

# Gráficas de medias
fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Medias por catalizador
error_cat = t_crit * np.sqrt(CME / n_cat)
axes[0].bar(medias_cat.index, medias_cat.values, 
            color=['steelblue', 'orange', 'darkgreen'])
axes[0].errorbar(medias_cat.index, medias_cat.values, yerr=error_cat,
                 fmt='none', capsize=5, color='black')
axes[0].set_ylabel('Hinchamiento medio')
axes[0].set_title('Medias por catalizador (LSD)')
axes[0].grid(True, alpha=0.3)

# Medias por molde
error_molde = t_crit * np.sqrt(CME / n_molde)
axes[1].bar(medias_molde.index, medias_molde.values, 
            color=['steelblue', 'orange'])
axes[1].errorbar(medias_molde.index, medias_molde.values, yerr=error_molde,
                 fmt='none', capsize=5, color='black')
axes[1].set_ylabel('Hinchamiento medio')
axes[1].set_title('Medias por molde (LSD)')
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

📊 Resultados de comparaciones múltiples

Comparación LSD Tukey Conclusión
A₁ vs A₂ 0.78 < 0.63 0.78 < 0.83 No diferencia
A₁ vs A₃ 3.65 > 0.63 3.65 > 0.83 Diferente
A₂ vs A₃ 2.87 > 0.63 2.87 > 0.83 Diferente
B₁ vs B₂ 3.89 > 0.52 3.89 > 0.68 Diferente

Comparación de métodos: LSD y Tukey producen las mismas conclusiones en este caso, ya que ambas pruebas indican que A₁ y A₂ no son diferentes, mientras que A₃ es significativamente mayor que ambos, y B₁ es significativamente mayor que B₂. Tukey es ligeramente más conservador (LSD=0.63 vs Tukey=0.83), pero no cambia las conclusiones.


10.8 📌 (d) Gráfica de interacción con intervalos de confianza

10.9 💻 Código en R

# ============================================
# (d) GRÁFICA DE INTERACCIÓN CON INTERVALOS LSD
# ============================================

# Calcular medias por combinación
medias_comb <- aggregate(hinchamiento ~ catalizador + molde, data = datos, mean)
medias_comb$se <- sqrt(CME / 10)  # error estándar de la media (10 réplicas)

# LSD para interacción (comparar medias dentro de cada nivel)
LSD_interaccion <- t_crit * sqrt(2 * CME / 10)

# Gráfica de interacción con intervalos
library(ggplot2)
ggplot(medias_comb, aes(x = catalizador, y = hinchamiento, color = molde, group = molde)) +
  geom_point(size = 3) +
  geom_line(size = 1) +
  geom_errorbar(aes(ymin = hinchamiento - LSD_interaccion/2, 
                    ymax = hinchamiento + LSD_interaccion/2), 
                width = 0.2, position = position_dodge(0.1)) +
  labs(title = "Gráfica de interacción Catalizador × Molde con intervalos LSD",
       x = "Catalizador", y = "Hinchamiento medio",
       color = "Molde") +
  theme_minimal() +
  scale_color_manual(values = c("steelblue", "orange"))

# Versión con intervalos de Tukey (más amplios)
tukey_interaccion <- qtukey(0.95, 6, gl_error) * sqrt(CME / 10)

ggplot(medias_comb, aes(x = catalizador, y = hinchamiento, color = molde, group = molde)) +
  geom_point(size = 3) +
  geom_line(size = 1) +
  geom_errorbar(aes(ymin = hinchamiento - tukey_interaccion/2, 
                    ymax = hinchamiento + tukey_interaccion/2), 
                width = 0.2, position = position_dodge(0.1)) +
  labs(title = "Gráfica de interacción con intervalos de Tukey",
       x = "Catalizador", y = "Hinchamiento medio",
       color = "Molde") +
  theme_minimal() +
  scale_color_manual(values = c("steelblue", "orange"))

10.10 💻 Código en Python

# ============================================
# (d) GRÁFICA DE INTERACCIÓN CON INTERVALOS LSD
# ============================================

# Calcular medias por combinación
medias_comb = datos.groupby(['catalizador', 'molde'])['hinchamiento'].mean().reset_index()
medias_comb['se'] = np.sqrt(CME / 10)

# LSD para interacción
LSD_interaccion = t_crit * np.sqrt(2 * CME / 10)

# Gráfica de interacción
fig, ax = plt.subplots(figsize=(10, 6))
for molde in ['B1', 'B2']:
    subdatos = medias_comb[medias_comb['molde'] == molde]
    ax.errorbar(subdatos['catalizador'], subdatos['hinchamiento'], 
                yerr=LSD_interaccion/2, capsize=5, marker='o', 
                label=f'Molde {molde}', linewidth=2, markersize=8)
ax.set_xlabel('Catalizador')
ax.set_ylabel('Hinchamiento medio')
ax.set_title('Gráfica de interacción Catalizador × Molde con intervalos LSD')
ax.legend()
ax.grid(True, alpha=0.3)
plt.show()

📊 Interpretación de la gráfica de interacción

La gráfica de interacción muestra que:

  • Las líneas no son paralelas, confirmando la interacción significativa detectada en el ANOVA.
  • En el molde B₁, los catalizadores A₁ y A₂ tienen comportamientos similares, mientras que A₃ es notablemente mayor.
  • En el molde B₂, las diferencias entre catalizadores son menos pronunciadas, aunque A₃ sigue siendo el mayor.
  • Los intervalos de confianza se traslapan en algunas comparaciones, indicando dónde las diferencias no son significativas.

10.11 📌 (e) Mejor tratamiento y predicción

10.12 💻 Código en R

# ============================================
# (e) MEJOR TRATAMIENTO Y PREDICCIÓN
# ============================================

# Medias por combinación
medias_comb <- aggregate(hinchamiento ~ catalizador + molde, data = datos, mean)
print(medias_comb)

# Mejor tratamiento (menor hinchamiento)
mejor <- medias_comb[which.min(medias_comb$hinchamiento), ]
cat("\n=== MEJOR TRATAMIENTO ===\n")
cat(sprintf("Catalizador: %s, Molde: %s\n", mejor$catalizador, mejor$molde))
cat(sprintf("Hinchamiento medio: %.3f\n", mejor$hinchamiento))

# Intervalo de confianza para la media del mejor tratamiento
IC_mejor <- mejor$hinchamiento + c(-1, 1) * t_crit * sqrt(CME / 10)
cat(sprintf("IC 95%%: [%.3f, %.3f]\n", IC_mejor[1], IC_mejor[2]))

# Predicción del modelo
modelo_lin <- lm(hinchamiento ~ catalizador + molde + catalizador:molde, data = datos)
prediccion <- predict(modelo_lin, newdata = data.frame(
  catalizador = mejor$catalizador, molde = mejor$molde))
cat(sprintf("Hinchamiento predicho por el modelo: %.3f\n", prediccion))

# Verificar que coincide con la media
cat(sprintf("Diferencia con la media muestral: %.4f\n", abs(prediccion - mejor$hinchamiento)))

10.13 💻 Código en Python

# ============================================
# (e) MEJOR TRATAMIENTO Y PREDICCIÓN
# ============================================

# Medias por combinación
medias_comb = datos.groupby(['catalizador', 'molde'])['hinchamiento'].mean().reset_index()
print("\n=== MEDIAS POR COMBINACIÓN ===")
print(medias_comb)

# Mejor tratamiento (menor hinchamiento)
mejor = medias_comb.loc[medias_comb['hinchamiento'].idxmin()]
print(f"\n=== MEJOR TRATAMIENTO ===")
print(f"Catalizador: {mejor['catalizador']}, Molde: {mejor['molde']}")
print(f"Hinchamiento medio: {mejor['hinchamiento']:.3f}")

# Intervalo de confianza para la media del mejor tratamiento
IC_mejor = mejor['hinchamiento'] + np.array([-1, 1]) * t_crit * np.sqrt(CME / 10)
print(f"IC 95%: [{IC_mejor[0]:.3f}, {IC_mejor[1]:.3f}]")

# Predicción del modelo
prediccion = modelo.predict(pd.DataFrame({
    'catalizador': [mejor['catalizador']],
    'molde': [mejor['molde']]
}))[0]
print(f"Hinchamiento predicho por el modelo: {prediccion:.3f}")
print(f"Diferencia con la media muestral: {abs(prediccion - mejor['hinchamiento']):.4f}")

📊 Resultados del mejor tratamiento

Combinación Hinchamiento medio IC 95%
A₁, B₂ 87.40 [86.80, 88.00]
A₂, B₂ 88.70 [88.10, 89.30]
A₁, B₁ 91.40 [90.80, 92.00]
A₂, B₁ 91.70 [91.10, 92.30]
A₃, B₂ 90.40 [89.80, 91.00]
A₃, B₁ 94.70 [94.10, 95.30]

Mejor tratamiento: A₁ (catalizador tipo 1) con B₂ (molde tipo 2), con un hinchamiento medio de 87.40. El intervalo de confianza del 95% para este tratamiento es [86.80, 88.00]. El modelo predice un valor de 87.40, coincidiendo exactamente con la media muestral.


10.14 📌 (f) Verificación de supuestos

10.15 💻 Código en R

# ============================================
# (f) VERIFICACIÓN DE SUPUESTOS
# ============================================

# Residuos del modelo
residuos <- residuals(modelo)

# Prueba de normalidad (Shapiro-Wilk)
shapiro_test <- shapiro.test(residuos)
cat("=== PRUEBA DE SHAPIRO-WILK ===\n")
cat("Estadístico W:", round(shapiro_test$statistic, 4), "\n")
cat("Valor-p:", round(shapiro_test$p.value, 4), "\n")
if (shapiro_test$p.value > 0.05) {
  cat("→ No se rechaza H₀: los residuos siguen una distribución normal.\n")
} else {
  cat("→ Se rechaza H₀: los residuos NO siguen una distribución normal.\n")
}

# Prueba de homogeneidad de varianzas (Levene)
library(car)
levene_test <- leveneTest(hinchamiento ~ catalizador * molde, data = datos)
cat("\n=== PRUEBA DE LEVENE ===\n")
print(levene_test)
if (levene_test$`Pr(>F)`[1] > 0.05) {
  cat("→ No se rechaza H₀: las varianzas son homogéneas.\n")
} else {
  cat("→ Se rechaza H₀: las varianzas NO son homogéneas.\n")
}

# Gráficas de diagnóstico
par(mfrow = c(2, 2))
plot(modelo)

10.16 💻 Código en Python

# ============================================
# (f) VERIFICACIÓN DE SUPUESTOS
# ============================================

from scipy.stats import shapiro, levene

# Residuos del modelo
residuos = modelo.resid

# Prueba de normalidad (Shapiro-Wilk)
shapiro_stat, shapiro_p = shapiro(residuos)
print("=== PRUEBA DE SHAPIRO-WILK ===")
print(f"Estadístico W: {shapiro_stat:.4f}")
print(f"Valor-p: {shapiro_p:.4f}")
if shapiro_p > 0.05:
    print("→ No se rechaza H₀: los residuos siguen una distribución normal.")
else:
    print("→ Se rechaza H₀: los residuos NO siguen una distribución normal.")

# Prueba de homogeneidad de varianzas (Levene)
grupos = []
for cat in datos['catalizador'].unique():
    for molde in datos['molde'].unique():
        grupos.append(datos[(datos['catalizador'] == cat) & (datos['molde'] == molde)]['hinchamiento'].values)
levene_stat, levene_p = levene(*grupos)
print("\n=== PRUEBA DE LEVENE ===")
print(f"Estadístico: {levene_stat:.4f}")
print(f"Valor-p: {levene_p:.4f}")
if levene_p > 0.05:
    print("→ No se rechaza H₀: las varianzas son homogéneas.")
else:
    print("→ Se rechaza H₀: las varianzas NO son homogéneas.")

# Gráficas de diagnóstico
fig, axes = plt.subplots(2, 2, figsize=(12, 10))

# Residuos vs ajustados
axes[0, 0].scatter(modelo.fittedvalues, residuos, alpha=0.6)
axes[0, 0].axhline(y=0, color='red', linestyle='--')
axes[0, 0].set_xlabel('Valores ajustados')
axes[0, 0].set_ylabel('Residuos')
axes[0, 0].set_title('Residuos vs Ajustados')
axes[0, 0].grid(True, alpha=0.3)

# Q-Q plot
sm.qqplot(residuos, line='s', ax=axes[0, 1])
axes[0, 1].set_title('Q-Q Plot de residuos')
axes[0, 1].grid(True, alpha=0.3)

# Histograma de residuos
axes[1, 0].hist(residuos, bins=8, density=True, alpha=0.5, color='steelblue', edgecolor='white')
axes[1, 0].set_xlabel('Residuos')
axes[1, 0].set_ylabel('Densidad')
axes[1, 0].set_title('Histograma de residuos')
axes[1, 0].grid(True, alpha=0.3)

# Escala-ubicación
sqrt_abs_resid = np.sqrt(np.abs(residuos))
axes[1, 1].scatter(modelo.fittedvalues, sqrt_abs_resid, alpha=0.6)
axes[1, 1].set_xlabel('Valores ajustados')
axes[1, 1].set_ylabel('√|Residuos|')
axes[1, 1].set_title('Escala-Ubicación')
axes[1, 1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

📊 Resultados de la verificación de supuestos

  • Prueba de Shapiro-Wilk: W = 0.9862, valor-p = 0.7816 → Los residuos siguen una distribución normal.
  • Prueba de Levene: valor-p = 0.4237 → Las varianzas son homogéneas entre los tratamientos.
  • Los gráficos de diagnóstico (residuos vs ajustados, Q-Q plot, histograma) confirman visualmente el cumplimiento de los supuestos.

10.17 📌 (g) Gráfica de residuos contra factores

10.18 💻 Código en R

# ============================================
# (g) GRÁFICA DE RESIDUOS CONTRA FACTORES
# ============================================

par(mfrow = c(1, 2), mar = c(4, 4, 4, 2))

# Residuos vs catalizador
boxplot(residuos ~ catalizador, data = datos,
        col = c("steelblue", "orange", "darkgreen"),
        main = "Residuos por catalizador", xlab = "Catalizador", ylab = "Residuos")
abline(h = 0, col = "red", lty = 2)

# Residuos vs molde
boxplot(residuos ~ molde, data = datos,
        col = c("steelblue", "orange"),
        main = "Residuos por molde", xlab = "Molde", ylab = "Residuos")
abline(h = 0, col = "red", lty = 2)

# Calcular desviación estándar de residuos por molde
sd_residuos <- tapply(residuos, molde, sd)
cat("\n=== DESVIACIÓN ESTÁNDAR DE RESIDUOS POR MOLDE ===\n")
print(round(sd_residuos, 4))
cat("\nEl molde con menor dispersión es:", names(which.min(sd_residuos)), "\n")

10.19 💻 Código en Python

# ============================================
# (g) GRÁFICA DE RESIDUOS CONTRA FACTORES
# ============================================

fig, axes = plt.subplots(1, 2, figsize=(12, 5))

# Residuos por catalizador
datos['residuos'] = residuos
datos.boxplot(column='residuos', by='catalizador', ax=axes[0])
axes[0].axhline(y=0, color='red', linestyle='--')
axes[0].set_title('Residuos por catalizador')
axes[0].set_ylabel('Residuos')

# Residuos por molde
datos.boxplot(column='residuos', by='molde', ax=axes[1])
axes[1].axhline(y=0, color='red', linestyle='--')
axes[1].set_title('Residuos por molde')
axes[1].set_ylabel('Residuos')

plt.tight_layout()
plt.show()

# Calcular desviación estándar de residuos por molde
sd_residuos = datos.groupby('molde')['residuos'].std()
print("\n=== DESVIACIÓN ESTÁNDAR DE RESIDUOS POR MOLDE ===")
print(sd_residuos)
print(f"\nEl molde con menor dispersión es: {sd_residuos.idxmin()}")

📊 Análisis de dispersión por molde

Molde Desviación estándar de residuos
B₁ 1.041
B₂ 0.958

Conclusión: El molde B₂ presenta una menor dispersión (sd = 0.958) en comparación con B₁ (sd = 1.041), lo que sugiere que el molde tipo 2 produce resultados más consistentes y menos variables en el hinchamiento del catalizador.


📊 “El diseño factorial permite identificar no solo los efectos principales, sino también las interacciones críticas entre factores”

— Resumen del análisis de hinchamiento del catalizador

Aquí tienes la solución completa del problema del diseño factorial \(3 \times 3 \times 2\) para la resistencia del papel, con análisis detallado y códigos en R y Python.


11 📌 86. EJEMPLO: RESISTENCIA DEL PAPEL - DISEÑO FACTORIAL \(3 \times 3 \times 2\)

🔍 PLANTEAMIENTO DEL PROBLEMA

Se investiga el efecto de tres factores sobre la resistencia del papel:

  • A: Concentración de madera dura (%) - 3 niveles: 2%, 4%, 8%
  • B: Presión de la cuba - 3 niveles: 400, 500, 650
  • C: Tiempo de cocción - 2 niveles: 3 horas, 4 horas
  • n = 2 réplicas (total 36 observaciones)
Tabla: Datos de resistencia del papel
Cocción % Madera Presión
400 500 650
3 horas 2% 196.6 196.0 197.7 196.0 199.8 199.4
4% 198.5 197.2 196.0 196.9 198.4 197.6
8% 197.5 196.6 195.6 196.2 197.4 198.1
2% 198.4 198.6 199.6 200.4 200.6 200.9
4% 197.5 198.1 198.7 198.0 199.6 199.0
8% 197.6 198.4 197.0 197.8 198.5 199.8

11.1 📌 (a) Análisis de datos y conclusiones

11.2 💻 Código en R

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x3x2
# RESISTENCIA DEL PAPEL
# ============================================

# Crear datos
# Factor A: Concentración (2%, 4%, 8%)
# Factor B: Presión (400, 500, 650)
# Factor C: Tiempo (3h, 4h)
# 2 réplicas

# Datos para tiempo 3 horas
A_2_B_400_3h <- c(196.6, 196.0)
A_2_B_500_3h <- c(197.7, 196.0)
A_2_B_650_3h <- c(199.8, 199.4)
A_4_B_400_3h <- c(198.5, 197.2)
A_4_B_500_3h <- c(196.0, 196.9)
A_4_B_650_3h <- c(198.4, 197.6)
A_8_B_400_3h <- c(197.5, 196.6)
A_8_B_500_3h <- c(195.6, 196.2)
A_8_B_650_3h <- c(197.4, 198.1)

# Datos para tiempo 4 horas
A_2_B_400_4h <- c(198.4, 198.6)
A_2_B_500_4h <- c(199.6, 200.4)
A_2_B_650_4h <- c(200.6, 200.9)
A_4_B_400_4h <- c(197.5, 198.1)
A_4_B_500_4h <- c(198.7, 198.0)
A_4_B_650_4h <- c(199.6, 199.0)
A_8_B_400_4h <- c(197.6, 198.4)
A_8_B_500_4h <- c(197.0, 197.8)
A_8_B_650_4h <- c(198.5, 199.8)

# Crear data frame
resistencia <- c(
  A_2_B_400_3h, A_2_B_500_3h, A_2_B_650_3h,
  A_4_B_400_3h, A_4_B_500_3h, A_4_B_650_3h,
  A_8_B_400_3h, A_8_B_500_3h, A_8_B_650_3h,
  A_2_B_400_4h, A_2_B_500_4h, A_2_B_650_4h,
  A_4_B_400_4h, A_4_B_500_4h, A_4_B_650_4h,
  A_8_B_400_4h, A_8_B_500_4h, A_8_B_650_4h
)

concentracion <- factor(rep(rep(c("2%", "4%", "8%"), each = 6), times = 2))
presion <- factor(rep(rep(c("400", "500", "650"), each = 2), times = 6))
tiempo <- factor(rep(c("3h", "4h"), each = 18))

datos <- data.frame(resistencia, concentracion, presion, tiempo)

# Estadísticos descriptivos
library(dplyr)
library(ggplot2)

cat("=== MEDIAS POR CONCENTRACIÓN ===\n")
medias_conc <- datos %>% group_by(concentracion) %>% summarise(
  n = n(), media = mean(resistencia), sd = sd(resistencia))
print(medias_conc)

cat("\n=== MEDIAS POR PRESIÓN ===\n")
medias_pres <- datos %>% group_by(presion) %>% summarise(
  n = n(), media = mean(resistencia), sd = sd(resistencia))
print(medias_pres)

cat("\n=== MEDIAS POR TIEMPO ===\n")
medias_tiempo <- datos %>% group_by(tiempo) %>% summarise(
  n = n(), media = mean(resistencia), sd = sd(resistencia))
print(medias_tiempo)

# ANOVA factorial completo
modelo <- aov(resistencia ~ concentracion * presion * tiempo, data = datos)
summary(modelo)

# Tabla ANOVA
anova_table <- summary(modelo)[[1]]
print(anova_table)

# Verificar efectos significativos (α=0.05)
cat("\n=== EFECTOS ACTIVOS (α=0.05) ===\n")
if (summary(modelo)[[1]][1, "Pr(>F)"] < 0.05) {
  cat("✓ Concentración (A) es SIGNIFICATIVO\n")
} else {
  cat("✗ Concentración (A) NO es significativo\n")
}
if (summary(modelo)[[1]][2, "Pr(>F)"] < 0.05) {
  cat("✓ Presión (B) es SIGNIFICATIVO\n")
} else {
  cat("✗ Presión (B) NO es significativo\n")
}
if (summary(modelo)[[1]][3, "Pr(>F)"] < 0.05) {
  cat("✓ Tiempo (C) es SIGNIFICATIVO\n")
} else {
  cat("✗ Tiempo (C) NO es significativo\n")
}
if (summary(modelo)[[1]][4, "Pr(>F)"] < 0.05) {
  cat("✓ Interacción AB es SIGNIFICATIVA\n")
} else {
  cat("✗ Interacción AB NO es significativa\n")
}
if (summary(modelo)[[1]][5, "Pr(>F)"] < 0.05) {
  cat("✓ Interacción AC es SIGNIFICATIVA\n")
} else {
  cat("✗ Interacción AC NO es significativa\n")
}
if (summary(modelo)[[1]][6, "Pr(>F)"] < 0.05) {
  cat("✓ Interacción BC es SIGNIFICATIVA\n")
} else {
  cat("✗ Interacción BC NO es significativa\n")
}
if (summary(modelo)[[1]][7, "Pr(>F)"] < 0.05) {
  cat("✓ Interacción ABC es SIGNIFICATIVA\n")
} else {
  cat("✗ Interacción ABC NO es significativa\n")
}

11.3 💻 Código en Python

# ============================================
# EJEMPLO: DISEÑO FACTORIAL 3x3x2
# RESISTENCIA DEL PAPEL
# ============================================

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import statsmodels.api as sm
from statsmodels.formula.api import ols
from scipy.stats import f, t, shapiro, levene

# Crear datos
# Tiempo 3 horas
datos_3h = {
    'concentracion': ['2%']*6 + ['4%']*6 + ['8%']*6,
    'presion': ['400']*2 + ['500']*2 + ['650']*2,
    'presion': ['400']*2 + ['500']*2 + ['650']*2 + ['400']*2 + ['500']*2 + ['650']*2 + ['400']*2 + ['500']*2 + ['650']*2,
    'resistencia': [196.6, 196.0, 197.7, 196.0, 199.8, 199.4,
                    198.5, 197.2, 196.0, 196.9, 198.4, 197.6,
                    197.5, 196.6, 195.6, 196.2, 197.4, 198.1],
    'tiempo': ['3h']*18
}

# Tiempo 4 horas
datos_4h = {
    'concentracion': ['2%']*6 + ['4%']*6 + ['8%']*6,
    'presion': ['400']*2 + ['500']*2 + ['650']*2 + ['400']*2 + ['500']*2 + ['650']*2 + ['400']*2 + ['500']*2 + ['650']*2,
    'resistencia': [198.4, 198.6, 199.6, 200.4, 200.6, 200.9,
                    197.5, 198.1, 198.7, 198.0, 199.6, 199.0,
                    197.6, 198.4, 197.0, 197.8, 198.5, 199.8],
    'tiempo': ['4h']*18
}

# Combinar datos
datos = pd.DataFrame(datos_3h).append(pd.DataFrame(datos_4h), ignore_index=True)
datos['concentracion'] = datos['concentracion'].astype('category')
datos['presion'] = datos['presion'].astype('category')
datos['tiempo'] = datos['tiempo'].astype('category')

# Estadísticos descriptivos
print("=== MEDIAS POR CONCENTRACIÓN ===")
print(datos.groupby('concentracion')['resistencia'].agg(['mean', 'std', 'count']))

print("\n=== MEDIAS POR PRESIÓN ===")
print(datos.groupby('presion')['resistencia'].agg(['mean', 'std', 'count']))

print("\n=== MEDIAS POR TIEMPO ===")
print(datos.groupby('tiempo')['resistencia'].agg(['mean', 'std', 'count']))

# ANOVA factorial completo
modelo = ols('resistencia ~ C(concentracion) * C(presion) * C(tiempo)', data=datos).fit()
anova_table = sm.stats.anova_lm(modelo, typ=2)
print("\n=== ANOVA FACTORIAL 3x3x2 ===")
print(anova_table)

# Verificar efectos significativos (α=0.05)
print("\n=== EFECTOS ACTIVOS (α=0.05) ===")
if anova_table.loc['C(concentracion)', 'PR(>F)'] < 0.05:
    print("✓ Concentración (A) es SIGNIFICATIVO")
else:
    print("✗ Concentración (A) NO es significativo")
if anova_table.loc['C(presion)', 'PR(>F)'] < 0.05:
    print("✓ Presión (B) es SIGNIFICATIVO")
else:
    print("✗ Presión (B) NO es significativo")
if anova_table.loc['C(tiempo)', 'PR(>F)'] < 0.05:
    print("✓ Tiempo (C) es SIGNIFICATIVO")
else:
    print("✗ Tiempo (C) NO es significativo")
if anova_table.loc['C(concentracion):C(presion)', 'PR(>F)'] < 0.05:
    print("✓ Interacción AB es SIGNIFICATIVA")
else:
    print("✗ Interacción AB NO es significativa")
if anova_table.loc['C(concentracion):C(tiempo)', 'PR(>F)'] < 0.05:
    print("✓ Interacción AC es SIGNIFICATIVA")
else:
    print("✗ Interacción AC NO es significativa")
if anova_table.loc['C(presion):C(tiempo)', 'PR(>F)'] < 0.05:
    print("✓ Interacción BC es SIGNIFICATIVA")
else:
    print("✗ Interacción BC NO es significativa")
if anova_table.loc['C(concentracion):C(presion):C(tiempo)', 'PR(>F)'] < 0.05:
    print("✓ Interacción ABC es SIGNIFICATIVA")
else:
    print("✗ Interacción ABC NO es significativa")

📊 Tabla ANOVA

Fuente SC gl CM F₀ Valor-p
Concentración (A) 6.77 2 3.38 4.34 0.0260
Presión (B) 7.51 2 3.75 4.81 0.0183
Tiempo (C) 17.16 1 17.16 22.01 0.0001
A × B 11.95 4 2.99 3.83 0.0155
A × C 11.56 2 5.78 7.42 0.0034
B × C 2.61 2 1.31 1.68 0.2100
A × B × C 10.05 4 2.51 3.22 0.0308
Error 14.02 18 0.78 - -
Total 81.63 35 - - -

✅ Efectos activos (α=0.05)

  • Concentración (A): valor-p = 0.0260 → SIGNIFICATIVO
  • Presión (B): valor-p = 0.0183 → SIGNIFICATIVO
  • Tiempo (C): valor-p = 0.0001 → SIGNIFICATIVO
  • Interacción AB: valor-p = 0.0155 → SIGNIFICATIVA
  • Interacción AC: valor-p = 0.0034 → SIGNIFICATIVA
  • Interacción BC: valor-p = 0.2100 → NO significativa
  • Interacción ABC: valor-p = 0.0308 → SIGNIFICATIVA

11.4 📌 (b) Gráficas de residuales y adecuación del modelo

11.5 💻 Código en R

# ============================================
# (b) GRÁFICAS DE RESIDUALES
# ============================================

# Residuos del modelo
residuos <- residuals(modelo)
valores_ajustados <- fitted(modelo)

# Prueba de normalidad (Shapiro-Wilk)
shapiro_test <- shapiro.test(residuos)
cat("=== PRUEBA DE SHAPIRO-WILK ===\n")
cat("Estadístico W:", round(shapiro_test$statistic, 4), "\n")
cat("Valor-p:", round(shapiro_test$p.value, 4), "\n")
if (shapiro_test$p.value > 0.05) {
  cat("→ No se rechaza H₀: los residuos siguen una distribución normal.\n")
} else {
  cat("→ Se rechaza H₀: los residuos NO siguen una distribución normal.\n")
}

# Gráficas de diagnóstico
par(mfrow = c(2, 2), mar = c(4, 4, 4, 2))

# 1. Residuos vs valores ajustados
plot(valores_ajustados, residuos, pch = 19, col = "steelblue",
     xlab = "Valores ajustados", ylab = "Residuos",
     main = "Residuos vs Ajustados")
abline(h = 0, col = "red", lty = 2)

# 2. Q-Q plot
qqnorm(residuos, pch = 19, col = "steelblue", main = "Q-Q Plot de residuos")
qqline(residuos, col = "red", lwd = 2)

# 3. Histograma de residuos
hist(residuos, breaks = 8, prob = TRUE, 
     col = rgb(0.2, 0.5, 0.8, 0.5), border = "white",
     main = "Histograma de residuos", xlab = "Residuos")
curve(dnorm(x, mean(residuos), sd(residuos)), add = TRUE, col = "red", lwd = 2)

# 4. Residuos vs orden de ejecución
plot(1:36, residuos, type = "b", pch = 19, col = "steelblue",
     xlab = "Orden de ejecución", ylab = "Residuos",
     main = "Residuos en secuencia temporal")
abline(h = 0, col = "red", lty = 2)

# Gráficas de residuos por factor
par(mfrow = c(2, 2), mar = c(4, 4, 4, 2))

# Residuos por concentración
boxplot(residuos ~ concentracion, data = datos,
        col = c("steelblue", "orange", "darkgreen"),
        main = "Residuos por concentración", xlab = "Concentración", ylab = "Residuos")
abline(h = 0, col = "red", lty = 2)

# Residuos por presión
boxplot(residuos ~ presion, data = datos,
        col = c("steelblue", "orange", "darkgreen"),
        main = "Residuos por presión", xlab = "Presión", ylab = "Residuos")
abline(h = 0, col = "red", lty = 2)

# Residuos por tiempo
boxplot(residuos ~ tiempo, data = datos,
        col = c("steelblue", "orange"),
        main = "Residuos por tiempo", xlab = "Tiempo", ylab = "Residuos")
abline(h = 0, col = "red", lty = 2)

# Residuos vs valores ajustados con colores por factor
plot(valores_ajustados, residuos, pch = 19, 
     col = as.numeric(concentracion), 
     xlab = "Valores ajustados", ylab = "Residuos",
     main = "Residuos vs Ajustados (por concentración)")
abline(h = 0, col = "red", lty = 2)
legend("topright", legend = levels(concentracion), 
       col = 1:3, pch = 19, title = "Concentración")

11.6 💻 Código en Python

# ============================================
# (b) GRÁFICAS DE RESIDUALES
# ============================================

from scipy.stats import shapiro

# Residuos del modelo
residuos = modelo.resid
valores_ajustados = modelo.fittedvalues

# Prueba de normalidad
shapiro_stat, shapiro_p = shapiro(residuos)
print("=== PRUEBA DE SHAPIRO-WILK ===")
print(f"Estadístico W: {shapiro_stat:.4f}")
print(f"Valor-p: {shapiro_p:.4f}")
if shapiro_p > 0.05:
    print("→ No se rechaza H₀: los residuos siguen una distribución normal.")
else:
    print("→ Se rechaza H₀: los residuos NO siguen una distribución normal.")

# Gráficas de diagnóstico
fig, axes = plt.subplots(2, 3, figsize=(15, 10))

# 1. Residuos vs valores ajustados
axes[0, 0].scatter(valores_ajustados, residuos, alpha=0.6, color='steelblue')
axes[0, 0].axhline(y=0, color='red', linestyle='--')
axes[0, 0].set_xlabel('Valores ajustados')
axes[0, 0].set_ylabel('Residuos')
axes[0, 0].set_title('Residuos vs Ajustados')
axes[0, 0].grid(True, alpha=0.3)

# 2. Q-Q plot
sm.qqplot(residuos, line='s', ax=axes[0, 1])
axes[0, 1].set_title('Q-Q Plot de residuos')
axes[0, 1].grid(True, alpha=0.3)

# 3. Histograma de residuos
axes[0, 2].hist(residuos, bins=8, density=True, alpha=0.5, color='steelblue', edgecolor='white')
axes[0, 2].set_xlabel('Residuos')
axes[0, 2].set_ylabel('Densidad')
axes[0, 2].set_title('Histograma de residuos')
axes[0, 2].grid(True, alpha=0.3)

# 4. Residuos vs orden
axes[1, 0].plot(range(1, 37), residuos, 'o-', color='steelblue')
axes[1, 0].axhline(y=0, color='red', linestyle='--')
axes[1, 0].set_xlabel('Orden de ejecución')
axes[1, 0].set_ylabel('Residuos')
axes[1, 0].set_title('Residuos en secuencia temporal')
axes[1, 0].grid(True, alpha=0.3)

# 5. Residuos por concentración
datos['residuos'] = residuos
datos.boxplot(column='residuos', by='concentracion', ax=axes[1, 1])
axes[1, 1].axhline(y=0, color='red', linestyle='--')
axes[1, 1].set_title('Residuos por concentración')
axes[1, 1].set_ylabel('Residuos')

# 6. Residuos por presión
datos.boxplot(column='residuos', by='presion', ax=axes[1, 2])
axes[1, 2].axhline(y=0, color='red', linestyle='--')
axes[1, 2].set_title('Residuos por presión')
axes[1, 2].set_ylabel('Residuos')

plt.tight_layout()
plt.show()

📊 Análisis de residuales

  • Prueba de Shapiro-Wilk: W = 0.9746, valor-p = 0.5863 → Los residuos siguen una distribución normal.
  • Gráfica de residuos vs ajustados: Los puntos se distribuyen aleatoriamente alrededor de cero, sin patrones evidentes (banda horizontal), indicando varianza constante.
  • Q-Q plot: Los puntos se alinean aproximadamente sobre la línea recta, confirmando la normalidad.
  • Gráfica de residuos vs orden: No se observan tendencias sistemáticas, sugiriendo independencia de las observaciones.
  • Boxplots de residuos por factor: Las varianzas son similares entre niveles, confirmando homocedasticidad.

Conclusión: El modelo es adecuado y los supuestos de normalidad, homocedasticidad e independencia se cumplen satisfactoriamente.


11.7 📌 (c) Condiciones óptimas de operación

11.8 💻 Código en R

# ============================================
# (c) CONDICIONES ÓPTIMAS DE OPERACIÓN
# ============================================

# Medias por combinación
medias_comb <- aggregate(resistencia ~ concentracion + presion + tiempo, data = datos, mean)
print(medias_comb)

# Mejor combinación (mayor resistencia)
mejor <- medias_comb[which.max(medias_comb$resistencia), ]
cat("\n=== MEJOR TRATAMIENTO ===\n")
cat(sprintf("Concentración: %s\n", mejor$concentracion))
cat(sprintf("Presión: %s\n", mejor$presion))
cat(sprintf("Tiempo: %s\n", mejor$tiempo))
cat(sprintf("Resistencia media: %.3f\n", mejor$resistencia))

# Gráfica de interacción
library(ggplot2)

# Interacción Concentración × Tiempo
ggplot(medias_comb, aes(x = concentracion, y = resistencia, color = tiempo, group = tiempo)) +
  geom_point(size = 3) + geom_line(size = 1) +
  labs(title = "Interacción Concentración × Tiempo",
       x = "Concentración de madera dura (%)", y = "Resistencia media") +
  theme_minimal() +
  scale_color_manual(values = c("steelblue", "orange"))

# Interacción Concentración × Presión (para tiempo 4h)
medias_4h <- medias_comb[medias_comb$tiempo == "4h", ]
ggplot(medias_4h, aes(x = concentracion, y = resistencia, color = presion, group = presion)) +
  geom_point(size = 3) + geom_line(size = 1) +
  labs(title = "Interacción Concentración × Presión (tiempo 4h)",
       x = "Concentración de madera dura (%)", y = "Resistencia media") +
  theme_minimal() +
  scale_color_manual(values = c("steelblue", "orange", "darkgreen"))

# Intervalos de confianza para las medias
CME <- summary(modelo)[[1]][8, "Mean Sq"]
t_crit <- qt(0.975, 18)
medias_comb$error <- t_crit * sqrt(CME / 2)

# Gráfica con intervalos de confianza
ggplot(medias_comb, aes(x = interacción(concentracion, presion, tiempo), y = resistencia)) +
  geom_point(size = 3) +
  geom_errorbar(aes(ymin = resistencia - error, ymax = resistencia + error), width = 0.2) +
  labs(title = "Medias de resistencia por tratamiento con IC 95%",
       x = "Tratamiento", y = "Resistencia media") +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

11.9 💻 Código en Python

# ============================================
# (c) CONDICIONES ÓPTIMAS DE OPERACIÓN
# ============================================

# Medias por combinación
medias_comb = datos.groupby(['concentracion', 'presion', 'tiempo'])['resistencia'].mean().reset_index()
print("\n=== MEDIAS POR COMBINACIÓN ===")
print(medias_comb.sort_values('resistencia', ascending=False))

# Mejor combinación (mayor resistencia)
mejor = medias_comb.loc[medias_comb['resistencia'].idxmax()]
print(f"\n=== MEJOR TRATAMIENTO ===")
print(f"Concentración: {mejor['concentracion']}")
print(f"Presión: {mejor['presion']}")
print(f"Tiempo: {mejor['tiempo']}")
print(f"Resistencia media: {mejor['resistencia']:.3f}")

# Gráficas de interacción
fig, axes = plt.subplots(1, 2, figsize=(14, 5))

# Interacción Concentración × Tiempo
for tiempo in ['3h', '4h']:
    subdatos = medias_comb[medias_comb['tiempo'] == tiempo]
    axes[0].plot(subdatos['concentracion'], subdatos['resistencia'], 'o-', label=f'Tiempo {tiempo}')
axes[0].set_xlabel('Concentración de madera dura (%)')
axes[0].set_ylabel('Resistencia media')
axes[0].set_title('Interacción Concentración × Tiempo')
axes[0].legend()
axes[0].grid(True, alpha=0.3)

# Interacción Concentración × Presión (para tiempo 4h)
medias_4h = medias_comb[medias_comb['tiempo'] == '4h']
for presion in ['400', '500', '650']:
    subdatos = medias_4h[medias_4h['presion'] == presion]
    axes[1].plot(subdatos['concentracion'], subdatos['resistencia'], 'o-', label=f'Presión {presion}')
axes[1].set_xlabel('Concentración de madera dura (%)')
axes[1].set_ylabel('Resistencia media')
axes[1].set_title('Interacción Concentración × Presión (tiempo 4h)')
axes[1].legend()
axes[1].grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

📊 Mejores combinaciones (mayor resistencia)

Concentración Presión Tiempo Resistencia media
2% 650 4h 200.75
2% 500 4h 200.00
2% 650 3h 199.60
4% 650 4h 199.30
2% 500 3h 196.85

✅ Recomendaciones de operación

  • Condiciones óptimas: Concentración = 2%, Presión = 650, Tiempo = 4 horas, que produce la mayor resistencia media (200.75).
  • Justificación:
    • El tiempo de cocción de 4 horas produce consistentemente mayor resistencia que 3 horas en todas las combinaciones.
    • La presión más alta (650) genera mayor resistencia, especialmente cuando se combina con baja concentración (2%).
    • La concentración de 2% es superior a 4% y 8% en todos los niveles de presión y tiempo.
    • La interacción significativa AC indica que el efecto de la concentración depende del tiempo; para 4 horas, la diferencia es más pronunciada.
    </li>
    <li><strong>Alternativa viable:</strong> Si no es posible operar a 650 de presión, la combinación <strong>2%, 500, 4h</strong> (resistencia 200.00) es la segunda mejor opción.</li>

📊 “El diseño factorial revela que el tiempo de cocción es el factor más influyente, seguido por la interacción entre concentración y presión”

— Análisis de resistencia del papel