Cardinal parameter models describe the effect of environmental factors on a microbial growth response.
In predmicror, the cardinal fitting wrapper expects the
response to be the square root of the growth rate, usually in a column
named sqrtGR. This vignette shows how to fit cardinal
models for temperature, pH, water activity, and inhibitory
compounds.
Available cardinal models
predmicror_models("cardinal")
#> type model response_scale parameters
#> 1 cardinal CMTI sqrtGR Tmax, Tmin, MUopt, Topt
#> 2 cardinal CMAW sqrtGR AWmin, MUopt, AWopt
#> 3 cardinal CMPH sqrtGR pHmax, pHmin, MUopt, pHopt
#> 4 cardinal CMInh sqrtGR MIC, MUopt, alphaRobust fitting helper
Nonlinear fitting can depend on starting values. The helper below keeps the vignette robust by reporting fitting problems without stopping the document build.
safe_fit <- function(expr) {
withCallingHandlers(
tryCatch(
expr,
error = function(e) {
message("Model fit failed: ", conditionMessage(e))
NULL
}
),
warning = function(w) {
message("Model fit warning: ", conditionMessage(w))
invokeRestart("muffleWarning")
}
)
}Temperature model
The salmonella dataset contains temperature and
growth-rate data for Salmonella typhimurium on cooked
chicken.
data(salmonella)
head(salmonella)
#> Temp GR sqrtGR
#> 1 8 0.012 0.110
#> 2 10 0.025 0.158
#> 3 12 0.048 0.219
#> 4 14 0.078 0.279
#> 5 16 0.107 0.327
#> 6 18 0.155 0.394
temp_fit <- safe_fit(
fit_cardinal(
salmonella,
model = "CMTI",
x = "Temp",
response = "sqrtGR",
start = list(Tmax = 42, Tmin = 1, MUopt = 1, Topt = 37)
)
)
temp_fit
#> predmicror fit
#> type: cardinal
#> model: CMTI
#> response: sqrtGR (sqrtGR)
#> formula: sqrtGR ~ CMTI(Temp, Tmax, Tmin, MUopt, Topt)
#>
#> Nonlinear regression model
#> model: sqrtGR ~ CMTI(Temp, Tmax, Tmin, MUopt, Topt)
#> data: data
#> Tmax Tmin MUopt Topt
#> 49.0326 5.0268 0.7521 40.3671
#> residual sum-of-squares: 0.005018
#>
#> Algorithm: multifit/levenberg-marquardt, (scaling: more, solver: qr)
#>
#> Number of iterations to convergence: 44
#> Achieved convergence tolerance: 7.98e-17
if (!is.null(temp_fit)) {
fit_metrics(temp_fit)
}
#> model type response response_scale n p SSE RMSE MAE
#> 1 CMTI cardinal sqrtGR sqrtGR 21 4 0.005018168 0.01545834 0.01253912
#> bias RSE R2 adj_R2 logLik AIC BIC
#> 1 -4.168898e-05 0.01718099 0.9960034 0.9950043 57.76403 -105.5281 -100.3054
#> converged
#> 1 TRUE
if (!is.null(temp_fit)) {
grid <- data.frame(
Temp = seq(min(salmonella$Temp), max(salmonella$Temp), length.out = 100)
)
pred <- predmicror_augment(temp_fit, newdata = grid)
plot(
sqrtGR ~ Temp,
data = salmonella,
xlab = "Temperature",
ylab = "Square root growth rate",
pch = 19
)
if (".fitted" %in% names(pred)) {
lines(pred$Temp, pred[[".fitted"]], lwd = 2)
}
}
pH model
data(ph)
head(ph)
#> pH sqrtGR GR
#> 1 4.4 0.14 0.0196
#> 2 4.5 0.20 0.0400
#> 3 4.6 0.39 0.1521
#> 4 4.8 0.46 0.2116
#> 5 5.0 0.58 0.3364
#> 6 5.1 0.59 0.3481
ph_fit <- safe_fit(
fit_cardinal(
ph,
model = "CMPH",
x = "pH",
response = "sqrtGR",
start = list(pHmax = 9.5, pHmin = 3.5, MUopt = 1, pHopt = 6.8)
)
)
ph_fit
#> predmicror fit
#> type: cardinal
#> model: CMPH
#> response: sqrtGR (sqrtGR)
#> formula: sqrtGR ~ CMPH(pH, pHmax, pHmin, MUopt, pHopt)
#>
#> Nonlinear regression model
#> model: sqrtGR ~ CMPH(pH, pHmax, pHmin, MUopt, pHopt)
#> data: data
#> pHmax pHmin MUopt pHopt
#> 9.355 4.425 1.066 7.257
#> residual sum-of-squares: 0.03246
#>
#> Algorithm: multifit/levenberg-marquardt, (scaling: more, solver: qr)
#>
#> Number of iterations to convergence: 9
#> Achieved convergence tolerance: 6.245e-17
if (!is.null(ph_fit)) {
fit_metrics(ph_fit)
}
#> model type response response_scale n p SSE RMSE MAE
#> 1 CMPH cardinal sqrtGR sqrtGR 14 4 0.03245538 0.04814812 0.03263188
#> bias RSE R2 adj_R2 logLik AIC BIC
#> 1 0.01020897 0.05696963 0.9664767 0.9515774 22.60348 -35.20697 -32.01168
#> converged
#> 1 TRUE
if (!is.null(ph_fit)) {
grid <- data.frame(pH = seq(min(ph$pH), max(ph$pH), length.out = 100))
pred <- predmicror_augment(ph_fit, newdata = grid)
plot(
sqrtGR ~ pH,
data = ph,
xlab = "pH",
ylab = "Square root growth rate",
pch = 19
)
if (".fitted" %in% names(pred)) {
lines(pred$pH, pred[[".fitted"]], lwd = 2)
}
}
Water activity model
data(aw)
head(aw)
#> aw sqrtGR GR
#> 1 0.765 0.036 0.001296
#> 2 0.790 0.240 0.057600
#> 3 0.820 0.384 0.147456
#> 4 0.862 0.792 0.627264
#> 5 0.895 1.082 1.170724
#> 6 0.920 1.204 1.449616
aw_fit <- safe_fit(
fit_cardinal(
aw,
model = "CMAW",
x = "aw",
response = "sqrtGR",
start = list(AWmin = 0.90, MUopt = 1, AWopt = 0.995)
)
)
aw_fit
#> predmicror fit
#> type: cardinal
#> model: CMAW
#> response: sqrtGR (sqrtGR)
#> formula: sqrtGR ~ CMAW(aw, AWmin, MUopt, AWopt)
#>
#> Nonlinear regression model
#> model: sqrtGR ~ CMAW(aw, AWmin, MUopt, AWopt)
#> data: data
#> AWmin MUopt AWopt
#> 0.7717 1.5586 0.9460
#> residual sum-of-squares: 0.01396
#>
#> Algorithm: multifit/levenberg-marquardt, (scaling: more, solver: qr)
#>
#> Number of iterations to convergence: 12
#> Achieved convergence tolerance: 6.072e-17
if (!is.null(aw_fit)) {
fit_metrics(aw_fit)
}
#> model type response response_scale n p SSE RMSE MAE
#> 1 CMAW cardinal sqrtGR sqrtGR 9 3 0.01396232 0.03938741 0.03342219
#> bias RSE R2 adj_R2 logLik AIC BIC
#> 1 0.003427043 0.04823953 0.991345 0.986152 16.33833 -24.67667 -23.88777
#> converged
#> 1 TRUE
if (!is.null(aw_fit)) {
grid <- data.frame(aw = seq(min(aw$aw), max(aw$aw), length.out = 100))
pred <- predmicror_augment(aw_fit, newdata = grid)
plot(
sqrtGR ~ aw,
data = aw,
xlab = "Water activity",
ylab = "Square root growth rate",
pch = 19
)
if (".fitted" %in% names(pred)) {
lines(pred$aw, pred[[".fitted"]], lwd = 2)
}
}
Inhibitor model
data(inh)
head(inh)
#> Conce sqrtGR GR
#> 1 0.0 0.864 0.746496
#> 2 0.6 0.704 0.495616
#> 3 1.0 0.683 0.466489
#> 4 1.9 0.523 0.273529
#> 5 2.7 0.384 0.147456
#> 6 3.8 0.338 0.114244
inh_fit <- safe_fit(
fit_cardinal(
inh,
model = "CMInh",
x = "Conce",
response = "sqrtGR",
start = list(MIC = max(inh$Conce) * 1.2, MUopt = 1, alpha = 1)
)
)
inh_fit
#> predmicror fit
#> type: cardinal
#> model: CMInh
#> response: sqrtGR (sqrtGR)
#> formula: sqrtGR ~ CMInh(Conce, MIC, MUopt, alpha)
#>
#> Nonlinear regression model
#> model: sqrtGR ~ CMInh(Conce, MIC, MUopt, alpha)
#> data: data
#> MIC MUopt alpha
#> 6.3231 0.7780 0.3569
#> residual sum-of-squares: 0.0166
#>
#> Algorithm: multifit/levenberg-marquardt, (scaling: more, solver: qr)
#>
#> Number of iterations to convergence: 14
#> Achieved convergence tolerance: 2.082e-16
if (!is.null(inh_fit)) {
fit_metrics(inh_fit)
}
#> model type response response_scale n p SSE RMSE MAE
#> 1 CMInh cardinal sqrtGR sqrtGR 8 3 0.01659573 0.04554632 0.03882655
#> bias RSE R2 adj_R2 logLik AIC BIC
#> 1 -0.003475956 0.05761204 0.961369 0.9323957 13.3607 -18.72139 -18.40363
#> converged
#> 1 TRUE
if (!is.null(inh_fit)) {
grid <- data.frame(Conce = seq(min(inh$Conce), max(inh$Conce), length.out = 100))
pred <- predmicror_augment(inh_fit, newdata = grid)
plot(
sqrtGR ~ Conce,
data = inh,
xlab = "Inhibitor concentration",
ylab = "Square root growth rate",
pch = 19
)
if (".fitted" %in% names(pred)) {
lines(pred$Conce, pred[[".fitted"]], lwd = 2)
}
}
Collecting diagnostics
The fitted models above describe different environmental factors and should not be ranked against each other using information criteria. Still, collecting their metrics in one table is useful for reporting and quality control.
cardinal_fits <- Filter(Negate(is.null), list(
temperature = temp_fit,
pH = ph_fit,
water_activity = aw_fit,
inhibitor = inh_fit
))
if (length(cardinal_fits) > 0L) {
diagnostics <- do.call(
rbind,
Map(function(name, fit) {
out <- fit_metrics(fit)
out$workflow <- name
out
}, names(cardinal_fits), cardinal_fits)
)
rownames(diagnostics) <- NULL
diagnostics
}
#> model type response response_scale n p SSE RMSE MAE
#> 1 CMTI cardinal sqrtGR sqrtGR 21 4 0.005018168 0.01545834 0.01253912
#> 2 CMPH cardinal sqrtGR sqrtGR 14 4 0.032455382 0.04814812 0.03263188
#> 3 CMAW cardinal sqrtGR sqrtGR 9 3 0.013962315 0.03938741 0.03342219
#> 4 CMInh cardinal sqrtGR sqrtGR 8 3 0.016595735 0.04554632 0.03882655
#> bias RSE R2 adj_R2 logLik AIC BIC
#> 1 -4.168898e-05 0.01718099 0.9960034 0.9950043 57.76403 -105.52805 -100.30544
#> 2 1.020897e-02 0.05696963 0.9664767 0.9515774 22.60348 -35.20697 -32.01168
#> 3 3.427043e-03 0.04823953 0.9913450 0.9861520 16.33833 -24.67667 -23.88777
#> 4 -3.475956e-03 0.05761204 0.9613690 0.9323957 13.36070 -18.72139 -18.40363
#> converged workflow
#> 1 TRUE temperature
#> 2 TRUE pH
#> 3 TRUE water_activity
#> 4 TRUE inhibitor