Modelizar la curva de acidificación en un caso real

Análisis de datos de elaboración

queso
estadística
modelos
R
FP
PUBLICADO

22 de abril de 2026

Práctica con datos reales: elaboraciones en Formación Profesional

Analizamos en este artículo algunos datos de producción obtenidos durante las prácticas del Curso de Especialización en Tecnología y Gestión Quesera, impartido en el Centro de Formación Profesional Juan de Villanueva (Pola de Siero). Retomamos aquí los conceptos presentados en el artículo anterior sobre la modelización de las curvas de acidificación; puede ser útil revisarlo para comprender los distintos modelos, los criterios que justifican la elección de la curva logística estándar como la más adecuada en este caso y la interpretación de los parámetros que la definen.

Los datos del aula: abundantes en variabilidad, limitados en densidad

Las prácticas de elaboración de queso en FP de Industrias Alimentarias generan datos reales de proceso, pero con una estructura muy diferente a los datos de laboratorio industrial. Las medidas de pH se toman de forma manual, en los momentos que la dinámica del aula permite: al inicio antes de añadir los fermentos, en los controles periódicos durante la cuba, y en los puntos críticos del proceso (corte de cuajada, inicio de prensado, entrada en salmuera). El resultado es un conjunto de datos con tres características que condicionan el análisis.

Veamos los datos originales; para ello inicializamos las bibliotecas que vamos a necesitar y leemoslos datos:

Mostrar código R
library(dplyr)
library(tidyr)
library(readr)
library(ggplot2)
library(lubridate)
library(knitr)
library(broom)
library(gt)

datos_raw <- read_csv2("../../datos/curvas_acidificacion.csv",
                       locale = locale(encoding = "ISO-8859-1"),
                       show_col_types = FALSE)
datos_raw |>
  gt()
Tabla 1: Tabla original de datos
lote fecha_fab hora_control punto_control pH temp
1 21-ene 21/01/2026 8:40 fermentos 6.83 4.0
1 21-ene NA cuba NA NA
1 21-ene 21/01/2026 11:30 cuba 6.67 32.0
1 21-ene 21/01/2026 12:50 cuba 6.60 38.0
1 21-ene 21/01/2026 14:05 moldeo 6.05 37.0
1 21-ene 21/01/2026 15:00 prensa 5.80 NA
1 21-ene 21/01/2026 17:15 cámara 5.30 NA
1 21-ene 22/01/2026 10:00 cámara 4.80 5.0
2 28-ene 28/01/2026 8:30 fermentos 6.81 NA
2 28-ene 28/01/2026 11:20 cuba 6.80 32.0
2 28-ene 28/01/2026 11:42 cuba 6.75 32.0
2 28-ene 28/01/2026 12:00 cuba 6.66 32.0
2 28-ene 28/01/2026 12:20 cuba 6.65 37.0
2 28-ene 28/01/2026 14:10 moldeo 6.30 37.0
2 28-ene 28/01/2026 14:30 moldeo NA NA
2 28-ene 28/01/2026 14:40 prensa 6.25 33.4
2 28-ene 28/01/2026 0:00 prensa NA NA
2 28-ene 28/01/2026 16:50 prensa 5.80 25.0
2 28-ene 28/01/2026 18:35 cámara 5.40 16.0
2 28-ene 29/01/2026 10:00 cámara 5.25 5.0
3 04-feb 04/02/2026 8:30 fermentos 6.78 NA
3 04-feb 04/02/2026 10:00 cuba 6.68 32.0
3 04-feb 04/02/2026 10:40 cuba 6.66 32.4
3 04-feb 04/02/2026 11:00 cuba 6.67 32.0
3 04-feb 04/02/2026 11:20 cuba 6.64 32.0
3 04-feb 04/02/2026 13:05 moldeo 6.51 32.0
3 04-feb 04/02/2026 13:30 moldeo 6.51 30.0
3 04-feb 04/02/2026 13:35 prensa 6.51 30.0
3 04-feb 04/02/2026 17:40 prensa 6.02 23.6
3 04-feb 04/02/2026 18:30 prensa 5.93 22.0
3 04-feb 04/02/2026 19:00 cámara 5.82 20.0
3 04-feb 05/02/2026 10:20 cámara 5.50 5.0
4 18-ene 18/01/2026 9:15 fermentos 6.78 27.0
4 18-ene 18/01/2026 10:05 cuba 6.68 36.0
4 18-ene 18/01/2026 10:10 cuba 6.75 34.0
4 18-ene 18/01/2026 11:35 cuba 6.63 32.0
4 18-ene 18/01/2026 12:00 moldeo 6.60 32.0
4 18-ene 18/01/2026 13:45 prensa 6.48 34.5
4 18-ene 18/01/2026 15:00 prensa NA NA
4 18-ene 18/01/2026 17:00 prensa NA NA
4 18-ene 18/01/2026 19:15 prensa 5.45 26.0
4 18-ene 18/01/2026 19:30 cámara 5.45 26.0
4 18-ene 19/01/2026 10:00 cámara 5.25 5.0

