Módulo III. Análisis de relación entre variables microbiológicas

Objetivo del módulo

Desarrollar herramientas estadísticas para analizar asociaciones, diferencias y relaciones entre variables cualitativas y cuantitativas en contextos microbiológicos y de control de calidad, manteniendo el punto de muestreo como eje central del análisis.

A partir de la misma base real empleada en el módulo anterior se estudiará:

  • la asociación entre el punto de muestreo y la ocurrencia de resultados estadísticamente elevados;
  • las diferencias de los resultados microbiológicos entre puntos;
  • la relación temporal de los resultados dentro de cada punto;
  • la formulación de hipótesis estadísticas;
  • la selección entre pruebas paramétricas y no paramétricas;
  • la evaluación de supuestos;
  • los intervalos de confianza;
  • los tamaños del efecto;
  • la correlación;
  • la regresión lineal;
  • los intervalos de predicción;
  • y la interpretación estadística y microbiológica de los resultados.

1. ¿Qué significa analizar la relación entre variables?

En los análisis descriptivos estudiamos cada variable por separado.

Ahora avanzaremos hacia preguntas que involucran dos variables simultáneamente.

Por ejemplo:

¿La frecuencia de resultados microbiológicos elevados cambia según el punto de muestreo?

¿Los resultados microbiológicos presentan el mismo comportamiento en todos los puntos?

¿Los resultados de un determinado punto aumentan o disminuyen a través del tiempo?

La técnica estadística apropiada dependerá principalmente de la naturaleza de las variables involucradas.


2. Orden del análisis

En este módulo trabajaremos en el siguiente orden:

\[ \boxed{ \text{Cualitativa–Cualitativa} } \]

\[ \Downarrow \]

\[ \boxed{ \text{Cuantitativa–Cualitativa} } \]

\[ \Downarrow \]

\[ \boxed{ \text{Cuantitativa–Cuantitativa} } \]

Esta secuencia permitirá avanzar desde asociaciones entre categorías hasta modelos de correlación, regresión e inferencia.


2.1 Esquema general según la naturaleza de las variables

Tipo de relación Ejemplo en la base Representación Pruebas principales Magnitud
Cuali–Cuali Punto × Alerta Barras / contingencia Chi-cuadrado / Fisher OR, V de Cramér
Cuanti–Cuali NMP × Punto Boxplot Welch / ANOVA / Kruskal-Wallis \(d\), \(\eta^2\)
Cuanti–Cuanti Tiempo × NMP dentro del punto Dispersión Pearson / Spearman / regresión \(r\), \(\rho_s\), \(b_1\), \(R^2\)

2.2 Ruta general para seleccionar el análisis

\[ \boxed{\text{Pregunta microbiológica}} \]

\[ \Downarrow \]

\[ \boxed{\text{Identificar las variables}} \]

\[ \Downarrow \]

\[ \boxed{\text{Clasificar su naturaleza}} \]

\[ \Downarrow \]

\[ \boxed{\text{Representar gráficamente}} \]

\[ \Downarrow \]

\[ \boxed{\text{Evaluar supuestos}} \]

\[ \Downarrow \]

\[ \boxed{\text{Seleccionar la prueba}} \]

\[ \Downarrow \]

\[ \boxed{ p+\text{IC}+\text{magnitud del efecto} } \]

\[ \Downarrow \]

\[ \boxed{\text{Interpretación estadística}} \]

\[ \Downarrow \]

\[ \boxed{\text{Interpretación microbiológica}} \]


3. Preparación de la base real

Trabajaremos nuevamente con:

data/DATOS I- SEM 2026.xlsx

La base contiene resultados de coliformes termotolerantes expresados en NMP/100 mL, correspondientes a diferentes puntos y fechas de muestreo.

library(readxl)

datos <- read_excel(
  "DATOS I- SEM 2026.xlsx"
)

names(datos) <- c(
  "analisis",
  "punto",
  "resultado_nmp",
  "matriz",
  "fecha"
)

# Eliminar registros sin identificación completa del punto
datos <- datos[
  !is.na(datos$punto) &
  trimws(datos$punto) != "PUNTO_",
]

# Convertir resultados microbiológicos a valores numéricos
datos$resultado_nmp <- as.numeric(
  gsub(
    ",",
    ".",
    as.character(datos$resultado_nmp),
    fixed = TRUE
  )
)

# Asegurar formato de fecha
datos$fecha <- as.Date(
  datos$fecha
)

# Crear transformación log10 únicamente para valores positivos
datos$log10_nmp <- ifelse(
  !is.na(datos$resultado_nmp) &
  datos$resultado_nmp > 0,
  log10(datos$resultado_nmp),
  NA_real_
)

# Variables temporales
datos$mes_num <- as.integer(
  format(
    datos$fecha,
    "%m"
  )
)

datos$mes <- factor(
  datos$mes_num,
  levels = 1:12,
  labels = c(
    "Enero", "Febrero", "Marzo", "Abril",
    "Mayo", "Junio", "Julio", "Agosto",
    "Septiembre", "Octubre", "Noviembre", "Diciembre"
  )
)

# Días transcurridos desde la primera fecha
fecha_inicio <- min(
  datos$fecha,
  na.rm = TRUE
)

datos$dia_estudio <- as.numeric(
  datos$fecha - fecha_inicio
)

# Percentil 90 global
P90_global <- as.numeric(
  quantile(
    datos$resultado_nmp,
    probs = 0.90,
    na.rm = TRUE
  )
)

# Clasificación global
datos$evento_alerta <- ifelse(
  datos$resultado_nmp > P90_global,
  "Alerta",
  "Sin alerta"
)

datos$evento_alerta <- factor(
  datos$evento_alerta,
  levels = c(
    "Alerta",
    "Sin alerta"
  )
)

# Percentil 90 específico por punto
datos$P90_punto <- ave(
  datos$resultado_nmp,
  datos$punto,
  FUN = function(x) {
    
    rep(
      as.numeric(
        quantile(
          x,
          probs = 0.90,
          na.rm = TRUE
        )
      ),
      length(x)
    )
  }
)

# Clasificación local
datos$evento_alerta_punto <- ifelse(
  datos$resultado_nmp > datos$P90_punto,
  "Alerta",
  "Sin alerta"
)

datos$evento_alerta_punto <- factor(
  datos$evento_alerta_punto,
  levels = c(
    "Alerta",
    "Sin alerta"
  )
)

# Lista de puntos disponible para todo el módulo
puntos_mod3 <- sort(
  unique(
    datos$punto
  )
)

4. El punto de muestreo como eje del análisis

El objetivo del módulo no será producir únicamente medidas globales.

Queremos identificar:

\[ \boxed{ \text{cómo se comporta cada punto} } \]

y posteriormente comparar esos comportamientos.

Los puntos pueden diferir en:

  • número de observaciones;
  • frecuencia de alertas;
  • nivel microbiológico;
  • dispersión;
  • estabilidad;
  • resultados extremos;
  • tendencia temporal.

4.1 Número de observaciones por punto

tabla_n_punto <- as.data.frame(
  table(
    datos$punto
  )
)

names(
  tabla_n_punto
) <- c(
  "Punto",
  "N"
)

tabla_n_punto <- tabla_n_punto[
  order(
    tabla_n_punto$N,
    decreasing = TRUE
  ),
]

knitr::kable(
  tabla_n_punto,
  caption = "Número de observaciones por punto de muestreo"
)
Número de observaciones por punto de muestreo
Punto N
5 PUNTO_19 22
8 PUNTO_23 22
17 PUNTO_9 22
1 PUNTO_11 21
2 PUNTO_16 21
3 PUNTO_17 21
4 PUNTO_18 21
6 PUNTO_20 21
7 PUNTO_21 21
15 PUNTO_7 21
14 PUNTO_6 20
16 PUNTO_8 20
13 PUNTO_5 19
10 PUNTO_25 11
11 PUNTO_26 11
9 PUNTO_24 7
12 PUNTO_4 6

4.2 Interpretación estadística

El tamaño muestral:

\[ n_j \]

no es necesariamente igual en todos los puntos.

Un mayor \(n_j\) proporciona generalmente mayor información para estimar:

  • proporciones;
  • medias;
  • dispersiones;
  • percentiles;
  • correlaciones;
  • tendencias.

4.3 Interpretación microbiológica

Un punto con más observaciones no tiene necesariamente mayor concentración microbiológica.

Significa únicamente que existe una mayor cantidad de información disponible para ese punto.


5. Relación cualitativa–cualitativa

Una relación cualitativa–cualitativa ocurre cuando las dos variables representan categorías.

En nuestra base estudiaremos:

\[ X= \text{Punto de muestreo} \]

y:

\[ Y= \text{Alerta / Sin alerta} \]

La pregunta será:

¿La frecuencia de resultados clasificados como alerta está asociada con el punto de muestreo?


5.1 Naturaleza de las variables

El punto de muestreo es una variable:

\[ \boxed{\text{cualitativa nominal}} \]

porque sus categorías no poseen un orden jerárquico.

La clasificación:

\[ \text{Alerta / Sin alerta} \]

es una variable:

\[ \boxed{\text{cualitativa nominal dicotómica}} \]


5.2 Criterio estadístico de alerta

Utilizamos el percentil 90:

\[ P_{90} = Q_{0.90} \]

Se clasifica:

\[ \text{Alerta} \quad \text{si} \quad X_i>P_{90} \]

y:

\[ \text{Sin alerta} \quad \text{si} \quad X_i\leq P_{90} \]

P90_global
## [1] 1897200

5.3 Interpretación microbiológica del P90

El P90 permite identificar resultados situados en la zona superior de la distribución observada.

Debe entenderse como:

\[ \boxed{\text{criterio estadístico de priorización}} \]

y no como:

\[ \boxed{\text{límite normativo}} \]

Superar el P90 no representa automáticamente una no conformidad microbiológica.


5.4 Ejemplo didáctico con dos puntos reales

Para explicar una tabla \(2\times2\), Chi-cuadrado, Fisher y odds ratio, debemos seleccionar dos puntos que permitan observar las dos categorías.

resumen_seleccion_cc <- do.call(
  rbind,
  lapply(
    split(
      datos,
      datos$punto
    ),
    function(df) {
      
      data.frame(
        N = nrow(df),
        Alertas = sum(
          df$evento_alerta == "Alerta",
          na.rm = TRUE
        ),
        Sin_alerta = sum(
          df$evento_alerta == "Sin alerta",
          na.rm = TRUE
        )
      )
    }
  )
)

resumen_seleccion_cc$Punto <- rownames(
  resumen_seleccion_cc
)

rownames(
  resumen_seleccion_cc
) <- NULL

# Caso ideal: dos puntos que tengan ambas categorías
puntos_completos_cc <- resumen_seleccion_cc$Punto[
  resumen_seleccion_cc$N >= 5 &
  resumen_seleccion_cc$Alertas >= 1 &
  resumen_seleccion_cc$Sin_alerta >= 1
]

if (
  length(
    puntos_completos_cc
  ) >= 2
) {
  
  puntos_cc <- puntos_completos_cc[
    1:2
  ]
  
} else {
  
  # Si no hay dos puntos completos, buscar una combinación
  # que genere al menos una tabla 2x2 válida globalmente
  puntos_con_alerta <- resumen_seleccion_cc$Punto[
    resumen_seleccion_cc$N >= 5 &
    resumen_seleccion_cc$Alertas >= 1
  ]
  
  puntos_con_sin_alerta <- resumen_seleccion_cc$Punto[
    resumen_seleccion_cc$N >= 5 &
    resumen_seleccion_cc$Sin_alerta >= 1
  ]
  
  encontrado_cc <- FALSE
  puntos_cc <- NULL
  
  for (
    p1 in puntos_con_alerta
  ) {
    
    candidatos <- setdiff(
      puntos_con_sin_alerta,
      p1
    )
    
    if (
      length(
        candidatos
      ) > 0
    ) {
      
      for (
        p2 in candidatos
      ) {
        
        temp_cc <- datos[
          datos$punto %in% c(
            p1,
            p2
          ),
        ]
        
        temp_tab <- table(
          temp_cc$punto,
          temp_cc$evento_alerta
        )
        
        temp_tab <- temp_tab[
          rowSums(
            temp_tab
          ) > 0,
          colSums(
            temp_tab
          ) > 0,
          drop = FALSE
        ]
        
        if (
          nrow(
            temp_tab
          ) == 2 &&
          ncol(
            temp_tab
          ) == 2
        ) {
          
          puntos_cc <- c(
            p1,
            p2
          )
          
          encontrado_cc <- TRUE
          
          break
        }
      }
    }
    
    if (
      encontrado_cc
    ) {
      break
    }
  }
}

if (
  !is.null(
    puntos_cc
  )
) {
  
  ejemplo_cc <- datos[
    datos$punto %in% puntos_cc,
    c(
      "punto",
      "fecha",
      "resultado_nmp",
      "evento_alerta"
    )
  ]
  
  ejemplo_cc$punto <- factor(
    ejemplo_cc$punto,
    levels = puntos_cc
  )
  
  ejemplo_cc$evento_alerta <- factor(
    ejemplo_cc$evento_alerta,
    levels = c(
      "Alerta",
      "Sin alerta"
    )
  )
  
  knitr::kable(
    head(
      ejemplo_cc,
      12
    ),
    caption = paste(
      "Ejemplo con los puntos",
      paste(
        puntos_cc,
        collapse = " y "
      )
    )
  )
  
} else {
  
  ejemplo_cc <- NULL
  
  cat(
    "La distribución actual de alertas no permite construir un ejemplo 2x2 con dos puntos. El análisis completo con todos los puntos continúa siendo válido."
  )
}
Ejemplo con los puntos PUNTO_19 y PUNTO_5
punto fecha resultado_nmp evento_alerta
PUNTO_5 2026-05-06 1.100e+07 Alerta
PUNTO_19 2026-05-06 2.400e+06 Alerta
PUNTO_5 2026-01-29 4.352e+07 Alerta
PUNTO_19 2026-01-29 1.439e+05 Sin alerta
PUNTO_5 2026-02-10 1.320e+06 Sin alerta
PUNTO_19 2026-02-10 8.400e+03 Sin alerta
PUNTO_19 2026-02-24 1.350e+04 Sin alerta
PUNTO_19 2026-03-03 1.019e+02 Sin alerta
PUNTO_19 2026-03-03 1.019e+05 Sin alerta
PUNTO_5 2026-03-24 2.187e+02 Sin alerta
PUNTO_5 2026-03-24 2.187e+07 Alerta
PUNTO_19 2026-03-24 3.873e+05 Sin alerta

5.5 Tabla de contingencia

Una tabla de contingencia presenta las frecuencias conjuntas de dos variables cualitativas.

Su estructura general es:

Punto Alerta Sin alerta Total
Punto 1 \(n_{11}\) \(n_{12}\) \(n_{1+}\)
Punto 2 \(n_{21}\) \(n_{22}\) \(n_{2+}\)
Total \(n_{+1}\) \(n_{+2}\) \(N\)

donde:

  • \(n_{ij}\): frecuencia observada en una celda;
  • \(n_{i+}\): total de la fila;
  • \(n_{+j}\): total de la columna;
  • \(N\): total general.
