--- title: "Microbial inactivation models" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Microbial inactivation models} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 5 ) ``` ```{r setup} library(predmicror) ``` This vignette shows a complete workflow for fitting microbial inactivation models with `predmicror`. Inactivation models in this package expect microbial counts on the base-10 logarithmic scale, usually in a column named `logN`. The workflow is: 1. inspect the available models; 2. fit one or more candidate models; 3. calculate model diagnostics; 4. extract fitted values and residuals; 5. generate predictions over a time grid. ## Available inactivation models ```{r} predmicror_models("inactivation") ``` ## Example data The `mafart2005Li11` dataset contains inactivation data with time and log10 microbial counts. ```{r} data(mafart2005Li11) head(mafart2005Li11) summary(mafart2005Li11) ``` ```{r, fig.alt="Observed inactivation data used to illustrate primary inactivation models."} plot( logN ~ Time, data = mafart2005Li11, xlab = "Time", ylab = expression(log[10](N)), pch = 19 ) ``` ## Fitting candidate models Nonlinear fitting can be sensitive to starting values. The helper below keeps this vignette robust across platforms by returning `NULL` if a candidate model cannot be fitted with the selected starting values. ```{r} 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") } ) } ``` A simple starting point is to fit the Weibull-Mafart model. ```{r} weibull <- safe_fit( fit_inactivation( mafart2005Li11, model = "WeibullM", time = "Time", response = "logN", start = list(Y0 = max(mafart2005Li11$logN), sigma = 3, alpha = 1) ) ) weibull ``` Additional candidate models can be fitted and compared when they converge. ```{r} weibull_ph <- safe_fit( fit_inactivation( mafart2005Li11, model = "WeibullPH", time = "Time", response = "logN", start = list(Y0 = max(mafart2005Li11$logN), k = 0.3, alpha = 1) ) ) huang_rgs <- safe_fit( fit_inactivation( mafart2005Li11, model = "HuangRGS", time = "Time", response = "logN", start = list(Y0 = max(mafart2005Li11$logN), k = 0.3, M = 1) ) ) fits <- Filter(Negate(is.null), list( WeibullM = weibull, WeibullPH = weibull_ph, HuangRGS = huang_rgs )) names(fits) ``` ## Diagnostics and model comparison Use `fit_metrics()` for one fitted model and `compare_models()` for a ranked summary of several fitted models. ```{r} if (length(fits) > 0L) { fit_metrics(fits[[1]]) } ``` ```{r} if (length(fits) > 1L) { compare_models(fits, sort_by = "AIC") } ``` ## Fitted values and residuals `predmicror_augment()` adds fitted values and residuals to the original data. This is useful for diagnostic plots and for exporting results. ```{r} if (length(fits) > 0L) { aug <- predmicror_augment(fits[[1]]) head(aug) } ``` ```{r, fig.alt="Observed and fitted values for the Weibull-Mafart inactivation model."} if (length(fits) > 0L) { aug <- predmicror_augment(fits[[1]]) if (all(c(".fitted", ".resid") %in% names(aug))) { plot( aug[[".fitted"]], aug[[".resid"]], xlab = "Fitted values", ylab = "Residuals", pch = 19 ) abline(h = 0, lty = 2) } } ``` ## Prediction over a time grid The same augment helper can be used with `newdata` to obtain predictions over a regular time grid. ```{r, fig.alt="Observed and fitted values for an alternative inactivation model."} if (length(fits) > 0L) { best_fit <- fits[[1]] if (length(fits) > 1L) { comparison <- compare_models(fits, sort_by = "AIC") best_fit <- fits[[comparison$model[1]]] } new_time <- data.frame( Time = seq(min(mafart2005Li11$Time), max(mafart2005Li11$Time), length.out = 100) ) pred <- predmicror_augment(best_fit, newdata = new_time) plot( logN ~ Time, data = mafart2005Li11, xlab = "Time", ylab = expression(log[10](N)), pch = 19 ) if (".fitted" %in% names(pred)) { lines(pred$Time, pred[[".fitted"]], lwd = 2) } } ``` ## Practical notes For inactivation models, verify the response scale before fitting. The wrappers expect base-10 log counts. When fitting more complex models with residual tails, use biologically plausible starting values and inspect parameter uncertainty; residual population parameters can be weakly identified if the data do not show a clear tail.