La primera característica de los datos es la densidad baja e irregular. Los cuatro lotes que vamos a considerar tienen entre 7 y 11 medidas de pH válidas cada uno, pero no distribuidas uniformemente en el tiempo.

  • El lote 2 concentra cuatro medidas en un intervalo de apenas una hora (de 2,8 h a 3,8 h desde la inoculación), todas en la zona plana inicial donde el pH todavía no ha caído significativamente, y luego salta directamente a las 5,7 h sin cobertura del tramo de máxima actividad.
  • El lote 3 tiene cuatro medidas seguidas entre 1,5 h y 2,8 h, también en la fase plana, y después un hueco de casi dos horas hasta el moldeo a las 4,6 h.
  • El lote 4 presenta dos medidas en los primeros 55 minutos que reflejan la inercia térmica de la leche (un descenso desde 6,68 hasta 6,75 seguido de una recuperación, probablemente por calentamiento de la cuba) y luego un único punto en la fase de prensa antes de los 4,5 h. Esta irregularidad refleja posiblemente el hecho de que en el aula se trabaja con varias tareas simultáneas y que los procesos de corte, desuerado y prensado han tenido prioridad operativa sobre la toma de datos analíticos.

La segunda es la variabilidad entre lotes. Los pH iniciales oscilan entre 6,78 y 6,83, lo que sugiere leches con calidad de partida similar. Pero las temperaturas de cuba en el momento de inoculación difieren notablemente: el lote 4 registra 27°C al añadir los fermentos y sube a 36°C en los siguientes 50 minutos, mientras que los lotes 2 y 3 arrancan directamente a 32°C. Esa diferencia de arranque térmico afecta al \(t_i\) y hace que la comparación directa de velocidades de acidificación entre lotes requiera interpretación cuidadosa.

La tercera es la presencia de datos faltantes estructurales. El lote 1 tiene una fila de cuba sin hora ni pH, que no aporta información al ajuste, y es eliminada en los cálculos. El lote 2 incluye una fila de prensa con hora registrada como medianoche (00:00) — un error de transcripción evidente que produce un tiempo calculado de −8,5 h y debe excluirse. El lote 4 tiene dos filas de prensa consecutivas sin pH, a las 5,75 h y 7,75 h, que dejan un hueco de 5,5 h sin ninguna medida en la fase de caída activa. En todos los lotes, el seguimiento al día siguiente en cámara (en torno a las 25 h) proporciona un punto en el talón, pero con la temperatura ya reducida a 5°C, lo que dificulta determinar si el pH registrado refleja el final de la acidificación activa o una estabilización por frío.

Ajuste a los cuatro lotes de fabricación

Para calcular los tiempos de acidificación de cada lote, agrupamos por lotes, y calculamos los tiempos de referencia por diferencia entre cada valor y el valor inicial en cada grupo, después de haber eliminado las filas para las que no hay valores. El punto de partida es el primer control, que es el momento en el que se han añadido los fermentos. t_refes el tiempo de referencia inicial para cada lote, y hora es el tiempo transcurrido desde ese momento hasta cada control, expresado en fraccion decimal de hora.

Mostrar código R
datos <- datos_raw |>
  filter(!is.na(pH), !is.na(hora_control)) |>
  mutate(hora_control = dmy_hm(hora_control), lote = factor(lote)) |>
  group_by(lote) |>
  mutate(
    t_ref = min(hora_control[punto_control == "fermentos"], na.rm = TRUE),
    hora   = as.numeric(difftime(hora_control, t_ref, units = "hours"))
  ) |>
  filter(hora >= 0) |>
  ungroup()