if (
  !is.null(
    ejemplo_cc
  )
) {
  
  tabla_2x2 <- table(
    Punto = ejemplo_cc$punto,
    Clasificacion = ejemplo_cc$evento_alerta
  )
  
  tabla_2x2 <- tabla_2x2[
    rowSums(
      tabla_2x2
    ) > 0,
    colSums(
      tabla_2x2
    ) > 0,
    drop = FALSE
  ]
  
  tabla_2x2
  
} else {
  
  tabla_2x2 <- NULL
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_19      1         21
##   PUNTO_5       9         10

5.6 Proporciones por punto

Para cada punto:

\[ \hat p_{ij} = \frac{ n_{ij} }{ n_{i+} } \]

donde:

  • \(n_{ij}\): frecuencia de la categoría \(j\) en el punto \(i\);
  • \(n_{i+}\): total de observaciones del punto \(i\).
if (
  !is.null(
    tabla_2x2
  )
) {
  
  prop_2x2 <- prop.table(
    tabla_2x2,
    margin = 1
  )
  
  round(
    prop_2x2,
    4
  )
  
} else {
  
  prop_2x2 <- NULL
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_19 0.0455     0.9545
##   PUNTO_5  0.4737     0.5263

5.7 Porcentajes por punto

if (
  !is.null(
    prop_2x2
  )
) {
  
  round(
    prop_2x2 * 100,
    1
  )
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_19    4.5       95.5
##   PUNTO_5    47.4       52.6

5.8 Interpretación estadística de las proporciones

Las frecuencias absolutas pueden resultar engañosas cuando los tamaños de muestra son diferentes.

Por ello es preferible comparar:

\[ \frac{ \text{Número de alertas} }{ \text{Número total del punto} } \]

Dos puntos podrían tener cinco alertas cada uno, pero si uno tiene 10 observaciones y el otro 100, sus porcentajes serían:

\[ 50\% \]

y:

\[ 5\% \]

respectivamente.


5.9 Interpretación microbiológica de las proporciones

Una mayor proporción de alertas significa que el punto presenta con mayor frecuencia resultados situados por encima del P90 global.

Esto señala una mayor frecuencia relativa de resultados elevados dentro de la distribución analizada.

No constituye, por sí mismo, una conclusión normativa.


5.10 Gráfico de barras porcentuales

if (
  !is.null(
    tabla_2x2
  ) &&
  nrow(
    tabla_2x2
  ) >= 2 &&
  ncol(
    tabla_2x2
  ) >= 2
) {
  
  porcentaje_2x2 <- prop.table(
    tabla_2x2,
    margin = 1
  ) * 100
  
  barplot(
    t(
      porcentaje_2x2
    ),
    beside = FALSE,
    col = c(
      "#1596a5",
      "#dcecef"
    ),
    border = NA,
    ylab = "Porcentaje dentro del punto",
    xlab = "Punto de muestreo",
    main = "Clasificación de alerta entre dos puntos"
  )
  
  legend(
    "topright",
    legend = colnames(
      porcentaje_2x2
    ),
    fill = c(
      "#1596a5",
      "#dcecef"
    ),
    border = NA,
    bty = "n"
  )
}


5.11 Hipótesis de independencia

Las hipótesis son:

\[ H_0: \text{Punto y alerta son independientes} \]

frente a:

\[ H_1: \text{Punto y alerta están asociados} \]


5.12 ¿Qué significa independencia?

En términos probabilísticos:

\[ P(A\cap B) = P(A)P(B) \]

donde:

  • \(A\): pertenecer a un punto;
  • \(B\): pertenecer a una categoría de alerta.

Si existe independencia, conocer el punto no modifica la distribución esperada de Alerta y Sin alerta.


5.13 Frecuencias esperadas

Bajo independencia:

\[ E_{ij} = \frac{ n_{i+}n_{+j} }{ N } \]

donde:

  • \(E_{ij}\): frecuencia esperada;
  • \(n_{i+}\): total de fila;
  • \(n_{+j}\): total de columna;
  • \(N\): total general.
if (
  !is.null(
    tabla_2x2
  ) &&
  nrow(
    tabla_2x2
  ) >= 2 &&
  ncol(
    tabla_2x2
  ) >= 2
) {
  
  chi_preliminar_cc <- suppressWarnings(
    chisq.test(
      tabla_2x2,
      correct = FALSE
    )
  )
  
  chi_preliminar_cc$expected
  
} else {
  
  chi_preliminar_cc <- NULL
}
##           Clasificacion
## Punto        Alerta Sin alerta
##   PUNTO_19 5.365854   16.63415
##   PUNTO_5  4.634146   14.36585

5.14 Estadístico Chi-cuadrado

La prueba compara frecuencias observadas:

\[ O_{ij} \]

con las frecuencias esperadas:

\[ E_{ij} \]

mediante:

\[ \chi^2 = \sum_{i=1}^{r} \sum_{j=1}^{c} \frac{ (O_{ij}-E_{ij})^2 }{ E_{ij} } \]

donde:

  • \(\chi^2\): estadístico de prueba;
  • \(O_{ij}\): frecuencia observada;
  • \(E_{ij}\): frecuencia esperada;
  • \(r\): número de filas;
  • \(c\): número de columnas.

5.15 Cálculo manual de las contribuciones

Cada celda aporta:

\[ \frac{ (O_{ij}-E_{ij})^2 }{ E_{ij} } \]

if (
  !is.null(
    chi_preliminar_cc
  )
) {
  
  contribuciones_chi <- (
    tabla_2x2 -
    chi_preliminar_cc$expected
  )^2 /
  chi_preliminar_cc$expected
  
  contribuciones_chi
  
  chi_manual <- sum(
    contribuciones_chi
  )
  
  chi_manual
}
## [1] 10.13799

5.16 Grados de libertad

Los grados de libertad son:

\[ gl= (r-1)(c-1) \]

Para una tabla \(2\times2\):

\[ gl= (2-1)(2-1) = 1 \]


5.17 Prueba Chi-cuadrado

if (
  !is.null(
    tabla_2x2
  ) &&
  nrow(
    tabla_2x2
  ) >= 2 &&
  ncol(
    tabla_2x2
  ) >= 2
) {
  
  prueba_chi_cc <- suppressWarnings(
    chisq.test(
      tabla_2x2,
      correct = FALSE
    )
  )
  
  prueba_chi_cc
  
} else {
  
  prueba_chi_cc <- NULL
  
  cat(
    "No existe una tabla 2x2 válida para realizar Chi-cuadrado en el ejemplo."
  )
}
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_2x2
## X-squared = 10.138, df = 1, p-value = 0.001452

5.18 Interpretación estadística del Chi-cuadrado

Si:

\[ p<0.05 \]

existe evidencia contra la independencia.

Por tanto, existe evidencia de asociación entre punto y alerta.

Si:

\[ p\geq0.05 \]

no existe evidencia suficiente para afirmar que ambas variables estén asociadas.


5.19 Interpretación microbiológica del Chi-cuadrado

Una asociación estadística indicaría que la frecuencia relativa de resultados elevados no es homogénea entre las ubicaciones estudiadas.

Algunos puntos podrían concentrar una proporción mayor de resultados situados en la zona superior de la distribución microbiológica.


5.20 Condiciones de Chi-cuadrado

La aproximación Chi-cuadrado requiere prestar atención a las frecuencias esperadas.

if (
  !is.null(
    prueba_chi_cc
  )
) {
  
  data.frame(
    Celdas_esperadas_menores_5 =
      sum(
        prueba_chi_cc$expected < 5
      ),
    Porcentaje_menores_5 =
      mean(
        prueba_chi_cc$expected < 5
      ) * 100
  )
}
##   Celdas_esperadas_menores_5 Porcentaje_menores_5
## 1                          1                   25

Una proporción elevada de celdas con valores esperados pequeños reduce la confiabilidad de la aproximación Chi-cuadrado.


5.21 Prueba exacta de Fisher

Cuando las frecuencias son pequeñas, especialmente en tablas \(2\times2\), puede utilizarse Fisher.

if (
  !is.null(
    tabla_2x2
  ) &&
  nrow(
    tabla_2x2
  ) >= 2 &&
  ncol(
    tabla_2x2
  ) >= 2 &&
  all(
    rowSums(
      tabla_2x2
    ) > 0
  ) &&
  all(
    colSums(
      tabla_2x2
    ) > 0
  )
) {
  
  prueba_fisher_cc <- fisher.test(
    tabla_2x2
  )
  
  prueba_fisher_cc
  
} else {
  
  prueba_fisher_cc <- NULL
  
  cat(
    "Fisher no puede calcularse porque el ejemplo no genera una tabla 2x2 válida."
  )
}
## 
##  Fisher's Exact Test for Count Data
## 
## data:  tabla_2x2
## p-value = 0.002472
## alternative hypothesis: true odds ratio is not equal to 1
## 95 percent confidence interval:
##  0.001159945 0.504227246
## sample estimates:
## odds ratio 
## 0.05698689

5.22 Interpretación estadística de Fisher

Fisher evalúa:

\[ H_0: \text{independencia} \]

frente a:

\[ H_1: \text{asociación} \]

Si:

\[ p<0.05 \]

existe evidencia de asociación entre las dos variables cualitativas.


5.23 Odds

Si:

\[ p= P(\text{Alerta}) \]

las odds se definen como:

\[ \text{odds} = \frac{ p }{ 1-p } \]

Por ejemplo, para:

\[ p=0.20 \]

se obtiene:

\[ \frac{0.20}{0.80} = 0.25 \]


5.24 Odds ratio

En una tabla:

Alerta Sin alerta
Punto 1 \(a\) \(b\)
Punto 2 \(c\) \(d\)

el odds ratio es:

\[ OR = \frac{ a/b }{ c/d } \]

equivalentemente:

\[ OR = \frac{ ad }{ bc } \]


5.25 Interpretación del OR

Si:

\[ OR=1 \]

las odds de alerta son iguales.

Si:

\[ OR>1 \]

el primer punto presenta mayores odds de alerta.

Si:

\[ OR<1 \]

presenta menores odds.

if (
  !is.null(
    prueba_fisher_cc
  )
) {
  
  prueba_fisher_cc$estimate
  prueba_fisher_cc$conf.int
  
} else {
  
  cat(
    "No fue posible estimar el odds ratio para el ejemplo."
  )
}
## [1] 0.001159945 0.504227246
## attr(,"conf.level")
## [1] 0.95

5.26 Interpretación microbiológica del OR

El OR permite cuantificar cuánto cambia la ocurrencia relativa de alertas entre dos puntos.

Un OR elevado no implica que la ubicación sea la causa directa de los resultados.

El punto puede representar condiciones ambientales, operacionales o hidráulicas diferentes.


5.27 V de Cramér

Para medir la magnitud de asociación:

\[ V = \sqrt{ \frac{ \chi^2 }{ N\min(r-1,c-1) } } \]

donde:

  • \(V\): V de Cramér;
  • \(\chi^2\): estadístico Chi-cuadrado;
  • \(N\): tamaño total;
  • \(r\): número de filas;
  • \(c\): número de columnas.
if (
  !is.null(
    prueba_chi_cc
  )
) {
  
  V_cc <- sqrt(
    unname(
      prueba_chi_cc$statistic
    ) /
    (
      sum(
        tabla_2x2
      ) *
      min(
        nrow(
          tabla_2x2
        ) - 1,
        ncol(
          tabla_2x2
        ) - 1
      )
    )
  )
  
  V_cc
}
## [1] 0.4972606

5.28 Interpretación de V de Cramér

Valores cercanos a cero representan asociación débil.

Valores mayores representan asociaciones más importantes.

La magnitud debe interpretarse conjuntamente con las dimensiones de la tabla y el contexto microbiológico.


5.29 Residuos estandarizados

if (
  !is.null(
    prueba_chi_cc
  )
) {
  
  round(
    prueba_chi_cc$stdres,
    2
  )
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_19  -3.18       3.18
##   PUNTO_5    3.18      -3.18

Como orientación:

\[ |\text{residuo}|>2 \]

sugiere una discrepancia importante entre observado y esperado.


5.30 Interpretación microbiológica de los residuos

Un residuo positivo grande en:

\[ \text{Punto X + Alerta} \]

indica:

Se observaron más alertas de las esperadas si las variables fueran independientes.

Un residuo negativo indica menos alertas de las esperadas.


5.31 Aplicación cualitativa–cualitativa a todos los puntos

tabla_alerta_total <- table(
  Punto = datos$punto,
  Clasificacion = datos$evento_alerta
)

tabla_alerta_total
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_11      0         21
##   PUNTO_16      0         21
##   PUNTO_17      0         21
##   PUNTO_18      0         21
##   PUNTO_19      1         21
##   PUNTO_20      0         21
##   PUNTO_21      0         21
##   PUNTO_23      0         22
##   PUNTO_24      0          7
##   PUNTO_25      0         11
##   PUNTO_26      0         11
##   PUNTO_4       0          6
##   PUNTO_5       9         10
##   PUNTO_6       9         11
##   PUNTO_7       2         19
##   PUNTO_8       8         12
##   PUNTO_9       2         20

5.32 Porcentaje de alertas por punto

porcentaje_alerta_total <- prop.table(
  tabla_alerta_total,
  margin = 1
) * 100

round(
  porcentaje_alerta_total,
  1
)
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_11    0.0      100.0
##   PUNTO_16    0.0      100.0
##   PUNTO_17    0.0      100.0
##   PUNTO_18    0.0      100.0
##   PUNTO_19    4.5       95.5
##   PUNTO_20    0.0      100.0
##   PUNTO_21    0.0      100.0
##   PUNTO_23    0.0      100.0
##   PUNTO_24    0.0      100.0
##   PUNTO_25    0.0      100.0
##   PUNTO_26    0.0      100.0
##   PUNTO_4     0.0      100.0
##   PUNTO_5    47.4       52.6
##   PUNTO_6    45.0       55.0
##   PUNTO_7     9.5       90.5
##   PUNTO_8    40.0       60.0
##   PUNTO_9     9.1       90.9

5.33 Gráfico de alertas por punto

if (
  "Alerta" %in%
  colnames(
    porcentaje_alerta_total
  )
) {
  
  porcentaje_alerta_simple <- porcentaje_alerta_total[
    ,
    "Alerta"
  ]
  
  porcentaje_alerta_simple <- sort(
    porcentaje_alerta_simple,
    decreasing = TRUE
  )
  
  par(
    mar = c(
      5,
      7,
      4,
      2
    )
  )
  
  barplot(
    porcentaje_alerta_simple,
    horiz = TRUE,
    las = 1,
    col = "#1596a5",
    border = NA,
    xlab = "Porcentaje de alertas",
    main = "Frecuencia relativa de alertas por punto"
  )
}


5.34 Chi-cuadrado para todos los puntos

tabla_alerta_total_valida <- tabla_alerta_total[
  rowSums(
    tabla_alerta_total
  ) > 0,
  colSums(
    tabla_alerta_total
  ) > 0,
  drop = FALSE
]

if (
  nrow(
    tabla_alerta_total_valida
  ) >= 2 &&
  ncol(
    tabla_alerta_total_valida
  ) >= 2
) {
  
  chi_total_cc <- suppressWarnings(
    chisq.test(
      tabla_alerta_total_valida
    )
  )
  
  chi_total_cc
  
} else {
  
  chi_total_cc <- NULL
  
  cat(
    "No existe variación suficiente en las categorías para calcular Chi-cuadrado."
  )
}
## 
##  Pearson's Chi-squared test
## 
## data:  tabla_alerta_total_valida
## X-squared = 96.944, df = 16, p-value = 1.291e-13

5.35 Frecuencias esperadas de todos los puntos

if (
  !is.null(
    chi_total_cc
  )
) {
  
  round(
    chi_total_cc$expected,
    2
  )
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_11   2.12      18.88
##   PUNTO_16   2.12      18.88
##   PUNTO_17   2.12      18.88
##   PUNTO_18   2.12      18.88
##   PUNTO_19   2.22      19.78
##   PUNTO_20   2.12      18.88
##   PUNTO_21   2.12      18.88
##   PUNTO_23   2.22      19.78
##   PUNTO_24   0.71       6.29
##   PUNTO_25   1.11       9.89
##   PUNTO_26   1.11       9.89
##   PUNTO_4    0.61       5.39
##   PUNTO_5    1.92      17.08
##   PUNTO_6    2.02      17.98
##   PUNTO_7    2.12      18.88
##   PUNTO_8    2.02      17.98
##   PUNTO_9    2.22      19.78

5.36 Residuos por punto

if (
  !is.null(
    chi_total_cc
  )
) {
  
  round(
    chi_total_cc$stdres,
    2
  )
}
##           Clasificacion
## Punto      Alerta Sin alerta
##   PUNTO_11  -1.59       1.59
##   PUNTO_16  -1.59       1.59
##   PUNTO_17  -1.59       1.59
##   PUNTO_18  -1.59       1.59
##   PUNTO_19  -0.90       0.90
##   PUNTO_20  -1.59       1.59
##   PUNTO_21  -1.59       1.59
##   PUNTO_23  -1.63       1.63
##   PUNTO_24  -0.90       0.90
##   PUNTO_25  -1.13       1.13
##   PUNTO_26  -1.13       1.13
##   PUNTO_4   -0.83       0.83
##   PUNTO_5    5.57      -5.57
##   PUNTO_6    5.36      -5.36
##   PUNTO_7   -0.09       0.09
##   PUNTO_8    4.59      -4.59
##   PUNTO_9   -0.16       0.16

5.37 Identificación de puntos con más o menos alertas de las esperadas

if (
  !is.null(
    chi_total_cc
  ) &&
  "Alerta" %in%
  colnames(
    chi_total_cc$stdres
  )
) {
  
  tabla_residuos_alerta <- data.frame(
    Punto = rownames(
      chi_total_cc$stdres
    ),
    Residuo_Alerta =
      chi_total_cc$stdres[
        ,
        "Alerta"
      ]
  )
  
  tabla_residuos_alerta$Interpretacion <- ifelse(
    tabla_residuos_alerta$Residuo_Alerta > 2,
    "Más alertas de las esperadas",
    ifelse(
      tabla_residuos_alerta$Residuo_Alerta < -2,
      "Menos alertas de las esperadas",
      "Comportamiento cercano a lo esperado"
    )
  )
  
  tabla_residuos_alerta <- tabla_residuos_alerta[
    order(
      tabla_residuos_alerta$Residuo_Alerta,
      decreasing = TRUE
    ),
  ]
  
  knitr::kable(
    tabla_residuos_alerta,
    digits = 3,
    caption = "Contribución de cada punto a la categoría Alerta"
  )
}
Contribución de cada punto a la categoría Alerta
Punto Residuo_Alerta Interpretacion
PUNTO_5 PUNTO_5 5.567 Más alertas de las esperadas
PUNTO_6 PUNTO_6 5.358 Más alertas de las esperadas
PUNTO_8 PUNTO_8 4.590 Más alertas de las esperadas
PUNTO_7 PUNTO_7 -0.090 Comportamiento cercano a lo esperado
PUNTO_9 PUNTO_9 -0.163 Comportamiento cercano a lo esperado
PUNTO_4 PUNTO_4 -0.829 Comportamiento cercano a lo esperado
PUNTO_24 PUNTO_24 -0.897 Comportamiento cercano a lo esperado
PUNTO_19 PUNTO_19 -0.897 Comportamiento cercano a lo esperado
PUNTO_25 PUNTO_25 -1.132 Comportamiento cercano a lo esperado
PUNTO_26 PUNTO_26 -1.132 Comportamiento cercano a lo esperado
PUNTO_11 PUNTO_11 -1.591 Comportamiento cercano a lo esperado
PUNTO_16 PUNTO_16 -1.591 Comportamiento cercano a lo esperado
PUNTO_17 PUNTO_17 -1.591 Comportamiento cercano a lo esperado
PUNTO_18 PUNTO_18 -1.591 Comportamiento cercano a lo esperado
PUNTO_20 PUNTO_20 -1.591 Comportamiento cercano a lo esperado
PUNTO_21 PUNTO_21 -1.591 Comportamiento cercano a lo esperado
PUNTO_23 PUNTO_23 -1.631 Comportamiento cercano a lo esperado

5.38 V de Cramér global

if (
  !is.null(
    chi_total_cc
  )
) {
  
  V_cramer_total <- sqrt(
    unname(
      chi_total_cc$statistic
    ) /
    (
      sum(
        tabla_alerta_total_valida
      ) *
      min(
        nrow(
          tabla_alerta_total_valida
        ) - 1,
        ncol(
          tabla_alerta_total_valida
        ) - 1
      )
    )
  )
  
  V_cramer_total
}
## [1] 0.5619404

5.39 P90 global y P90 por punto

También contamos con un criterio específico para cada punto:

\[ P90_j \]

Este identifica resultados elevados respecto del propio comportamiento del punto \(j\).

El criterio global responde:

¿El resultado es elevado frente a toda la base?

El criterio local responde:

¿El resultado es inusual frente al comportamiento propio del punto?


5.40 Comparación global versus local

tabla_global_local <- table(
  Global = datos$evento_alerta,
  Local = datos$evento_alerta_punto
)

tabla_global_local
##             Local
## Global       Alerta Sin alerta
##   Alerta         11         20
##   Sin alerta     20        256

Las cuatro posibilidades son:

  1. Alerta global y local.
  2. Solo alerta global.
  3. Solo alerta local.
  4. Sin alerta en ninguno.

5.41 Interpretación microbiológica global versus local

Alerta global y local El resultado es elevado frente a toda la base y también inusual para ese punto.

Solo alerta global El resultado es elevado frente al conjunto completo, pero puede encontrarse dentro del comportamiento habitual de un punto que generalmente presenta valores altos.

Solo alerta local El resultado no se encuentra entre los más elevados de toda la base, pero es inusual para el comportamiento histórico de ese punto.

Sin alerta No supera ninguno de los criterios estadísticos.

Un punto con comportamiento habitualmente elevado no debe considerarse microbiológicamente favorable únicamente porque un resultado no supere su P90 local.


5.42 Ruta completa cualitativa–cualitativa

\[ \boxed{\text{Punto + Alerta}} \]

\[ \Downarrow \]

\[ \boxed{\text{Frecuencias}} \]

\[ \Downarrow \]

\[ \boxed{\text{Proporciones y porcentajes}} \]

\[ \Downarrow \]

\[ \boxed{\text{Tabla de contingencia}} \]

\[ \Downarrow \]

\[ \boxed{\text{Frecuencias esperadas}} \]

\[ \Downarrow \]

\[ \boxed{\chi^2\text{ / Fisher}} \]

\[ \Downarrow \]

\[ \boxed{p} \]

\[ \Downarrow \]

\[ \boxed{\text{OR + V de Cramér}} \]

\[ \Downarrow \]

\[ \boxed{\text{Residuos}} \]

\[ \Downarrow \]

\[ \boxed{\text{Interpretación microbiológica}} \]


6. Relación cuantitativa–cualitativa

Una relación cuantitativa–cualitativa combina una variable numérica con una variable que define grupos.

En nuestra base:

\[ Y= \log_{10}(\text{NMP/100 mL}) \]

y:

\[ X= \text{Punto de muestreo} \]

La pregunta será:

¿Los resultados microbiológicos presentan el mismo comportamiento en todos los puntos?


6.1 ¿Cuál es la variable respuesta?

La variable respuesta será:

\[ Y= \log_{10}(\text{NMP/100 mL}) \]

La variable que define los grupos es:

\[ X= \text{Punto} \]

Por tanto:

  • resultado microbiológico = cuantitativa;
  • punto = cualitativa nominal.

6.2 Ejemplo con dos puntos reales

conteo_qc <- table(
  datos$punto
)

puntos_qc <- names(
  conteo_qc[
    conteo_qc >= 5
  ]
)

if (
  length(
    puntos_qc
  ) >= 2
) {
  
  puntos_qc <- puntos_qc[
    1:2
  ]
  
  ejemplo_qc <- datos[
    datos$punto %in% puntos_qc &
    is.finite(
      datos$log10_nmp
    ),
  ]
  
  ejemplo_qc$punto <- factor(
    ejemplo_qc$punto,
    levels = puntos_qc
  )
  
  knitr::kable(
    head(
      ejemplo_qc[
        ,
        c(
          "punto",
          "fecha",
          "resultado_nmp",
          "log10_nmp"
        )
      ],
      12
    ),
    digits = 3,
    caption = paste(
      "Ejemplo cuantitativa–cualitativa:",
      paste(
        puntos_qc,
        collapse = " y "
      )
    )
  )
  
} else {
  
  ejemplo_qc <- NULL
}
Ejemplo cuantitativa–cualitativa: PUNTO_11 y PUNTO_16
punto fecha resultado_nmp log10_nmp
PUNTO_11 2026-05-06 3500.0 3.544
PUNTO_16 2026-05-06 230.0 2.362
PUNTO_11 2026-01-29 8164.0 3.912
PUNTO_16 2026-01-29 1935.0 3.287
PUNTO_11 2026-02-10 3448.0 3.538
PUNTO_16 2026-02-10 5172.0 3.714
PUNTO_11 2026-03-03 8664.0 3.938
PUNTO_11 2026-03-03 866.4 2.938
PUNTO_16 2026-03-03 360.9 2.557
PUNTO_16 2026-03-03 3609.0 3.557
PUNTO_11 2026-03-24 4352.0 3.639
PUNTO_11 2026-03-24 435.2 2.639

6.3 Resumen descriptivo de los dos puntos

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  resumen_qc <- do.call(
    rbind,
    lapply(
      split(
        ejemplo_qc$log10_nmp,
        ejemplo_qc$punto
      ),
      function(x) {
        
        x <- x[
          is.finite(x)
        ]
        
        data.frame(
          N = length(x),
          Media = mean(x),
          Mediana = median(x),
          SD = sd(x),
          Q1 = as.numeric(
            quantile(
              x,
              0.25
            )
          ),
          Q3 = as.numeric(
            quantile(
              x,
              0.75
            )
          ),
          IQR = IQR(x)
        )
      }
    )
  )
  
  resumen_qc$Punto <- rownames(
    resumen_qc
  )
  
  rownames(
    resumen_qc
  ) <- NULL
  
  knitr::kable(
    resumen_qc,
    digits = 3,
    caption = "Resumen descriptivo de los dos puntos"
  )
}
Resumen descriptivo de los dos puntos
N Media Mediana SD Q1 Q3 IQR Punto
21 2.872 3.030 0.657 2.184 3.203 1.019 PUNTO_11
21 2.697 2.613 0.816 2.354 3.287 0.933 PUNTO_16

6.4 Interpretación estadística del resumen

Debemos comparar:

  • media;
  • mediana;
  • desviación estándar;
  • rango intercuartílico;
  • tamaño muestral.

Un punto puede presentar una media mayor pero una mediana similar debido a observaciones extremadamente elevadas.


6.5 Interpretación microbiológica del resumen

La media puede verse fuertemente afectada por episodios microbiológicos de gran magnitud.

La mediana describe mejor el comportamiento central cuando existe fuerte asimetría.

El IQR muestra qué tan variable es el comportamiento habitual del punto.


6.6 Boxplot de dos puntos

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  boxplot(
    log10_nmp ~ punto,
    data = ejemplo_qc,
    col = "#edf8f7",
    border = "#083d5c",
    xlab = "Punto de muestreo",
    ylab = expression(
      log[10]*"(NMP/100 mL)"
    ),
    main = "Comparación microbiológica entre dos puntos"
  )
  
  stripchart(
    log10_nmp ~ punto,
    data = ejemplo_qc,
    vertical = TRUE,
    method = "jitter",
    add = TRUE,
    pch = 19,
    cex = 0.65,
    col = "#1596a5"
  )
}


6.7 ¿Qué observamos en el boxplot?

Debemos revisar:

  • posición de la mediana;
  • tamaño de la caja;
  • longitud de los bigotes;
  • asimetría;
  • observaciones alejadas;
  • superposición entre grupos.

6.8 Supuestos para comparar dos grupos

Una comparación paramétrica requiere considerar:

Independencia Las observaciones deberían ser razonablemente independientes.

Normalidad La distribución de la respuesta dentro de cada grupo debería presentar comportamiento aproximadamente normal para la inferencia clásica.

Varianzas La prueba t clásica supone igualdad de varianzas.

La prueba t de Welch permite varianzas diferentes.


6.9 Normalidad de los dos puntos

Las hipótesis de Shapiro-Wilk son:

\[ H_0: \text{los datos son compatibles con normalidad} \]

\[ H_1: \text{los datos no son compatibles con normalidad} \]

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  normalidad_qc <- do.call(
    rbind,
    lapply(
      split(
        ejemplo_qc$log10_nmp,
        ejemplo_qc$punto
      ),
      function(x) {
        
        x <- x[
          is.finite(x)
        ]
        
        if (
          length(x) >= 3 &&
          length(
            unique(x)
          ) > 1
        ) {
          
          sh <- shapiro.test(
            x
          )
          
          data.frame(
            N = length(x),
            W = unname(
              sh$statistic
            ),
            p_value = sh$p.value
          )
          
        } else {
          
          data.frame(
            N = length(x),
            W = NA_real_,
            p_value = NA_real_
          )
        }
      }
    )
  )
  
  normalidad_qc$Punto <- rownames(
    normalidad_qc
  )
  
  rownames(
    normalidad_qc
  ) <- NULL
  
  normalidad_qc$Decision <- ifelse(
    is.na(
      normalidad_qc$p_value
    ),
    "No evaluable",
    ifelse(
      normalidad_qc$p_value < 0.05,
      "Evidencia contra normalidad",
      "No se rechaza normalidad"
    )
  )
  
  knitr::kable(
    normalidad_qc,
    digits = 4,
    caption = "Evaluación de normalidad de los dos puntos"
  )
}
Evaluación de normalidad de los dos puntos
N W p_value Punto Decision
21 0.9308 0.1432 PUNTO_11 No se rechaza normalidad
21 0.9688 0.7073 PUNTO_16 No se rechaza normalidad

6.10 Interpretación microbiológica de la normalidad

Una desviación de normalidad no implica necesariamente:

  • error analítico;
  • error de muestreo;
  • dato incorrecto.

Puede representar heterogeneidad microbiológica real o episodios de magnitud elevada.


6.11 Comparación de medias

Las hipótesis son:

\[ H_0: \mu_1=\mu_2 \]

frente a:

\[ H_1: \mu_1\neq\mu_2 \]

equivalentemente:

\[ H_0: \mu_1-\mu_2=0 \]


6.12 Diferencia observada de medias

El estimador es:

\[ \bar X_1-\bar X_2 \]

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  medias_qc <- tapply(
    ejemplo_qc$log10_nmp,
    ejemplo_qc$punto,
    mean,
    na.rm = TRUE
  )
  
  medias_qc
  
  diferencia_medias_qc <- medias_qc[1] -
    medias_qc[2]
  
  diferencia_medias_qc
}
##  PUNTO_11 
## 0.1748459

6.13 Prueba t de Welch

El estadístico es:

\[ t = \frac{ \bar X_1-\bar X_2 }{ \sqrt{ \frac{s_1^2}{n_1} + \frac{s_2^2}{n_2} } } \]

donde:

  • \(\bar X_1,\bar X_2\): medias;
  • \(s_1^2,s_2^2\): varianzas;
  • \(n_1,n_2\): tamaños muestrales.
if (
  !is.null(
    ejemplo_qc
  )
) {
  
  prueba_welch_qc <- t.test(
    log10_nmp ~ punto,
    data = ejemplo_qc,
    var.equal = FALSE,
    conf.level = 0.95
  )
  
  prueba_welch_qc
}
## 
##  Welch Two Sample t-test
## 
## data:  log10_nmp by punto
## t = 0.7651, df = 38.255, p-value = 0.4489
## alternative hypothesis: true difference in means between group PUNTO_11 and group PUNTO_16 is not equal to 0
## 95 percent confidence interval:
##  -0.2876804  0.6373722
## sample estimates:
## mean in group PUNTO_11 mean in group PUNTO_16 
##               2.871775               2.696929

6.14 Grados de libertad de Welch

Welch utiliza la aproximación:

\[ \nu= \frac{ \left( \frac{s_1^2}{n_1} + \frac{s_2^2}{n_2} \right)^2 }{ \frac{ (s_1^2/n_1)^2 }{ n_1-1 } + \frac{ (s_2^2/n_2)^2 }{ n_2-1 } } \]

Esto permite comparar grupos con varianzas y tamaños muestrales diferentes.


6.15 Intervalo de confianza para la diferencia de medias

if (
  exists(
    "prueba_welch_qc"
  )
) {
  
  prueba_welch_qc$conf.int
}
## [1] -0.2876804  0.6373722
## attr(,"conf.level")
## [1] 0.95

Si el intervalo incluye:

\[ 0 \]

los datos son compatibles con ausencia de diferencia de medias.


6.16 Interpretación estadística de Welch

Si:

\[ p<0.05 \]

existe evidencia de diferencia en las medias.

Pero la conclusión debe considerar también:

  • diferencia observada;
  • intervalo de confianza;
  • tamaño del efecto.

6.17 Interpretación microbiológica de Welch

Una diferencia estadísticamente detectable significa que los dos puntos presentan diferentes niveles promedio en escala logarítmica.

Esto puede reflejar perfiles microbiológicos distintos entre las ubicaciones.

No demuestra que el punto sea la causa.


6.18 Wilcoxon-Mann-Whitney

Cuando existe fuerte asimetría o valores extremos puede utilizarse una comparación basada en rangos.

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  wilcox.test(
    log10_nmp ~ punto,
    data = ejemplo_qc,
    exact = FALSE,
    conf.int = TRUE
  )
}
## 
##  Wilcoxon rank sum test with continuity correction
## 
## data:  log10_nmp by punto
## W = 249, p-value = 0.4812
## alternative hypothesis: true location shift is not equal to 0
## 95 percent confidence interval:
##  -0.3540329  0.6940347
## sample estimates:
## difference in location 
##              0.2573363

