Dar el salto a la estadística no paramétrica es vital para tus estudiantes. En el mundo real de la Ingeniería Agrícola y Agroindustrial, los datos no siempre son “perfectos”. A veces hay valores atípicos extremos (como una planta que creció el doble que las demás por una anomalía) o los datos están muy sesgados (como conteos de insectos o bacterias), lo que destruye los supuestos de normalidad y homogeneidad de varianzas del ANOVA tradicional.
Aquí tienes el desarrollo para enseñarles su primera herramienta de “rescate” estadístico.
La prueba de Kruskal-Wallis es el equivalente no paramétrico del Diseño Completamente al Azar (DCA). Cuando los datos no cumplen con los supuestos matemáticos para un ANOVA (normalidad de los residuos y varianzas iguales entre grupos), no podemos confiar en las medias para tomar decisiones.
En lugar de trabajar con los valores reales medidos (ej. kilogramos, centímetros), Kruskal-Wallis ordena todos los datos de menor a mayor y les asigna un rango (una posición). Luego, compara si la suma de esos rangos difiere significativamente entre los tratamientos. Al hacer esto, la prueba evalúa si las medianas (o las distribuciones en general) son diferentes, volviéndose altamente resistente a los valores atípicos.
El Estadístico de Prueba (\(H\)): La fórmula básica para calcular el estadístico de Kruskal-Wallis es:
\[H = \frac{12}{N(N+1)} \sum_{i=1}^{t} \frac{R_i^2}{n_i} - 3(N+1)\]
Donde: * \(N\): Es el número total de observaciones en todo el experimento. * \(t\): Es el número de tratamientos. * \(R_i\): Es la suma de los rangos para el \(i\)-ésimo tratamiento. * \(n_i\): Es el número de repeticiones (observaciones) en el \(i\)-ésimo tratamiento.
(Nota: Si el valor de \(H\) calculado es mayor que el valor crítico de la tabla de distribución Chi-cuadrado (\(\chi^2\)) con \(t-1\) grados de libertad, se rechaza la hipótesis nula y se concluye que al menos un tratamiento es diferente).
Para que los alumnos comprendan el concepto de “rango”, pídeles que resuelvan un ejercicio pequeño (ej. 3 tratamientos con 4 repeticiones cada uno) siguiendo estos pasos:
Aquí el objetivo del EDA no es solo ver diferencias, sino diagnosticar por qué no usamos el ANOVA clásico. Usaremos una estructura de datos de dos columnas (Tratamiento y Respuesta).
library(easyanova)
# Para este ejemplo, simularemos un dataset donde claramente fallan los supuestos
# (Ej: Datos muy sesgados o con valores atípicos fuertes en agroindustria)
set.seed(123)
datos_kw <- data.frame(
Tratamiento = rep(c("T1", "T2", "T3"), each = 10),
Respuesta = c(rexp(10, rate=0.5), rexp(10, rate=0.5), rexp(10, rate=0.1)) # Distribución exponencial (no normal)
)
# Añadimos un valor atípico extremo simulando un error de medición
datos_kw$Respuesta[1] <- 45.0
# EDA 1: Boxplot para evidenciar asimetría y valores atípicos
boxplot(Respuesta ~ Tratamiento, data = datos_kw,
col = c("lightgreen", "lightblue", "salmon"),
main = "Respuesta por Tratamiento (Datos No Normales)",
ylab = "Respuesta (con asimetría y atípicos)")
# EDA 2: Prueba de Normalidad de Shapiro-Wilk para toda la respuesta
# Esto justifica cuantitativamente el uso de Kruskal-Wallis
shapiro.test(datos_kw$Respuesta)
Propósito pedagógico: Pregúntales a los estudiantes qué observan en el boxplot. El valor atípico (el punto solitario muy arriba) y las cajas aplastadas hacia abajo indican asimetría. Si el p-value de la prueba de Shapiro-Wilk es menor a 0.05, los datos no son normales, y Kruskal-Wallis entra a salvar el análisis.
easyanovaEjecutar esta alternativa en el paquete es tan sencillo como cambiar
el diseño a 14. El paquete internamente hace la
transformación a rangos y arroja el resultado.
# Ejecutar Kruskal-Wallis (Alternativa no paramétrica para DCA) -> design = 14
# Orden requerido: Col 1 = Tratamiento, Col 2 = Respuesta
resultado_kw <- ea1(datos_kw, design = 14)
# Imprimir los resultados. easyanova mostrará la prueba basada en rangos.
print(resultado_kw)
Motiva la interacción analítica con estos prompts:
ea1 con el diseño
14 para unos datos de germinación de semillas. ¿Qué me dice el valor p
de la prueba de Kruskal-Wallis sobre las medianas de mis
tratamientos?”En Python (Usando scipy.stats y
seaborn): Python tiene librerías muy eficientes
para estadística no paramétrica. scipy.stats realiza la
prueba central y podemos usar scikit-posthocs si se desea
hacer la separación de grupos.
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from scipy import stats
# Dataset simulado (Tratamiento y Respuesta)
data = {
'Tratamiento': ['T1']*5 + ['T2']*5 + ['T3']*5,
'Respuesta': [2, 3, 2.5, 3.1, 40, # T1 tiene un atípico fuerte (40)
5, 6, 5.5, 6.2, 5.8,
10, 12, 11, 10.5, 11.2]
}
df = pd.DataFrame(data)
# EDA: Gráfico de enjambre (Swarmplot) para ver cada dato individual
sns.swarmplot(x='Tratamiento', y='Respuesta', data=df, size=8)
plt.title('Dispersión de Datos (Notar el valor atípico en T1)')
plt.show()
# Separar los datos por grupos para la función de scipy
grupo1 = df[df['Tratamiento'] == 'T1']['Respuesta']
grupo2 = df[df['Tratamiento'] == 'T2']['Respuesta']
grupo3 = df[df['Tratamiento'] == 'T3']['Respuesta']
# Ejecutar Kruskal-Wallis
estadistico_H, p_valor = stats.kruskal(grupo1, grupo2, grupo3)
print("\n--- Resultados Kruskal-Wallis ---")
print(f"Estadístico H: {estadistico_H:.4f}")
print(f"Valor p: {p_valor:.4f}")
En Julia (Usando DataFrames y
HypothesisTests):
using DataFrames, HypothesisTests
# DataFrame simulado
df = DataFrame(
Tratamiento = repeat(["T1", "T2", "T3"], inner=5),
Respuesta = [2.0, 3.0, 2.5, 3.1, 40.0,
5.0, 6.0, 5.5, 6.2, 5.8,
10.0, 12.0, 11.0, 10.5, 11.2]
)
# Para usar Kruskal-Wallis en Julia, se pasan los arreglos de cada grupo
t1 = df[df.Tratamiento .== "T1", :Respuesta]
t2 = df[df.Tratamiento .== "T2", :Respuesta]
t3 = df[df.Tratamiento .== "T3", :Respuesta]
# Ejecutar la prueba
prueba_kw = KruskalWallisTest(t1, t2, t3)
println("--- Prueba de Kruskal-Wallis en Julia ---")
println(prueba_kw)
Con esto, los estudiantes ya tienen cómo defenderse de datos
problemáticos en un escenario completamente al azar. ¿Te gustaría que
pasemos a la 15. Prueba de suma de rangos de Friedman
(Alternativa no paramétrica para DBCA) para cerrar el ciclo de
la función ea1?