datos |>
  gt()
Tabla 2: Tabla de datos editados
lote fecha_fab hora_control punto_control pH temp t_ref hora
1 21-ene 2026-01-21 08:40:00 fermentos 6.83 4.0 2026-01-21 08:40:00 0.0000000
1 21-ene 2026-01-21 11:30:00 cuba 6.67 32.0 2026-01-21 08:40:00 2.8333333
1 21-ene 2026-01-21 12:50:00 cuba 6.60 38.0 2026-01-21 08:40:00 4.1666667
1 21-ene 2026-01-21 14:05:00 moldeo 6.05 37.0 2026-01-21 08:40:00 5.4166667
1 21-ene 2026-01-21 15:00:00 prensa 5.80 NA 2026-01-21 08:40:00 6.3333333
1 21-ene 2026-01-21 17:15:00 cámara 5.30 NA 2026-01-21 08:40:00 8.5833333
1 21-ene 2026-01-22 10:00:00 cámara 4.80 5.0 2026-01-21 08:40:00 25.3333333
2 28-ene 2026-01-28 08:30:00 fermentos 6.81 NA 2026-01-28 08:30:00 0.0000000
2 28-ene 2026-01-28 11:20:00 cuba 6.80 32.0 2026-01-28 08:30:00 2.8333333
2 28-ene 2026-01-28 11:42:00 cuba 6.75 32.0 2026-01-28 08:30:00 3.2000000
2 28-ene 2026-01-28 12:00:00 cuba 6.66 32.0 2026-01-28 08:30:00 3.5000000
2 28-ene 2026-01-28 12:20:00 cuba 6.65 37.0 2026-01-28 08:30:00 3.8333333
2 28-ene 2026-01-28 14:10:00 moldeo 6.30 37.0 2026-01-28 08:30:00 5.6666667
2 28-ene 2026-01-28 14:40:00 prensa 6.25 33.4 2026-01-28 08:30:00 6.1666667
2 28-ene 2026-01-28 16:50:00 prensa 5.80 25.0 2026-01-28 08:30:00 8.3333333
2 28-ene 2026-01-28 18:35:00 cámara 5.40 16.0 2026-01-28 08:30:00 10.0833333
2 28-ene 2026-01-29 10:00:00 cámara 5.25 5.0 2026-01-28 08:30:00 25.5000000
3 04-feb 2026-02-04 08:30:00 fermentos 6.78 NA 2026-02-04 08:30:00 0.0000000
3 04-feb 2026-02-04 10:00:00 cuba 6.68 32.0 2026-02-04 08:30:00 1.5000000
3 04-feb 2026-02-04 10:40:00 cuba 6.66 32.4 2026-02-04 08:30:00 2.1666667
3 04-feb 2026-02-04 11:00:00 cuba 6.67 32.0 2026-02-04 08:30:00 2.5000000
3 04-feb 2026-02-04 11:20:00 cuba 6.64 32.0 2026-02-04 08:30:00 2.8333333
3 04-feb 2026-02-04 13:05:00 moldeo 6.51 32.0 2026-02-04 08:30:00 4.5833333
3 04-feb 2026-02-04 13:30:00 moldeo 6.51 30.0 2026-02-04 08:30:00 5.0000000
3 04-feb 2026-02-04 13:35:00 prensa 6.51 30.0 2026-02-04 08:30:00 5.0833333
3 04-feb 2026-02-04 17:40:00 prensa 6.02 23.6 2026-02-04 08:30:00 9.1666667
3 04-feb 2026-02-04 18:30:00 prensa 5.93 22.0 2026-02-04 08:30:00 10.0000000
3 04-feb 2026-02-04 19:00:00 cámara 5.82 20.0 2026-02-04 08:30:00 10.5000000
3 04-feb 2026-02-05 10:20:00 cámara 5.50 5.0 2026-02-04 08:30:00 25.8333333
4 18-ene 2026-01-18 09:15:00 fermentos 6.78 27.0 2026-01-18 09:15:00 0.0000000
4 18-ene 2026-01-18 10:05:00 cuba 6.68 36.0 2026-01-18 09:15:00 0.8333333
4 18-ene 2026-01-18 10:10:00 cuba 6.75 34.0 2026-01-18 09:15:00 0.9166667
4 18-ene 2026-01-18 11:35:00 cuba 6.63 32.0 2026-01-18 09:15:00 2.3333333
4 18-ene 2026-01-18 12:00:00 moldeo 6.60 32.0 2026-01-18 09:15:00 2.7500000
4 18-ene 2026-01-18 13:45:00 prensa 6.48 34.5 2026-01-18 09:15:00 4.5000000
4 18-ene 2026-01-18 19:15:00 prensa 5.45 26.0 2026-01-18 09:15:00 10.0000000
4 18-ene 2026-01-18 19:30:00 cámara 5.45 26.0 2026-01-18 09:15:00 10.2500000
4 18-ene 2026-01-19 10:00:00 cámara 5.25 5.0 2026-01-18 09:15:00 24.7500000