6.19 Interpretación de Wilcoxon

Wilcoxon evalúa diferencias en rangos/distribuciones.

No debe interpretarse automáticamente como una prueba estrictamente de medianas.

La interpretación como diferencia de localización resulta más apropiada cuando las distribuciones presentan formas comparables.


6.20 Tamaño del efecto para dos grupos: Cohen d

\[ d = \frac{ \bar X_1-\bar X_2 }{ s_p } \]

donde:

\[ s_p = \sqrt{ \frac{ (n_1-1)s_1^2+ (n_2-1)s_2^2 }{ n_1+n_2-2 } } \]

if (
  !is.null(
    ejemplo_qc
  )
) {
  
  grupos_qc <- split(
    ejemplo_qc$log10_nmp,
    ejemplo_qc$punto
  )
  
  x1_qc <- grupos_qc[[1]]
  x2_qc <- grupos_qc[[2]]
  
  n1_qc <- length(
    x1_qc
  )
  
  n2_qc <- length(
    x2_qc
  )
  
  s1_qc <- sd(
    x1_qc
  )
  
  s2_qc <- sd(
    x2_qc
  )
  
  sp_qc <- sqrt(
    (
      (n1_qc - 1) * s1_qc^2 +
      (n2_qc - 1) * s2_qc^2
    ) /
    (
      n1_qc + n2_qc - 2
    )
  )
  
  d_cohen_qc <- (
    mean(
      x1_qc
    ) -
    mean(
      x2_qc
    )
  ) /
  sp_qc
  
  d_cohen_qc
}
## [1] 0.2361155

Una guía descriptiva frecuente es:

\(|d|\) Magnitud
0.20 Pequeña
0.50 Moderada
0.80 Grande

6.21 Interpretación microbiológica del tamaño del efecto

El valor \(p\) informa sobre evidencia estadística.

Cohen \(d\) informa sobre cuánto se separan los dos puntos en relación con su variabilidad interna.

Una diferencia estadísticamente significativa puede tener una magnitud microbiológica pequeña.


6.22 Aplicación cuantitativa–cualitativa a todos los puntos

Ahora consideramos:

\[ k>2 \]

puntos.

La pregunta será:

¿Todos los puntos presentan el mismo nivel microbiológico?


6.23 Resumen descriptivo de todos los puntos

resumen_total_qc <- do.call(
  rbind,
  lapply(
    split(
      datos$log10_nmp,
      datos$punto
    ),
    function(x) {
      
      x <- x[
        is.finite(x)
      ]
      
      data.frame(
        N = length(x),
        Media = mean(x),
        Mediana = median(x),
        SD = sd(x),
        Q1 = as.numeric(
          quantile(
            x,
            0.25
          )
        ),
        Q3 = as.numeric(
          quantile(
            x,
            0.75
          )
        ),
        IQR = IQR(x)
      )
    }
  )
)

resumen_total_qc$Punto <- rownames(
  resumen_total_qc
)

rownames(
  resumen_total_qc
) <- NULL

resumen_total_qc <- resumen_total_qc[
  ,
  c(
    "Punto",
    "N",
    "Media",
    "Mediana",
    "SD",
    "Q1",
    "Q3",
    "IQR"
  )
]

knitr::kable(
  resumen_total_qc,
  digits = 3,
  caption = "Resumen cuantitativo por punto"
)
Resumen cuantitativo por punto
Punto N Media Mediana SD Q1 Q3 IQR
PUNTO_11 21 2.872 3.030 0.657 2.184 3.203 1.019
PUNTO_16 21 2.697 2.613 0.816 2.354 3.287 0.933
PUNTO_17 21 2.662 2.681 0.699 1.993 3.322 1.328
PUNTO_18 21 2.649 2.738 1.226 2.074 3.304 1.229
PUNTO_19 22 4.115 4.569 1.518 2.538 5.476 2.938
PUNTO_20 21 3.504 3.362 1.672 2.221 4.843 2.622
PUNTO_21 21 2.947 3.080 1.207 1.831 3.831 2.000
PUNTO_23 22 4.095 4.688 1.495 2.689 5.494 2.806
PUNTO_24 7 3.859 5.382 2.186 1.601 5.601 4.000
PUNTO_25 11 4.054 4.380 1.513 2.488 5.464 2.976
PUNTO_26 11 4.219 4.380 1.506 2.713 5.626 2.913
PUNTO_4 6 3.434 3.713 2.329 1.574 5.302 3.729
PUNTO_5 19 5.001 6.233 2.566 2.360 7.289 4.929
PUNTO_6 20 4.764 5.494 1.981 2.594 6.557 3.962
PUNTO_7 21 4.199 5.041 1.875 2.252 5.957 3.705
PUNTO_8 20 4.766 5.777 2.017 2.657 6.458 3.801
PUNTO_9 22 4.265 5.490 2.040 2.085 5.983 3.898

6.24 Interpretación microbiológica del resumen global

Los puntos pueden mostrar diferentes combinaciones de:

  • nivel central;
  • dispersión;
  • asimetría;
  • episodios elevados.

Por ello no debe seleccionarse un punto prioritario únicamente a partir de la media.


6.25 Boxplot para todos los puntos

par(
  mar = c(
    8,
    5,
    4,
    2
  )
)

boxplot(
  log10_nmp ~ punto,
  data = datos,
  col = "#edf8f7",
  border = "#083d5c",
  las = 2,
  cex.axis = 0.67,
  xlab = "",
  ylab = expression(
    log[10]*"(NMP/100 mL)"
  ),
  main = "Distribución microbiológica por punto"
)

stripchart(
  log10_nmp ~ punto,
  data = datos,
  vertical = TRUE,
  method = "jitter",
  add = TRUE,
  pch = 19,
  cex = 0.45,
  col = "#1596a5"
)

mtext(
  "Punto de muestreo",
  side = 1,
  line = 6
)


6.26 Histogramas por punto

rango_log_mod3 <- range(
  datos$log10_nmp,
  na.rm = TRUE
)

breaks_mod3 <- seq(
  floor(
    rango_log_mod3[1]
  ),
  ceiling(
    rango_log_mod3[2]
  ),
  length.out = 13
)

par_original_hist <- par(
  no.readonly = TRUE
)

par(
  mfrow = c(5,4),
  mar = c(3.2,3.2,2.7,1),
  oma = c(4.5,4,4.5,1)
)

for (
  p in puntos_mod3
) {
  
  x <- datos$log10_nmp[
    datos$punto == p &
    is.finite(
      datos$log10_nmp
    )
  ]
  
  if (
    length(x) > 0
  ) {
    
    hist(
      x,
      breaks = breaks_mod3,
      xlim = rango_log_mod3,
      col = "#1596a5",
      border = "white",
      main = p,
      xlab = "",
      ylab = "",
      cex.main = 0.85,
      cex.axis = 0.68
    )
    
    abline(
      v = median(
        x,
        na.rm = TRUE
      ),
      col = "#083d5c",
      lty = 2,
      lwd = 2
    )
  }
}

mtext(
  expression(
    log[10]*"(NMP/100 mL)"
  ),
  side = 1,
  outer = TRUE,
  line = 2.3
)

mtext(
  "Frecuencia",
  side = 2,
  outer = TRUE,
  line = 2.2
)

mtext(
  "Distribución microbiológica por punto",
  side = 3,
  outer = TRUE,
  line = 1.6,
  font = 2
)

par(
  par_original_hist
)


6.27 Gráficos Q-Q por punto

par_original_qq <- par(
  no.readonly = TRUE
)

par(
  mfrow = c(5,4),
  mar = c(3.2,3.2,2.7,1),
  oma = c(4.5,4.5,4,1)
)

for (
  p in puntos_mod3
) {
  
  x <- datos$log10_nmp[
    datos$punto == p &
    is.finite(
      datos$log10_nmp
    )
  ]
  
  if (
    length(x) >= 3
  ) {
    
    qqnorm(
      x,
      pch = 19,
      cex = 0.6,
      col = "#1596a5",
      main = p,
      xlab = "",
      ylab = ""
    )
    
    qqline(
      x,
      col = "#083d5c",
      lwd = 2
    )
  }
}