Ahora calculamos los parámetros de la curva logística para los valores de pH de cada lote:

Mostrar código R
analizar_lote <- function(df) {
  
  # 1. Ajuste del modelo logístico
  # Usamos ti como parámetro de tiempo de inflexión
  modelo <- tryCatch({
    nls(pH ~ K + (A - K) / (1 + exp(r * (hora - ti))), 
        data = df,
        start = list(A = max(df$pH), K = min(df$pH), r = 0.5, ti = median(df$hora)))
  }, error = function(e) return(NULL))
  
  if (is.null(modelo)) return(NULL)

  # 2. Extraer coeficientes
  cf <- coef(modelo)
  A  <- cf["A"]
  K  <- cf["K"]
  r  <- cf["r"]
  ti <- cf["ti"]

  # 3. Cálculo de Vmax y estadísticos de calidad
  v_max <- abs(r * (A - K) / 4)
  
  rss <- sum(residuals(modelo)^2)
  tss <- sum((df$pH - mean(df$pH))^2)
  r2  <- 1 - (rss / tss)

  # 4. Crear la tabla de resultados para este lote
  tibble(
    A    = round(A, 2),
    K    = round(K, 2),
    r    = round(r, 4),
    ti   = round(ti, 2),    
    Vmax = round(v_max, 4),
    R2   = round(r2, 4),
    AIC  = round(AIC(modelo), 2)
  )
}

# 5. Ejecutar el análisis para todos los lotes
resultados <- datos |>
  group_by(lote) |>
  reframe(analizar_lote(pick(everything())))

# print(tabla_final)
resultados |>
  gt()
Tabla 3: Tabla de parámetros del modelo logístico
lote A K r ti Vmax R2 AIC
1 6.91 4.83 0.6142 6.28 0.3196 0.9897 -7.06
2 6.87 5.24 0.5998 6.97 0.2449 0.9946 -25.45
3 6.79 5.49 0.3953 8.09 0.1284 0.9972 -48.31
4 6.77 5.25 0.5630 6.85 0.2146 0.9977 -28.21

Análisis de Resultados: Modelización de la Fermentación Láctica

A continuación se resumen los estadísticos clave utilizados para evaluar la calidad del ajuste de las curvas de pH obtenidas.

1. Tabla Comparativa: R² vs. AIC

Característica \(R^2\) (Coeficiente de Determinación) AIC (Criterio de Akaike)
¿Qué mide? El porcentaje de variabilidad de los datos que el modelo explica. La pérdida de información y la eficiencia del modelo.
Interpretación Qué tan “cerca” están los puntos reales de la línea trazada. El equilibrio entre el buen ajuste y la sencillez del modelo.
Valor ideal Lo más cercano a 1 (o 100%). El valor más bajo (el más pequeño o más negativo).
Uso principal Validar la precisión del ajuste en un solo modelo. Comparar modelos para ver cuál es estadísticamente más robusto.
Puntos débiles Tiende a subir siempre que añadimos parámetros, aunque no sean útiles. No indica el % de éxito, solo indica qué modelo es preferible.

2. Definiciones

Coeficiente de Determinación (\(R^2\))

El \(R^2\) es una medida de la bondad de ajuste. Un valor de 0.99 significa que el modelo logístico explica el 99% de los cambios de pH observados durante la fermentación. Si el valor es bajo (por ejemplo, menor a 0.90), indica que los datos tienen mucho “ruido” o que la fermentación no siguió un curso normal.

Criterio de Información de Akaike (AIC)

El AIC es un estadístico de selección de modelos. Su función es penalizar a los modelos que son demasiado complejos (con demasiados parámetros) y premiar a los que son sencillos y ajustan bien.

Regla de oro: Al comparar lotes, aquel con el AIC más bajo (por ejemplo, -49.4 frente a -7.1) es el que presenta un comportamiento más consistente con la teoría matemática del modelo logístico.

3. Parámetros de la Curva Logística

En la tabla de resultados se encuentran también estos parámetros fundamentales:

  • \(A\) (asíntota superior): pH inicial de la leche antes de la acidificación intensa.
  • \(K\) (asíntota inferior): pH final de equilibrio cuando la fermentación se detiene.
  • \(t_i\) (tiempo de inflexión): Momento exacto (en horas) donde la caída del pH es más rápida. En la curva logística estándar se sitúa en la mitad de la curva (50%)
  • \(r\) (tasa): Velocidad relativa de caída, la inclinación media de la curva en la sección de descenso (mayor valor \(r\) significa mayor inclinación)
  • \(V_{max}\) : La pendiente máxima de la curva, calculada como \(V_{max} = |r \cdot (A - K) / 4|\). Representa la velocidad máxima de acidificación real en unidades de pH/hora.

Gráfico de las curvas logísticas ajustadas a los valores reales de los diferentes lotes de fabricación

Una vez calculados los parámetros de cada curva, podemos proceder a dibujarlas, dejando que ggplot() se ocupe de dibujar la función logística. Para ello le decimos que utilice la función nls(), que es la función de regresión no linear de R. Necesitamos dar unos valores iniciales, que calculamos con los valores extremos del conjunto de curvas. A veces esta estimación de los valores iniciales no es suficientemente buena para hacer converger la función y genera un error; en ese caso podríamos recurrir a funciones más sofisticadas, como SSlogis(), o usar una función más estable que nls(), como por ejemplo nlsLM() de la librería minpack.lm. Como en este caso no es necesario, mantengo el esquema más simple para facilitar su comprensión. Alternativamente, se pueden explorar también estas opciones.

Mostrar código R
# 1. Definimos la fórmula logística (más fácil de leer)
formula_logistica <- y ~ K + (A - K) / (1 + exp(-r * (x - ti)))

# 2. Buscamos las estimaciones iniciales para nls
K_ini <- min(resultados$K)
A_ini <- max(resultados$A)
r_ini <- median(resultados$r)
ti_ini <- median(resultados$ti)

# 3. Creamos el gráfico directamente
ggplot(datos, aes(x = hora, y = pH, color = lote)) +
  geom_point() + 
  geom_smooth(
    method = "nls", 
    formula = formula_logistica, 
    method.args = list(start = list(K = K_ini, A = A_ini, r = r_ini, ti = ti_ini)), 
    se = FALSE
  ) +
  labs(title = "Ajuste Logístico de pH a los diferentes lotes de fabricación", x = "Tiempo (h)", y = "pH") +
  theme_minimal()
Figura 1: Ajuste de la curva logística sobre los 4 lotes de prácticas. Los puntos representan las medidas reales de pH; su distribución temporal irregular es visible en el espaciado entre puntos.

Lo que los parámetros dicen del proceso

La elección de la logística como modelo de referencia se apoya en el ajuste estadístico, que en nuestro caso es muy bueno, y la adecuación a la dinámica real de la acidificación láctica y fórmula de \(V_{max}\) más sencilla.

Los resultados de \(K\) cuentan la historia del proceso:

  • Lote 1 \(K\) = 4,83: sobreacidificación. El fermento siguió activo más tiempo del previsto y la cuajada llegó a un pH muy bajo para una pasta prensada estándar. En este lote es de esperar una desmineralización excesiva.
  • Lotes 2 y 4 \(K\) = 5,25: acidificación correcta para pasta prensada.
  • Lote 3 \(K\) = 5,49: acidificación incompleta. El fermento no llegó al pH objetivo, posiblemente por temperatura insuficiente o por un problema con el cultivo. Puede haber riesgo de contaminación o de microorganismos no deseados.

Además de estos resultados, los parámetros del modelo que hemos calculado nos permiten incluir en nuestra tabla de datos de producción los principales indicadores que definen cada curva. Esto nos permitirá su análisis posterior junto con el resto de indicadores de la producción.