mtext(
  "Cuantiles teóricos",
  side = 1,
  outer = TRUE,
  line = 2
)

mtext(
  "Cuantiles observados",
  side = 2,
  outer = TRUE,
  line = 2
)

mtext(
  "Gráficos Q-Q por punto",
  side = 3,
  outer = TRUE,
  line = 1.5,
  font = 2
)

par(
  par_original_qq
)


6.28 Shapiro-Wilk por punto

normalidad_total_qc <- do.call(
  rbind,
  lapply(
    split(
      datos$log10_nmp,
      datos$punto
    ),
    function(x) {
      
      x <- x[
        is.finite(x)
      ]
      
      if (
        length(x) >= 3 &&
        length(x) <= 5000 &&
        length(
          unique(x)
        ) > 1
      ) {
        
        sh <- shapiro.test(
          x
        )
        
        data.frame(
          N = length(x),
          W = unname(
            sh$statistic
          ),
          p_value = sh$p.value
        )
        
      } else {
        
        data.frame(
          N = length(x),
          W = NA_real_,
          p_value = NA_real_
        )
      }
    }
  )
)

normalidad_total_qc$Punto <- rownames(
  normalidad_total_qc
)

rownames(
  normalidad_total_qc
) <- NULL

normalidad_total_qc$Decision <- ifelse(
  is.na(
    normalidad_total_qc$p_value
  ),
  "No evaluable",
  ifelse(
    normalidad_total_qc$p_value < 0.05,
    "Evidencia contra normalidad",
    "No se rechaza normalidad"
  )
)

normalidad_total_qc <- normalidad_total_qc[
  ,
  c(
    "Punto",
    "N",
    "W",
    "p_value",
    "Decision"
  )
]

knitr::kable(
  normalidad_total_qc,
  digits = 4,
  caption = "Normalidad en escala log10 por punto"
)
Normalidad en escala log10 por punto
Punto N W p_value Decision
PUNTO_11 21 0.9308 0.1432 No se rechaza normalidad
PUNTO_16 21 0.9688 0.7073 No se rechaza normalidad
PUNTO_17 21 0.9486 0.3201 No se rechaza normalidad
PUNTO_18 21 0.9650 0.6220 No se rechaza normalidad
PUNTO_19 22 0.8510 0.0035 Evidencia contra normalidad
PUNTO_20 21 0.9655 0.6323 No se rechaza normalidad
PUNTO_21 21 0.9350 0.1732 No se rechaza normalidad
PUNTO_23 22 0.8687 0.0074 Evidencia contra normalidad
PUNTO_24 7 0.7275 0.0073 Evidencia contra normalidad
PUNTO_25 11 0.7848 0.0059 Evidencia contra normalidad
PUNTO_26 11 0.7757 0.0045 Evidencia contra normalidad
PUNTO_4 6 0.8968 0.3555 No se rechaza normalidad
PUNTO_5 19 0.7877 0.0008 Evidencia contra normalidad
PUNTO_6 20 0.7841 0.0005 Evidencia contra normalidad
PUNTO_7 21 0.8562 0.0054 Evidencia contra normalidad
PUNTO_8 20 0.8126 0.0013 Evidencia contra normalidad
PUNTO_9 22 0.7695 0.0002 Evidencia contra normalidad

6.29 Homogeneidad de varianzas

El ANOVA clásico supone:

\[ \sigma_1^2 = \sigma_2^2 = \cdots = \sigma_k^2 \]

Una prueba disponible en R es Bartlett:

bartlett.test(
  log10_nmp ~ punto,
  data = datos
)
## 
##  Bartlett test of homogeneity of variances
## 
## data:  log10_nmp by punto
## Bartlett's K-squared = 74.572, df = 16, p-value = 1.558e-09

Bartlett es sensible a desviaciones de normalidad.

Por ello no debe utilizarse de forma aislada para seleccionar el método.


6.30 Modelo ANOVA de una vía

El modelo es:

\[ Y_{ij} = \mu + \tau_j + \varepsilon_{ij} \]

donde:

  • \(Y_{ij}\): resultado \(i\) del punto \(j\);
  • \(\mu\): media general;
  • \(\tau_j\): efecto asociado al punto \(j\);
  • \(\varepsilon_{ij}\): componente aleatorio.

6.31 Hipótesis de ANOVA

\[ H_0: \mu_1= \mu_2= \cdots= \mu_k \]

frente a:

\[ H_1: \text{al menos una media difiere} \]


6.32 Descomposición de la variabilidad

ANOVA separa:

\[ SS_T = SS_B + SS_W \]

donde:

  • \(SS_T\): suma de cuadrados total;
  • \(SS_B\): suma de cuadrados entre puntos;
  • \(SS_W\): suma de cuadrados dentro de los puntos.

6.33 Estadístico F

\[ F = \frac{ MS_B }{ MS_W } \]

donde:

\[ MS_B = \frac{ SS_B }{ k-1 } \]

y:

\[ MS_W = \frac{ SS_W }{ N-k } \]


6.54 ¿Cómo se construye matemáticamente una tabla ANOVA?

Hasta este punto hemos presentado el modelo general:

\[ Y_{ij} = \mu+\tau_j+\varepsilon_{ij} \]

pero para comprender realmente el ANOVA necesitamos estudiar de dónde sale cada componente de la tabla.

En este análisis:

  • \(i\) identifica la observación dentro de un punto;
  • \(j\) identifica el punto de muestreo;
  • \(Y_{ij}\) es el resultado microbiológico, trabajado en escala \(\log_{10}(\text{NMP/100 mL})\);
  • \(\mu\) representa la media general;
  • \(\tau_j\) representa el efecto asociado al punto \(j\);
  • \(\varepsilon_{ij}\) representa la variabilidad que permanece dentro del punto.

El ANOVA responde a la pregunta:

¿La variabilidad existente entre los puntos es suficientemente grande en comparación con la variabilidad que existe dentro de los propios puntos?


6.55 Descomposición matemática de una observación

Para cualquier observación puede escribirse:

\[ Y_{ij}-\bar Y_{..} = \left( \bar Y_{.j}-\bar Y_{..} \right) + \left( Y_{ij}-\bar Y_{.j} \right) \]

donde:

  • \(Y_{ij}-\bar Y_{..}\): desviación total de la observación respecto de la media general;
  • \(\bar Y_{.j}-\bar Y_{..}\): desviación atribuible a que el punto tiene una media diferente de la general;
  • \(Y_{ij}-\bar Y_{.j}\): desviación individual respecto de la media de su propio punto.

Esta identidad conduce a:

\[ SS_T = SS_B + SS_W \]

donde:

  • \(SS_T\): suma de cuadrados total;
  • \(SS_B\): suma de cuadrados entre puntos;
  • \(SS_W\): suma de cuadrados dentro de los puntos.

6.56 Suma de cuadrados total

\[ SS_T = \sum_{j=1}^{k} \sum_{i=1}^{n_j} \left( Y_{ij}-\bar Y_{..} \right)^2 \]

donde:

  • \(k\): número de puntos;
  • \(n_j\): observaciones del punto \(j\);
  • \(N=\sum_{j=1}^{k}n_j\): total de observaciones;
  • \(\bar Y_{..}\): media general.

Los grados de libertad son:

\[ gl_T=N-1 \]


6.57 Suma de cuadrados entre puntos

\[ SS_B = \sum_{j=1}^{k} n_j \left( \bar Y_{.j}-\bar Y_{..} \right)^2 \]

Cuanto más separadas estén las medias de los puntos respecto de la media general, mayor será \(SS_B\).

\[ gl_B=k-1 \]

\[ MS_B = \frac{SS_B}{k-1} \]


6.58 Suma de cuadrados dentro de los puntos

\[ SS_W = \sum_{j=1}^{k} \sum_{i=1}^{n_j} \left( Y_{ij}-\bar Y_{.j} \right)^2 \]

Esta cantidad mide la variabilidad microbiológica que permanece dentro de los puntos.

\[ gl_W=N-k \]

\[ MS_W = \frac{SS_W}{N-k} \]


6.59 Estadístico F y lógica del contraste

\[ F = \frac{MS_B}{MS_W} \]

Bajo:

\[ H_0: \mu_1=\mu_2=\cdots=\mu_k \]

el estadístico sigue una distribución:

\[ F \sim F_{k-1,N-k} \]

y el valor \(p\) es:

\[ p = P \left( F_{k-1,N-k} \geq F_{\text{obs}} \right) \]

Si \(F\) es grande, la variabilidad entre puntos domina sobre la variabilidad interna.


6.60 Ejemplo manual con datos reales de la base

Para comprender el procedimiento utilizaremos un ejemplo pequeño tomado directamente de la misma base del laboratorio.

Se seleccionarán automáticamente tres puntos con al menos tres observaciones válidas y se tomarán tres resultados de cada punto.

El ejemplo es pedagógico: sirve para construir el ANOVA paso a paso. La conclusión general debe realizarse después con todos los datos.

conteo_anova_manual <- table(
  datos$punto[
    is.finite(datos$log10_nmp)
  ]
)

puntos_anova_manual <- names(
  conteo_anova_manual[
    conteo_anova_manual >= 3
  ]
)

if (
  length(puntos_anova_manual) >= 3
) {

  puntos_anova_manual <- puntos_anova_manual[1:3]

  ejemplo_anova_manual <- do.call(
    rbind,
    lapply(
      puntos_anova_manual,
      function(p) {

        temp <- datos[
          datos$punto == p &
          is.finite(datos$log10_nmp),
          c(
            "punto",
            "fecha",
            "resultado_nmp",
            "log10_nmp"
          )
        ]

        temp <- temp[
          order(temp$fecha),
        ]

        head(
          temp,
          3
        )
      }
    )
  )

  ejemplo_anova_manual$punto <- factor(
    ejemplo_anova_manual$punto,
    levels = puntos_anova_manual
  )

  rownames(ejemplo_anova_manual) <- NULL

  knitr::kable(
    ejemplo_anova_manual,
    digits = 4,
    caption = "Ejemplo manual con tres puntos y tres observaciones reales por punto"
  )

} else {

  ejemplo_anova_manual <- NULL

  cat(
    "No existen tres puntos con al menos tres observaciones válidas."
  )
}
Ejemplo manual con tres puntos y tres observaciones reales por punto
punto fecha resultado_nmp log10_nmp
PUNTO_11 2026-01-29 8164.0 3.9119
PUNTO_11 2026-02-10 3448.0 3.5376
PUNTO_11 2026-03-03 8664.0 3.9377
PUNTO_16 2026-01-29 1935.0 3.2867
PUNTO_16 2026-02-10 5172.0 3.7137
PUNTO_16 2026-03-03 360.9 2.5574
PUNTO_17 2026-01-29 2333.0 3.3679
PUNTO_17 2026-02-10 3654.0 3.5628
PUNTO_17 2026-03-03 2603.0 3.4155

6.61 Paso 1. Media de cada punto

\[ \bar Y_{.j} = \frac{1}{n_j} \sum_{i=1}^{n_j} Y_{ij} \]

if (!is.null(ejemplo_anova_manual)) {

  medias_manual_anova <- tapply(
    ejemplo_anova_manual$log10_nmp,
    ejemplo_anova_manual$punto,
    mean
  )

  n_manual_anova <- table(
    ejemplo_anova_manual$punto
  )

  tabla_medias_manual <- data.frame(
    Punto = names(medias_manual_anova),
    n = as.numeric(n_manual_anova),
    Media_log10 = as.numeric(medias_manual_anova)
  )

  knitr::kable(
    tabla_medias_manual,
    digits = 4,
    caption = "Media de cada punto"
  )
}
Media de cada punto
Punto n Media_log10
PUNTO_11 3 3.7957
PUNTO_16 3 3.1859
PUNTO_17 3 3.4487

6.62 Paso 2. Media general

\[ \bar Y_{..} = \frac{1}{N} \sum_{j=1}^{k} \sum_{i=1}^{n_j} Y_{ij} \]

if (!is.null(ejemplo_anova_manual)) {

  media_general_manual <- mean(
    ejemplo_anova_manual$log10_nmp
  )

  media_general_manual
}
## [1] 3.476786

En el ejemplo:

\[ \bar Y_{..} = \text{3.4768} \]


6.63 Paso 3. Suma de cuadrados entre puntos

Para cada punto:

\[ n_j \left( \bar Y_{.j}-\bar Y_{..} \right)^2 \]

if (!is.null(ejemplo_anova_manual)) {

  contribuciones_entre <- data.frame(
    Punto = names(medias_manual_anova),
    n = as.numeric(n_manual_anova),
    Media_punto = as.numeric(medias_manual_anova)
  )

  contribuciones_entre$Media_general <- media_general_manual

  contribuciones_entre$Diferencia <- (
    contribuciones_entre$Media_punto -
    media_general_manual
  )

  contribuciones_entre$Diferencia_cuadrado <- (
    contribuciones_entre$Diferencia^2
  )

  contribuciones_entre$Contribucion_SS_entre <- (
    contribuciones_entre$n *
    contribuciones_entre$Diferencia_cuadrado
  )

  SS_entre_manual <- sum(
    contribuciones_entre$Contribucion_SS_entre
  )

  knitr::kable(
    contribuciones_entre,
    digits = 4,
    caption = "Cálculo de la suma de cuadrados entre puntos"
  )

  SS_entre_manual
}
## [1] 0.5613669

\[ SS_B = \text{0.5614} \]


6.64 Paso 4. Suma de cuadrados dentro de los puntos

Para cada observación:

\[ Y_{ij}-\bar Y_{.j} \]

y:

\[ \left( Y_{ij}-\bar Y_{.j} \right)^2 \]

if (!is.null(ejemplo_anova_manual)) {

  tabla_dentro_manual <- ejemplo_anova_manual[
    ,
    c(
      "punto",
      "resultado_nmp",
      "log10_nmp"
    )
  ]

  tabla_dentro_manual$Media_punto <- ave(
    tabla_dentro_manual$log10_nmp,
    tabla_dentro_manual$punto,
    FUN = mean
  )

  tabla_dentro_manual$Desviacion_dentro <- (
    tabla_dentro_manual$log10_nmp -
    tabla_dentro_manual$Media_punto
  )

  tabla_dentro_manual$Desviacion_dentro_cuadrado <- (
    tabla_dentro_manual$Desviacion_dentro^2
  )

  SS_dentro_manual <- sum(
    tabla_dentro_manual$Desviacion_dentro_cuadrado
  )

  knitr::kable(
    tabla_dentro_manual,
    digits = 4,
    caption = "Cálculo de la suma de cuadrados dentro de los puntos"
  )

  SS_dentro_manual
}
## [1] 0.8046613

\[ SS_W = \text{0.8047} \]


6.65 Paso 5. Suma de cuadrados total

\[ SS_T = \sum \left( Y_{ij}-\bar Y_{..} \right)^2 \]

y debe cumplirse:

\[ SS_T = SS_B+SS_W \]

if (!is.null(ejemplo_anova_manual)) {

  tabla_total_manual <- data.frame(
    Punto = ejemplo_anova_manual$punto,
    Y = ejemplo_anova_manual$log10_nmp,
    Media_general = media_general_manual
  )

  tabla_total_manual$Desviacion_total <- (
    tabla_total_manual$Y -
    tabla_total_manual$Media_general
  )

  tabla_total_manual$Desviacion_total_cuadrado <- (
    tabla_total_manual$Desviacion_total^2
  )

  SS_total_manual <- sum(
    tabla_total_manual$Desviacion_total_cuadrado
  )

  comprobacion_SS <- (
    SS_entre_manual +
    SS_dentro_manual
  )

  knitr::kable(
    tabla_total_manual,
    digits = 4,
    caption = "Cálculo de la suma de cuadrados total"
  )

  knitr::kable(
    data.frame(
      Metodo = c(
        "Cálculo directo",
        "SS entre + SS dentro"
      ),
      Resultado = c(
        SS_total_manual,
        comprobacion_SS
      )
    ),
    digits = 4,
    caption = "Comprobación de la identidad fundamental del ANOVA"
  )
}
Comprobación de la identidad fundamental del ANOVA
Metodo Resultado
Cálculo directo 1.366
SS entre + SS dentro 1.366

6.66 Paso 6. Grados de libertad

En general:

\[ gl_B=k-1 \]

\[ gl_W=N-k \]

\[ gl_T=N-1 \]

if (!is.null(ejemplo_anova_manual)) {

  k_manual <- nlevels(
    ejemplo_anova_manual$punto
  )

  N_manual <- nrow(
    ejemplo_anova_manual
  )

  gl_entre_manual <- k_manual - 1
  gl_dentro_manual <- N_manual - k_manual
  gl_total_manual <- N_manual - 1

  knitr::kable(
    data.frame(
      Fuente = c(
        "Entre puntos",
        "Dentro de puntos",
        "Total"
      ),
      Grados_libertad = c(
        gl_entre_manual,
        gl_dentro_manual,
        gl_total_manual
      )
    ),
    caption = "Grados de libertad del ejemplo"
  )
}
Grados de libertad del ejemplo
Fuente Grados_libertad
Entre puntos 2
Dentro de puntos 6
Total 8

6.67 Paso 7. Cuadrados medios

\[ MS_B = \frac{SS_B}{gl_B} \]

\[ MS_W = \frac{SS_W}{gl_W} \]

if (!is.null(ejemplo_anova_manual)) {

  MS_entre_manual <- (
    SS_entre_manual /
    gl_entre_manual
  )

  MS_dentro_manual <- (
    SS_dentro_manual /
    gl_dentro_manual
  )

  knitr::kable(
    data.frame(
      Fuente = c(
        "Entre puntos",
        "Dentro de puntos"
      ),
      SS = c(
        SS_entre_manual,
        SS_dentro_manual
      ),
      gl = c(
        gl_entre_manual,
        gl_dentro_manual
      ),
      MS = c(
        MS_entre_manual,
        MS_dentro_manual
      )
    ),
    digits = 4,
    caption = "Cálculo de los cuadrados medios"
  )
}
Cálculo de los cuadrados medios
Fuente SS gl MS
Entre puntos 0.5614 2 0.2807
Dentro de puntos 0.8047 6 0.1341

6.68 Paso 8. Estadístico F y valor p

\[ F = \frac{MS_B}{MS_W} \]

if (!is.null(ejemplo_anova_manual)) {

  F_manual <- (
    MS_entre_manual /
    MS_dentro_manual
  )

  p_manual <- pf(
    F_manual,
    df1 = gl_entre_manual,
    df2 = gl_dentro_manual,
    lower.tail = FALSE
  )

  knitr::kable(
    data.frame(
      F_observado = F_manual,
      gl_numerador = gl_entre_manual,
      gl_denominador = gl_dentro_manual,
      p_value = p_manual
    ),
    digits = 4,
    caption = "Cálculo manual del estadístico F"
  )
}
Cálculo manual del estadístico F
F_observado gl_numerador gl_denominador p_value
2.0929 2 6 0.2044

En el ejemplo:

\[ F = \text{2.0929} \]

y:

\[ p = \text{0.2044} \]


6.69 Tabla ANOVA construida a mano

if (!is.null(ejemplo_anova_manual)) {

  tabla_anova_manual_mod3 <- data.frame(
    Fuente = c(
      "Entre puntos",
      "Dentro de puntos",
      "Total"
    ),
    SS = c(
      SS_entre_manual,
      SS_dentro_manual,
      SS_total_manual
    ),
    gl = c(
      gl_entre_manual,
      gl_dentro_manual,
      gl_total_manual
    ),
    MS = c(
      MS_entre_manual,
      MS_dentro_manual,
      NA_real_
    ),
    F = c(
      F_manual,
      NA_real_,
      NA_real_
    ),
    p_value = c(
      p_manual,
      NA_real_,
      NA_real_
    )
  )

  knitr::kable(
    tabla_anova_manual_mod3,
    digits = 4,
    caption = "Tabla ANOVA construida manualmente"
  )
}
Tabla ANOVA construida manualmente
Fuente SS gl MS F p_value
Entre puntos 0.5614 2 0.2807 2.0929 0.2044
Dentro de puntos 0.8047 6 0.1341 NA NA
Total 1.3660 8 NA NA NA

6.70 Comprobación mediante aov()

if (!is.null(ejemplo_anova_manual)) {

  modelo_comprobacion_manual <- aov(
    log10_nmp ~ punto,
    data = ejemplo_anova_manual
  )

  summary(
    modelo_comprobacion_manual
  )
}
##             Df Sum Sq Mean Sq F value Pr(>F)
## punto        2 0.5614  0.2807   2.093  0.204
## Residuals    6 0.8047  0.1341

Los valores obtenidos mediante el procedimiento manual deben coincidir, salvo redondeo, con la salida de aov().


6.71 Interpretación estadística del ejemplo manual

Si:

\[ p<0.05 \]

existe evidencia contra:

\[ H_0: \mu_1=\mu_2=\mu_3 \]

y podemos concluir que al menos una media difiere.

Si:

\[ p\geq0.05 \]

no existe evidencia suficiente para afirmar que las medias sean distintas.

Este ejemplo utiliza solo nueve observaciones con fines didácticos; no sustituye el ANOVA completo de la base.


6.52 Interpretación microbiológica del ejemplo manual

El estadístico \(F\) compara:

\[ \boxed{\text{variabilidad entre puntos}} \]

contra:

\[ \boxed{\text{variabilidad dentro de los puntos}} \]

Un valor grande de \(F\) indica que los niveles promedio de los puntos están separados en relación con la variabilidad que existe dentro de cada punto.

Microbiológicamente esto sugiere que las ubicaciones pueden presentar perfiles diferentes.

Sin embargo:

\[ \boxed{ \text{diferencia estadística} \neq \text{causalidad} } \]

La base es observacional. Por ello las diferencias pueden estar asociadas con características ambientales, hidráulicas, temporales u operacionales que no aparecen explícitamente en la base.


6.53 Tabla general del ANOVA

Fuente Suma de cuadrados Grados de libertad Cuadrado medio Estadístico
Entre puntos \(SS_B\) \(k-1\) \(MS_B=SS_B/(k-1)\) \(F=MS_B/MS_W\)
Dentro de puntos \(SS_W\) \(N-k\) \(MS_W=SS_W/(N-k)\)
Total \(SS_T\) \(N-1\)

La identidad fundamental es:

\[ \boxed{ SS_T=SS_B+SS_W } \]

6.54 ANOVA aplicado a la base

modelo_anova_qc <- aov(
  log10_nmp ~ punto,
  data = datos
)

summary(
  modelo_anova_qc
)
##              Df Sum Sq Mean Sq F value   Pr(>F)    
## punto        16  195.3  12.207   4.588 3.35e-08 ***
## Residuals   290  771.6   2.661                     
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

6.55 Interpretación estadística del ANOVA

Si:

\[ p<0.05 \]

existe evidencia de que no todas las medias son iguales.

Sin embargo, la prueba global no identifica cuáles puntos difieren.


6.56 Interpretación microbiológica del ANOVA

Un resultado significativo indica heterogeneidad microbiológica entre las ubicaciones.

Algunos puntos presentan niveles medios diferentes de otros, pero es necesario continuar con comparaciones posteriores.


6.57 ANOVA de Welch

Cuando las varianzas pueden ser diferentes:

resultado_welch_total <- oneway.test(
  log10_nmp ~ punto,
  data = datos,
  var.equal = FALSE
)

resultado_welch_total
## 
##  One-way analysis of means (not assuming equal variances)
## 
## data:  log10_nmp and punto
## F = 5.7343, num df = 16.000, denom df = 85.756, p-value = 3.209e-08

6.58 Kruskal-Wallis

Cuando las condiciones paramétricas no son adecuadas puede utilizarse:

\[ H = \frac{ 12 }{ N(N+1) } \sum_{j=1}^{k} \frac{ R_j^2 }{ n_j } - 3(N+1) \]

donde:

  • \(H\): estadístico de Kruskal-Wallis;
  • \(N\): número total de observaciones;
  • \(R_j\): suma de rangos del punto \(j\);
  • \(n_j\): tamaño del punto \(j\);
  • \(k\): número de puntos.
resultado_kruskal_qc <- kruskal.test(
  resultado_nmp ~ punto,
  data = datos
)

resultado_kruskal_qc
## 
##  Kruskal-Wallis rank sum test
## 
## data:  resultado_nmp by punto
## Kruskal-Wallis chi-squared = 45.19, df = 16, p-value = 0.0001298

6.59 Interpretación estadística de Kruskal-Wallis

Si:

\[ p<0.05 \]

existe evidencia de heterogeneidad en rangos/distribuciones entre puntos.

No significa automáticamente que todas las medianas sean diferentes.


6.60 Interpretación microbiológica de Kruskal-Wallis

Los puntos no presentan un comportamiento microbiológico homogéneo.

La diferencia puede involucrar:

  • posición;
  • dispersión;
  • forma;
  • frecuencia de resultados elevados.

6.61 ¿ANOVA, Welch o Kruskal-Wallis?

Situación Método
Comportamiento aproximadamente normal y varianzas similares ANOVA
Medias de interés pero varianzas diferentes Welch
Fuerte no normalidad, extremos o rangos Kruskal-Wallis

La selección debe considerar conjuntamente:

  • histogramas;
  • Q-Q;
  • tamaños muestrales;
  • residuos;
  • varianzas;
  • propósito del análisis.

6.62 Comparaciones posteriores después de ANOVA

TukeyHSD(
  modelo_anova_qc
)
##   Tukey multiple comparisons of means
##     95% family-wise confidence level
## 
## Fit: aov(formula = log10_nmp ~ punto, data = datos)
## 
## $punto
##                           diff         lwr       upr     p adj
## PUNTO_16-PUNTO_11 -0.174845893 -1.93147335 1.5817816 1.0000000
## PUNTO_17-PUNTO_11 -0.210130108 -1.96675756 1.5464973 1.0000000
## PUNTO_18-PUNTO_11 -0.223180466 -1.97980792 1.5334470 1.0000000
## PUNTO_19-PUNTO_11  1.243225111 -0.49332594 2.9797762 0.5053624
## PUNTO_20-PUNTO_11  0.632523218 -1.12410424 2.3891507 0.9980389
## PUNTO_21-PUNTO_11  0.075702432 -1.68092502 1.8323299 1.0000000
## PUNTO_23-PUNTO_11  1.223304954 -0.51324610 2.9598560 0.5355531
## PUNTO_24-PUNTO_11  0.986992460 -1.49725391 3.4712388 0.9940951
## PUNTO_25-PUNTO_11  1.181783645 -0.93678878 3.3003561 0.8728556
## PUNTO_26-PUNTO_11  1.347210245 -0.77136218 3.4657827 0.7123867
## PUNTO_4-PUNTO_11   0.562330771 -2.07261041 3.1972720 0.9999980
## PUNTO_5-PUNTO_11   2.129701714  0.32743997 3.9319635 0.0054744
## PUNTO_6-PUNTO_11   1.892613884  0.11416413 3.6710636 0.0241980
## PUNTO_7-PUNTO_11   1.327234606 -0.42939285 3.0838621 0.4042740
## PUNTO_8-PUNTO_11   1.893763925  0.11531418 3.6722137 0.0240139
## PUNTO_9-PUNTO_11   1.392819693 -0.34373136 3.1293707 0.2977587
## PUNTO_17-PUNTO_16 -0.035284215 -1.79191167 1.7213432 1.0000000
## PUNTO_18-PUNTO_16 -0.048334574 -1.80496203 1.7082929 1.0000000
## PUNTO_19-PUNTO_16  1.418071003 -0.31848005 3.1546221 0.2680489
## PUNTO_20-PUNTO_16  0.807369110 -0.94925834 2.5639966 0.9742952
## PUNTO_21-PUNTO_16  0.250548324 -1.50607913 2.0071758 1.0000000
## PUNTO_23-PUNTO_16  1.398150846 -0.33840021 3.1347019 0.2913315
## PUNTO_24-PUNTO_16  1.161838352 -1.32240802 3.6460847 0.9697960
## PUNTO_25-PUNTO_16  1.356629537 -0.76194288 3.4752020 0.7015305
## PUNTO_26-PUNTO_16  1.522056138 -0.59651628 3.6406286 0.4987677
## PUNTO_4-PUNTO_16   0.737176664 -1.89776452 3.3721178 0.9999156
## PUNTO_5-PUNTO_16   2.304547606  0.50228587 4.1068093 0.0013979
## PUNTO_6-PUNTO_16   2.067459777  0.28901003 3.8459095 0.0070688
## PUNTO_7-PUNTO_16   1.502080499 -0.25454695 3.2587080 0.1987096
## PUNTO_8-PUNTO_16   2.068609818  0.29016007 3.8470596 0.0070087
## PUNTO_9-PUNTO_16   1.567665585 -0.16888547 3.3042166 0.1317030
## PUNTO_18-PUNTO_17 -0.013050359 -1.76967781 1.7435771 1.0000000
## PUNTO_19-PUNTO_17  1.453355218 -0.28319583 3.1899063 0.2297351
## PUNTO_20-PUNTO_17  0.842653325 -0.91397413 2.5992808 0.9620158
## PUNTO_21-PUNTO_17  0.285832539 -1.47079491 2.0424600 1.0000000
## PUNTO_23-PUNTO_17  1.433435061 -0.30311599 3.1699861 0.2508996
## PUNTO_24-PUNTO_17  1.197122567 -1.28712380 3.6813689 0.9604530
## PUNTO_25-PUNTO_17  1.391913752 -0.72665867 3.5104862 0.6597761
## PUNTO_26-PUNTO_17  1.557340353 -0.56123207 3.6759128 0.4556403
## PUNTO_4-PUNTO_17   0.772460879 -1.86248030 3.4074021 0.9998443
## PUNTO_5-PUNTO_17   2.339831821  0.53757008 4.1420936 0.0010466
## PUNTO_6-PUNTO_17   2.102743991  0.32429424 3.8811937 0.0054257
## PUNTO_7-PUNTO_17   1.537364714 -0.21926274 3.2939922 0.1679789
## PUNTO_8-PUNTO_17   2.103894033  0.32544428 3.8823438 0.0053786
## PUNTO_9-PUNTO_17   1.602949800 -0.13360125 3.3395009 0.1090809
## PUNTO_19-PUNTO_18  1.466405577 -0.27014548 3.2029566 0.2165318
## PUNTO_20-PUNTO_18  0.855703684 -0.90092377 2.6123311 0.9565105
## PUNTO_21-PUNTO_18  0.298882898 -1.45774456 2.0555104 0.9999999
## PUNTO_23-PUNTO_18  1.446485420 -0.29006563 3.1830365 0.2368964
## PUNTO_24-PUNTO_18  1.210172926 -1.27407344 3.6944193 0.9565027
## PUNTO_25-PUNTO_18  1.404964111 -0.71360831 3.5235365 0.6439717
## PUNTO_26-PUNTO_18  1.570390711 -0.54818171 3.6889631 0.4399607
## PUNTO_4-PUNTO_18   0.785511237 -1.84942994 3.4204524 0.9998067
## PUNTO_5-PUNTO_18   2.352882180  0.55062044 4.1551439 0.0009393
## PUNTO_6-PUNTO_18   2.115794350  0.33734460 3.8942441 0.0049135
## PUNTO_7-PUNTO_18   1.550415073 -0.20621238 3.3070425 0.1575438
## PUNTO_8-PUNTO_18   2.116944391  0.33849464 3.8953941 0.0048706
## PUNTO_9-PUNTO_18   1.616000159 -0.12055089 3.3525512 0.1015416
## PUNTO_20-PUNTO_19 -0.610701893 -2.34725295 1.1258492 0.9985121
## PUNTO_21-PUNTO_19 -1.167522679 -2.90407373 0.5690284 0.6202066
## PUNTO_23-PUNTO_19 -0.019920157 -1.73615997 1.6963197 1.0000000
## PUNTO_24-PUNTO_19 -0.256232651 -2.72632366 2.2138584 1.0000000
## PUNTO_25-PUNTO_19 -0.061441466 -2.16339738 2.0405144 1.0000000
## PUNTO_26-PUNTO_19  0.103985134 -1.99797078 2.2059410 1.0000000
## PUNTO_4-PUNTO_19  -0.680894340 -3.30249396 1.9407053 0.9999688
## PUNTO_5-PUNTO_19   0.886476603 -0.89622273 2.6691759 0.9480801
## PUNTO_6-PUNTO_19   0.649388773 -1.10923372 2.4080113 0.9973799
## PUNTO_7-PUNTO_19   0.084009496 -1.65254156 1.8205605 1.0000000
## PUNTO_8-PUNTO_19   0.650538814 -1.10808367 2.4091613 0.9973265
## PUNTO_9-PUNTO_19   0.149594582 -1.56664523 1.8658344 1.0000000
## PUNTO_21-PUNTO_20 -0.556820786 -2.31344824 1.1998067 0.9995788
## PUNTO_23-PUNTO_20  0.590781736 -1.14576932 2.3273328 0.9989974
## PUNTO_24-PUNTO_20  0.354469242 -2.12977713 2.8387156 1.0000000
## PUNTO_25-PUNTO_20  0.549260427 -1.56931200 2.6678328 0.9999696
## PUNTO_26-PUNTO_20  0.714687027 -1.40388539 2.8332594 0.9990945
## PUNTO_4-PUNTO_20  -0.070192447 -2.70513363 2.5647487 1.0000000
## PUNTO_5-PUNTO_20   1.497178496 -0.30508324 3.2994402 0.2410204
## PUNTO_6-PUNTO_20   1.260090666 -0.51835908 3.0385404 0.5247703
## PUNTO_7-PUNTO_20   0.694711389 -1.06191607 2.4513388 0.9943810
## PUNTO_8-PUNTO_20   1.261240707 -0.51720904 3.0396905 0.5230666
## PUNTO_9-PUNTO_20   0.760296475 -0.97625458 2.4968475 0.9838879
## PUNTO_23-PUNTO_21  1.147602522 -0.58894853 2.8841536 0.6499276
## PUNTO_24-PUNTO_21  0.911290028 -1.57295634 3.3955364 0.9975706
## PUNTO_25-PUNTO_21  1.106081213 -1.01249121 3.2246536 0.9226015
## PUNTO_26-PUNTO_21  1.271507813 -0.84706461 3.3900802 0.7937470
## PUNTO_4-PUNTO_21   0.486628340 -2.14831284 3.1215695 0.9999998
## PUNTO_5-PUNTO_21   2.053999282  0.25173754 3.8562610 0.0095214
## PUNTO_6-PUNTO_21   1.816911452  0.03846170 3.5953612 0.0394406
## PUNTO_7-PUNTO_21   1.251532175 -0.50509528 3.0081596 0.5144272
## PUNTO_8-PUNTO_21   1.818061493  0.03961174 3.5965112 0.0391573
## PUNTO_9-PUNTO_21   1.317117261 -0.41943379 3.0536683 0.3971612
## PUNTO_24-PUNTO_23 -0.236312494 -2.70640350 2.2337785 1.0000000
## PUNTO_25-PUNTO_23 -0.041521309 -2.14347722 2.0604346 1.0000000
## PUNTO_26-PUNTO_23  0.123905291 -1.97805062 2.2258612 1.0000000
## PUNTO_4-PUNTO_23  -0.660974182 -3.28257380 1.9606254 0.9999792
## PUNTO_5-PUNTO_23   0.906396760 -0.87630258 2.6890961 0.9374890
## PUNTO_6-PUNTO_23   0.669308930 -1.08931356 2.4279314 0.9963155
## PUNTO_7-PUNTO_23   0.103929653 -1.63262140 1.8404807 1.0000000
## PUNTO_8-PUNTO_23   0.670458971 -1.08816352 2.4290815 0.9962443
## PUNTO_9-PUNTO_23   0.169514739 -1.54672508 1.8857546 1.0000000
## PUNTO_25-PUNTO_24  0.194791185 -2.55731512 2.9468975 1.0000000
## PUNTO_26-PUNTO_24  0.360217785 -2.39188852 3.1123241 1.0000000
## PUNTO_4-PUNTO_24  -0.424661689 -3.59146687 2.7421435 1.0000000
## PUNTO_5-PUNTO_24   1.142709254 -1.37401230 3.6594308 0.9770843
## PUNTO_6-PUNTO_24   0.905621424 -1.59410327 3.4053461 0.9978949
## PUNTO_7-PUNTO_24   0.340242147 -2.14400422 2.8244885 1.0000000
## PUNTO_8-PUNTO_24   0.906771465 -1.59295322 3.4064962 0.9978638
## PUNTO_9-PUNTO_24   0.405827233 -2.06426377 2.8759182 1.0000000
## PUNTO_26-PUNTO_25  0.165426600 -2.26170302 2.5925562 1.0000000
## PUNTO_4-PUNTO_25  -0.619452873 -3.50831495 2.2694092 0.9999979
## PUNTO_5-PUNTO_25   0.947918069 -1.20864319 3.1044793 0.9832497
## PUNTO_6-PUNTO_25   0.710830239 -1.42587110 2.8475316 0.9992353
## PUNTO_7-PUNTO_25   0.145450962 -1.97312146 2.2640234 1.0000000
## PUNTO_8-PUNTO_25   0.711980280 -1.42472106 2.8486816 0.9992201
## PUNTO_9-PUNTO_25   0.211036048 -1.89091986 2.3129920 1.0000000
## PUNTO_4-PUNTO_26  -0.784879474 -3.67374155 2.1039826 0.9999428
## PUNTO_5-PUNTO_26   0.782491469 -1.37406979 2.9390527 0.9978574
## PUNTO_6-PUNTO_26   0.545403639 -1.59129770 2.6821050 0.9999754
## PUNTO_7-PUNTO_26  -0.019975639 -2.13854806 2.0985968 1.0000000
## PUNTO_8-PUNTO_26   0.546553680 -1.59014766 2.6832550 0.9999747
## PUNTO_9-PUNTO_26   0.045609448 -2.05634646 2.1475654 1.0000000
## PUNTO_5-PUNTO_4    1.567370942 -1.09821012 4.2329520 0.8185984
## PUNTO_6-PUNTO_4    1.330283113 -1.31925619 3.9798224 0.9436882
## PUNTO_7-PUNTO_4    0.764903835 -1.87003735 3.3998450 0.9998630
## PUNTO_8-PUNTO_4    1.331433154 -1.31810615 3.9809725 0.9432796
## PUNTO_9-PUNTO_4    0.830488921 -1.79111070 3.4520885 0.9995820
## PUNTO_6-PUNTO_5   -0.237087830 -2.06062584 1.5864502 1.0000000
## PUNTO_7-PUNTO_5   -0.802467107 -2.60472885 0.9997946 0.9810031
## PUNTO_8-PUNTO_5   -0.235937789 -2.05947580 1.5876002 1.0000000
## PUNTO_9-PUNTO_5   -0.736882021 -2.51958136 1.0458173 0.9910117
## PUNTO_7-PUNTO_6   -0.565379278 -2.34382903 1.2130705 0.9995632
## PUNTO_8-PUNTO_6    0.001150041 -1.79885746 1.8011575 1.0000000
## PUNTO_9-PUNTO_6   -0.499794191 -2.25841668 1.2588283 0.9998962
## PUNTO_8-PUNTO_7    0.566529319 -1.21192043 2.3449791 0.9995520
## PUNTO_9-PUNTO_7    0.065585086 -1.67096597 1.8021361 1.0000000
## PUNTO_9-PUNTO_8   -0.500944232 -2.25956672 1.2576783 0.9998930

Tukey identifica cuáles pares de medias presentan diferencias.


6.63 Comparaciones posteriores después de Kruskal-Wallis

if (
  resultado_kruskal_qc$p.value < 0.05
) {
  
  comparaciones_kruskal <- pairwise.wilcox.test(
    datos$resultado_nmp,
    datos$punto,
    p.adjust.method = "BH",
    exact = FALSE
  )
  
  comparaciones_kruskal
}
## 
##  Pairwise comparisons using Wilcoxon rank sum test with continuity correction 
## 
## data:  datos$resultado_nmp and datos$punto 
## 
##          PUNTO_11 PUNTO_16 PUNTO_17 PUNTO_18 PUNTO_19 PUNTO_20 PUNTO_21
## PUNTO_16 0.648    -        -        -        -        -        -       
## PUNTO_17 0.471    0.959    -        -        -        -        -       
## PUNTO_18 0.812    0.907    0.907    -        -        -        -       
## PUNTO_19 0.097    0.079    0.079    0.088    -        -        -       
## PUNTO_20 0.334    0.177    0.174    0.214    0.463    -        -       
## PUNTO_21 0.846    0.564    0.471    0.564    0.089    0.501    -       
## PUNTO_23 0.122    0.079    0.079    0.079    0.998    0.531    0.097   
## PUNTO_24 0.757    0.564    0.757    0.508    0.947    0.846    0.531   
## PUNTO_25 0.174    0.132    0.136    0.161    0.959    0.613    0.168   
## PUNTO_26 0.168    0.079    0.094    0.123    0.471    0.564    0.168   
## PUNTO_4  0.564    0.564    0.531    0.471    0.889    0.939    0.818   
## PUNTO_5  0.151    0.136    0.136    0.120    0.364    0.168    0.120   
## PUNTO_6  0.079    0.079    0.079    0.079    0.245    0.151    0.079   
## PUNTO_7  0.161    0.136    0.122    0.114    0.983    0.360    0.097   
## PUNTO_8  0.097    0.079    0.079    0.079    0.226    0.136    0.079   
## PUNTO_9  0.261    0.190    0.168    0.136    0.804    0.310    0.115   
##          PUNTO_23 PUNTO_24 PUNTO_25 PUNTO_26 PUNTO_4 PUNTO_5 PUNTO_6 PUNTO_7
## PUNTO_16 -        -        -        -        -       -       -       -      
## PUNTO_17 -        -        -        -        -       -       -       -      
## PUNTO_18 -        -        -        -        -       -       -       -      
## PUNTO_19 -        -        -        -        -       -       -       -      
## PUNTO_20 -        -        -        -        -       -       -       -      
## PUNTO_21 -        -        -        -        -       -       -       -      
## PUNTO_23 -        -        -        -        -       -       -       -      
## PUNTO_24 0.845    -        -        -        -       -       -       -      
## PUNTO_25 0.904    1.000    -        -        -       -       -       -      
## PUNTO_26 0.826    0.695    0.471    -        -       -       -       -      
## PUNTO_4  0.904    0.907    0.939    0.904    -       -       -       -      
## PUNTO_5  0.319    0.223    0.515    0.559    0.257   -       -       -      
## PUNTO_6  0.310    0.236    0.382    0.564    0.310   0.714   -       -      
## PUNTO_7  0.907    0.725    0.983    0.904    0.564   0.214   0.177   -      
## PUNTO_8  0.236    0.252    0.323    0.564    0.310   0.564   0.907   0.183  
## PUNTO_9  0.691    0.419    0.845    0.983    0.508   0.161   0.136   0.904  
##          PUNTO_8
## PUNTO_16 -      
## PUNTO_17 -      
## PUNTO_18 -      
## PUNTO_19 -      
## PUNTO_20 -      
## PUNTO_21 -      
## PUNTO_23 -      
## PUNTO_24 -      
## PUNTO_25 -      
## PUNTO_26 -      
## PUNTO_4  -      
## PUNTO_5  -      
## PUNTO_6  -      
## PUNTO_7  -      
## PUNTO_8  -      
## PUNTO_9  0.202  
## 
## P value adjustment method: BH

El ajuste BH controla el problema de realizar numerosas comparaciones.


6.64 Intervalos de confianza de la media por punto

\[ IC_{95\%} = \bar x \pm t_{\alpha/2,n-1} \frac{ s }{ \sqrt n } \]

donde:

  • \(\bar x\): media;
  • \(s\): desviación estándar;
  • \(n\): tamaño muestral;
  • \(t\): cuantil de Student.
IC_medias_qc <- do.call(
  rbind,
  lapply(
    split(
      datos$log10_nmp,
      datos$punto
    ),
    function(x) {
      
      x <- x[
        is.finite(x)
      ]
      
      n <- length(x)
      
      if (
        n >= 2
      ) {
        
        media <- mean(x)
        s <- sd(x)
        
        error <- qt(
          0.975,
          df = n - 1
        ) *
        s /
        sqrt(n)
        
        data.frame(
          N = n,
          Media = media,
          IC_inf = media - error,
          IC_sup = media + error
        )
        
      } else {
        
        data.frame(
          N = n,
          Media = NA_real_,
          IC_inf = NA_real_,
          IC_sup = NA_real_
        )
      }
    }
  )
)

IC_medias_qc$Punto <- rownames(
  IC_medias_qc
)

rownames(
  IC_medias_qc
) <- NULL

IC_medias_qc <- IC_medias_qc[
  ,
  c(
    "Punto",
    "N",
    "Media",
    "IC_inf",
    "IC_sup"
  )
]

knitr::kable(
  IC_medias_qc,
  digits = 3,
  caption = "Intervalo de confianza del 95% para la media por punto"
)
Intervalo de confianza del 95% para la media por punto
Punto N Media IC_inf IC_sup
PUNTO_11 21 2.872 2.573 3.171
PUNTO_16 21 2.697 2.326 3.068
PUNTO_17 21 2.662 2.344 2.980
PUNTO_18 21 2.649 2.091 3.207
PUNTO_19 22 4.115 3.442 4.788
PUNTO_20 21 3.504 2.743 4.265
PUNTO_21 21 2.947 2.398 3.497
PUNTO_23 22 4.095 3.432 4.758
PUNTO_24 7 3.859 1.837 5.880
PUNTO_25 11 4.054 3.037 5.070
PUNTO_26 11 4.219 3.207 5.231
PUNTO_4 6 3.434 0.990 5.878
PUNTO_5 19 5.001 3.765 6.238
PUNTO_6 20 4.764 3.837 5.692
PUNTO_7 21 4.199 3.345 5.053
PUNTO_8 20 4.766 3.821 5.710
PUNTO_9 22 4.265 3.360 5.169

6.65 Gráfico de medias e intervalos de confianza

datos_ic_qc <- IC_medias_qc[
  complete.cases(
    IC_medias_qc[
      ,
      c(
        "Media",
        "IC_inf",
        "IC_sup"
      )
    ]
  ),
]

datos_ic_qc <- datos_ic_qc[
  order(
    datos_ic_qc$Media
  ),
]

x_ic_qc <- seq_len(
  nrow(
    datos_ic_qc
  )
)

plot(
  x_ic_qc,
  datos_ic_qc$Media,
  pch = 19,
  cex = 1.1,
  col = "#1596a5",
  xaxt = "n",
  xlab = "Punto de muestreo",
  ylab = expression(
    "Media de " *
    log[10] *
    "(NMP/100 mL)"
  ),
  main = "Media e IC 95% por punto",
  ylim = range(
    c(
      datos_ic_qc$IC_inf,
      datos_ic_qc$IC_sup
    ),
    na.rm = TRUE
  )
)

arrows(
  x0 = x_ic_qc,
  y0 = datos_ic_qc$IC_inf,
  x1 = x_ic_qc,
  y1 = datos_ic_qc$IC_sup,
  angle = 90,
  code = 3,
  length = 0.05,
  col = "#083d5c"
)

axis(
  1,
  at = x_ic_qc,
  labels = datos_ic_qc$Punto,
  las = 2,
  cex.axis = 0.70
)


6.66 Tamaño del efecto global: Eta cuadrado

\[ \eta^2 = \frac{ SS_B }{ SS_T } \]

donde:

  • \(SS_B\): variabilidad entre puntos;
  • \(SS_T\): variabilidad total.
tabla_anova_qc <- summary(
  modelo_anova_qc
)[[1]]

SS_entre_qc <- tabla_anova_qc[
  "punto",
  "Sum Sq"
]

SS_total_qc <- sum(
  tabla_anova_qc[
    ,
    "Sum Sq"
  ]
)

eta2_qc <- SS_entre_qc /
  SS_total_qc

eta2_qc
## [1] 0.2020074

Una orientación descriptiva:

\(\eta^2\) Magnitud aproximada
0.01 Pequeña
0.06 Moderada
0.14 o superior Grande

6.67 Interpretación microbiológica de Eta cuadrado

Eta cuadrado responde:

¿Qué proporción de la variabilidad microbiológica observada está asociada con diferencias entre puntos?

Un valor elevado significa que la ubicación está asociada con una parte importante de la variabilidad observada.

No implica causalidad.


6.68 Valores atípicos

Una observación atípica estadísticamente no debe eliminarse automáticamente.

Debe revisarse:

  • trazabilidad;
  • procedimiento analítico;
  • condiciones de la muestra;
  • fecha;
  • repetición;
  • posibilidad de un episodio microbiológico real.

Atípico estadístico no significa dato incorrecto.

En un sistema microbiológico puede representar precisamente un evento importante.


6.69 Interpretación estadística integral cuantitativa–cualitativa

No debemos concluir únicamente a partir de:

\[ p<0.05 \]

La interpretación debe integrar:

\[ \boxed{ \text{Boxplot} + N + \bar X + \text{Mediana} + IQR } \]

\[ + \]

\[ \boxed{ \text{Normalidad} + \text{Varianzas} } \]

\[ + \]

\[ \boxed{ \text{ANOVA / Welch / Kruskal} } \]

\[ + \]

\[ \boxed{ \text{Post hoc} + IC + \text{Tamaño del efecto} } \]


6.70 Interpretación microbiológica integral cuantitativa–cualitativa

Una conclusión puede redactarse:

Los puntos de muestreo presentaron diferentes perfiles microbiológicos. Algunos mostraron resultados centrales superiores, mientras otros presentaron una mayor dispersión o episodios extremos. La prueba estadística permitió establecer si las diferencias observadas fueron compatibles o no con la variabilidad muestral. La interpretación debe complementarse con las condiciones propias de cada ubicación y con su comportamiento temporal.


6.71 Ruta completa cuantitativa–cualitativa

\[ \boxed{\text{Resultado microbiológico + Punto}} \]

\[ \Downarrow \]

\[ \boxed{\text{Resumen por punto}} \]

\[ \Downarrow \]

\[ \boxed{\text{Boxplot + observaciones}} \]

\[ \Downarrow \]

\[ \boxed{\text{Normalidad + varianzas}} \]

\[ \Downarrow \]

\[ \begin{cases} \text{Welch / ANOVA}\\ \text{Wilcoxon / Kruskal-Wallis} \end{cases} \]

\[ \Downarrow \]

\[ \boxed{\text{Post hoc}} \]

\[ \Downarrow \]

\[ \boxed{\text{IC + tamaño del efecto}} \]

\[ \Downarrow \]

\[ \boxed{\text{Interpretación microbiológica}} \]


7. Relación cuantitativa–cuantitativa por punto de muestreo

Finalmente estudiaremos una relación entre dos variables numéricas.

En nuestra base:

\[ X= \text{Tiempo transcurrido} \]

y:

\[ Y= \log_{10}(\text{NMP/100 mL}) \]

El análisis se realizará independientemente para cada punto.

La pregunta será:

¿Los resultados microbiológicos de cada punto presentan una tendencia a través del tiempo?


7.1 Variable temporal

Definimos:

\[ X_i = \text{Fecha}_i - \text{Fecha inicial} \]

expresada en días.

Por tanto:

\[ X_i=20 \]

significa que la observación ocurrió 20 días después de la primera fecha de referencia.


7.2 Variable microbiológica

La respuesta es:

\[ Y_i = \log_{10} (\text{NMP}_i) \]

La transformación facilita el análisis de datos que abarcan varios órdenes de magnitud.

Por ejemplo:

\[ 10^2=100 \]

\[ 10^4=10\,000 \]

\[ 10^6=1\,000\,000 \]


7.3 Cada muestra produce un par de datos

Cada observación se representa como:

\[ (x_i,y_i) \]

donde:

  • \(x_i\): tiempo;
  • \(y_i\): resultado microbiológico;
  • \(i=1,\ldots,n\);
  • \(n\): número de observaciones del punto.

7.4 Ejemplo didáctico con cinco datos reales

conteo_qq <- table(
  datos$punto
)

puntos_qq <- names(
  conteo_qq[
    conteo_qq >= 5
  ]
)

punto_ejemplo_qq <- puntos_qq[
  1
]

ejemplo_5_qq <- datos[
  datos$punto == punto_ejemplo_qq &
  is.finite(
    datos$dia_estudio
  ) &
  is.finite(
    datos$log10_nmp
  ),
  c(
    "punto",
    "fecha",
    "dia_estudio",
    "resultado_nmp",
    "log10_nmp"
  )
]

ejemplo_5_qq <- ejemplo_5_qq[
  order(
    ejemplo_5_qq$fecha
  ),
]

ejemplo_5_qq <- head(
  ejemplo_5_qq,
  5
)

knitr::kable(
  ejemplo_5_qq,
  digits = 3,
  caption = paste(
    "Cinco observaciones reales del punto",
    punto_ejemplo_qq
  )
)
Cinco observaciones reales del punto PUNTO_11
punto fecha dia_estudio resultado_nmp log10_nmp
PUNTO_11 2026-01-29 0 8164.0 3.912
PUNTO_11 2026-02-10 12 3448.0 3.538
PUNTO_11 2026-03-03 33 8664.0 3.938
PUNTO_11 2026-03-03 33 866.4 2.938
PUNTO_11 2026-03-24 54 4352.0 3.639

7.5 Diagrama de dispersión

El diagrama de dispersión debe ser el primer recurso para estudiar dos variables cuantitativas.

plot(
  ejemplo_5_qq$dia_estudio,
  ejemplo_5_qq$log10_nmp,
  pch = 19,
  cex = 1.2,
  col = "#1596a5",
  xlab = "Días transcurridos",
  ylab = expression(
    log[10]*"(NMP/100 mL)"
  ),
  main = paste(
    "Diagrama de dispersión -",
    punto_ejemplo_qq
  )
)

text(
  ejemplo_5_qq$dia_estudio,
  ejemplo_5_qq$log10_nmp,
  labels = seq_len(
    nrow(
      ejemplo_5_qq
    )
  ),
  pos = 3,
  cex = 0.8,
  col = "#083d5c"
)


7.6 ¿Qué debemos observar en la dispersión?

Debemos revisar:

  • dirección;
  • intensidad;
  • linealidad;
  • curvatura;
  • agrupamientos;
  • observaciones extremas.

7.7 Interpretación microbiológica del diagrama

Una tendencia ascendente puede indicar que los resultados microbiológicos aumentan conforme transcurre el periodo.

Una tendencia descendente puede indicar disminución.

Una nube sin una dirección clara indica ausencia de una tendencia lineal evidente.


7.8 Medias de X y Y

\[ \bar{x} = \frac{1}{n} \sum_{i=1}^{n} x_i \]

\[ \bar{y} = \frac{1}{n} \sum_{i=1}^{n} y_i \]

x5_qq <- ejemplo_5_qq$dia_estudio
y5_qq <- ejemplo_5_qq$log10_nmp

media_x_qq <- mean(
  x5_qq
)

media_y_qq <- mean(
  y5_qq
)

media_x_qq
## [1] 26.4
media_y_qq
## [1] 3.592719

7.9 Covarianza

La covarianza es:

\[ s_{XY} = \frac{ \sum_{i=1}^{n} (x_i-\bar{x}) (y_i-\bar{y}) }{ n-1 } \]

donde:

  • \(s_{XY}\): covarianza;
  • \(x_i-\bar{x}\): desviación temporal;
  • \(y_i-\bar{y}\): desviación microbiológica;
  • \(n-1\): grados de libertad.

7.10 Cálculo paso a paso de la covarianza

tabla_covarianza_qq <- data.frame(
  X = x5_qq,
  Y = y5_qq,
  X_menos_media =
    x5_qq - media_x_qq,
  Y_menos_media =
    y5_qq - media_y_qq,
  Producto =
    (
      x5_qq - media_x_qq
    ) *
    (
      y5_qq - media_y_qq
    )
)

knitr::kable(
  tabla_covarianza_qq,
  digits = 4,
  caption = "Cálculo de la covarianza"
)
Cálculo de la covarianza
X Y X_menos_media Y_menos_media Producto
0 3.9119 -26.4 0.3192 -8.4265
12 3.5376 -14.4 -0.0552 0.7942
33 3.9377 6.6 0.3450 2.2770
33 2.9377 6.6 -0.6550 -4.3230
54 3.6387 27.6 0.0460 1.2688
suma_productos_qq <- sum(
  (
    x5_qq - media_x_qq
  ) *
  (
    y5_qq - media_y_qq
  )
)

cov_manual_qq <- suma_productos_qq /
  (
    length(
      x5_qq
    ) - 1
  )

cov_manual_qq
## [1] -2.102378
cov(
  x5_qq,
  y5_qq
)
## [1] -2.102378

7.11 Interpretación de la covarianza

Si:

\[ s_{XY}>0 \]

las variables tienden a moverse en el mismo sentido.

Si:

\[ s_{XY}<0 \]

tienden a moverse en sentidos contrarios.

Su magnitud depende de las unidades, por lo que se utiliza principalmente como paso conceptual hacia la correlación.


7.12 Correlación de Pearson

Pearson mide la dirección y fuerza de una relación lineal.

\[ r = \frac{ s_{XY} }{ s_Xs_Y } \]

o:

\[ r = \frac{ \sum (x_i-\bar{x}) (y_i-\bar{y}) }{ \sqrt{ \sum (x_i-\bar{x})^2 } \sqrt{ \sum (y_i-\bar{y})^2 } } \]

donde:

  • \(r\): correlación muestral;
  • \(s_{XY}\): covarianza;
  • \(s_X\): desviación estándar de \(X\);
  • \(s_Y\): desviación estándar de \(Y\).

7.13 Rango de Pearson

\[ -1 \leq r \leq 1 \]

Si:

\[ r>0 \]

la relación es positiva.

Si:

\[ r<0 \]

es negativa.

Si:

\[ r\approx0 \]

la asociación lineal es débil.


7.14 Magnitud orientativa de Pearson

\(|r|\) Interpretación
0.00–0.19 Muy débil
0.20–0.39 Débil
0.40–0.59 Moderada
0.60–0.79 Fuerte
0.80–1.00 Muy fuerte

7.15 Cálculo de Pearson

sx_qq <- sd(
  x5_qq
)

sy_qq <- sd(
  y5_qq
)

r_manual_qq <- cov_manual_qq /
  (
    sx_qq *
    sy_qq
  )

r_manual_qq
## [1] -0.2481456
cor(
  x5_qq,
  y5_qq,
  method = "pearson"
)
## [1] -0.2481456

7.16 Inferencia sobre Pearson

Queremos evaluar:

\[ H_0: \rho=0 \]

frente a:

\[ H_1: \rho\neq0 \]

El estadístico es:

\[ t = r \sqrt{ \frac{ n-2 }{ 1-r^2 } } \]

con:

\[ gl=n-2 \]

prueba_pearson_qq <- cor.test(
  x5_qq,
  y5_qq,
  method = "pearson"
)

prueba_pearson_qq
## 
##  Pearson's product-moment correlation
## 
## data:  x5_qq and y5_qq
## t = -0.44368, df = 3, p-value = 0.6873
## alternative hypothesis: true correlation is not equal to 0
## 95 percent confidence interval:
##  -0.9273802  0.8118623
## sample estimates:
##        cor 
## -0.2481456

7.17 Intervalo de confianza de Pearson

prueba_pearson_qq$conf.int
## [1] -0.9273802  0.8118623
## attr(,"conf.level")
## [1] 0.95

Si el intervalo incluye:

\[ 0 \]

la muestra es compatible con ausencia de correlación lineal poblacional.


7.18 Interpretación microbiológica de Pearson

Una correlación positiva significa que dentro del punto los resultados tienden a aumentar conforme avanza el tiempo.

Una correlación negativa indica tendencia contraria.

Una correlación pequeña indica poca relación lineal temporal.


7.19 Correlación de Spearman

Cuando existe:

  • fuerte asimetría;
  • valores extremos;
  • relación monotónica no lineal;

puede utilizarse Spearman.

Cuando no existen empates:

\[ \rho_s = 1- \frac{ 6\sum d_i^2 }{ n(n^2-1) } \]

donde:

  • \(\rho_s\): correlación de Spearman;
  • \(d_i\): diferencia entre rangos;
  • \(n\): número de observaciones.
cor.test(
  x5_qq,
  y5_qq,
  method = "spearman",
  exact = FALSE
)
## 
##  Spearman's rank correlation rho
## 
## data:  x5_qq and y5_qq
## S = 22.052, p-value = 0.8696
## alternative hypothesis: true rho is not equal to 0
## sample estimates:
##        rho 
## -0.1025978

7.20 ¿Pearson o Spearman?

Situación Método
Relación aproximadamente lineal Pearson
Ausencia de observaciones extremadamente influyentes Pearson
Fuerte asimetría Spearman
Valores extremos Spearman
Relación monotónica no lineal Spearman

7.21 Modelo de regresión lineal simple

El modelo es:

\[ Y_i = \beta_0 + \beta_1X_i + \varepsilon_i \]

donde:

  • \(Y_i\): resultado microbiológico;
  • \(X_i\): tiempo;
  • \(\beta_0\): intercepto poblacional;
  • \(\beta_1\): pendiente poblacional;
  • \(\varepsilon_i\): componente aleatorio.

7.22 Interpretación del intercepto

\[ \beta_0 \]

representa la respuesta promedio esperada cuando:

\[ X=0 \]

En este análisis corresponde al inicio del periodo utilizado como referencia.


7.23 Interpretación de la pendiente

\[ \beta_1 \]

representa el cambio promedio esperado en:

\[ \log_{10}(\text{NMP/100 mL}) \]

por cada día adicional.

Si:

\[ \beta_1>0 \]

la tendencia es creciente.

Si:

\[ \beta_1<0 \]

la tendencia es decreciente.


7.24 Método de mínimos cuadrados

La recta estimada es:

\[ \hat Y = b_0+ b_1X \]

Los coeficientes minimizan:

\[ SSE = \sum_{i=1}^{n} (y_i-\hat y_i)^2 \]

donde:

  • \(SSE\): suma de cuadrados de residuos;
  • \(y_i\): valor observado;
  • \(\hat y_i\): valor estimado.

7.25 Estimación de la pendiente

\[ b_1 = \frac{ \sum (x_i-\bar{x}) (y_i-\bar{y}) }{ \sum (x_i-\bar{x})^2 } \]


7.26 Estimación del intercepto

\[ b_0 = \bar y- b_1\bar x \]


7.27 Cálculo manual de pendiente e intercepto

numerador_b1_qq <- sum(
  (
    x5_qq -
    media_x_qq
  ) *
  (
    y5_qq -
    media_y_qq
  )
)

denominador_b1_qq <- sum(
  (
    x5_qq -
    media_x_qq
  )^2
)

b1_qq <- numerador_b1_qq /
  denominador_b1_qq

b0_qq <- media_y_qq -
  b1_qq *
  media_x_qq

b0_qq
## [1] 3.719351
b1_qq
## [1] -0.004796664

7.28 Ajuste del modelo en R

modelo_qq <- lm(
  log10_nmp ~ dia_estudio,
  data = ejemplo_5_qq
)

summary(
  modelo_qq
)
## 
## Call:
## lm(formula = log10_nmp ~ dia_estudio, data = ejemplo_5_qq)
## 
## Residuals:
##       1       2       3       4       5 
##  0.1926 -0.1242  0.3767 -0.6233  0.1784 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)   
## (Intercept)  3.719351   0.349920  10.629  0.00178 **
## dia_estudio -0.004797   0.010811  -0.444  0.68732   
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.4527 on 3 degrees of freedom
## Multiple R-squared:  0.06158,    Adjusted R-squared:  -0.2512 
## F-statistic: 0.1969 on 1 and 3 DF,  p-value: 0.6873

7.29 Recta de regresión

plot(
  ejemplo_5_qq$dia_estudio,
  ejemplo_5_qq$log10_nmp,
  pch = 19,
  cex = 1.2,
  col = "#1596a5",
  xlab = "Días transcurridos",
  ylab = expression(
    log[10]*"(NMP/100 mL)"
  ),
  main = paste(
    "Modelo lineal -",
    punto_ejemplo_qq
  )
)

abline(
  modelo_qq,
  col = "#083d5c",
  lwd = 2
)


7.30 Valores ajustados

Para cada observación:

\[ \hat y_i = b_0+ b_1x_i \]


7.31 Residuos

El residuo es:

\[ e_i = y_i- \hat y_i \]

tabla_residuos_qq <- data.frame(
  Dia =
    ejemplo_5_qq$dia_estudio,
  Observado =
    ejemplo_5_qq$log10_nmp,
  Ajustado =
    fitted(
      modelo_qq
    ),
  Residuo =
    residuals(
      modelo_qq
    )
)

knitr::kable(
  tabla_residuos_qq,
  digits = 4,
  caption = "Valores observados, ajustados y residuos"
)
Valores observados, ajustados y residuos
Dia Observado Ajustado Residuo
0 3.9119 3.7194 0.1926
12 3.5376 3.6618 -0.1242
33 3.9377 3.5611 0.3767
33 2.9377 3.5611 -0.6233
54 3.6387 3.4603 0.1784

7.32 Supuestos del modelo lineal

El modelo clásico considera:

Linealidad \[ E(Y|X) = \beta_0+ \beta_1X \]

Media del error \[ E(\varepsilon_i)=0 \]

Homocedasticidad \[ Var( \varepsilon_i|X_i ) = \sigma^2 \]

Normalidad \[ \varepsilon_i \sim N( 0, \sigma^2 ) \]

Independencia \[ Cov( \varepsilon_i, \varepsilon_j ) = 0 \]

para:

\[ i\neq j \]


7.33 Precisión sobre normalidad

En regresión, el supuesto de normalidad se refiere fundamentalmente a:

\[ \boxed{\text{los residuos}} \]

y no exige necesariamente que la variable microbiológica original presente distribución normal.


7.34 Diagnóstico gráfico de residuos

par(
  mfrow = c(
    1,
    2
  )
)

plot(
  fitted(
    modelo_qq
  ),
  residuals(
    modelo_qq
  ),
  pch = 19,
  col = "#1596a5",
  xlab = "Valores ajustados",
  ylab = "Residuos",
  main = "Residuos vs. ajustados"
)

abline(
  h = 0,
  col = "#083d5c",
  lty = 2,
  lwd = 2
)

qqnorm(
  residuals(
    modelo_qq
  ),
  pch = 19,
  col = "#1596a5",
  main = "Q-Q de residuos"
)

qqline(
  residuals(
    modelo_qq
  ),
  col = "#083d5c",
  lwd = 2
)

par(
  mfrow = c(
    1,
    1
  )
)

7.35 Interpretación de residuos

En residuos versus ajustados buscamos:

  • nube aleatoria;
  • ausencia de curvatura;
  • ausencia de embudo;
  • ausencia de patrones sistemáticos.

En Q-Q buscamos una aproximación razonable a la línea.


7.36 Inferencia sobre la pendiente

Las hipótesis son:

\[ H_0: \beta_1=0 \]

\[ H_1: \beta_1\neq0 \]

El estadístico es:

\[ t = \frac{ b_1 }{ SE(b_1) } \]

con:

\[ gl=n-2 \]


7.37 Error estándar residual

\[ s = \sqrt{ \frac{ SSE }{ n-2 } } \]

summary(
  modelo_qq
)$sigma
## [1] 0.4526758

7.38 Error estándar de la pendiente

\[ SE(b_1) = \frac{ s }{ \sqrt{ \sum (x_i-\bar{x})^2 } } \]

coef(
  summary(
    modelo_qq
  )
)
##                 Estimate Std. Error    t value    Pr(>|t|)
## (Intercept)  3.719351128 0.34992042 10.6291343 0.001779544
## dia_estudio -0.004796664 0.01081114 -0.4436778 0.687324309

7.39 Intervalo de confianza de la pendiente

\[ IC_{95\%} (\beta_1) = b_1 \pm t_{\alpha/2,n-2} SE(b_1) \]

confint(
  modelo_qq,
  level = 0.95
)
##                   2.5 %     97.5 %
## (Intercept)  2.60574819 4.83295407
## dia_estudio -0.03920254 0.02960921

Si el intervalo incluye:

\[ 0 \]

los datos son compatibles con ausencia de tendencia lineal.


7.40 Coeficiente de determinación

\[ R^2 = 1- \frac{ SSE }{ SST } \]

donde:

\[ SSE = \sum (y_i-\hat y_i)^2 \]

y:

\[ SST = \sum (y_i-\bar y)^2 \]

summary(
  modelo_qq
)$r.squared
## [1] 0.06157625

7.41 Relación entre Pearson y R²

En regresión lineal simple con intercepto:

\[ R^2 = r^2 \]

Pearson conserva la dirección mediante el signo.

\(R^2\) expresa proporción de variabilidad explicada y no tiene signo.


7.42 Interpretación microbiológica de R²

Un \(R^2\) elevado indica que la tendencia temporal lineal representa una parte importante de la variabilidad observada en el punto.

Un \(R^2\) pequeño indica que el tiempo explica poco y que otros factores pueden estar relacionados con el comportamiento microbiológico.


7.43 Estimación de la media esperada

Para:

\[ X=x_0 \]

la respuesta estimada es:

\[ \hat Y_0 = b_0+ b_1x_0 \]

El error estándar de la media estimada es:

\[ SE( \hat Y_0 ) = s \sqrt{ \frac{1}{n} + \frac{ (x_0-\bar{x})^2 }{ \sum (x_i-\bar{x})^2 } } \]


7.44 Intervalo de confianza de la media

nuevo_dia_qq <- data.frame(
  dia_estudio = median(
    ejemplo_5_qq$dia_estudio
  )
)

IC_media_qq <- predict(
  modelo_qq,
  newdata = nuevo_dia_qq,
  interval = "confidence",
  level = 0.95
)

IC_media_qq
##        fit      lwr      upr
## 1 3.561061 2.877951 4.244172

Este intervalo responde:

¿Dónde se encuentra el promedio esperado?


7.45 Intervalo de predicción

Para una nueva observación individual:

IP_qq <- predict(
  modelo_qq,
  newdata = nuevo_dia_qq,
  interval = "prediction",
  level = 0.95
)

IP_qq
##        fit      lwr      upr
## 1 3.561061 1.966691 5.155431

El intervalo de predicción responde:

¿Dónde podría encontrarse una nueva muestra individual?


7.46 Diferencia entre confianza y predicción

\[ \boxed{ \text{IC de la media} } \]

estima incertidumbre sobre el promedio.

\[ \boxed{ \text{Intervalo de predicción} } \]

incluye además la variabilidad de una nueva observación.

Por eso:

\[ IP_{95\%} \]

normalmente es más amplio.


7.47 Gráfico con intervalo de confianza e intervalo de predicción

secuencia_qq <- data.frame(
  dia_estudio = seq(
    min(
      ejemplo_5_qq$dia_estudio
    ),
    max(
      ejemplo_5_qq$dia_estudio
    ),
    length.out = 100
  )
)

banda_ic_qq <- predict(
  modelo_qq,
  newdata = secuencia_qq,
  interval = "confidence",
  level = 0.95
)

banda_ip_qq <- predict(
  modelo_qq,
  newdata = secuencia_qq,
  interval = "prediction",
  level = 0.95
)

plot(
  ejemplo_5_qq$dia_estudio,
  ejemplo_5_qq$log10_nmp,
  pch = 19,
  col = "#1596a5",
  xlab = "Días transcurridos",
  ylab = expression(
    log[10]*"(NMP/100 mL)"
  ),
  main = paste(
    "Regresión e intervalos -",
    punto_ejemplo_qq
  )
)

lines(
  secuencia_qq$dia_estudio,
  banda_ic_qq[
    ,
    "fit"
  ],
  col = "#083d5c",
  lwd = 2
)

lines(
  secuencia_qq$dia_estudio,
  banda_ic_qq[
    ,
    "lwr"
  ],
  col = "#2c9f80",
  lty = 2,
  lwd = 1.5
)

lines(
  secuencia_qq$dia_estudio,
  banda_ic_qq[
    ,
    "upr"
  ],
  col = "#2c9f80",
  lty = 2,
  lwd = 1.5
)

lines(
  secuencia_qq$dia_estudio,
  banda_ip_qq[
    ,
    "lwr"
  ],
  col = "#315f66",
  lty = 3,
  lwd = 1.2
)

lines(
  secuencia_qq$dia_estudio,
  banda_ip_qq[
    ,
    "upr"
  ],
  col = "#315f66",
  lty = 3,
  lwd = 1.2
)

legend(
  "topright",
  legend = c(
    "Recta estimada",
    "IC 95% de la media",
    "IP 95% individual"
  ),
  col = c(
    "#083d5c",
    "#2c9f80",
    "#315f66"
  ),
  lty = c(
    1,
    2,
    3
  ),
  lwd = c(
    2,
    1.5,
    1.2
  ),
  bty = "n",
  cex = 0.75
)


7.48 Regresar a la escala microbiológica original

Como:

\[ Y = \log_{10}(\text{NMP}) \]

entonces:

\[ \text{NMP} = 10^Y \]

Por ejemplo:

10^(
  IC_media_qq
)
##        fit      lwr      upr
## 1 3639.663 755.0066 17545.74

La retrotransformación de la media en escala logarítmica no coincide necesariamente con la media aritmética en la escala original.


7.49 Extrapolación

La predicción es más confiable dentro del rango observado:

\[ X_{\min} \leq x_0 \leq X_{\max} \]

Predecir fuera de este intervalo se denomina:

\[ \boxed{\text{extrapolación}} \]

y debe interpretarse con mucha cautela.


7.50 Aplicación cuantitativa–cuantitativa a todos los puntos

Ahora repetiremos:

\[ \text{Tiempo} \quad \text{vs.} \quad \log_{10}(\text{NMP}) \]

independientemente para cada punto.


7.51 Diagramas de dispersión para todos los puntos

par_original_disp <- par(
  no.readonly = TRUE
)

par(
  mfrow = c(5,4),
  mar = c(3.5,3.5,2.8,1),
  oma = c(5,4.5,4.5,1)
)

for (
  p in puntos_mod3
) {
  
  dp <- datos[
    datos$punto == p &
    is.finite(
      datos$dia_estudio
    ) &
    is.finite(
      datos$log10_nmp
    ),
  ]
  
  if (
    nrow(dp) > 0
  ) {
    
    plot(
      dp$dia_estudio,
      dp$log10_nmp,
      pch = 19,
      cex = 0.7,
      col = "#1596a5",
      main = p,
      xlab = "",
      ylab = "",
      cex.main = 0.86,
      cex.axis = 0.67
    )
    
    if (
      nrow(dp) >= 3 &&
      length(
        unique(
          dp$dia_estudio
        )
      ) >= 2
    ) {
      
      mod_p <- lm(
        log10_nmp ~ dia_estudio,
        data = dp
      )
      
      abline(
        mod_p,
        col = "#083d5c",
        lwd = 2
      )
    }
  }
}

mtext(
  "Días transcurridos",
  side = 1,
  outer = TRUE,
  line = 2.8
)

mtext(
  expression(
    log[10]*"(NMP/100 mL)"
  ),
  side = 2,
  outer = TRUE,
  line = 2.8
)

mtext(
  "Relación temporal por punto",
  side = 3,
  outer = TRUE,
  line = 1.7,
  font = 2
)

mtext(
  "Línea continua: tendencia lineal estimada",
  side = 3,
  outer = TRUE,
  line = 0.3,
  cex = 0.75
)

par(
  par_original_disp
)


7.52 Pearson y Spearman para todos los puntos

correlaciones_puntos_qq <- do.call(
  rbind,
  lapply(
    split(
      datos,
      datos$punto
    ),
    function(df) {
      
      df <- df[
        is.finite(
          df$dia_estudio
        ) &
        is.finite(
          df$log10_nmp
        ),
      ]
      
      if (
        nrow(df) >= 4 &&
        length(
          unique(
            df$dia_estudio
          )
        ) >= 2 &&
        length(
          unique(
            df$log10_nmp
          )
        ) >= 2
      ) {
        
        pearson <- cor.test(
          df$dia_estudio,
          df$log10_nmp,
          method = "pearson"
        )
        
        spearman <- suppressWarnings(
          cor.test(
            df$dia_estudio,
            df$log10_nmp,
            method = "spearman",
            exact = FALSE
          )
        )
        
        data.frame(
          N = nrow(df),
          Pearson_r =
            unname(
              pearson$estimate
            ),
          Pearson_IC_inf =
            pearson$conf.int[1],
          Pearson_IC_sup =
            pearson$conf.int[2],
          Pearson_p =
            pearson$p.value,
          Spearman_rho =
            unname(
              spearman$estimate
            ),
          Spearman_p =
            spearman$p.value
        )
        
      } else {
        
        data.frame(
          N = nrow(df),
          Pearson_r = NA_real_,
          Pearson_IC_inf = NA_real_,
          Pearson_IC_sup = NA_real_,
          Pearson_p = NA_real_,
          Spearman_rho = NA_real_,
          Spearman_p = NA_real_
        )
      }
    }
  )
)

correlaciones_puntos_qq$Punto <- rownames(
  correlaciones_puntos_qq
)

rownames(
  correlaciones_puntos_qq
) <- NULL

correlaciones_puntos_qq <- correlaciones_puntos_qq[
  ,
  c(
    "Punto",
    "N",
    "Pearson_r",
    "Pearson_IC_inf",
    "Pearson_IC_sup",
    "Pearson_p",
    "Spearman_rho",
    "Spearman_p"
  )
]

knitr::kable(
  correlaciones_puntos_qq,
  digits = 4,
  caption = "Correlaciones temporales por punto"
)
Correlaciones temporales por punto
Punto N Pearson_r Pearson_IC_inf Pearson_IC_sup Pearson_p Spearman_rho Spearman_p
PUNTO_11 21 -0.5432 -0.7897 -0.1457 0.0109 -0.5327 0.0129
PUNTO_16 21 -0.1616 -0.5546 0.2903 0.4839 -0.1612 0.4850
PUNTO_17 21 -0.4929 -0.7623 -0.0777 0.0232 -0.4670 0.0328
PUNTO_18 21 -0.3851 -0.7004 0.0558 0.0847 -0.4420 0.0448
PUNTO_19 22 0.0184 -0.4063 0.4367 0.9351 0.1552 0.4904
PUNTO_20 21 0.2959 -0.1557 0.6452 0.1928 0.2768 0.2245
PUNTO_21 21 0.0058 -0.4270 0.4364 0.9801 0.0551 0.8125
PUNTO_23 22 -0.0092 -0.4291 0.4140 0.9677 0.1399 0.5346
PUNTO_24 7 -0.2782 -0.8526 0.6007 0.5458 -0.2386 0.6063
PUNTO_25 11 0.0316 -0.5792 0.6198 0.9264 0.2402 0.4768
PUNTO_26 11 0.0384 -0.5747 0.6239 0.9107 0.2575 0.4446
PUNTO_4 6 -0.8146 -0.9790 -0.0089 0.0484 -0.8533 0.0307
PUNTO_5 19 -0.1420 -0.5601 0.3337 0.5620 -0.0053 0.9829
PUNTO_6 20 -0.0811 -0.5055 0.3749 0.7340 0.0075 0.9748
PUNTO_7 21 -0.1032 -0.5121 0.3438 0.6561 0.0961 0.6787
PUNTO_8 20 -0.2752 -0.6398 0.1905 0.2402 -0.2413 0.3054
PUNTO_9 22 -0.1713 -0.5530 0.2698 0.4458 -0.0696 0.7582

7.53 Modelos lineales para todos los puntos

regresiones_puntos_qq <- do.call(
  rbind,
  lapply(
    split(
      datos,
      datos$punto
    ),
    function(df) {
      
      df <- df[
        is.finite(
          df$dia_estudio
        ) &
        is.finite(
          df$log10_nmp
        ),
      ]
      
      if (
        nrow(df) >= 4 &&
        length(
          unique(
            df$dia_estudio
          )
        ) >= 2
      ) {
        
        mod <- lm(
          log10_nmp ~ dia_estudio,
          data = df
        )
        
        sm <- summary(
          mod
        )
        
        ic <- confint(
          mod,
          "dia_estudio",
          level = 0.95
        )
        
        data.frame(
          N = nrow(df),
          Intercepto =
            coef(mod)[1],
          Pendiente =
            coef(mod)[2],
          IC95_inf =
            ic[1],
          IC95_sup =
            ic[2],
          p_pendiente =
            coef(sm)[
              "dia_estudio",
              "Pr(>|t|)"
            ],
          R2 =
            sm$r.squared,
          R2_ajustado =
            sm$adj.r.squared,
          Error_residual =
            sm$sigma
        )
        
      } else {
        
        data.frame(
          N = nrow(df),
          Intercepto = NA_real_,
          Pendiente = NA_real_,
          IC95_inf = NA_real_,
          IC95_sup = NA_real_,
          p_pendiente = NA_real_,
          R2 = NA_real_,
          R2_ajustado = NA_real_,
          Error_residual = NA_real_
        )
      }
    }
  )
)

regresiones_puntos_qq$Punto <- rownames(
  regresiones_puntos_qq
)

rownames(
  regresiones_puntos_qq
) <- NULL

regresiones_puntos_qq <- regresiones_puntos_qq[
  ,
  c(
    "Punto",
    "N",
    "Intercepto",
    "Pendiente",
    "IC95_inf",
    "IC95_sup",
    "p_pendiente",
    "R2",
    "R2_ajustado",
    "Error_residual"
  )
]

knitr::kable(
  regresiones_puntos_qq,
  digits = 4,
  caption = "Modelos temporales por punto"
)
Modelos temporales por punto
Punto N Intercepto Pendiente IC95_inf IC95_sup p_pendiente R2 R2_ajustado Error_residual
PUNTO_11 21 3.5094 -0.0065 -0.0113 -0.0017 0.0109 0.2951 0.2580 0.5657
PUNTO_16 21 2.9326 -0.0024 -0.0094 0.0046 0.4839 0.0261 -0.0251 0.8260
PUNTO_17 21 3.2772 -0.0063 -0.0116 -0.0010 0.0232 0.2429 0.2031 0.6238
PUNTO_18 21 3.4924 -0.0086 -0.0185 0.0013 0.0847 0.1483 0.1035 1.1606
PUNTO_19 22 4.0674 0.0005 -0.0122 0.0132 0.9351 0.0003 -0.0496 1.5551
PUNTO_20 21 2.6201 0.0090 -0.0049 0.0229 0.1928 0.0876 0.0395 1.6383
PUNTO_21 21 2.9350 0.0001 -0.0104 0.0107 0.9801 0.0000 -0.0526 1.2388
PUNTO_23 22 4.1184 -0.0002 -0.0127 0.0122 0.9677 0.0001 -0.0499 1.5317
PUNTO_24 7 6.8535 -0.0201 -0.0999 0.0597 0.5458 0.0774 -0.1071 2.3001
PUNTO_25 11 3.7931 0.0018 -0.0415 0.0452 0.9264 0.0010 -0.1100 1.5940
PUNTO_26 11 3.9041 0.0022 -0.0410 0.0454 0.9107 0.0015 -0.1095 1.5868
PUNTO_4 6 6.1470 -0.0512 -0.1018 -0.0006 0.0484 0.6635 0.5794 1.5104
PUNTO_5 19 5.7209 -0.0068 -0.0312 0.0176 0.5620 0.0202 -0.0375 2.6132
PUNTO_6 20 5.0648 -0.0030 -0.0210 0.0150 0.7340 0.0066 -0.0486 2.0286
PUNTO_7 21 4.5412 -0.0035 -0.0196 0.0127 0.6561 0.0107 -0.0414 1.9137
PUNTO_8 20 5.7914 -0.0101 -0.0277 0.0074 0.2402 0.0758 0.0244 1.9924
PUNTO_9 22 4.8593 -0.0063 -0.0230 0.0105 0.4458 0.0294 -0.0192 2.0595

7.54 Normalidad de los residuos por punto

normalidad_residuos_qq <- do.call(
  rbind,
  lapply(
    split(
      datos,
      datos$punto
    ),
    function(df) {
      
      df <- df[
        is.finite(
          df$dia_estudio
        ) &
        is.finite(
          df$log10_nmp
        ),
      ]
      
      if (
        nrow(df) >= 4 &&
        length(
          unique(
            df$dia_estudio
          )
        ) >= 2
      ) {
        
        mod <- lm(
          log10_nmp ~ dia_estudio,
          data = df
        )
        
        res <- residuals(
          mod
        )
        
        if (
          length(res) >= 3 &&
          length(
            unique(
              res
            )
          ) > 1
        ) {
          
          sh <- shapiro.test(
            res
          )
          
          data.frame(
            N = length(res),
            W =
              unname(
                sh$statistic
              ),
            p_value =
              sh$p.value
          )
          
        } else {
          
          data.frame(
            N = length(res),
            W = NA_real_,
            p_value = NA_real_
          )
        }
        
      } else {
        
        data.frame(
          N = nrow(df),
          W = NA_real_,
          p_value = NA_real_
        )
      }
    }
  )
)

normalidad_residuos_qq$Punto <- rownames(
  normalidad_residuos_qq
)

rownames(
  normalidad_residuos_qq
) <- NULL

normalidad_residuos_qq$Decision <- ifelse(
  is.na(
    normalidad_residuos_qq$p_value
  ),
  "No evaluable",
  ifelse(
    normalidad_residuos_qq$p_value < 0.05,
    "Evidencia contra normalidad",
    "No se rechaza normalidad"
  )
)

normalidad_residuos_qq <- normalidad_residuos_qq[
  ,
  c(
    "Punto",
    "N",
    "W",
    "p_value",
    "Decision"
  )
]

knitr::kable(
  normalidad_residuos_qq,
  digits = 4,
  caption = "Normalidad de residuos de los modelos temporales por punto"
)
Normalidad de residuos de los modelos temporales por punto
Punto N W p_value Decision
PUNTO_11 21 0.9208 0.0901 No se rechaza normalidad
PUNTO_16 21 0.9698 0.7279 No se rechaza normalidad
PUNTO_17 21 0.9635 0.5884 No se rechaza normalidad
PUNTO_18 21 0.9542 0.4070 No se rechaza normalidad
PUNTO_19 22 0.8448 0.0028 Evidencia contra normalidad
PUNTO_20 21 0.8928 0.0255 Evidencia contra normalidad
PUNTO_21 21 0.9345 0.1693 No se rechaza normalidad
PUNTO_23 22 0.8727 0.0088 Evidencia contra normalidad
PUNTO_24 7 0.8803 0.2277 No se rechaza normalidad
PUNTO_25 11 0.7692 0.0037 Evidencia contra normalidad
PUNTO_26 11 0.7572 0.0026 Evidencia contra normalidad
PUNTO_4 6 0.9121 0.4504 No se rechaza normalidad
PUNTO_5 19 0.8485 0.0063 Evidencia contra normalidad
PUNTO_6 20 0.8099 0.0012 Evidencia contra normalidad
PUNTO_7 21 0.9009 0.0365 Evidencia contra normalidad
PUNTO_8 20 0.8881 0.0248 Evidencia contra normalidad
PUNTO_9 22 0.8392 0.0022 Evidencia contra normalidad

7.55 Interpretación estadística integral cuantitativa–cuantitativa

Para cada punto debemos considerar simultáneamente:

\[ \boxed{ \text{Dispersión} + r + IC(r) + \rho_s } \]

\[ + \]

\[ \boxed{ b_1 + IC(\beta_1) + p + R^2 } \]

\[ + \]

\[ \boxed{ \text{Diagnóstico de residuos} } \]

Una conclusión no debe construirse únicamente a partir del valor \(p\).


7.56 Interpretación microbiológica integral cuantitativa–cuantitativa

Para cada punto se debe responder:

¿Los resultados microbiológicos aumentan o disminuyen a través del tiempo?

¿Qué tan fuerte es esa tendencia?

¿Qué tan grande es el cambio estimado?

¿Cuánta incertidumbre existe?

¿Qué proporción de la variabilidad puede ser representada mediante una tendencia lineal?

Una pendiente positiva puede indicar una tendencia microbiológica creciente.

Una pendiente negativa puede indicar una tendencia decreciente.

Una pendiente cercana a cero indica poca tendencia lineal.

Sin embargo:

\[ \boxed{ \text{asociación temporal} \neq \text{causalidad} } \]

Pueden intervenir:

  • lluvia;
  • temperatura;
  • operación;
  • características ambientales;
  • condiciones hidráulicas;
  • condiciones de muestreo;
  • otras variables no registradas.

7.57 Consideración sobre mediciones repetidas

Un mismo punto aparece en diferentes fechas.

Por tanto, las observaciones pueden presentar dependencia temporal.

Los métodos presentados constituyen una primera aproximación estadística.

Para análisis avanzados podrían utilizarse:

  • modelos longitudinales;
  • modelos de efectos mixtos;
  • modelos de medidas repetidas;
  • modelos de series temporales.

7.58 Ruta completa cuantitativa–cuantitativa

\[ \boxed{\text{Seleccionar punto}} \]

\[ \Downarrow \]

\[ \boxed{\text{Tiempo + Resultado}} \]

\[ \Downarrow \]

\[ \boxed{\text{Diagrama de dispersión}} \]

\[ \Downarrow \]

\[ \boxed{\text{Covarianza}} \]

\[ \Downarrow \]

\[ \boxed{\text{Pearson / Spearman}} \]

\[ \Downarrow \]

\[ \boxed{ Y= \beta_0+ \beta_1X+ \varepsilon } \]

\[ \Downarrow \]

\[ \boxed{\text{Pendiente + IC + }p} \]

\[ \Downarrow \]

\[ \boxed{\text{Diagnóstico de residuos}} \]

\[ \Downarrow \]

\[ \boxed{R^2} \]

\[ \Downarrow \]

\[ \boxed{\text{IC de media + Predicción}} \]

\[ \Downarrow \]

\[ \boxed{\text{Interpretación microbiológica}} \]


8. Comparación de los tres tipos de análisis

Tipo Pregunta Método principal Medida de magnitud
Cuali–Cuali ¿La frecuencia de alerta depende del punto? Chi/Fisher OR, V de Cramér
Cuanti–Cuali ¿Los resultados difieren entre puntos? Welch/ANOVA/Kruskal \(d,\eta^2\)
Cuanti–Cuanti ¿El resultado cambia con el tiempo dentro del punto? Pearson/Spearman/regresión \(r,\rho_s,b_1,R^2\)

8.1 Significación estadística y magnitud no son lo mismo

Un valor:

\[ p<0.001 \]

indica evidencia estadística.

No indica automáticamente una relación fuerte o una diferencia grande.

Por ello:

\[ \boxed{ p \neq \text{magnitud del efecto} } \]

Siempre debemos reportar también:

  • OR;
  • V de Cramér;
  • Cohen \(d\);
  • Eta cuadrado;
  • correlación;
  • pendiente;
  • \(R^2\);

según corresponda.


8.2 Asociación no significa causalidad

Puede observarse:

\[ \text{Punto} \leftrightarrow \text{Alerta} \]

o:

\[ \text{Tiempo} \leftrightarrow \text{Resultado} \]

sin demostrar:

\[ X \longrightarrow Y \]

Pueden existir factores adicionales no observados.


8.3 Significación estadística versus relevancia microbiológica

La pregunta estadística es:

¿Existe evidencia de una diferencia o asociación?

La pregunta microbiológica es:

¿La magnitud y naturaleza de esa diferencia tiene importancia para comprender el comportamiento del sistema?

Ambas deben responderse.


8.4 El punto siempre debe permanecer visible en la interpretación

El análisis final debe permitir responder:

  • ¿qué puntos presentan más alertas?
  • ¿qué puntos presentan niveles mayores?
  • ¿qué puntos presentan mayor variabilidad?
  • ¿qué puntos muestran tendencias temporales?
  • ¿qué puntos presentan comportamientos inusuales?

9. Actividad de aplicación

Cada estudiante trabajará con el periodo temporal asignado.

Todos utilizarán la misma base, pero cada estudiante o grupo analizará un periodo diferente.

Deberán desarrollar los tres tipos de análisis.

Cualitativa–Cualitativa \[ \text{Punto} \times \text{Alerta} \]

Deberán incluir:

  • tabla de contingencia;
  • frecuencias;
  • proporciones;
  • porcentajes;
  • gráfico;
  • frecuencias esperadas;
  • Chi-cuadrado o Fisher;
  • V de Cramér;
  • residuos;
  • interpretación estadística;
  • interpretación microbiológica.

Cuantitativa–Cualitativa \[ \text{Resultado} \times \text{Punto} \]

Deberán incluir:

  • resumen descriptivo;
  • boxplot;
  • evaluación de normalidad;
  • evaluación de varianzas;
  • selección entre Welch, ANOVA o Kruskal-Wallis;
  • comparaciones posteriores;
  • intervalos;
  • tamaño del efecto;
  • interpretación estadística;
  • interpretación microbiológica.

Cuantitativa–Cuantitativa \[ \text{Tiempo} \times \text{Resultado} \]

analizado dentro de cada punto.

Deberán incluir:

  • diagrama de dispersión;
  • Pearson o Spearman;
  • regresión;
  • pendiente;
  • intervalo de confianza;
  • valor \(p\);
  • \(R^2\);
  • diagnóstico de residuos;
  • interpretación estadística;
  • interpretación microbiológica.

10. Síntesis del módulo

1. La naturaleza de las variables determina el procedimiento estadístico.

2. El punto de muestreo constituye el eje principal del análisis.

3. Cuali–Cuali estudia asociaciones entre categorías.

4. Cuanti–Cuali compara una variable numérica entre puntos.

5. Cuanti–Cuanti estudia relaciones numéricas y tendencias temporales.

6. El gráfico debe preceder a la inferencia.

7. Los supuestos orientan la selección de la prueba.

8. El valor p no mide la magnitud del efecto.

9. Los intervalos de confianza expresan incertidumbre.

10. Una asociación estadística no implica causalidad.

11. Una diferencia estadística debe evaluarse también por su importancia microbiológica.

12. Toda conclusión debe integrar estadística, punto de muestreo y contexto microbiológico.

Referencias

Blair, R. C., & Taylor, R. A. (2008). Bioestadística. Pearson Educación.

Bermúdez, J. D. (2013). Diez lecciones de estadística básica. Universitat de València.

Díaz Portillo, J. (2011). Guía práctica del curso de Bioestadística aplicada a las Ciencias de la Salud. Instituto Nacional de Gestión Sanitaria.

Yousefi, M., Najafi Saleh, H., Yaseri, M., Mahvi, A. H., Soleimani, H., Saeedi, Z., Zohdi, S., & Mohammadi, A. A. (2018). Data on microbiological quality assessment of rural drinking water supplies in Poldasht county. Data in Brief, 17, 763–769. https://doi.org/10.1016/j.dib.2018.02.003