├── .github ├── .gitignore └── workflows │ └── pkgdown.yaml ├── demo ├── 00Index └── growthrates.R ├── _pkgdown.yml ├── .Rbuildignore ├── R ├── utilities.R ├── aad_set_generics.R ├── antibiotic.R ├── bactgrowth.R ├── aaa_growthmodel-class.R ├── grow_logistic.R ├── grow_exponential.R ├── parse_formula.R ├── lm_window.R ├── cost.R ├── extract.R ├── aab_growthmodel-constructor.R ├── grow_gompertz.R ├── grow_richards.R ├── grow_baranyi.R ├── names.R ├── grow_gompertz2.R ├── parse_formula_nonlin.R ├── grow_huang.R ├── growthrates-package.R ├── all_easylinear.R ├── grow_twostep.R ├── aac_classes.R ├── fit_spline.R ├── multisplit.R ├── fit_easylinear.R ├── grow_genlogistic.R ├── all_splines.R ├── predict.R └── fit_growthmodel.R ├── man ├── lm_or_NULL-class.Rd ├── growthmodel-class.Rd ├── function_growthmodel-class.Rd ├── generics.Rd ├── parse_formula.Rd ├── cost.Rd ├── extract.Rd ├── lm_window.Rd ├── growthrates-classes.Rd ├── antibiotic.Rd ├── bactgrowth.Rd ├── parse_formula_nonlin.Rd ├── grow_exponential.Rd ├── grow_logistic.Rd ├── names.Rd ├── grow_gompertz.Rd ├── growthmodel-constructor.Rd ├── grow_baranyi.Rd ├── grow_richards.Rd ├── grow_gompertz2.Rd ├── all_easylinear.Rd ├── multisplit.Rd ├── fit_growthmodel.Rd ├── fit_easylinear.Rd ├── fit_spline.Rd ├── predict.Rd ├── growthrates-package.Rd ├── grow_huang.Rd ├── methods.Rd ├── plot.Rd ├── grow_genlogistic.Rd ├── grow_twostep.Rd ├── all_splines.Rd ├── all_growthmodels.Rd └── extcoef_logistic.Rd ├── .gitignore ├── growthrates.Rproj ├── src ├── twostep.c ├── genlogistic.c └── R_init_growthrates.c ├── todo.txt ├── inst └── doc │ └── examples │ ├── example_easylinear.R │ ├── example_spline.R │ ├── example_user_defined_growthmodel.R │ ├── example_huang_p_as_df.R │ ├── example_user_defined_ode_model.R │ ├── example_growthrates-logistic-lag.R │ ├── benchmark_compiled_ode.R │ ├── import_grofit_data.R │ ├── example_growthmodel.R │ ├── example_logistic_saturation.R │ ├── example_baranyi.R │ ├── example_masking.R │ ├── example_user_defined_ode_compiled.R │ ├── example_huang.R │ └── example_ggplot.R ├── DESCRIPTION ├── NAMESPACE └── README.md /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /demo/00Index: -------------------------------------------------------------------------------- 1 | growthrates Demonstrates main features of the package 2 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: http://tpetzoldt.github.io/growthrates/ 2 | template: 3 | bootstrap: 5 4 | 5 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | ^work 4 | LICENSE 5 | ^local 6 | ^_local 7 | todo.txt 8 | 9 | ^_pkgdown\.yml$ 10 | ^docs$ 11 | ^pkgdown$ 12 | ^\.github$ 13 | -------------------------------------------------------------------------------- /R/utilities.R: -------------------------------------------------------------------------------- 1 | ## small utilities that are not exported 2 | renameListElement <- function(L, old, new) { 3 | if (is.null(L)) return(NULL) 4 | names(L)[match(old, names(L))] <- new 5 | L 6 | } 7 | 8 | is.numericOrNull <- function(x) { 9 | is.numeric(x) | is.null(x) 10 | } 11 | -------------------------------------------------------------------------------- /man/lm_or_NULL-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa_growthmodel-class.R 3 | \docType{class} 4 | \name{lm_or_NULL-class} 5 | \alias{lm_or_NULL-class} 6 | \title{Union Class of Linear Model or NULL} 7 | \description{ 8 | Class to handle no-growth cases 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # History files 2 | pkg/.Rhistory 3 | pkg/.Rapp.history 4 | 5 | # Example code in package build process 6 | *-Ex.R 7 | 8 | # RStudio files 9 | pkg/.Rproj.user/ 10 | 11 | # produced vignettes 12 | pkg/vignettes/*.html 13 | pkg/vignettes/*.pdf 14 | .Rproj.user 15 | /local 16 | /_local 17 | docs 18 | -------------------------------------------------------------------------------- /growthrates.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: No 4 | SaveWorkspace: No 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | AutoAppendNewline: Yes 16 | StripTrailingWhitespace: Yes 17 | 18 | BuildType: Package 19 | PackageUseDevtools: Yes 20 | PackageInstallArgs: --no-multiarch --with-keep.source 21 | PackageRoxygenize: rd,collate,namespace,vignette 22 | -------------------------------------------------------------------------------- /man/growthmodel-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa_growthmodel-class.R 3 | \name{growthmodel-class} 4 | \alias{growthmodel-class} 5 | \title{Class of Growth Model Functions} 6 | \description{ 7 | This class is used for the parametric \code{grow_\dots} functions of the 8 | package and can also be used for user-defined functions to describe 9 | time-dependent growth of organisms. 10 | } 11 | \seealso{ 12 | the constructor function \code{\link{growthmodel}} how to create 13 | instances of this class. 14 | } 15 | -------------------------------------------------------------------------------- /man/function_growthmodel-class.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aaa_growthmodel-class.R 3 | \docType{class} 4 | \name{function_growthmodel-class} 5 | \alias{function_growthmodel-class} 6 | \title{Union Class of Growth Model or Function} 7 | \description{ 8 | This class union comprises parametric model functions from class 9 | \code{growthmodel} and ordinary functions to describe time-dependent 10 | growth of organisms. 11 | } 12 | \seealso{ 13 | the constructor function \code{\link{growthmodel}} how to create 14 | instances of class \code{growthmodel}. 15 | } 16 | -------------------------------------------------------------------------------- /man/generics.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aad_set_generics.R 3 | \name{rsquared} 4 | \alias{rsquared} 5 | \alias{obs} 6 | \alias{results} 7 | \title{Additional Generic Functions} 8 | \usage{ 9 | rsquared(object, ...) 10 | 11 | obs(object, ...) 12 | 13 | results(object, ...) 14 | } 15 | \arguments{ 16 | \item{object}{name of a 'growthrate' object} 17 | 18 | \item{\dots}{other arguments passed to the methods} 19 | } 20 | \description{ 21 | These functions are specifically defined for package \pkg{growthrates}, 22 | all other generics are imported. 23 | } 24 | \keyword{internal} 25 | -------------------------------------------------------------------------------- /src/twostep.c: -------------------------------------------------------------------------------- 1 | /* C implementation of two-step logistic growth equations */ 2 | 3 | 4 | #include /* gives F77_CALL through R_ext/RS.h */ 5 | 6 | static double parms[3]; 7 | 8 | /* define 5 parameters as macros: mumax, K, alpha, beta, gamma */ 9 | #define kw parms[0] 10 | #define mumax parms[1] 11 | #define K parms[2] 12 | 13 | 14 | /* initializer */ 15 | void ini_twostep(void (* odeparms)(int *, double *)) 16 | { 17 | int N = 3; 18 | odeparms(&N, parms); 19 | } 20 | 21 | /* derivatives */ 22 | void d_twostep(int *neq, double *t, double *y, double *ydot, double *yout, int*ip) 23 | { 24 | if (ip[0] < 1) error("nout should be >= 1"); 25 | ydot[0] = -kw * y[0]; 26 | ydot[1] = kw * y[0] + mumax * (1.0 - (y[0] + y[1])/K) * y[1]; 27 | 28 | yout[0] = y[0] + y[1]; 29 | //yout[1] = log(y[0] + y[1]); 30 | } 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/genlogistic.c: -------------------------------------------------------------------------------- 1 | /* C implementation of generalised logistic differential equation */ 2 | /* The "generalized logistic" follows the definition of Tsoularis, 2001 */ 3 | 4 | #include /* gives F77_CALL through R_ext/RS.h */ 5 | 6 | static double parms[5]; 7 | 8 | /* define 5 parameters as macros: mumax, K, alpha, beta, gamma */ 9 | #define mumax parms[0] 10 | #define K parms[1] 11 | #define alpha parms[2] 12 | #define beta parms[3] 13 | #define gamma parms[4] 14 | 15 | /* initializer */ 16 | void ini_genlogistic(void (* odeparms)(int *, double *)) 17 | { 18 | int N=5; 19 | odeparms(&N, parms); 20 | } 21 | 22 | /* derivatives */ 23 | void d_genlogistic(int *neq, double *t, double *y, double *ydot, double *yout, int*ip) 24 | { 25 | if (ip[0] < 0) error("nout should be zero"); 26 | ydot[0] = mumax * pow(*y, alpha) * pow((1-pow(*y/K, beta)), gamma); 27 | } 28 | 29 | 30 | -------------------------------------------------------------------------------- /man/parse_formula.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parse_formula.R 3 | \name{parse_formula} 4 | \alias{parse_formula} 5 | \title{Simple Formula Interface} 6 | \usage{ 7 | parse_formula(grouping) 8 | } 9 | \arguments{ 10 | \item{grouping}{a model formula specifying dependent, 11 | independent and grouping variables in the form: 12 | \code{dependent ~ independent | group1 + group2 + ...}.} 13 | } 14 | \value{ 15 | a list with the elements \code{valuevar}, \code{timevar}, and 16 | \code{groups} 17 | } 18 | \description{ 19 | This simple formula interface handles formulae of the form 20 | \code{dependent ~ independent | group1 + group2 + ...}. 21 | } 22 | \details{ 23 | This function is used by \code{\link{multisplit}} and normally not called 24 | by the user. 25 | } 26 | \examples{ 27 | 28 | parse_formula(y ~ x | a+b+c) 29 | 30 | } 31 | \seealso{ 32 | \code{\link{multisplit}}, \code{\link{split}} 33 | } 34 | \keyword{internal} 35 | -------------------------------------------------------------------------------- /R/aad_set_generics.R: -------------------------------------------------------------------------------- 1 | #' Additional Generic Functions 2 | #' 3 | #' These functions are specifically defined for package \pkg{growthrates}, 4 | #' all other generics are imported. 5 | #' 6 | #' @param object name of a 'growthrate' object 7 | #' @param \dots other arguments passed to the methods 8 | # 9 | #' 10 | #' @rdname generics 11 | #' @keywords internal 12 | #' @include aac_classes.R 13 | #' 14 | #' @exportMethod rsquared 15 | #' 16 | setGeneric("rsquared", function(object, ...) standardGeneric("rsquared")) 17 | 18 | #' @rdname generics 19 | #' @exportMethod obs 20 | #' 21 | setGeneric("obs", function(object, ...) standardGeneric("obs")) 22 | 23 | #' @rdname generics 24 | #' @exportMethod results 25 | #' 26 | setGeneric("results", function(object, ...) standardGeneric("results")) 27 | 28 | #' @rdname multisplit 29 | #' @exportMethod multisplit 30 | #' 31 | setGeneric("multisplit", function(data, grouping, drop = TRUE, sep = ":", ...) standardGeneric("multisplit")) 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /man/cost.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cost.R 3 | \name{cost} 4 | \alias{cost} 5 | \title{Cost Function for Nonlinear Fits} 6 | \usage{ 7 | cost(p, obs, FUN, fixed.p = NULL, transform, ...) 8 | } 9 | \arguments{ 10 | \item{p}{vector of fitted parameters of the growth model.} 11 | 12 | \item{FUN}{function of growth model to be fitted.} 13 | 14 | \item{fixed.p}{vector of fixed parameters of the growth model.} 15 | 16 | \item{\dots}{additional parameters passed to the optimizer.} 17 | } 18 | \value{ 19 | an object of class \code{modCost}, see \code{\link[FME]{modCost}} in 20 | package \pkg{FME} 21 | } 22 | \description{ 23 | Defines a cost function from the residual sum of squares between model 24 | and observational data. 25 | } 26 | \details{ 27 | Function 'cost' is implemented along the lines of the 28 | following template, see package FME for details: 29 | \preformatted{ 30 | cost <- function(p, obs, FUN, fixed.p = NULL, ...) { 31 | out <- FUN(obs$time, c(p, fixed.p)) 32 | modCost(out, obs, weight = "none", ...) 33 | } 34 | } 35 | } 36 | \keyword{internal} 37 | -------------------------------------------------------------------------------- /R/antibiotic.R: -------------------------------------------------------------------------------- 1 | #' Plate Reader Data of Bacterial Growth 2 | #' 3 | #' Example data set from growth experiments with Pseudomonas putida 4 | #' on a tetracycline concentration gradient. 5 | #' 6 | #' The sample data set shows four out of six replicates of the original experiment. 7 | #' 8 | #' @format Data frame with the following columns: 9 | #' \describe{ 10 | #' \item{time}{time in hours.} 11 | #' \item{variable}{sample code.} 12 | #' \item{value}{bacteria concentration measured as optical density.} 13 | #' \item{conc}{concentration of the antibiotics (Tetracycline).} 14 | #' \item{repl}{Replicate.} 15 | #' } 16 | #' 17 | #' @source Claudia Seiler, TU Dresden, Institute of Hydrobiology. 18 | #' 19 | #' @name antibiotic 20 | #' @docType data 21 | #' @keywords data 22 | #' 23 | #' @examples 24 | #' ## plot data and determine growth rates 25 | #' data(antibiotic) 26 | #' 27 | #' dat <- subset(antibiotic, conc==0.078 & repl=="R4") 28 | #' parms <- c(y0=0.01, mumax=0.2, K=0.5) 29 | #' fit <- fit_growthmodel(grow_logistic, parms, dat$time, dat$value) 30 | #' plot(fit); plot(fit, log="y") 31 | NULL 32 | -------------------------------------------------------------------------------- /man/extract.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extract.R 3 | \name{[,multiple_fits,ANY,missing-method} 4 | \alias{[,multiple_fits,ANY,missing-method} 5 | \alias{[[,multiple_fits,ANY,missing-method} 6 | \title{Extract or Replace Parts of a 'multiple_fits' Object} 7 | \usage{ 8 | \S4method{[}{multiple_fits,ANY,missing}(x, i, j, ..., drop = TRUE) 9 | 10 | \S4method{[[}{multiple_fits,ANY,missing}(x, i, j, ...) 11 | } 12 | \arguments{ 13 | \item{x}{object of class multiple_fits} 14 | 15 | \item{i}{numeric or character index} 16 | 17 | \item{j}{NULL (for compatibility with other uses of \code{[} or \code{[[})} 18 | 19 | \item{\dots}{optional arguments passed to \code{[}} 20 | 21 | \item{drop}{If \code{TRUE} the result is coerced to the lowest possible 22 | dimension} 23 | } 24 | \description{ 25 | Operators to access parts of 'multiple_fits' objects 26 | } 27 | \examples{ 28 | 29 | data(bactgrowth) 30 | L <- all_splines(value ~ time | strain + conc +replicate, data=bactgrowth) 31 | 32 | coef(L[[1]]) 33 | 34 | plot(L[["R:0:2"]]) 35 | 36 | # par(mfrow=c(2, 2)) 37 | plot(L[1:4]) 38 | 39 | } 40 | -------------------------------------------------------------------------------- /R/bactgrowth.R: -------------------------------------------------------------------------------- 1 | #' Plate Reader Data of Bacterial Growth 2 | #' 3 | #' Example data set from growth experiments with different 4 | #' concentrations of antibiotics. 5 | #' 6 | #' This rather 'difficult' data set was intentionally selected to make model 7 | #' fitting by the package more challenging. 8 | #' 9 | #' @format Data frame with the following columns: 10 | #' \describe{ 11 | #' \item{strain}{identifier of the bacterial strain, D=donor, R=recipient, T=transconjugant.} 12 | #' \item{replicate}{replicate of the trial.} 13 | #' \item{conc}{concentration of the antibiotics (Tetracycline).} 14 | #' \item{time}{time in hours.} 15 | #' \item{value}{bacteria concentration measured as optical density.} 16 | #' } 17 | #' 18 | #' @source Claudia Seiler, TU Dresden, Institute of Hydrobiology. 19 | #' 20 | #' @name bactgrowth 21 | #' @docType data 22 | #' @keywords data 23 | #' 24 | #' @examples 25 | #' ## plot data and determine growth rates 26 | #' data(bactgrowth) 27 | #' 28 | #' 29 | #' library(lattice) 30 | #' xyplot(value ~ time | strain + as.factor(conc), 31 | #' data = bactgrowth, groups = replicate) 32 | 33 | NULL 34 | -------------------------------------------------------------------------------- /man/lm_window.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/lm_window.R 3 | \name{lm_window} 4 | \alias{lm_window} 5 | \alias{lm_parms} 6 | \title{Fit Exponential Growth Model with a Heuristic Linear Method} 7 | \usage{ 8 | lm_window(x, y, i0, h = 5) 9 | 10 | lm_parms(m) 11 | } 12 | \arguments{ 13 | \item{x}{vector of independent variable (e.g. time).} 14 | 15 | \item{y}{vector of dependent variable (concentration of organisms).} 16 | 17 | \item{i0}{index of first value used for a window.} 18 | 19 | \item{h}{with of the window (number of data).} 20 | 21 | \item{m}{linear model (\code{lm}) object} 22 | } 23 | \value{ 24 | linear model object (\code{lm_window} 25 | resp. vector with parameters of the fit (lm_parms). 26 | } 27 | \description{ 28 | Helper functions for handling linear fits. 29 | } 30 | \details{ 31 | The functions are used by a heuristic linear approach, similar to the 32 | ``growth rates made easy''-method of Hall et al. (2013). 33 | } 34 | \references{ 35 | Hall, B. G., H. Acar and M. Barlow 2013. Growth Rates Made Easy. 36 | Mol. Biol. Evol. 31: 232-238 \doi{10.1093/molbev/mst197} 37 | } 38 | \keyword{internal} 39 | -------------------------------------------------------------------------------- /man/growthrates-classes.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aac_classes.R 3 | \docType{class} 4 | \name{growthrates_fit-class} 5 | \alias{growthrates_fit-class} 6 | \alias{nonlinear_fit-class} 7 | \alias{easylinear_fit-class} 8 | \alias{smooth.spline_fit-class} 9 | \alias{multiple_fits-class} 10 | \alias{multiple_fits} 11 | \alias{multiple_easylinear_fits-class} 12 | \alias{multiple_nonlinear_fits-class} 13 | \alias{multiple_smooth.spline_fits-class} 14 | \title{S4 Classes of Package \pkg{growthrates}} 15 | \description{ 16 | \code{growthrates_fit}: top-level class representing a growthrates fit with 17 | any method. 18 | } 19 | \section{Slots}{ 20 | 21 | \describe{ 22 | \item{\code{FUN}}{model function used.} 23 | 24 | \item{\code{fit}}{results of the model fit.} 25 | 26 | \item{\code{obs}}{observation data used for model fitting.} 27 | 28 | \item{\code{rsquared}}{coefficient of determination.} 29 | 30 | \item{\code{par}}{parameters of the fit.} 31 | 32 | \item{\code{ndx}}{index values of the time points used (for \code{easylinear_fit}).} 33 | 34 | \item{\code{xy}}{x and y values at the maximum of the spline.} 35 | }} 36 | 37 | -------------------------------------------------------------------------------- /man/antibiotic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/antibiotic.R 3 | \docType{data} 4 | \name{antibiotic} 5 | \alias{antibiotic} 6 | \title{Plate Reader Data of Bacterial Growth} 7 | \format{ 8 | Data frame with the following columns: 9 | \describe{ 10 | \item{time}{time in hours.} 11 | \item{variable}{sample code.} 12 | \item{value}{bacteria concentration measured as optical density.} 13 | \item{conc}{concentration of the antibiotics (Tetracycline).} 14 | \item{repl}{Replicate.} 15 | } 16 | } 17 | \source{ 18 | Claudia Seiler, TU Dresden, Institute of Hydrobiology. 19 | } 20 | \description{ 21 | Example data set from growth experiments with Pseudomonas putida 22 | on a tetracycline concentration gradient. 23 | } 24 | \details{ 25 | The sample data set shows four out of six replicates of the original experiment. 26 | } 27 | \examples{ 28 | ## plot data and determine growth rates 29 | data(antibiotic) 30 | 31 | dat <- subset(antibiotic, conc==0.078 & repl=="R4") 32 | parms <- c(y0=0.01, mumax=0.2, K=0.5) 33 | fit <- fit_growthmodel(grow_logistic, parms, dat$time, dat$value) 34 | plot(fit); plot(fit, log="y") 35 | } 36 | \keyword{data} 37 | -------------------------------------------------------------------------------- /man/bactgrowth.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/bactgrowth.R 3 | \docType{data} 4 | \name{bactgrowth} 5 | \alias{bactgrowth} 6 | \title{Plate Reader Data of Bacterial Growth} 7 | \format{ 8 | Data frame with the following columns: 9 | \describe{ 10 | \item{strain}{identifier of the bacterial strain, D=donor, R=recipient, T=transconjugant.} 11 | \item{replicate}{replicate of the trial.} 12 | \item{conc}{concentration of the antibiotics (Tetracycline).} 13 | \item{time}{time in hours.} 14 | \item{value}{bacteria concentration measured as optical density.} 15 | } 16 | } 17 | \source{ 18 | Claudia Seiler, TU Dresden, Institute of Hydrobiology. 19 | } 20 | \description{ 21 | Example data set from growth experiments with different 22 | concentrations of antibiotics. 23 | } 24 | \details{ 25 | This rather 'difficult' data set was intentionally selected to make model 26 | fitting by the package more challenging. 27 | } 28 | \examples{ 29 | ## plot data and determine growth rates 30 | data(bactgrowth) 31 | 32 | 33 | library(lattice) 34 | xyplot(value ~ time | strain + as.factor(conc), 35 | data = bactgrowth, groups = replicate) 36 | } 37 | \keyword{data} 38 | -------------------------------------------------------------------------------- /R/aaa_growthmodel-class.R: -------------------------------------------------------------------------------- 1 | #' Class of Growth Model Functions 2 | #' 3 | #' This class is used for the parametric \code{grow_\dots} functions of the 4 | #' package and can also be used for user-defined functions to describe 5 | #' time-dependent growth of organisms. 6 | #' 7 | #' @seealso the constructor function \code{\link{growthmodel}} how to create 8 | #' instances of this class. 9 | #' @name growthmodel-class 10 | #' @exportClass growthmodel 11 | #' 12 | setOldClass("growthmodel") # S3 class 13 | 14 | #' Union Class of Growth Model or Function 15 | #' 16 | #' This class union comprises parametric model functions from class 17 | #' \code{growthmodel} and ordinary functions to describe time-dependent 18 | #' growth of organisms. 19 | #' 20 | #' @seealso the constructor function \code{\link{growthmodel}} how to create 21 | #' instances of class \code{growthmodel}. 22 | #' 23 | #' @name function_growthmodel-class 24 | #' @exportClass function_growthmodel 25 | #' 26 | setClassUnion("function_growthmodel", c("growthmodel", "function")) 27 | 28 | #' Union Class of Linear Model or NULL 29 | #' 30 | #' Class to handle no-growth cases 31 | #' 32 | #' @name lm_or_NULL-class 33 | #' @exportClass lm_or_NULL 34 | #' 35 | setClassUnion("lm_or_NULL", c("lm", "NULL")) 36 | 37 | -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | Planned features 2 | ================ 3 | 4 | o implement ODE models in C or Fortran 5 | --> done 6 | 7 | o "masking" of parameters during fit 8 | --> done 9 | 10 | o multiple start parameters, as lists (or data frames) 11 | --> done 12 | 13 | o enable "trace" for all optimizers 14 | --> done 15 | 16 | o estimate lag phase 17 | --> done 18 | 19 | o formula interface 20 | - simple interface: done 21 | - parse nonlinear formula: experimental 22 | - for single fits: to do 23 | 24 | o predict method 25 | --> done (at least partly) 26 | 27 | o more argument checking 28 | - can always be improved 29 | 30 | o streamline documentation 31 | - done 32 | - can always be improved 33 | 34 | o data conversion functions 35 | - plate reader 36 | - grofit --> see examples/import_grofit_data.R 37 | 38 | o direct use of log-transformed parameters y0 and ymax = K 39 | - tested, was less advantageous than expected. 40 | --> not implemented to avoid confusion. 41 | 42 | Ideas 43 | ===== 44 | 45 | o estimate and plot confidence intervals 46 | 47 | o interactive data brushing 48 | 49 | o interfaces to related packages (Suggests) 50 | - growthmodels (generic function interface) 51 | - grofit 52 | - drc 53 | - ccSolve 54 | -------------------------------------------------------------------------------- /man/parse_formula_nonlin.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/parse_formula_nonlin.R 3 | \name{parse_formula_nonlin} 4 | \alias{parse_formula_nonlin} 5 | \title{Simple Formula Interface for Grouped Nonlinear Functions} 6 | \usage{ 7 | parse_formula_nonlin(formula) 8 | } 9 | \arguments{ 10 | \item{formula}{a model formula specifying dependent and 11 | independent variables, nonlinear model and grouping variables in the form: 12 | \code{dependent ~ FUN(independent, parms) | group1 + group2 + ...}. 13 | FUN can be a name of an existing growth model (e.g. \code{grow_logistic}) 14 | or a valid user-defined function (see \code{\link{growthmodel}}).} 15 | } 16 | \value{ 17 | a list with the elements \code{FUN}, \code{valuevar}, \code{timevar}, 18 | and \code{groups} 19 | } 20 | \description{ 21 | This simple formula interface handles formulae of the form 22 | \code{dependent ~ FUN(independent, parms) | group1 + group2 + ...}. 23 | } 24 | \details{ 25 | This function is used by \code{\link{all_growthmodels}} and normally not 26 | called for the user. 27 | } 28 | \examples{ 29 | 30 | ret <- parse_formula_nonlin(y ~ f(x, parms) | a + b + c) 31 | 32 | } 33 | \seealso{ 34 | \code{\link{multisplit}}, \code{\link{split}}, \code{\link{parse_formula}} 35 | } 36 | \keyword{internal} 37 | -------------------------------------------------------------------------------- /src/R_init_growthrates.c: -------------------------------------------------------------------------------- 1 | #ifndef R_R_H 2 | # include 3 | #endif 4 | 5 | #ifndef R_EXT_DYNLOAD_H_ 6 | # include 7 | #endif 8 | 9 | #include 10 | #include // for NULL 11 | 12 | 13 | extern void ini_twostep(void (* odeparms)(int *, double *)); 14 | extern void d_twostep(int *neq, double *t, double *y, double *ydot, double *yout, int*ip); 15 | 16 | extern void ini_genlogistic(void (* odeparms)(int *, double *)); 17 | extern void d_genlogistic(int *neq, double *t, double *y, double *ydot, double *yout, int*ip); 18 | 19 | 20 | static const R_CMethodDef CEntries[] = { 21 | {"ini_twostep", (DL_FUNC) &ini_twostep, 1}, 22 | {"d_twostep", (DL_FUNC) &d_twostep, 6}, 23 | {"ini_genlogistic", (DL_FUNC) &ini_genlogistic, 1}, 24 | {"d_genlogistic", (DL_FUNC) &d_genlogistic, 6}, 25 | {NULL, NULL, 0} 26 | }; 27 | 28 | void R_init_growthrates(DllInfo *dll) { 29 | // register entry points 30 | R_registerRoutines(dll, CEntries, NULL, NULL, NULL); 31 | 32 | // the following two lines protect against accidentially finding entry points 33 | R_useDynamicSymbols(dll, FALSE); // disable dynamic searching 34 | //R_forceSymbols(dll, TRUE); // entry points as R objects, not as strings 35 | } 36 | -------------------------------------------------------------------------------- /man/grow_exponential.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_exponential.R 3 | \name{grow_exponential} 4 | \alias{grow_exponential} 5 | \title{Exponential Growth Model} 6 | \usage{ 7 | grow_exponential(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable).} 11 | 12 | \item{parms}{named parameter vector of the exponential growth model with: 13 | \itemize{ 14 | \item \code{y0} initial abundance (e.g. concentration of bacterial cells). 15 | \item \code{mumax} maximum growth rate (1/time). 16 | }} 17 | } 18 | \value{ 19 | vector of dependent variable (\code{y}). 20 | } 21 | \description{ 22 | Unlimited exponential growth model. 23 | } 24 | \details{ 25 | The equation used is: 26 | \deqn{y = y0 * exp(mumax * time)} 27 | } 28 | \examples{ 29 | 30 | time <- seq(0, 30, length=200) 31 | y <- grow_exponential(time, c(y0=1, mumax=0.5))[,"y"] 32 | plot(time, y, type="l") 33 | 34 | } 35 | \seealso{ 36 | Other growth models: 37 | \code{\link{grow_baranyi}()}, 38 | \code{\link{grow_gompertz}()}, 39 | \code{\link{grow_gompertz2}()}, 40 | \code{\link{grow_huang}()}, 41 | \code{\link{grow_logistic}()}, 42 | \code{\link{grow_richards}()}, 43 | \code{\link{growthmodel}}, 44 | \code{\link{ode_genlogistic}()}, 45 | \code{\link{ode_twostep}()} 46 | } 47 | \concept{growth models} 48 | -------------------------------------------------------------------------------- /inst/doc/examples/example_easylinear.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Determine growth rates with a heuristic linear method, similar to 3 | ## the method of Hall et al. 2013, doi:10.1093/molbev/mst197 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | 11 | library("growthrates") 12 | library(lattice) 13 | 14 | data(bactgrowth) 15 | 16 | ## select a single data set 17 | dat <- splitted.data[[1]] 18 | plot(value ~ time, data = dat) 19 | 20 | ## fit linear model to steepest part of the log-transformed data 21 | fit <- fit_easylinear(dat$time, dat$value) 22 | plot(fit) 23 | plot(fit, log = "y") 24 | 25 | ## diagnostic plot 26 | plot(fit, which = "diagnostics") 27 | 28 | ## change settings of the algorithm 29 | fitx <- fit_easylinear(dat$time, dat$value, h = 8, quota = 0.95) 30 | 31 | par(mfrow=c(1, 2)) 32 | plot(fit, log="y") 33 | lines(fitx, pch = "+", col = "blue") 34 | 35 | plot(fit) 36 | lines(fitx, pch = "+", col = "blue") 37 | 38 | ## fit all data sets 39 | allFits <- all_easylinear(value ~ time|strain + conc + replicate, 40 | data = bactgrowth) 41 | coef(allFits) 42 | -------------------------------------------------------------------------------- /R/grow_logistic.R: -------------------------------------------------------------------------------- 1 | #' Logistic Growth Model 2 | #' 3 | #' Classical logistic growth model written as analytical solution of the differential equation. 4 | #' 5 | #' The equation used is: 6 | #' \deqn{y = (K * y0) / (y0 + (K - y0) * exp(-mumax * time))} 7 | #' 8 | #' @param time vector of time steps (independent variable) 9 | #' @param parms named parameter vector of the logistic growth model with: 10 | #' \itemize{ 11 | #' \item \code{y0} initial value of population measure 12 | #' \item \code{mumax} intrinsic growth rate (1/time) 13 | #' \item \code{K} carrying capacity (max. total concentration of cells) 14 | #' } 15 | #' 16 | #' @return vector of dependent variable (\code{y}). 17 | #' 18 | #' @examples 19 | #' 20 | #' time <- seq(0, 30, length=200) 21 | #' y <- grow_logistic(time, c(y0=1, mumax=0.5, K=10))[,"y"] 22 | #' plot(time, y, type="l") 23 | #' 24 | #' @family growth models 25 | #' 26 | #' @rdname grow_logistic 27 | #' @export grow_logistic 28 | #' 29 | grow_logistic <- function(time, parms) { 30 | with(as.list(parms), { 31 | y <- (K * y0) / (y0 + (K - y0) * exp(-mumax * time)) 32 | return(as.matrix(data.frame(time=time, y=y))) 33 | }) 34 | } 35 | ## attach names of parameters as attributes 36 | attr(grow_logistic, "fname") <- c("grow_logistic") 37 | attr(grow_logistic, "pnames") <- c("y0", "mumax", "K") 38 | class(grow_logistic) <- c("growthmodel", "function") 39 | -------------------------------------------------------------------------------- /R/grow_exponential.R: -------------------------------------------------------------------------------- 1 | #' Exponential Growth Model 2 | #' 3 | #' Unlimited exponential growth model. 4 | #' 5 | #' The equation used is: 6 | #' \deqn{y = y0 * exp(mumax * time)} 7 | #' 8 | #' @param time vector of time steps (independent variable). 9 | #' @param parms named parameter vector of the exponential growth model with: 10 | #' \itemize{ 11 | #' \item \code{y0} initial abundance (e.g. concentration of bacterial cells). 12 | #' \item \code{mumax} maximum growth rate (1/time). 13 | #' } 14 | #' 15 | #' @return vector of dependent variable (\code{y}). 16 | #' 17 | #' @examples 18 | #' 19 | #' time <- seq(0, 30, length=200) 20 | #' y <- grow_exponential(time, c(y0=1, mumax=0.5))[,"y"] 21 | #' plot(time, y, type="l") 22 | #' 23 | #' @family growth models 24 | #' 25 | #' @rdname grow_exponential 26 | #' @export 27 | #' 28 | grow_exponential <- function(time, parms) { 29 | ## lm object coefficients have no names 30 | if (is.null(names(parms))) { 31 | y0 <- parms[1] 32 | mumax <- parms[2] 33 | } else { 34 | y0 <- parms["y0"] 35 | mumax <- parms["mumax"] 36 | } 37 | y <- y0 * exp(mumax * time) 38 | return(as.matrix(data.frame(time=time, y=y))) 39 | } 40 | ## attach names of parameters as attributes 41 | attr(grow_exponential, "fname") <- c("grow_exponential") 42 | attr(grow_exponential, "pnames") <- c("y0", "mumax") 43 | class(grow_exponential) <- c("growthmodel", "function") 44 | -------------------------------------------------------------------------------- /man/grow_logistic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_logistic.R 3 | \name{grow_logistic} 4 | \alias{grow_logistic} 5 | \title{Logistic Growth Model} 6 | \usage{ 7 | grow_logistic(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable)} 11 | 12 | \item{parms}{named parameter vector of the logistic growth model with: 13 | \itemize{ 14 | \item \code{y0} initial value of population measure 15 | \item \code{mumax} intrinsic growth rate (1/time) 16 | \item \code{K} carrying capacity (max. total concentration of cells) 17 | }} 18 | } 19 | \value{ 20 | vector of dependent variable (\code{y}). 21 | } 22 | \description{ 23 | Classical logistic growth model written as analytical solution of the differential equation. 24 | } 25 | \details{ 26 | The equation used is: 27 | \deqn{y = (K * y0) / (y0 + (K - y0) * exp(-mumax * time))} 28 | } 29 | \examples{ 30 | 31 | time <- seq(0, 30, length=200) 32 | y <- grow_logistic(time, c(y0=1, mumax=0.5, K=10))[,"y"] 33 | plot(time, y, type="l") 34 | 35 | } 36 | \seealso{ 37 | Other growth models: 38 | \code{\link{grow_baranyi}()}, 39 | \code{\link{grow_exponential}()}, 40 | \code{\link{grow_gompertz}()}, 41 | \code{\link{grow_gompertz2}()}, 42 | \code{\link{grow_huang}()}, 43 | \code{\link{grow_richards}()}, 44 | \code{\link{growthmodel}}, 45 | \code{\link{ode_genlogistic}()}, 46 | \code{\link{ode_twostep}()} 47 | } 48 | \concept{growth models} 49 | -------------------------------------------------------------------------------- /inst/doc/examples/example_spline.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Determine growth rates with a nonparametric smoothing spline. 3 | ## system of differential equations. 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | library("growthrates") 11 | 12 | data(bactgrowth) 13 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 14 | 15 | ## select a single data set 16 | dat <- splitted.data[[2]] 17 | time <- dat$time 18 | y <- dat$value 19 | 20 | ## automatic smoothing with cv 21 | res <- fit_spline(time, y) 22 | 23 | plot(res, log="y") 24 | plot(res) 25 | coef(res) 26 | 27 | ## a more difficult data set 28 | dat <- splitted.data[[56]] 29 | time <- dat$time 30 | y <- dat$value 31 | 32 | ## default parameters 33 | res <- fit_spline(time, y) 34 | plot(res, log="y") 35 | 36 | ## small optgrid, trapped in local minimum 37 | res <- fit_spline(time, y, optgrid=5) 38 | plot(res, log="y") 39 | 40 | 41 | ## manually selected smoothing parameter 42 | res <- fit_spline(time, y, spar=.5) 43 | plot(res, log="y") 44 | plot(res, ylim=c(0.005, 0.03)) 45 | 46 | ## evaluate results 47 | summary(res) 48 | residuals(res) # log-transformed 49 | rsquared(res) # log-transformed 50 | deviance(res) # 51 | 52 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown.yaml 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | pkgdown: 17 | runs-on: ubuntu-latest 18 | # Only restrict concurrency for non-PR jobs 19 | concurrency: 20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | permissions: 24 | contents: write 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - uses: r-lib/actions/setup-pandoc@v2 29 | 30 | - uses: r-lib/actions/setup-r@v2 31 | with: 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-r-dependencies@v2 35 | with: 36 | extra-packages: any::pkgdown, local::. 37 | needs: website 38 | 39 | - name: Build site 40 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 41 | shell: Rscript {0} 42 | 43 | - name: Deploy to GitHub pages 🚀 44 | if: github.event_name != 'pull_request' 45 | uses: JamesIves/github-pages-deploy-action@v4.5.0 46 | with: 47 | clean: false 48 | branch: gh-pages 49 | folder: docs 50 | -------------------------------------------------------------------------------- /R/parse_formula.R: -------------------------------------------------------------------------------- 1 | #' Simple Formula Interface 2 | #' 3 | #' This simple formula interface handles formulae of the form 4 | #' \code{dependent ~ independent | group1 + group2 + ...}. 5 | #' 6 | #' This function is used by \code{\link{multisplit}} and normally not called 7 | #' by the user. 8 | #' 9 | #' @param grouping a model formula specifying dependent, 10 | #' independent and grouping variables in the form: 11 | #' \code{dependent ~ independent | group1 + group2 + ...}. 12 | #' 13 | #' @return a list with the elements \code{valuevar}, \code{timevar}, and 14 | #' \code{groups} 15 | #' 16 | #' @seealso \code{\link{multisplit}}, \code{\link{split}} 17 | #' 18 | #' @examples 19 | #' 20 | #' parse_formula(y ~ x | a+b+c) 21 | #' 22 | #' @keywords internal 23 | #' 24 | #' @export 25 | #' 26 | parse_formula <- function(grouping) { 27 | tm <- terms(grouping) 28 | 29 | valuevar <- as.character(tm[[2]]) 30 | RHS <- as.character(tm[[3]]) 31 | if (RHS[1] == "|") { 32 | timevar <- RHS[2] # with grouping 33 | } else { 34 | timevar <- RHS[1] # without grouping 35 | } 36 | groups <- 37 | gsub("[*:]", "+", RHS[3]) # convert "*" or ":" to "+" 38 | groups <- 39 | unlist(strsplit(groups, "[+]")) # split right hand side 40 | groups <- gsub("^\\s+|\\s+$", "", groups) # trim 41 | 42 | ## replace NA by NULL 43 | if (is.na(groups[1])) groups <- NULL 44 | 45 | list(valuevar = valuevar, 46 | timevar = timevar, 47 | groups = groups) 48 | } 49 | -------------------------------------------------------------------------------- /R/lm_window.R: -------------------------------------------------------------------------------- 1 | #' Fit Exponential Growth Model with a Heuristic Linear Method 2 | #' 3 | #' Helper functions for handling linear fits. 4 | #' 5 | #' The functions are used by a heuristic linear approach, similar to the 6 | #' ``growth rates made easy''-method of Hall et al. (2013). 7 | #' 8 | #' 9 | #' @param x vector of independent variable (e.g. time). 10 | #' @param y vector of dependent variable (concentration of organisms). 11 | #' @param i0 index of first value used for a window. 12 | #' @param h with of the window (number of data). 13 | #' 14 | #' @return linear model object (\code{lm_window} 15 | #' resp. vector with parameters of the fit (lm_parms). 16 | #' 17 | #' @references Hall, B. G., H. Acar and M. Barlow 2013. Growth Rates Made Easy. 18 | #' Mol. Biol. Evol. 31: 232-238 \doi{10.1093/molbev/mst197} 19 | #' 20 | #' @keywords internal 21 | #' 22 | #' @export lm_window 23 | #' 24 | lm_window <- function(x, y, i0, h=5) { 25 | ## function that fits a linear model 26 | ## to a selected window of data from a series, e.g. 5 points 27 | #cat(h, "\n") 28 | x <- x[i0 - 1 + (1:h)] 29 | y <- y[i0 - 1 + (1:h)] 30 | m <- lm(y ~ x) 31 | return(m) 32 | } 33 | 34 | #' @param m linear model (\code{lm}) object 35 | #' 36 | #' @rdname lm_window 37 | #' @export lm_parms 38 | #' 39 | #' @keywords internal 40 | #' 41 | lm_parms <- function(m) { 42 | sm <- summary(m) 43 | a <- sm$coefficients[1, 1] # intercept 44 | b <- sm$coefficients[2, 1] # slope 45 | b.se <- sm$coefficients[2, 2] # SE of slope 46 | r2 <- sm$r.squared 47 | c(a=a, b=b, se=b.se, r2=r2, cv=b.se/b) 48 | } 49 | 50 | -------------------------------------------------------------------------------- /R/cost.R: -------------------------------------------------------------------------------- 1 | #' Cost Function for Nonlinear Fits 2 | #' 3 | #' Defines a cost function from the residual sum of squares between model 4 | #' and observational data. 5 | #' 6 | #' 7 | #' @param FUN function of growth model to be fitted. 8 | #' @param p vector of fitted parameters of the growth model. 9 | #' @param fixed.p vector of fixed parameters of the growth model. 10 | #' @param \dots additional parameters passed to the optimizer. 11 | #' 12 | #' @return an object of class \code{modCost}, see \code{\link[FME]{modCost}} in 13 | #' package \pkg{FME} 14 | #' 15 | #' @export cost 16 | #' @keywords internal 17 | #' 18 | #' @details 19 | #' 20 | #' Function 'cost' is implemented along the lines of the 21 | #' following template, see package FME for details: 22 | #' \preformatted{ 23 | #' cost <- function(p, obs, FUN, fixed.p = NULL, ...) { 24 | #' out <- FUN(obs$time, c(p, fixed.p)) 25 | #' modCost(out, obs, weight = "none", ...) 26 | #' } 27 | #' } 28 | 29 | cost <- function(p, obs, FUN, fixed.p = NULL, transform, ...) { 30 | out <- FUN(obs$time, c(p, fixed.p)) 31 | 32 | if (transform == "log") out <- cbind(out, log_y=log(out[,"y"])) 33 | 34 | ## check NA and NaN, out has the columns: time, y, y_log 35 | if (any(is.infinite(out[,2]), is.nan(out[,2]), is.na(out[,2]))) { 36 | warning("Invalid return values of FUN.\n", 37 | "Use constraints (lower, upper), change optimizer or try another model.\n", 38 | "The set of optimization parameters was:\n", 39 | paste(names(p), p, sep="=", collapse=", ")) 40 | ret <- Inf 41 | } else { 42 | ret <-modCost(out, obs, weight = "none", ...) 43 | } 44 | ret 45 | } 46 | -------------------------------------------------------------------------------- /R/extract.R: -------------------------------------------------------------------------------- 1 | #' Extract or Replace Parts of a 'multiple_fits' Object 2 | #' 3 | #' Operators to access parts of 'multiple_fits' objects 4 | #' 5 | #' @param x object of class multiple_fits 6 | #' @param i numeric or character index 7 | #' @param j NULL (for compatibility with other uses of \code{[} or \code{[[}) 8 | #' @param drop If \code{TRUE} the result is coerced to the lowest possible 9 | #' dimension 10 | #' @param \dots optional arguments passed to \code{[} 11 | #' 12 | #' @examples 13 | #' 14 | #' data(bactgrowth) 15 | #' L <- all_splines(value ~ time | strain + conc +replicate, data=bactgrowth) 16 | #' 17 | #' coef(L[[1]]) 18 | #' 19 | #' plot(L[["R:0:2"]]) 20 | #' 21 | #' # par(mfrow=c(2, 2)) 22 | #' plot(L[1:4]) 23 | #' 24 | #' @rdname extract 25 | #' @exportMethod "[" 26 | #' 27 | setMethod("[", c(x="multiple_fits", i="ANY", j="missing"), 28 | function(x, i, j=NULL, ...) { 29 | ## if (!is.null(j)) 30 | ## stop("incorrect number of subscripts") 31 | #if (length(i) == 1) { 32 | # x@fits[i=i] 33 | #} else { 34 | new("multiple_fits", 35 | fits = x@fits[i=i], 36 | grouping = x@grouping 37 | ) 38 | #} 39 | }) 40 | 41 | #' @rdname extract 42 | #' @exportMethod "[[" 43 | #' 44 | setMethod("[[", c(x="multiple_fits", i="ANY", j="missing"), 45 | function(x, i, j, ...) { 46 | ## if (!is.null(j)) 47 | ## stop("incorrect number of subscripts") 48 | if (length(i) > 1) 49 | stop("[[ can only be used to select one single element") 50 | x@fits[[i]] 51 | }) 52 | -------------------------------------------------------------------------------- /R/aab_growthmodel-constructor.R: -------------------------------------------------------------------------------- 1 | #' Create a User-defined Parametric Growth Model 2 | #' 3 | #' This constructor method allows to create user-defined functions 4 | #' that can be used as parametric models describing time-dependent 5 | #' growth of organisms. 6 | #' 7 | #' Package \pkg{growthrates} has a plug-in architecture allowing 8 | #' user-defined growth models of the following form: 9 | #' 10 | #' \preformatted{ 11 | #' identifier <- function(time, parms) { 12 | #' ... content of function here ... 13 | #' return(as.matrix(data.frame(time=time, y=y))) 14 | #' } 15 | #' } 16 | #' 17 | #' where \code{time} is a numeric vector and \code{parms} a named, non-nested 18 | #' list of model parameters. The constructor function \code{growthmodel} 19 | #' is used to attach the names of the parameters as an optional 20 | #' attribute. 21 | #' 22 | #' @param x a function with arguments \code{times} and \code{parms}, and 23 | #' returning a matrix with two columns \code{time} and \code{y}. 24 | #' @param pnames character vector with the names of the model parameters. 25 | #' 26 | #' @examples 27 | #' 28 | #' test <- function(time, parms) { 29 | #' with(as.list(parms), { 30 | #' y <- (K * y0) / (y0 + (K - y0) * exp(-mumax * time)) + y_shift 31 | #' return(as.matrix(data.frame(time=time, y=y))) 32 | #' }) 33 | #' } 34 | #' 35 | #' mygrowthmodel <- growthmodel(test, c("y0", "mumax", "K", "y_shift")) 36 | #' 37 | #' 38 | #' @family growth models 39 | #' @rdname growthmodel-constructor 40 | #' @aliases user-defined functions 41 | #' @export growthmodel 42 | #' 43 | growthmodel <- function(x, pnames=NULL) { 44 | if (!is.function(x)) stop("X must be a function") 45 | structure(x, pnames=pnames, class = c("growthmodel", "function")) 46 | } 47 | -------------------------------------------------------------------------------- /inst/doc/examples/example_user_defined_growthmodel.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## The following example shows how to create user-defined growth models 3 | ## 4 | ## Author: Thomas Petzoldt, TU Dresden 5 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 6 | ## Please cite our work when using this package. 7 | ## ============================================================================= 8 | 9 | 10 | library("growthrates") 11 | 12 | 13 | ## ============================================================================= 14 | ## create a "growthmodel" with interfaces compatible to package growthrates 15 | ## see ?growthmodel for details 16 | ## Note: 17 | ## It is essential to use consistent names for parameters and initial values! 18 | ## ============================================================================= 19 | 20 | grow_logistic_yshift <- function(time, parms) { 21 | with(as.list(parms), { 22 | y <- (K * y0) / (y0 + (K - y0) * exp(-mumax * time)) + y_shift 23 | return(as.matrix(data.frame(time=time, y=y))) 24 | }) 25 | } 26 | 27 | grow_logistic_yshift <- growthmodel(grow_logistic_yshift, 28 | c("y0", "mumax", "K", "y_shift")) 29 | 30 | 31 | ## ============================================================================= 32 | ## Fit the model 33 | ## ============================================================================= 34 | 35 | x <- seq(5, 100, 5) 36 | 37 | y <- c(2.1, 2.3, 5, 4.7, 4.3, 6.9, 8.2, 11.5, 8.8, 10.2, 14.5, 12.5, 38 | 13.6, 12.7, 14.2, 12.5, 13.8, 15.1, 12.7, 14.9) + 5 39 | 40 | fit <- fit_growthmodel(grow_logistic_yshift, 41 | p=c(y0=1, mumax=0.1, K=10, K = 10, y_shift=1), 42 | time=x, y=y) 43 | plot(fit) 44 | summary(fit) 45 | 46 | -------------------------------------------------------------------------------- /man/names.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/names.R 3 | \name{names.growthmodel} 4 | \alias{names.growthmodel} 5 | \alias{names,multiple_fits-method} 6 | \alias{names<-,multiple_fits-method} 7 | \title{Get Names Attributes of Growth Models} 8 | \usage{ 9 | \method{names}{growthmodel}(x) 10 | 11 | \S4method{names}{multiple_fits}(x) 12 | 13 | \S4method{names}{multiple_fits}(x) <- value 14 | } 15 | \arguments{ 16 | \item{x}{either a function being a parametric growth model of 17 | package \pkg{growthmodels} or an object with multiple fits.} 18 | 19 | \item{value}{a character vector of up to the same length as x, or NULL} 20 | } 21 | \value{ 22 | character vector of the parameter names 23 | } 24 | \description{ 25 | Methods to get the parameter names of a growth model or to get or set 26 | identifiers of \code{\link{multiple_fits}} objects. 27 | } 28 | \section{Methods}{ 29 | 30 | \describe{ 31 | \item{Method for class \code{\link{growthmodel}}:}{ returns information about 32 | valid parameter names if a \code{pnames} attribute exists, else \code{NULL}. 33 | \code{NULL}.} 34 | \item{Method for class \code{\link{multiple_fits}}:}{ can be applied to objects 35 | returned by \code{all_growthmodels}, \code{all_splines} or 36 | \code{all_easylinear} respectively. This can be useful for selecting 37 | subsets, e.g. for plotting, see example below.} 38 | } 39 | } 40 | 41 | \examples{ 42 | 43 | ## growthmodel-method 44 | names(grow_baranyi) 45 | 46 | ## multiple_fits-method 47 | L <- all_splines(value ~ time | strain + conc + replicate, 48 | data = bactgrowth) 49 | 50 | names(L) 51 | 52 | ## plot only the 'R' strain 53 | # par(mfrow=c(4, 6)) 54 | plot(L[grep("R:", names(L))]) 55 | 56 | 57 | } 58 | \seealso{ 59 | \code{\link{multiple_fits}}, \code{\link{all_growthmodels}}, 60 | \code{\link{all_splines}}, \code{\link{all_easylinear}} 61 | } 62 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Encoding: UTF-8 2 | Package: growthrates 3 | Type: Package 4 | Title: Estimate Growth Rates from Experimental Data 5 | Version: 0.8.6 6 | Date: 2025-08-28 7 | LazyData: yes 8 | Authors@R: c(person("Thomas","Petzoldt", role = c("aut", "cre"), 9 | email = "thomas.petzoldt@tu-dresden.de", 10 | comment = c(ORCID = "0000-0002-4951-6468"))) 11 | Maintainer: Thomas Petzoldt 12 | Description: A collection of methods to determine growth rates from 13 | experimental data, in particular from batch experiments and 14 | plate reader trials. 15 | Depends: 16 | R (>= 3.2), 17 | lattice, 18 | deSolve 19 | Imports: 20 | stats, 21 | graphics, 22 | methods, 23 | parallel, 24 | utils, 25 | FME 26 | Suggests: 27 | knitr, 28 | rmarkdown, 29 | dplyr, 30 | ggplot2 31 | License: GPL (>= 2) 32 | URL: https://github.com/tpetzoldt/growthrates, http://tpetzoldt.github.io/growthrates/ 33 | VignetteBuilder: knitr 34 | RoxygenNote: 7.3.2 35 | Collate: 36 | 'aaa_growthmodel-class.R' 37 | 'aab_growthmodel-constructor.R' 38 | 'aac_classes.R' 39 | 'aad_set_generics.R' 40 | 'all_easylinear.R' 41 | 'all_growthmodels.R' 42 | 'all_splines.R' 43 | 'antibiotic.R' 44 | 'bactgrowth.R' 45 | 'cost.R' 46 | 'extcoef_logistic.R' 47 | 'extract.R' 48 | 'fit_easylinear.R' 49 | 'fit_growthmodel.R' 50 | 'fit_spline.R' 51 | 'grow_baranyi.R' 52 | 'grow_exponential.R' 53 | 'grow_genlogistic.R' 54 | 'grow_gompertz.R' 55 | 'grow_gompertz2.R' 56 | 'grow_huang.R' 57 | 'grow_logistic.R' 58 | 'grow_richards.R' 59 | 'grow_twostep.R' 60 | 'growthrates-package.R' 61 | 'lm_window.R' 62 | 'methods.R' 63 | 'multisplit.R' 64 | 'names.R' 65 | 'parse_formula.R' 66 | 'parse_formula_nonlin.R' 67 | 'plot.R' 68 | 'predict.R' 69 | 'utilities.R' 70 | -------------------------------------------------------------------------------- /R/grow_gompertz.R: -------------------------------------------------------------------------------- 1 | #' Growth Model According to Gompertz 2 | #' 3 | #' Gompertz growth model written as analytical solution of the differential 4 | #' equation system. 5 | #' 6 | #' The equation used here is: 7 | #' \deqn{y = K * exp(log(y0 / K) * exp(-mumax * time))} 8 | #' 9 | #' @param time vector of time steps (independent variable). 10 | #' @param parms named parameter vector of the Gompertz growth model with: 11 | #' \itemize{ 12 | #' \item \code{y0} initial value of abundance, 13 | #' \item \code{mumax} maximum growth rate (1/time), 14 | #' \item \code{K} maximum abundance (carrying capacity). 15 | #' 16 | #' } 17 | #' 18 | #' @note The naming of parameter "mumax" was done in analogy to the other growth 19 | #' models, but it turned out that it was not consistent with the maximum 20 | #' growth rate of the population. This can be considered as bug. The function 21 | #' will be removed or replaced in future versions of the package. Please use 22 | #' \code{grow_gompertz2} instead. 23 | #' 24 | #' @references Tsoularis, A. (2001) Analysis of Logistic Growth Models. 25 | #' Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 26 | #' 27 | #' @return vector of dependent variable (\code{y}) 28 | #' 29 | #' @examples 30 | #' 31 | #' time <- seq(0, 30, length=200) 32 | #' y <- grow_gompertz(time, c(y0=1, mumax=.2, K=10))[,"y"] 33 | #' plot(time, y, type="l", ylim=c(0, 20)) 34 | #' 35 | #' 36 | #' @family growth models 37 | #' 38 | #' @rdname grow_gompertz 39 | #' @export grow_gompertz 40 | #' 41 | grow_gompertz <- function(time, parms) { 42 | with(as.list(parms), { 43 | y <- K * exp(log(y0 / K) * exp(-mumax * time)) 44 | return(as.matrix(data.frame(time = time, y = y))) 45 | }) 46 | } 47 | ## attach names of parameters as attributes 48 | attr(grow_gompertz, "fname") <- c("grow_gompertz") 49 | attr(grow_gompertz, "pnames") <- c("y0", "mumax", "K") 50 | class(grow_gompertz) <- c("growthmodel", "function") 51 | -------------------------------------------------------------------------------- /man/grow_gompertz.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_gompertz.R 3 | \name{grow_gompertz} 4 | \alias{grow_gompertz} 5 | \title{Growth Model According to Gompertz} 6 | \usage{ 7 | grow_gompertz(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable).} 11 | 12 | \item{parms}{named parameter vector of the Gompertz growth model with: 13 | \itemize{ 14 | \item \code{y0} initial value of abundance, 15 | \item \code{mumax} maximum growth rate (1/time), 16 | \item \code{K} maximum abundance (carrying capacity). 17 | 18 | }} 19 | } 20 | \value{ 21 | vector of dependent variable (\code{y}) 22 | } 23 | \description{ 24 | Gompertz growth model written as analytical solution of the differential 25 | equation system. 26 | } 27 | \details{ 28 | The equation used here is: 29 | \deqn{y = K * exp(log(y0 / K) * exp(-mumax * time))} 30 | } 31 | \note{ 32 | The naming of parameter "mumax" was done in analogy to the other growth 33 | models, but it turned out that it was not consistent with the maximum 34 | growth rate of the population. This can be considered as bug. The function 35 | will be removed or replaced in future versions of the package. Please use 36 | \code{grow_gompertz2} instead. 37 | } 38 | \examples{ 39 | 40 | time <- seq(0, 30, length=200) 41 | y <- grow_gompertz(time, c(y0=1, mumax=.2, K=10))[,"y"] 42 | plot(time, y, type="l", ylim=c(0, 20)) 43 | 44 | 45 | } 46 | \references{ 47 | Tsoularis, A. (2001) Analysis of Logistic Growth Models. 48 | Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 49 | } 50 | \seealso{ 51 | Other growth models: 52 | \code{\link{grow_baranyi}()}, 53 | \code{\link{grow_exponential}()}, 54 | \code{\link{grow_gompertz2}()}, 55 | \code{\link{grow_huang}()}, 56 | \code{\link{grow_logistic}()}, 57 | \code{\link{grow_richards}()}, 58 | \code{\link{growthmodel}}, 59 | \code{\link{ode_genlogistic}()}, 60 | \code{\link{ode_twostep}()} 61 | } 62 | \concept{growth models} 63 | -------------------------------------------------------------------------------- /inst/doc/examples/example_huang_p_as_df.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Set start values for parametric model fitting as a data frame. 3 | ## 4 | ## Author: Thomas Petzoldt, TU Dresden 5 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 6 | ## Please cite our work when using this package. 7 | ## ============================================================================= 8 | 9 | 10 | library("growthrates") 11 | library("lattice") 12 | 13 | ## load data 14 | data(bactgrowth) 15 | splitted.data <- multisplit(value ~ time | strain + conc + replicate, data = bactgrowth) 16 | dat <- splitted.data[[23]] 17 | 18 | ## initial parameters and box constraints 19 | p <- c(y0 = 0.03, mumax = .1, K = 0.1, alpha = 1, lambda = 2) 20 | lower <- c(y0 = 0.001, mumax = 1e-2, K = 0.005, alpha = -100, lambda = -20) 21 | upper <- c(y0 = 0.1, mumax = 1, K = 0.5, alpha = 200, lambda = 20) 22 | 23 | ## fit model 24 | fit <- fit_growthmodel(FUN = grow_huang, p = p, time = dat$time, y = dat$value, 25 | lower = lower, upper = upper, 26 | control = list(trace = TRUE)) 27 | 28 | ## coefficients and plot 29 | coef(fit) 30 | plot(fit) 31 | 32 | ## create data frame of parameters 33 | ndata <- length(splitted.data) 34 | pp <- as.data.frame(matrix(rep(p, ndata), byrow = TRUE, ncol = length(p))) 35 | names(pp) <- names(p) 36 | 37 | ## fit 66 and 68 (T:62.5:2; R:125:2) need different start parameters 38 | pp[66, ] <- pp[68, ] <- c(y0 = 0.01, mumax = .1, K = 0.02, alpha = 1, lambda = 2) 39 | 40 | 41 | ## fit growth models to all data 42 | L <- all_growthmodels(value ~ time | strain + conc + replicate, FUN = grow_huang, 43 | data = bactgrowth, 44 | p = pp, lower = lower, upper = upper, 45 | log = "y", ncores = 4, method = "L-BFGS-B") 46 | 47 | par(mfrow = c(4,3)) 48 | plot(L) 49 | 50 | res <- results(L) 51 | -------------------------------------------------------------------------------- /man/growthmodel-constructor.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aab_growthmodel-constructor.R 3 | \name{growthmodel} 4 | \alias{growthmodel} 5 | \alias{user-defined} 6 | \alias{functions} 7 | \title{Create a User-defined Parametric Growth Model} 8 | \usage{ 9 | growthmodel(x, pnames = NULL) 10 | } 11 | \arguments{ 12 | \item{x}{a function with arguments \code{times} and \code{parms}, and 13 | returning a matrix with two columns \code{time} and \code{y}.} 14 | 15 | \item{pnames}{character vector with the names of the model parameters.} 16 | } 17 | \description{ 18 | This constructor method allows to create user-defined functions 19 | that can be used as parametric models describing time-dependent 20 | growth of organisms. 21 | } 22 | \details{ 23 | Package \pkg{growthrates} has a plug-in architecture allowing 24 | user-defined growth models of the following form: 25 | 26 | \preformatted{ 27 | identifier <- function(time, parms) { 28 | ... content of function here ... 29 | return(as.matrix(data.frame(time=time, y=y))) 30 | } 31 | } 32 | 33 | where \code{time} is a numeric vector and \code{parms} a named, non-nested 34 | list of model parameters. The constructor function \code{growthmodel} 35 | is used to attach the names of the parameters as an optional 36 | attribute. 37 | } 38 | \examples{ 39 | 40 | test <- function(time, parms) { 41 | with(as.list(parms), { 42 | y <- (K * y0) / (y0 + (K - y0) * exp(-mumax * time)) + y_shift 43 | return(as.matrix(data.frame(time=time, y=y))) 44 | }) 45 | } 46 | 47 | mygrowthmodel <- growthmodel(test, c("y0", "mumax", "K", "y_shift")) 48 | 49 | 50 | } 51 | \seealso{ 52 | Other growth models: 53 | \code{\link{grow_baranyi}()}, 54 | \code{\link{grow_exponential}()}, 55 | \code{\link{grow_gompertz}()}, 56 | \code{\link{grow_gompertz2}()}, 57 | \code{\link{grow_huang}()}, 58 | \code{\link{grow_logistic}()}, 59 | \code{\link{grow_richards}()}, 60 | \code{\link{ode_genlogistic}()}, 61 | \code{\link{ode_twostep}()} 62 | } 63 | \concept{growth models} 64 | -------------------------------------------------------------------------------- /demo/growthrates.R: -------------------------------------------------------------------------------- 1 | library("growthrates") 2 | 3 | ## ============================================================================= 4 | ## single fits 5 | ## ============================================================================= 6 | 7 | data(bactgrowth) 8 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 9 | 10 | ## make subset of single experiment 11 | dat <- splitted.data[["D:0:1"]] 12 | 13 | fit1 <- fit_spline(dat$time, dat$value) 14 | par(mfrow=c(1, 2)) 15 | plot(fit1, log="y") 16 | plot(fit1) 17 | 18 | ## derive start parameters from spline fit 19 | p <- coef(fit1) 20 | 21 | ## fit exponential to subset of first 10 data of the experiment 22 | first10 <- dat[1:10, ] 23 | fit2 <- fit_growthmodel(grow_exponential, p=p, time=first10$time, y=first10$value) 24 | 25 | ## fit logistic curve to all data of the subset 26 | p <- c(coef(fit1), K = max(dat$value)) 27 | fit3 <- fit_growthmodel(grow_logistic, p=p, time=dat$time, y=dat$value, transform="log") 28 | 29 | plot(fit1) 30 | lines(fit2, col="green") 31 | lines(fit3, col="red") 32 | 33 | ## ============================================================================= 34 | ## multiple fits 35 | ## ============================================================================= 36 | 37 | ## use spline method with user-definded smoothness (spar) 38 | sfit <- all_splines(value ~ time | strain + conc + replicate, data = bactgrowth, 39 | spar=0.5) 40 | 41 | ## initial parameters 42 | (p <- c(coef(fit1), K = max(dat$value))) 43 | 44 | ## avoid negative parameters 45 | lower = c(y0=0, mu=0, K=0) 46 | 47 | ## total fit to all data 48 | pfit1 <- all_growthmodels(value ~ grow_logistic(time, parms), data=bactgrowth, 49 | p = p, lower = lower) 50 | 51 | par(mfrow=c(1,1)) 52 | plot(pfit1) 53 | ## fit all models 54 | pfit2 <- all_growthmodels(value ~ grow_logistic(time, parms) | 55 | strain + conc + replicate, data=bactgrowth, 56 | p = p, lower = lower, ncores=2) 57 | par(mfrow=c(4, 3)) 58 | plot(pfit2) 59 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(all_easylinear,data.frame) 4 | S3method(all_easylinear,formula) 5 | S3method(all_growthmodels,"function") 6 | S3method(all_growthmodels,formula) 7 | S3method(all_splines,data.frame) 8 | S3method(all_splines,formula) 9 | S3method(names,growthmodel) 10 | export(all_easylinear) 11 | export(all_growthmodels) 12 | export(all_splines) 13 | export(cost) 14 | export(extcoef_logistic) 15 | export(fit_easylinear) 16 | export(fit_growthmodel) 17 | export(fit_spline) 18 | export(grow_baranyi) 19 | export(grow_exponential) 20 | export(grow_genlogistic) 21 | export(grow_gompertz) 22 | export(grow_gompertz2) 23 | export(grow_gompertz3) 24 | export(grow_huang) 25 | export(grow_logistic) 26 | export(grow_richards) 27 | export(grow_twostep) 28 | export(growthmodel) 29 | export(lm_parms) 30 | export(lm_window) 31 | export(ode_genlogistic) 32 | export(ode_twostep) 33 | export(parse_formula) 34 | export(parse_formula_nonlin) 35 | export(predict) 36 | export(rsquared) 37 | exportClasses(easylinear_fit) 38 | exportClasses(function_growthmodel) 39 | exportClasses(growthmodel) 40 | exportClasses(growthrates_fit) 41 | exportClasses(lm_or_NULL) 42 | exportClasses(multiple_easylinear_fits) 43 | exportClasses(multiple_fits) 44 | exportClasses(multiple_nonlinear_fits) 45 | exportClasses(multiple_smooth.spline_fits) 46 | exportClasses(nonlinear_fit) 47 | exportClasses(smooth.spline_fit) 48 | exportMethods("[") 49 | exportMethods("[[") 50 | exportMethods("names<-") 51 | exportMethods(coef) 52 | exportMethods(deviance) 53 | exportMethods(df.residual) 54 | exportMethods(lines) 55 | exportMethods(multisplit) 56 | exportMethods(names) 57 | exportMethods(obs) 58 | exportMethods(plot) 59 | exportMethods(predict) 60 | exportMethods(residuals) 61 | exportMethods(results) 62 | exportMethods(rsquared) 63 | exportMethods(summary) 64 | import(FME) 65 | import(graphics) 66 | import(lattice) 67 | import(methods) 68 | import(parallel) 69 | import(stats) 70 | importFrom(deSolve,ode) 71 | importFrom(utils,type.convert) 72 | useDynLib(growthrates) 73 | -------------------------------------------------------------------------------- /inst/doc/examples/example_user_defined_ode_model.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## The following example shows how to create user-defined growth models 3 | ## from a system of differential equations in R. 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | library("growthrates") 11 | 12 | ## ============================================================================= 13 | ## create a "growthmodel" with interfaces compatible to package growthrates 14 | ## see ?growthmodel for details 15 | ## Note: 16 | ## It is essential to use consistent names for parameters and initial values! 17 | ## ============================================================================= 18 | 19 | ode_K_linear <- function (time, init, parms, ...) { 20 | with(as.list(c(parms, init)), { 21 | dy <- mumax * y * (1 - y/K) 22 | dK <- dK 23 | list(c(dy, dK)) 24 | }) 25 | } 26 | 27 | 28 | grow_K_linear <- function(time, parms, ...) { 29 | init <- parms[c("y0", "K")] # initial values 30 | names(init) <- c("y", "K") # the names of state variables 31 | parms <- parms[c("mumax", "dK")] # the "real" ODE model parms 32 | out <- ode(init, time, ode_K_linear, parms) 33 | out 34 | } 35 | 36 | grow_K_linear <- growthmodel(grow_K_linear, pnames=c("y0", "K", "mumax", "deltaK")) 37 | 38 | head(grow_K_linear(time=1:10, c(y0=.1, K=1, mumax=0.1, dK = 0.5))) 39 | 40 | ## ============================================================================= 41 | ## Fit the model 42 | ## ============================================================================= 43 | 44 | x <- seq(5, 100, 5) 45 | 46 | y <- c(0.1, 2.2, 3.1, 1.5, 8.9, 8, 8.4, 9.8, 9.3, 10.6, 12, 13.6, 47 | 13.1, 13.3, 11.6, 14.7, 12.6, 13.9, 16.9, 14.4) 48 | 49 | 50 | 51 | fit <- fit_growthmodel(grow_K_linear, 52 | p=c(y0=0.1, mumax=0.2, K=10, dK = .1), time=x, y=y) 53 | plot(fit) 54 | summary(fit) 55 | -------------------------------------------------------------------------------- /man/grow_baranyi.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_baranyi.R 3 | \name{grow_baranyi} 4 | \alias{grow_baranyi} 5 | \title{The Baranyi and Roberts Growth Model} 6 | \usage{ 7 | grow_baranyi(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable).} 11 | 12 | \item{parms}{named parameter vector of the Baranyi growth model with: 13 | \itemize{ 14 | \item \code{y0} initial value of abundance, 15 | \item \code{mumax} maximum growth rate (1/time), 16 | \item \code{K} carrying capacity (max. abundance), 17 | \item \code{h0} parameter specifying the initial physiological state of 18 | organisms (e.g. cells) and in consequence the lag phase 19 | (h0 = max growth rate * lag phase). 20 | }} 21 | } 22 | \value{ 23 | vector of dependent variable (\code{y}). 24 | } 25 | \description{ 26 | The growth model of Baranyi and Roberts (1995) written as analytical solution 27 | of the system of differential equations. 28 | } 29 | \details{ 30 | The version of the equation used in this package has the following form: 31 | 32 | \deqn{A = time + 1/mumax * log(exp(-mumax * time) + exp(-h0) - exp(-mumax * time - h0))} 33 | \deqn{log(y) = log(y0) + mumax * A - log(1 + (exp(mumax * A) - 1) / exp(log(K) - log(y0)))} 34 | } 35 | \examples{ 36 | 37 | time <- seq(0, 30, length=200) 38 | y <- grow_baranyi(time, c(y0=0.01, mumax=.5, K=0.1, h0=5))[,"y"] 39 | plot(time, y, type="l") 40 | plot(time, y, type="l", log="y") 41 | 42 | } 43 | \references{ 44 | Baranyi, J. and Roberts, T. A. (1994). 45 | A dynamic approach to predicting bacterial growth in food. 46 | International Journal of Food Microbiology, 23, 277-294. 47 | 48 | Baranyi, J. and Roberts, T.A. (1995). Mathematics of predictive microbiology. 49 | International Journal of Food Microbiology, 26, 199-218. 50 | } 51 | \seealso{ 52 | Other growth models: 53 | \code{\link{grow_exponential}()}, 54 | \code{\link{grow_gompertz}()}, 55 | \code{\link{grow_gompertz2}()}, 56 | \code{\link{grow_huang}()}, 57 | \code{\link{grow_logistic}()}, 58 | \code{\link{grow_richards}()}, 59 | \code{\link{growthmodel}}, 60 | \code{\link{ode_genlogistic}()}, 61 | \code{\link{ode_twostep}()} 62 | } 63 | \concept{growth models} 64 | -------------------------------------------------------------------------------- /inst/doc/examples/example_growthrates-logistic-lag.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## The following example shows a user-defined logistic model with lag 3 | ## and its comparison to the Baranyi growth model. 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | library("growthrates") 11 | 12 | ## time (t) 13 | x <- c(0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20) 14 | ## Algae cell counts (per ml) 15 | y <- c(0.88, 1.02, 1.1, 2.79, 4.61, 7.12, 16 | 7.4, 8.16, 8.3, 7.9, 8.1) * 2 17 | 18 | grow_logistic_lag <- function(time, parms) { 19 | with(as.list(parms), { 20 | y <- ifelse (time < lambda, 21 | y0, 22 | y <- (K * y0) / (y0 + (K - y0) * exp(-mumax * (time - lambda))) 23 | ) 24 | as.matrix(data.frame(time = time, y = y)) 25 | }) 26 | } 27 | 28 | ## this line is optional, ensuring compatibility with future versions 29 | grow_logistic_lag <- growthmodel(grow_logistic_lag, 30 | c("y0", "mumax", "K", "lambda")) 31 | 32 | 33 | pstart <- c(mumax = 0.5, K= 7, y0 = 0.8) 34 | fit_logistic <- fit_growthmodel(grow_logistic, p=pstart, time=x, y=y) 35 | 36 | pstart <- c(mumax = 0.5, K= 7, y0 = 0.8, lambda = 2) 37 | fit_logistic2 <- fit_growthmodel(grow_logistic_lag, p=pstart, time=x, y=y) 38 | 39 | pstart <- c(mumax = 0.5, K= 7, y0 = 0.8, h0 = 2) 40 | fit_baranyi <- fit_growthmodel(grow_baranyi, p=pstart, time=x, y=y) 41 | 42 | plot(fit_logistic, lwd=1, ylim=c(0, 10)) 43 | lines(fit_logistic2, lwd=2, lty="dotted", col="blue") 44 | lines(fit_baranyi, lwd=2, lty="dashed", col="red") 45 | 46 | coef(fit_logistic) 47 | coef(fit_logistic2) 48 | coef(fit_baranyi) 49 | 50 | ## Note: the lag-specifying parameters h0 and lambda have different meaning. 51 | ## See Baranyi and Roberts 1994, https://doi.org/10.1016/0168-1605(94)90157-0 52 | ## according to Fig. 3 and Eq. (8) it approximates to: 53 | 54 | coef(fit_baranyi)["h0"] / coef(fit_baranyi)["mumax"] 55 | 56 | ## but see the full discussion, including Baranyi, 1998, 57 | ## https://doi.org/10.1006/jtbi.1998.0673 58 | -------------------------------------------------------------------------------- /man/grow_richards.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_richards.R 3 | \name{grow_richards} 4 | \alias{grow_richards} 5 | \title{Growth Model According to Richards} 6 | \usage{ 7 | grow_richards(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable).} 11 | 12 | \item{parms}{named parameter vector of the Richards growth model with: 13 | \itemize{ 14 | \item \code{y0} initial value of abundance, 15 | \item \code{mumax} maximum growth rate (note different interpretation compared 16 | to exponential growth), 17 | \item \code{K} carrying capacity (max. total concentration of cells), 18 | \item \code{beta} shape parameter determining the curvature. 19 | 20 | }} 21 | } 22 | \value{ 23 | vector of dependent variable (\code{y}). 24 | } 25 | \description{ 26 | Richards growth model written as analytical solution of the differential equation. 27 | } 28 | \details{ 29 | The equation used is: 30 | 31 | \deqn{y = K*(1-exp(-beta * mumax * time)*(1-(y0/K)^-beta))^(-1/beta)} 32 | 33 | The naming of parameters used here follows the convention of Tsoularis (2001), 34 | but uses \code{mumax} for growthrate and \code{y} for abundance to make them 35 | consistent to other growth functions. 36 | } 37 | \examples{ 38 | 39 | time <- seq(0, 30, length=200) 40 | y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=2))[,"y"] 41 | plot(time, y, type="l") 42 | y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=100))[,"y"] 43 | lines(time, y, col="red") 44 | y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=.2))[,"y"] 45 | lines(time, y, col="blue") 46 | 47 | } 48 | \references{ 49 | Richards, F. J. (1959) A Flexible Growth Function for Empirical Use. 50 | Journal of Experimental Botany 10 (2): 290--300. 51 | 52 | Tsoularis, A. (2001) Analysis of Logistic Growth Models. 53 | Res. Lett. Inf. Math. Sci, (2001) 2, 23--46. 54 | } 55 | \seealso{ 56 | Other growth models: 57 | \code{\link{grow_baranyi}()}, 58 | \code{\link{grow_exponential}()}, 59 | \code{\link{grow_gompertz}()}, 60 | \code{\link{grow_gompertz2}()}, 61 | \code{\link{grow_huang}()}, 62 | \code{\link{grow_logistic}()}, 63 | \code{\link{growthmodel}}, 64 | \code{\link{ode_genlogistic}()}, 65 | \code{\link{ode_twostep}()} 66 | } 67 | \concept{growth models} 68 | -------------------------------------------------------------------------------- /man/grow_gompertz2.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_gompertz2.R 3 | \name{grow_gompertz2} 4 | \alias{grow_gompertz2} 5 | \alias{grow_gompertz3} 6 | \title{Growth Model According to Gompertz} 7 | \usage{ 8 | grow_gompertz2(time, parms) 9 | 10 | grow_gompertz3(time, parms) 11 | } 12 | \arguments{ 13 | \item{time}{vector of time steps (independent variable).} 14 | 15 | \item{parms}{named parameter vector of the Gompertz growth model with: 16 | \itemize{ 17 | \item \code{y0} initial value of abundance, 18 | \item \code{mumax} maximum growth rate (1/time), 19 | \item \code{K} maximum abundance (carrying capacity), 20 | \item \code{lambda} time of lag phase of the 3 parameter Gompertz model . 21 | }} 22 | } 23 | \value{ 24 | vector of dependent variable (\code{y}) 25 | } 26 | \description{ 27 | Gompertz growth model written as analytical solution of the differential 28 | equation system. 29 | } 30 | \details{ 31 | The equation used here is: 32 | 33 | \deqn{y = y0*(K/y0)^(exp(-exp((exp(1)*mumax*(lambda - time))/log(K/y0)+1)))} 34 | 35 | Functions \code{grow_gompert2} and \code{grow_gompertz3} describe 36 | sigmoidal growth with an exponentially decreasing intrinsic growth rate with 37 | or without an additional lag parameter. The formula follows the 38 | reparametrization of Zwietering et al (1990), with parameters that have 39 | a biological meaning. 40 | } 41 | \examples{ 42 | 43 | time <- seq(0, 30, length=200) 44 | y <- grow_gompertz(time, c(y0=1, mumax=.2, K=10))[,"y"] 45 | plot(time, y, type="l", ylim=c(0, 12)) 46 | 47 | 48 | } 49 | \references{ 50 | Tsoularis, A. (2001) Analysis of Logistic Growth Models. 51 | Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 52 | 53 | Zwietering, M. H., Jongenburger, I., Rombouts, F. M., and Van't Riet, K. 54 | (1990). Modeling of the bacterial growth curve. 55 | Appl. Environ. Microbiol., 56(6), 1875-1881. 56 | } 57 | \seealso{ 58 | Other growth models: 59 | \code{\link{grow_baranyi}()}, 60 | \code{\link{grow_exponential}()}, 61 | \code{\link{grow_gompertz}()}, 62 | \code{\link{grow_huang}()}, 63 | \code{\link{grow_logistic}()}, 64 | \code{\link{grow_richards}()}, 65 | \code{\link{growthmodel}}, 66 | \code{\link{ode_genlogistic}()}, 67 | \code{\link{ode_twostep}()} 68 | } 69 | \concept{growth models} 70 | -------------------------------------------------------------------------------- /R/grow_richards.R: -------------------------------------------------------------------------------- 1 | #' Growth Model According to Richards 2 | #' 3 | #' Richards growth model written as analytical solution of the differential equation. 4 | #' 5 | #' The equation used is: 6 | #' 7 | #' \deqn{y = K*(1-exp(-beta * mumax * time)*(1-(y0/K)^-beta))^(-1/beta)} 8 | #' 9 | #' The naming of parameters used here follows the convention of Tsoularis (2001), 10 | #' but uses \code{mumax} for growthrate and \code{y} for abundance to make them 11 | #' consistent to other growth functions. 12 | #' 13 | #' @param time vector of time steps (independent variable). 14 | #' @param parms named parameter vector of the Richards growth model with: 15 | #' \itemize{ 16 | #' \item \code{y0} initial value of abundance, 17 | #' \item \code{mumax} maximum growth rate (note different interpretation compared 18 | #' to exponential growth), 19 | #' \item \code{K} carrying capacity (max. total concentration of cells), 20 | #' \item \code{beta} shape parameter determining the curvature. 21 | #' 22 | #' } 23 | #' 24 | #' @return vector of dependent variable (\code{y}). 25 | #' 26 | #' @references 27 | #' 28 | #' Richards, F. J. (1959) A Flexible Growth Function for Empirical Use. 29 | #' Journal of Experimental Botany 10 (2): 290--300. 30 | #' 31 | #' Tsoularis, A. (2001) Analysis of Logistic Growth Models. 32 | #' Res. Lett. Inf. Math. Sci, (2001) 2, 23--46. 33 | #' 34 | #' @examples 35 | #' 36 | #' time <- seq(0, 30, length=200) 37 | #' y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=2))[,"y"] 38 | #' plot(time, y, type="l") 39 | #' y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=100))[,"y"] 40 | #' lines(time, y, col="red") 41 | #' y <- grow_richards(time, c(y0=1, mumax=.5, K=10, beta=.2))[,"y"] 42 | #' lines(time, y, col="blue") 43 | #' 44 | #' @family growth models 45 | #' 46 | #' @rdname grow_richards 47 | #' @export grow_richards 48 | #' 49 | grow_richards <- function(time, parms) { 50 | with(as.list(parms), { 51 | y <- K*(1-exp(-beta * mumax * time)*(1-(y0/K)^-beta))^(-1/beta) 52 | 53 | return(as.matrix(data.frame(time = time, y = y))) 54 | }) 55 | } 56 | ## attach names of parameters as attributes 57 | attr(grow_richards, "fname") <- c("grow_richards") 58 | attr(grow_richards, "pnames") <- c("y0", "mumax", "K", "beta") 59 | class(grow_richards) <- c("growthmodel", "function") 60 | -------------------------------------------------------------------------------- /inst/doc/examples/benchmark_compiled_ode.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## This script demonstrates the performance gain 3 | ## from using compiled versions of the ODE models 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package: 8 | ## citation(package="deSolve" # doi:10.18637/jss.v033.i09 9 | ## citation(package="FME") # doi:10.18637/jss.v033.i03 10 | ## citation(package="growthrates") 11 | ## Repository: https://github.com/tpetzoldt/growthrates 12 | ## Mailing list: https://stat.ethz.ch/mailman/listinfo/r-sig-dynamic-models 13 | ## ============================================================================= 14 | 15 | library("growthrates") 16 | 17 | ## R versions of the models (the C versions are in the package) 18 | grow_twostep.R <- function(time, parms, ...) { 19 | y0 <- parms[c("yi", "ya")] 20 | parms <- parms[c("kw", "mumax", "K")] 21 | out <- ode(y0, time, ode_twostep, parms, ...) 22 | } 23 | 24 | grow_genlogistic.R <- function(time, parms, ...) { 25 | y0 <- c(y = unname(parms[c("y0")])) 26 | parms <- parms[c("mumax", "K", "alpha", "beta", "gamma")] 27 | out <- as.matrix(ode(y0, time, ode_genlogistic, parms, ...)) 28 | } 29 | 30 | 31 | ### Two-Step growth model (2 ODE equations) 32 | ## R code 33 | system.time(for (i in 1:100) 34 | o1 <- 35 | grow_twostep.R(0:100, c( 36 | yi = 0.01, ya = 0.0, kw = 0.1, mumax = 0.2, K = 0.1 37 | ))) 38 | 39 | ## compiled C code 40 | system.time(for (i in 1:100) 41 | o2 <- 42 | grow_twostep(0:100, c( 43 | yi = 0.01, ya = 0.0, kw = 0.1, mumax = 0.2, K = 0.1 44 | ))) 45 | 46 | ### extended logistic growth model (2 ODE equations) 47 | ## R code 48 | system.time(for (i in 1:100) 49 | o3 <- 50 | grow_genlogistic.R(0:100, c( 51 | y0 = 0.1, mumax = 0.5, K = 10, alpha = 1.2, beta = 1.2, gamma = 1.2 52 | ))) 53 | 54 | ## compiled C code 55 | system.time(for (i in 1:100) 56 | o4 <- 57 | grow_genlogistic(0:100, c( 58 | y0 = 0.1, mumax = 0.5, K = 10, alpha = 1.2, beta = 1.2, gamma = 1.2 59 | ))) 60 | 61 | 62 | ## check if results are identical 63 | summary(o1 - o2) 64 | 65 | summary(o3 - o4) 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /man/all_easylinear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/all_easylinear.R 3 | \name{all_easylinear} 4 | \alias{all_easylinear} 5 | \alias{all_easylinear.formula} 6 | \alias{all_easylinear.data.frame} 7 | \title{Easy Growth Rates Fit to data Frame} 8 | \usage{ 9 | all_easylinear(...) 10 | 11 | \method{all_easylinear}{formula}(formula, data, h = 5, quota = 0.95, subset = NULL, ...) 12 | 13 | \method{all_easylinear}{data.frame}( 14 | data, 15 | grouping, 16 | time = "time", 17 | y = "value", 18 | h = 5, 19 | quota = 0.95, 20 | ... 21 | ) 22 | } 23 | \arguments{ 24 | \item{\dots}{generic parameters, reserved for future extensions.} 25 | 26 | \item{formula}{model formula specifying dependent, independent and grouping 27 | variables in the form: 28 | \code{dependent ~ independent | group1 + group2 + \dots}.} 29 | 30 | \item{data}{data frame of observational data.} 31 | 32 | \item{h}{with of the window (number of data).} 33 | 34 | \item{quota}{part of window fits considered for the overall linear fit 35 | (relative to max. growth rate).} 36 | 37 | \item{subset}{a specification of the rows to be used: defaults to all rows.} 38 | 39 | \item{grouping}{model formula or character vector of criteria defining 40 | subsets in the data frame.} 41 | 42 | \item{time}{character vectors with name independent variabl.e.} 43 | 44 | \item{y}{character vector with name of dependent variable} 45 | } 46 | \value{ 47 | object with parameters of all fits. 48 | } 49 | \description{ 50 | Determine maximum growth rates from log-linear part of the growth curve for 51 | a series of experiments. 52 | } 53 | \examples{ 54 | 55 | \donttest{ 56 | library("growthrates") 57 | L <- all_easylinear(value ~ time | strain + conc + replicate, data=bactgrowth) 58 | summary(L) 59 | coef(L) 60 | rsquared(L) 61 | 62 | results <- results(L) 63 | 64 | library(lattice) 65 | xyplot(mumax ~ conc|strain, data=results) 66 | } 67 | 68 | } 69 | \references{ 70 | Hall, BG., Acar, H, Nandipati, A and Barlow, M (2014) Growth Rates Made Easy. 71 | Mol. Biol. Evol. 31: 232-38, \doi{10.1093/molbev/mst187} 72 | } 73 | \seealso{ 74 | Other fitting functions: 75 | \code{\link{all_growthmodels}()}, 76 | \code{\link{all_splines}()}, 77 | \code{\link{fit_easylinear}()}, 78 | \code{\link{fit_growthmodel}()}, 79 | \code{\link{fit_spline}()} 80 | } 81 | \concept{fitting functions} 82 | -------------------------------------------------------------------------------- /inst/doc/examples/import_grofit_data.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Import data from the grofit package (Kahm et al. 2010, J. Stat. Software, 3 | ## doi:10.18637/jss.v033.i07). 4 | ## 5 | ## Convert its specific data structure into a generic data base form 6 | ## with package reshape2 (Wickham, 2007, doi:10.18637/jss.v021.i12) 7 | ## and fit several growth models with package growthrates. 8 | ## 9 | ## Author: Thomas Petzoldt, TU Dresden 10 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 11 | ## Please cite our work when using this package. 12 | ## ============================================================================= 13 | 14 | library("grofit") 15 | library("reshape2") 16 | 17 | ## read data from package grofit 18 | data(grofit.data) 19 | data(grofit.time) 20 | 21 | ## convert data structure to generic format 22 | m.data <- melt(grofit.data, id.vars = 1:3, measure.vars = 4:ncol(grofit.data)) 23 | m.time <- melt(grofit.time, id.vars = NULL) 24 | 25 | ## rename columns 26 | names(m.data)[1:3] <- c("experiment", "info", "conc") 27 | names(m.time) <- c("variable2", "time") 28 | 29 | ## convert to "data base" format 30 | database <- cbind(m.time, m.data)[c("experiment", "conc", "time", "value", "info")] 31 | 32 | ## plot the data 33 | xyplot(value ~ time|experiment+conc, data = database) 34 | 35 | ## determine growth rate from maximum of 1st derivative of smoothing splines 36 | sfit <- all_splines(value ~ time | experiment + conc, data = database, spar = .9) 37 | 38 | ## plot results in log scale 39 | par(mfrow = c(3, 3)) 40 | plot(sfit, log = "y") 41 | 42 | ## plot results in linear scale 43 | par(mfrow = c(3, 3)) 44 | plot(sfit) 45 | 46 | ## fit parametric model 47 | p <- c(y0 = 0.01, mumax = 0.2, K = 0.2) 48 | 49 | pfit <- all_growthmodels(value ~ grow_logistic(time, parms) | experiment + conc, 50 | p = p, database) 51 | par(mfrow = c(3, 3)) 52 | plot(pfit) 53 | 54 | ## easy linear fit with default parameters, "experiment" is redundant 55 | efit <- all_easylinear(value ~ time | experiment + conc, data = database) 56 | par(mfrow = c(3,3)) 57 | plot(efit, log = "y") 58 | 59 | ## easy linear fit with adapted settings 60 | efit2 <- all_easylinear(value ~ time | conc, data = database, h = 10, quota = 0.95) 61 | mfrow = c(3,3) 62 | plot(efit2, log = "y") 63 | 64 | 65 | -------------------------------------------------------------------------------- /R/grow_baranyi.R: -------------------------------------------------------------------------------- 1 | #' The Baranyi and Roberts Growth Model 2 | #' 3 | #' The growth model of Baranyi and Roberts (1995) written as analytical solution 4 | #' of the system of differential equations. 5 | #' 6 | #' The version of the equation used in this package has the following form: 7 | #' 8 | #' \deqn{A = time + 1/mumax * log(exp(-mumax * time) + exp(-h0) - exp(-mumax * time - h0))} 9 | #' \deqn{log(y) = log(y0) + mumax * A - log(1 + (exp(mumax * A) - 1) / exp(log(K) - log(y0)))} 10 | #' 11 | #' @param time vector of time steps (independent variable). 12 | #' @param parms named parameter vector of the Baranyi growth model with: 13 | #' \itemize{ 14 | #' \item \code{y0} initial value of abundance, 15 | #' \item \code{mumax} maximum growth rate (1/time), 16 | #' \item \code{K} carrying capacity (max. abundance), 17 | #' \item \code{h0} parameter specifying the initial physiological state of 18 | #' organisms (e.g. cells) and in consequence the lag phase 19 | #' (h0 = max growth rate * lag phase). 20 | #' } 21 | #' 22 | #' @return vector of dependent variable (\code{y}). 23 | #' 24 | #' 25 | #' 26 | #' @references 27 | #' 28 | #' Baranyi, J. and Roberts, T. A. (1994). 29 | #' A dynamic approach to predicting bacterial growth in food. 30 | #' International Journal of Food Microbiology, 23, 277-294. 31 | #' 32 | #' Baranyi, J. and Roberts, T.A. (1995). Mathematics of predictive microbiology. 33 | #' International Journal of Food Microbiology, 26, 199-218. 34 | #' 35 | #' 36 | #' @examples 37 | #' 38 | #' time <- seq(0, 30, length=200) 39 | #' y <- grow_baranyi(time, c(y0=0.01, mumax=.5, K=0.1, h0=5))[,"y"] 40 | #' plot(time, y, type="l") 41 | #' plot(time, y, type="l", log="y") 42 | #' 43 | #' @family growth models 44 | #' 45 | #' @export 46 | #' 47 | grow_baranyi <- function(time, parms) { 48 | with(as.list(parms), { 49 | ## todo: q0 in original paper, h0 in Huang 50 | A <- time + 1/mumax * log(exp(-mumax * time) + exp(-h0) - exp(-mumax * time - h0)) 51 | #log_y <- y0 + mumax * A - log(1 + (exp(mumax * A) - 1)/(exp(K - y0))) 52 | log_y <- log(y0) + mumax * A - log(1 + (exp(mumax * A) - 1) / exp(log(K) - log(y0))) 53 | 54 | return(as.matrix(data.frame(time = time, y = exp(log_y)))) 55 | }) 56 | } 57 | ## attach names of parameters as attributes 58 | attr(grow_baranyi, "fname") <- c("grow_baranyi") 59 | attr(grow_baranyi, "pnames") <- c("y0", "mumax", "K", "h0") 60 | class(grow_baranyi) <- c("growthmodel", "function") 61 | 62 | -------------------------------------------------------------------------------- /R/names.R: -------------------------------------------------------------------------------- 1 | #' Get Names Attributes of Growth Models 2 | #' 3 | #' Methods to get the parameter names of a growth model or to get or set 4 | #' identifiers of \code{\link{multiple_fits}} objects. 5 | #' 6 | #' @param x either a function being a parametric growth model of 7 | #' package \pkg{growthmodels} or an object with multiple fits. 8 | #' @param value a character vector of up to the same length as x, or NULL 9 | #' 10 | #' @return character vector of the parameter names 11 | #' 12 | #' @section Methods: 13 | #' \describe{ 14 | #' \item{Method for class \code{\link{growthmodel}}:}{ returns information about 15 | #' valid parameter names if a \code{pnames} attribute exists, else \code{NULL}. 16 | #' \code{NULL}.} 17 | #' \item{Method for class \code{\link{multiple_fits}}:}{ can be applied to objects 18 | #' returned by \code{all_growthmodels}, \code{all_splines} or 19 | #' \code{all_easylinear} respectively. This can be useful for selecting 20 | #' subsets, e.g. for plotting, see example below.} 21 | #' } 22 | 23 | #' 24 | #' @seealso \code{\link{multiple_fits}}, \code{\link{all_growthmodels}}, 25 | #' \code{\link{all_splines}}, \code{\link{all_easylinear}} 26 | #' 27 | #' @examples 28 | #' 29 | #' ## growthmodel-method 30 | #' names(grow_baranyi) 31 | #' 32 | #' ## multiple_fits-method 33 | #' L <- all_splines(value ~ time | strain + conc + replicate, 34 | #' data = bactgrowth) 35 | #' 36 | #' names(L) 37 | #' 38 | #' ## plot only the 'R' strain 39 | #' # par(mfrow=c(4, 6)) 40 | #' plot(L[grep("R:", names(L))]) 41 | #' 42 | #' 43 | #' @rdname names 44 | #' @export 45 | #' 46 | names.growthmodel <- function(x) attr(x, "pnames") 47 | 48 | ## S4 method does not work here (even if a setGeneric 'pnames' would do) 49 | ## so we use the S3 method above 50 | #setMethod("names", "growthmodel", 51 | # function(x) { 52 | # attr(x, "pnames") 53 | # } 54 | #) 55 | 56 | 57 | #' @rdname names 58 | #' @exportMethod names 59 | #' 60 | setMethod("names", "multiple_fits", 61 | function(x) { 62 | names(x@fits) 63 | } 64 | ) 65 | 66 | #' @rdname names 67 | #' @exportMethod names<- 68 | #' 69 | setMethod("names<-", c("multiple_fits", "ANY"), 70 | function(x, value) { 71 | if (!is.character(value)) 72 | value <- as.character(value) 73 | ## todo: check length? 74 | names(x@fits) <- value 75 | } 76 | ) 77 | 78 | 79 | -------------------------------------------------------------------------------- /inst/doc/examples/example_growthmodel.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Determine growth rates with parametric models given in closed form or as 3 | ## system of differential equations. 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | library("growthrates") 11 | 12 | data(bactgrowth) 13 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 14 | 15 | ## select a single data set 16 | names(splitted.data) 17 | dat <- splitted.data[["D:0.49:1"]] 18 | 19 | ## Logistic growth model 20 | p <- c(y0 = 0.01, mumax = 0.03, K = 0.1) 21 | lower <- c(y0 = 1e-6, mumax = 0, K = 0) 22 | upper <- c(y0 = 0.05, mumax = 5, K = 0.5) 23 | 24 | fit1 <- fit_growthmodel(FUN = grow_logistic, dat$time, dat$value, p = p, 25 | control = list(trace = TRUE), 26 | lower = lower, upper = upper) 27 | 28 | fit2 <- fit_growthmodel(FUN = grow_logistic, dat$time, dat$value, p = p, 29 | lower = lower, upper = upper, 30 | transform = "log", control = list(trace = TRUE)) 31 | 32 | 33 | ## Two step model with lag phase given as system of differential equations 34 | p <- c(yi = 0.01, ya = 0.01, kw = 0.1, mumax = 0.2, K = 0.1) 35 | lower <- c(yi = 1e-6, ya = 1e-6, kw = 0, mumax = 0, K = 0) 36 | upper <- c(yi = 0.05, ya = 0.05, kw = 10, mumax = 5, K = 0.5) 37 | 38 | 39 | fit3 <- fit_growthmodel(FUN = grow_twostep, p = p, time = dat$time, y = dat$value, 40 | lower = lower, upper = upper, 41 | control = list(trace = TRUE), 42 | method = "L-BFGS-B") 43 | 44 | fit4 <- fit_growthmodel(FUN = grow_twostep, dat$time, dat$value, p = p, 45 | lower = lower, upper = upper, transform = "log", 46 | control = list(trace = TRUE), 47 | method = "L-BFGS-B") 48 | 49 | ## show results graphically 50 | plot(fit1)#, log = "y") 51 | lines(fit2, col = "red") 52 | lines(fit3, col = "blue", lty = "dotted") 53 | lines(fit4, col = "cyan", lty = "dashed") 54 | 55 | 56 | ## Exercise: 57 | ## try to improve the model formulation to get a better fit. 58 | ## see example user_defined_ode_model.R 59 | 60 | -------------------------------------------------------------------------------- /inst/doc/examples/example_logistic_saturation.R: -------------------------------------------------------------------------------- 1 | library("growthrates") 2 | 3 | ## derivatives of the logistic --------------------------------------------- 4 | # the derivatives are internal functions of the package 5 | # for demonstration and visualisation of the slope 6 | deriv1 <- function(time, y0, mumax, K) { 7 | ret <- (K*mumax*y0*(K - y0)*exp(mumax * time))/ 8 | ((K + y0 * (exp(mumax * time) - 1))^2) 9 | unname(ret) 10 | } 11 | 12 | deriv2 <- function(time, y0, mumax, K) { 13 | ret <- -(K * mumax^2 * y0 * (K - y0) * exp(mumax * time) * 14 | (-K + y0 * exp(mumax * time) + y0))/ 15 | (K + y0 * (exp(mumax * time) - 1))^3 16 | unname(ret) 17 | } 18 | ## ========================================================================= 19 | 20 | data(bactgrowth) 21 | ## extract one growth experiment by name 22 | dat <- multisplit(bactgrowth, c("strain", "conc", "replicate"))[["D:0:1"]] 23 | 24 | 25 | ## unconstraied fitting 26 | p <- c(y0 = 0.01, mumax = 0.2, K = 0.1) # start parameters 27 | fit1 <- fit_growthmodel(FUN = grow_logistic, p = p, dat$time, dat$value) 28 | summary(fit1) 29 | p <- coef(fit1, extended=TRUE) 30 | 31 | ## copy parameters to separate variables to improve readability ------------ 32 | y0 <- p["y0"] 33 | mumax <- p["mumax"] 34 | K <- p["K"] 35 | turnpoint <- p["turnpoint"] 36 | sat1 <- p["sat_deriv2"] 37 | sat2 <- p["sat_mumax"] 38 | sat3 <- p["sat_quantile"] 39 | 40 | ## show saturation values in growth curve and 1st and 2nd derivatives ------ 41 | opar <- par(no.readonly=TRUE) 42 | par(mfrow=c(3, 1), mar=c(4,4,0.2,0)) 43 | plot(fit1) 44 | 45 | ## 95% saturation 46 | abline(h=0.95*K, col="magenta", lty="dashed") 47 | 48 | ## Intercept between steepest increase and 100% saturation 49 | b <- deriv1(turnpoint, y0, mumax, K) 50 | a <- K/2 - b*turnpoint 51 | abline(a=a, b=b, col="orange", lty="dashed") 52 | abline(h=K, col="orange", lty="dashed") 53 | points(sat2, K, pch=16, col="orange") 54 | points(turnpoint, K/2, pch=16, col="blue") 55 | 56 | ## sat2 is the minimum of the 2nd derivative 57 | abline(v=c(turnpoint, sat1, sat2, sat3), 58 | col=c("blue", "grey", "orange", "magenta"), lty="dashed") 59 | 60 | ## plot the derivatives 61 | with(dat, plot(time, deriv1(time, y0, mumax, K), type="l", ylab="y'")) 62 | abline(v=c(turnpoint, sat1), col=c("blue", "grey"), lty="dashed") 63 | 64 | with(dat, plot(time, deriv2(time, y0, mumax, K), type="l", ylab="y''")) 65 | abline(v=sat1, col="grey", lty="dashed") 66 | par(opar) 67 | 68 | 69 | -------------------------------------------------------------------------------- /inst/doc/examples/example_baranyi.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Test of the Baranyi Growth model, cf. Baranyi, L (1995), 3 | ## Int. J. Food Microbiology, doi:10.1016/0168-1605(94)00121-L 4 | ## 5 | ## Note: the original model formulation works in log space, so that 6 | ## y(t), y_0 and y_max are all given as natural log. 7 | ## This is advantageous for model fitting, but here we use y_0 and K in 8 | ## untransformed space to be compatible with the growth parameters of 9 | ## most other models. The downside is, that we need box constraints in most 10 | ## cases and possibly more iterations. 11 | ## 12 | ## Author: Thomas Petzoldt, TU Dresden 13 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 14 | ## Please cite our work when using this package. 15 | ## ============================================================================= 16 | 17 | 18 | 19 | library("growthrates") 20 | library("lattice") 21 | 22 | data(bactgrowth) 23 | splitted.data <- multisplit(value ~ time|strain + conc + replicate, data = bactgrowth) 24 | 25 | ## use a single data set 26 | dat <- splitted.data[[23]] 27 | 28 | ## initial parameters and bocx constraints 29 | p <- c(y0=0.03, mumax=.1, K=0.1, h0=1) 30 | 31 | lower <- c(y0=0.001, mumax=1e-2, K=0.005, h0=0) 32 | upper <- c(y0=0.1, mumax=1, K=0.5, h0=10) 33 | 34 | ## fit model 35 | fit <- fit_growthmodel(FUN=grow_baranyi, p=p, time=dat$time, y=dat$value, 36 | lower = lower, upper = upper, 37 | control=list(trace=TRUE) 38 | ) 39 | 40 | ## coefficients and plot 41 | coef(fit) 42 | plot(fit) 43 | 44 | 45 | ## fit growth models to all data using (log transformed residuals) 46 | system.time( 47 | L <- all_growthmodels(grow_baranyi, p=p, data=bactgrowth, 48 | grouping = c("strain", "conc", "replicate"), 49 | lower = lower, upper=upper, 50 | transform = "log") 51 | ) 52 | 53 | ## same with formula interface 54 | system.time( 55 | L <- all_growthmodels(value ~ grow_baranyi(time, parms) | strain + conc + replicate, 56 | data = bactgrowth, 57 | p=p, lower = lower, upper=upper, 58 | transform = "log" 59 | ) 60 | ) 61 | 62 | par(mfrow=c(4,3)) 63 | par(mar=c(2.5,4,2,1)) 64 | plot(L, log="y") 65 | 66 | par(mfrow=c(4,3)) 67 | plot(L) 68 | 69 | res <- results(L) 70 | xyplot(mumax ~ log(conc + 1)| strain, data=res, layout=c(3,1)) 71 | -------------------------------------------------------------------------------- /inst/doc/examples/example_masking.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## This script demonstrates 'masking' of parameters, 3 | ## i.e. splitting the parameter set in fitted and fixed parameters 4 | ## 5 | ## Author: Thomas Petzoldt, TU Dresden 6 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 7 | ## Please cite our work when using this package. 8 | ## ============================================================================= 9 | 10 | 11 | library("growthrates") 12 | 13 | 14 | data(bactgrowth) 15 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 16 | 17 | ## select a single data set 18 | dat <- splitted.data[[7]] 19 | 20 | 21 | p <- c(yi=0.01, ya=0.01, kw=0.1, mumax=0.2, K=0.1) 22 | lower <- c(yi=1e-6, ya=1e-6, kw=0, mumax=0, K=0) 23 | upper <- c(yi=0.05, ya=0.05, kw=10, mumax=5, K=0.5) 24 | 25 | 26 | ## fit of all parameters 27 | fit1 <- fit_growthmodel(FUN=grow_twostep, p=p, time=dat$time, y=dat$value, 28 | lower=lower, upper=upper, 29 | method="L-BFGS-B") 30 | 31 | p <- c(yi=0.031, ya=0.00, kw=0.01, mumax=0.2, K=0.1) 32 | lower <- c(yi=1e-6, ya=1e-6, kw=0, mumax=0, K=0) 33 | upper <- c(yi=0.05, ya=0.05, kw=10, mumax=5, K=0.5) 34 | 35 | ## fit only kw, mumax and K, but fix y_i and y_a 36 | fit2 <- fit_growthmodel(FUN=grow_twostep, p=p, time=dat$time, y=dat$value, 37 | lower=lower, upper=upper, 38 | which = c("kw", "mumax", "K"), 39 | method="L-BFGS-B") 40 | 41 | 42 | p <- c(y0=0.03, mumax=.176, K=.101, alpha=1, beta=1, gamma=1) 43 | lower <- c(y0=0.01, mumax=0, K=0, alpha=0.5, beta=0.5, gamma=1) 44 | upper <- c(y0=0.04, mumax=5, K=10, alpha=5, beta=10, gamma=10) 45 | 46 | 47 | ## generalized logistic 48 | fit3 <- fit_growthmodel(FUN=grow_genlogistic, p=p, time=dat$time, y=dat$value, 49 | lower=lower, upper=upper, 50 | which = c("mumax", "K"), 51 | method="L-BFGS-B") 52 | 53 | 54 | p <- c(y0=0.03, mumax=.176, K=.101, alpha=1, beta=1, gamma=1) 55 | fit3 <- fit_growthmodel(FUN=grow_genlogistic, p=p, time=dat$time, y=dat$value, 56 | lower=lower, upper=upper, 57 | which = c("alpha", "beta", "gamma"), 58 | method="L-BFGS-B") 59 | 60 | coef(fit1) 61 | coef(fit2) 62 | coef(fit3) 63 | 64 | plot(fit1) 65 | lines(fit2, col="blue") 66 | lines(fit3, col="red") 67 | 68 | -------------------------------------------------------------------------------- /man/multisplit.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/aad_set_generics.R, R/multisplit.R 3 | \name{multisplit} 4 | \alias{multisplit} 5 | \alias{multisplit,data.frame,formula-method} 6 | \alias{multisplit,data.frame,character-method} 7 | \alias{multisplit,data.frame,factor-method} 8 | \alias{multisplit,data.frame,list-method} 9 | \alias{multisplit,ANY,ANY-method} 10 | \title{Split Data Frame into Multiple Groups} 11 | \usage{ 12 | multisplit(data, grouping, drop = TRUE, sep = ":", ...) 13 | 14 | \S4method{multisplit}{data.frame,formula}(data, grouping, drop = TRUE, sep = ":", ...) 15 | 16 | \S4method{multisplit}{data.frame,character}(data, grouping, drop = TRUE, sep = ":", ...) 17 | 18 | \S4method{multisplit}{data.frame,factor}(data, grouping, drop = TRUE, sep = ":", ...) 19 | 20 | \S4method{multisplit}{data.frame,list}(data, grouping, drop = TRUE, sep = ":", ...) 21 | 22 | \S4method{multisplit}{ANY,ANY}(data, grouping, drop = TRUE, sep = ":", ...) 23 | } 24 | \arguments{ 25 | \item{data}{data frame, matrix or vector containing several subsets of data} 26 | 27 | \item{grouping}{either a character vector containing the names of the grouping variables 28 | or a model formula specifying dependent, 29 | independent and grouping variables in the form: 30 | \code{dependent ~ independent | group1 + group2 + ...}. 31 | It may also be a factor or list of factors as in \code{\link{split}}.} 32 | 33 | \item{drop}{if drop is TRUE, unused factor levels are dropped from the result. 34 | The default is to drop all factor levels.} 35 | 36 | \item{sep}{string to construct the new level labels by joining the 37 | constituent ones.} 38 | 39 | \item{\dots}{other parameters passed to \code{\link{split}}, see details.} 40 | } 41 | \value{ 42 | list containing data frames of the data subsets as its elements. 43 | The components of the list are named by their grouping levels. 44 | } 45 | \description{ 46 | A data frame is split into a list of data subsets defined by multiple groups. 47 | } 48 | \details{ 49 | This function is wrapper around \code{\link{split}} with 50 | different defaults, slightly different behavior, and methods for additional 51 | argument classes. \code{multisplit} returns always a data frame. 52 | } 53 | \examples{ 54 | 55 | 56 | data(bactgrowth) 57 | 58 | ## simple method 59 | spl <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 60 | 61 | ## preferred method 62 | spl <- multisplit(bactgrowth, value ~ time | strain + conc + replicate) 63 | 64 | ## show what is in one data set 65 | spl[[1]] 66 | summary(spl[[1]]) 67 | 68 | ## use factor combination 69 | spl[["D:0:1"]] 70 | summary(spl[["D:0:1"]]) 71 | 72 | 73 | lapply(spl, FUN=function(x) 74 | plot(x$time, x$value, 75 | main=paste(x[1, "strain"], x[1, "conc"], x[1, "replicate"], sep=":"))) 76 | 77 | 78 | } 79 | \seealso{ 80 | \code{\link{split}} 81 | } 82 | \keyword{internal} 83 | -------------------------------------------------------------------------------- /man/fit_growthmodel.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fit_growthmodel.R 3 | \name{fit_growthmodel} 4 | \alias{fit_growthmodel} 5 | \title{Fit Nonlinear Parametric Growth Model} 6 | \usage{ 7 | fit_growthmodel( 8 | FUN, 9 | p, 10 | time, 11 | y, 12 | lower = -Inf, 13 | upper = Inf, 14 | which = names(p), 15 | method = "Marq", 16 | transform = c("none", "log"), 17 | control = NULL, 18 | ... 19 | ) 20 | } 21 | \arguments{ 22 | \item{FUN}{function of growth model to be fitted.} 23 | 24 | \item{p}{named vector of start parameters and initial values of the growth model.} 25 | 26 | \item{time}{vector of independent variable.} 27 | 28 | \item{y}{vector of dependent variable (concentration of organisms).} 29 | 30 | \item{lower}{lower bound of the parameter vector (optional).} 31 | 32 | \item{upper}{upper bound of the parameter vector (optional).} 33 | 34 | \item{which}{vector of parameter names that are to be fitted.} 35 | 36 | \item{method}{character vector specifying the optimization algorithm (see \code{\link[FME]{modFit}}).} 37 | 38 | \item{transform}{fit model to non-transformed or log-transformed data.} 39 | 40 | \item{control}{A list of control parameters for the optimizers. See Details.} 41 | 42 | \item{\dots}{additional parameters passed to the optimizer.} 43 | } 44 | \value{ 45 | object with parameters of the fit. 46 | } 47 | \description{ 48 | Determine maximum growth rates by fitting nonlinear models. 49 | } 50 | \details{ 51 | This function calls \code{\link[FME]{modFit}} from package \pkg{FME}. 52 | Syntax of control parameters and available options may differ, depending 53 | on the optimizer used, except \code{control=list(trace=...)} that switches 54 | tracing on and off for all methods and is either \code{TRUE}, or \code{FALSE}, 55 | or an integer value like 0, 1, 2, 3, depending on the optimizer. 56 | } 57 | \examples{ 58 | 59 | data(bactgrowth) 60 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 61 | 62 | ## get one element either by index or by name 63 | dat <- splitted.data[[1]] 64 | dat <- splitted.data[["D:0:1"]] 65 | 66 | p <- c(y0 = 0.01, mumax = 0.2, K = 0.1) 67 | 68 | ## unconstraied fitting 69 | fit1 <- fit_growthmodel(FUN = grow_logistic, p = p, dat$time, dat$value) 70 | coef(fit1) 71 | summary(fit1) 72 | 73 | ## optional box-constraints 74 | lower <- c(y0 = 1e-6, mumax = 0, K = 0) 75 | upper <- c(y0 = 0.05, mumax = 5, K = 0.5) 76 | fit1 <- fit_growthmodel( 77 | FUN = grow_logistic, p = p, dat$time, dat$value, 78 | lower = lower, upper = upper) 79 | 80 | plot(fit1, log="y") 81 | 82 | } 83 | \seealso{ 84 | \code{\link[FME]{modFit}} about constrained fitting of models to data 85 | 86 | Other fitting functions: 87 | \code{\link{all_easylinear}()}, 88 | \code{\link{all_growthmodels}()}, 89 | \code{\link{all_splines}()}, 90 | \code{\link{fit_easylinear}()}, 91 | \code{\link{fit_spline}()} 92 | } 93 | \concept{fitting functions} 94 | -------------------------------------------------------------------------------- /inst/doc/examples/example_user_defined_ode_compiled.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## The following example shows how to use compiled growth models 3 | ## from inline code, by using the 'cOde' package of Daniel Kaschek 4 | ## Note: This example needs the R development tools. 5 | ## - suitable compilers on Linux and Mac 6 | ## - Rtools on Windows from https://cran.r-project.org/bin/windows/Rtools/ 7 | ## 8 | ## Author: Thomas Petzoldt, TU Dresden 9 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 10 | ## Please cite our work when using this package. 11 | ## ============================================================================= 12 | 13 | library("growthrates") 14 | library("cOde") 15 | 16 | ## ============================================================================= 17 | ## define a system of ODEs and compile it 18 | ## ============================================================================= 19 | ode_K_linear <- funC(c( 20 | y = "mumax * y * (1-y/K)", 21 | K = "dK" 22 | )) 23 | 24 | yini <- c(y = 1, K = 10) 25 | parms = c(mumax = 0.1, dK = 0.05) 26 | 27 | ## run the model 28 | out1 <- odeC(yini, times=0:100, ode_K_linear, parms = parms) 29 | 30 | ## generate artificial test data with normally distributed noise 31 | x <- seq(5, 100, 5) 32 | y <- odeC(yini, x, ode_K_linear, parms)[, "y"] + rnorm(x) 33 | 34 | ## ============================================================================= 35 | ## create a "growthmodel" with interfaces compatible to package growthrates 36 | ## see ?growthmodel for details 37 | ## Note: 38 | ## It is essential to use consistent names for parameters and initial values 39 | ## ============================================================================= 40 | 41 | grow_K_linear <- function(time, parms, ...) { 42 | init <- parms[c("y0", "K")] # initial values 43 | names(init) <- c("y", "K") # force names 44 | out <- odeC(init, time, ode_K_linear, parms) 45 | cbind(out, log_y = log(out[,"y"])) 46 | } 47 | 48 | ## convert this to an object, (maybe needed by future extensions) 49 | # grow_K_linear <- growthmodel(grow_K_linear, pnames=c("y0", "mumax", "K", "dK")) 50 | 51 | ## Test the growthmodel. 52 | ## Columns with names 'time', 'y' and 'log_y' are mandatory. 53 | head(grow_K_linear(time=x, c(y0=1, mumax=0.1, K=10, dK = 0.1))) 54 | 55 | ## ============================================================================= 56 | ## Fit the model 57 | ## ============================================================================= 58 | fit <- fit_growthmodel( 59 | grow_K_linear, p=c(y0=1, mumax=0.1, K=10, dK = 0.1), time=x, y=y) 60 | 61 | plot(fit) 62 | summary(fit) 63 | 64 | ## unload DLL and cleanup, should ideally work in a temp dir 65 | dll <- paste(ode_K_linear, .Platform$dynlib.ext, sep="") 66 | dyn.unload(dll) 67 | unlink(dll) 68 | unlink(paste(ode_K_linear, ".c", sep="")) 69 | unlink(paste(ode_K_linear, ".o", sep="")) 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /man/fit_easylinear.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fit_easylinear.R 3 | \name{fit_easylinear} 4 | \alias{fit_easylinear} 5 | \title{Fit Exponential Growth Model with a Heuristic Linear Method} 6 | \usage{ 7 | fit_easylinear(time, y, h = 5, quota = 0.95) 8 | } 9 | \arguments{ 10 | \item{time}{vector of independent variable.} 11 | 12 | \item{y}{vector of dependent variable (concentration of organisms).} 13 | 14 | \item{h}{width of the window (number of data).} 15 | 16 | \item{quota}{part of window fits considered for the overall linear fit 17 | (relative to max. growth rate)} 18 | } 19 | \value{ 20 | object with parameters of the fit. The lag time is currently estimated 21 | as the intersection between the fit and the horizontal line with \eqn{y=y_0}, 22 | where \code{y0} is the first value of the dependent variable. The intersection 23 | of the fit with the abscissa is indicated as \code{y0_lm} (lm for linear model). 24 | These identifieres and their assumptions may change in future versions. 25 | } 26 | \description{ 27 | Determine maximum growth rates from the log-linear part of a growth curve using 28 | a heuristic approach similar to the ``growth rates made easy''-method of 29 | Hall et al. (2013). 30 | } 31 | \details{ 32 | The algorithm works as follows: 33 | \enumerate{ 34 | \item Fit linear regressions to all subsets of \code{h} consecutive data 35 | points. If for example \eqn{h=5}, fit a linear regression to points 36 | 1 \dots 5, 2 \dots 6, 3\dots 7 and so on. The method seeks the highest 37 | rate of exponential growth, so the dependent variable is of course 38 | log-transformed. 39 | \item Find the subset with the highest slope \eqn{b_{max}}{b_max} and 40 | include also the data points of adjacent subsets that have a slope of 41 | at least \eqn{quota \cdot b_{max}}{quota * b_max}, 42 | e.g. all data sets that have at least 95\% of the maximum slope. 43 | \item Fit a new linear model to the extended data window identified in step 2. 44 | } 45 | } 46 | \examples{ 47 | data(bactgrowth) 48 | 49 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 50 | dat <- splitted.data[[1]] 51 | 52 | plot(value ~ time, data=dat) 53 | fit <- fit_easylinear(dat$time, dat$value) 54 | 55 | plot(fit) 56 | plot(fit, log="y") 57 | plot(fit, which="diagnostics") 58 | 59 | fitx <- fit_easylinear(dat$time, dat$value, h=8, quota=0.95) 60 | 61 | plot(fit, log="y") 62 | lines(fitx, pch="+", col="blue") 63 | 64 | plot(fit) 65 | lines(fitx, pch="+", col="blue") 66 | 67 | 68 | } 69 | \references{ 70 | Hall, BG., Acar, H, Nandipati, A and Barlow, M (2014) Growth Rates Made Easy. 71 | Mol. Biol. Evol. 31: 232-38, \doi{10.1093/molbev/mst187} 72 | } 73 | \seealso{ 74 | Other fitting functions: 75 | \code{\link{all_easylinear}()}, 76 | \code{\link{all_growthmodels}()}, 77 | \code{\link{all_splines}()}, 78 | \code{\link{fit_growthmodel}()}, 79 | \code{\link{fit_spline}()} 80 | } 81 | \concept{fitting functions} 82 | -------------------------------------------------------------------------------- /R/grow_gompertz2.R: -------------------------------------------------------------------------------- 1 | #' Growth Model According to Gompertz 2 | #' 3 | #' Gompertz growth model written as analytical solution of the differential 4 | #' equation system. 5 | #' 6 | #' The equation used here is: 7 | #' 8 | #' \deqn{y = y0*(K/y0)^(exp(-exp((exp(1)*mumax*(lambda - time))/log(K/y0)+1)))} 9 | #' 10 | #' @param time vector of time steps (independent variable). 11 | #' @param parms named parameter vector of the Gompertz growth model with: 12 | #' \itemize{ 13 | #' \item \code{y0} initial value of abundance, 14 | #' \item \code{mumax} maximum growth rate (1/time), 15 | #' \item \code{K} maximum abundance (carrying capacity), 16 | #' \item \code{lambda} time of lag phase of the 3 parameter Gompertz model . 17 | #' } 18 | #' 19 | #' @details Functions \code{grow_gompert2} and \code{grow_gompertz3} describe 20 | #' sigmoidal growth with an exponentially decreasing intrinsic growth rate with 21 | #' or without an additional lag parameter. The formula follows the 22 | #' reparametrization of Zwietering et al (1990), with parameters that have 23 | #' a biological meaning. 24 | #' 25 | #' @references Tsoularis, A. (2001) Analysis of Logistic Growth Models. 26 | #' Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 27 | #' 28 | #' Zwietering, M. H., Jongenburger, I., Rombouts, F. M., and Van't Riet, K. 29 | #' (1990). Modeling of the bacterial growth curve. 30 | #' Appl. Environ. Microbiol., 56(6), 1875-1881. 31 | #' 32 | #' @return vector of dependent variable (\code{y}) 33 | #' 34 | #' @examples 35 | #' 36 | #' time <- seq(0, 30, length=200) 37 | #' y <- grow_gompertz(time, c(y0=1, mumax=.2, K=10))[,"y"] 38 | #' plot(time, y, type="l", ylim=c(0, 12)) 39 | #' 40 | #' 41 | #' @family growth models 42 | #' 43 | #' @rdname grow_gompertz2 44 | #' @export grow_gompertz2 45 | #' 46 | grow_gompertz2 <- function(time, parms) { 47 | with(as.list(parms), { 48 | #A <- log(K/y0) 49 | #y <- exp(log(y0) + A*exp(-exp(mumax*exp(1)/A*(-time)+1))) 50 | y <- y0*(K/y0)^(exp(-exp((-exp(1)*mumax*time)/log(K/y0)+1))) 51 | as.matrix(data.frame(time = time, y = y)) 52 | }) 53 | } 54 | 55 | #' @rdname grow_gompertz2 56 | #' @export grow_gompertz3 57 | #' 58 | grow_gompertz3 <- function(time, parms) { 59 | with(as.list(parms), { 60 | #A <- log(K/y0) 61 | #y <- exp(log(y0) + A*exp(-exp(mumax*exp(1)/A*(lambda-time)+1))) 62 | y <- y0*(K/y0)^(exp(-exp((exp(1)*mumax*(lambda - time))/log(K/y0)+1))) 63 | as.matrix(data.frame(time = time, y = y)) 64 | }) 65 | } 66 | 67 | ## attach names of parameters as attributes 68 | attr(grow_gompertz2, "fname") <- c("grow_gompertz2") 69 | attr(grow_gompertz2, "pnames") <- c("y0", "mumax", "K") 70 | class(grow_gompertz2) <- c("growthmodel", "function") 71 | 72 | attr(grow_gompertz3, "fname") <- c("grow_gompertz3") 73 | attr(grow_gompertz3, "pnames") <- c("y0", "mumax", "K", "lambda") 74 | class(grow_gompertz3) <- c("growthmodel", "function") 75 | 76 | 77 | -------------------------------------------------------------------------------- /man/fit_spline.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fit_spline.R 3 | \name{fit_spline} 4 | \alias{fit_spline} 5 | \title{Fit Exponential Growth Model with Smoothing Spline} 6 | \usage{ 7 | fit_spline(time, y, optgrid = length(time), ...) 8 | } 9 | \arguments{ 10 | \item{time}{vector of independent variable.} 11 | 12 | \item{y}{vector of dependent variable (concentration of organisms).} 13 | 14 | \item{optgrid}{number of steps on the x-axis used for the optimum search . 15 | algorithm. The default should work in most cases, as long as the data are equally spaced. 16 | A smaller number may lead to non-detectable speed-up, but has the risk that 17 | the search gets trapped in a local minimum.} 18 | 19 | \item{\dots}{other parameters passed to \code{\link{smooth.spline}}, see details.} 20 | } 21 | \value{ 22 | object with parameters of the fit 23 | } 24 | \description{ 25 | Determine maximum growth rates from the first derivative of a smoothing spline. 26 | } 27 | \details{ 28 | The method was inspired by an algorithm of Kahm et al. (2010), 29 | with different settings and assumptions. In the moment, spline fitting 30 | is always done with log-transformed data, assuming exponential growth 31 | at the time point of the maximum of the first derivative of the spline fit. 32 | 33 | All the hard work is done by function \code{\link{smooth.spline}} from package 34 | \pkg{stats}, that is highly user configurable. Normally, smoothness is 35 | automatically determined via cross-validation. This works well in many cases, 36 | whereas manual adjustment is required otherwise, e.g. by setting \code{spar} 37 | to a fixed value \eqn{[0, 1]} that also disables cross-validation. 38 | } 39 | \examples{ 40 | 41 | data(bactgrowth) 42 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 43 | 44 | dat <- splitted.data[[2]] 45 | time <- dat$time 46 | y <- dat$value 47 | 48 | ## automatic smoothing with cv 49 | res <- fit_spline(time, y) 50 | 51 | plot(res, log="y") 52 | plot(res) 53 | coef(res) 54 | 55 | ## a more difficult data set 56 | dat <- splitted.data[[56]] 57 | time <- dat$time 58 | y <- dat$value 59 | 60 | ## default parameters 61 | res <- fit_spline(time, y) 62 | plot(res, log="y") 63 | 64 | ## small optgrid, trapped in local minimum 65 | res <- fit_spline(time, y, optgrid=5) 66 | plot(res, log="y") 67 | 68 | ## manually selected smoothing parameter 69 | res <- fit_spline(time, y, spar=.5) 70 | plot(res, log="y") 71 | plot(res, ylim=c(0.005, 0.03)) 72 | 73 | 74 | } 75 | \references{ 76 | Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 77 | 2010. grofit: Fitting Biological Growth Curves with R. 78 | Journal of Statistical Software, 33(7), 1-21, 79 | \doi{10.18637/jss.v033.i07} 80 | } 81 | \seealso{ 82 | Other fitting functions: 83 | \code{\link{all_easylinear}()}, 84 | \code{\link{all_growthmodels}()}, 85 | \code{\link{all_splines}()}, 86 | \code{\link{fit_easylinear}()}, 87 | \code{\link{fit_growthmodel}()} 88 | } 89 | \concept{fitting functions} 90 | -------------------------------------------------------------------------------- /inst/doc/examples/example_huang.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## Test of the Huang Growth model, cf. Huang, L (2011) 3 | ## Int. J. Food Microbiology, 10.1016/j.fm.2010.05.019 4 | ## 5 | ## Note: the original model formulation works in log space, so that 6 | ## y(t), y_0 and y_max are all given as natural log. 7 | ## This is advantageous for model fitting, but here we use y_0 and K in 8 | ## untransformed space to be compatible with the growth parameters of 9 | ## most other models. The downside is, that we need box constraints in most 10 | ## cases and possibly more iterations. 11 | ## 12 | ## Author: Thomas Petzoldt, TU Dresden 13 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 14 | ## Please cite our work when using this package. 15 | ## ============================================================================= 16 | 17 | 18 | library("growthrates") 19 | library("lattice") 20 | 21 | ## load data 22 | data(bactgrowth) 23 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 24 | dat <- splitted.data[[23]] 25 | 26 | ## initial parameters and bocx constraints 27 | p <- c(y0 = 0.03, mumax = .1, K = 0.1, alpha = 1, lambda = 2) 28 | 29 | lower <- c(y0 = 0.001, mumax = 1e-2, K = 0.005, alpha = -100, lambda = -20) 30 | upper <- c(y0 = 0.1, mumax = 1, K = 0.5, alpha = 200, lambda = 20) 31 | 32 | ## fit model 33 | fit <- fit_growthmodel(FUN = grow_huang, p = p, time = dat$time, y = dat$value, 34 | lower = lower, upper = upper, 35 | control = list(trace = TRUE)) 36 | 37 | ## coefficients and plot 38 | coef(fit) 39 | plot(fit) 40 | 41 | 42 | ## fit growth models to all data using (log transformed residuals) 43 | L <- all_growthmodels(value ~ grow_huang(time, parms) | strain + conc + replicate, 44 | data = bactgrowth, 45 | p = p, lower = lower, upper = upper, 46 | transform = "log") 47 | 48 | par(mfrow = c(4,3)) 49 | plot(L, log = "y") 50 | 51 | par(mfrow = c(4,3)) 52 | plot(L) 53 | 54 | res <- results(L) 55 | xyplot(lambda ~ log(conc + 1)| strain, data = res) 56 | xyplot(alpha ~ log(conc + 1)| strain, data = res) 57 | 58 | ## and most importantly, the max growth rates 59 | xyplot(mumax ~ log(conc + 1)| strain, data = res) 60 | 61 | ## 2nd approach: fit selected parameters, fix the remaining (here: alpha) 62 | ## alpha = 4 from the IPMP tutorial 63 | p <- c(y0 = 0.03, mumax = .1, K = 0.1, alpha = 4, lambda = 2) 64 | L2 <- all_growthmodels(value ~ grow_huang(time, parms) | strain + conc + replicate, 65 | data = bactgrowth, 66 | p = p, lower = lower, upper = upper, 67 | which = c("y0", "mumax", "K", "lambda"), 68 | method = "Marq", transform = "log") 69 | 70 | par(mfrow = c(4,3)) 71 | plot(L2) 72 | 73 | res2 <- results(L2) 74 | xyplot(mumax ~ log(conc + 1)| strain, data = res2) 75 | 76 | plot(res$mumax, res2$mumax) 77 | -------------------------------------------------------------------------------- /man/predict.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/predict.R 3 | \name{predict,growthrates_fit-method} 4 | \alias{predict,growthrates_fit-method} 5 | \alias{predict,smooth.spline_fit-method} 6 | \alias{predict,easylinear_fit-method} 7 | \alias{predict,nonlinear_fit-method} 8 | \alias{predict,multiple_fits-method} 9 | \title{Model Predictions for \pkg{growthrates} Fits} 10 | \usage{ 11 | \S4method{predict}{growthrates_fit}(object, ...) 12 | 13 | \S4method{predict}{smooth.spline_fit}(object, newdata = NULL, ..., type = c("exponential", "spline")) 14 | 15 | \S4method{predict}{easylinear_fit}(object, newdata = NULL, ..., type = c("exponential", "no_lag")) 16 | 17 | \S4method{predict}{nonlinear_fit}(object, newdata, ...) 18 | 19 | \S4method{predict}{multiple_fits}(object, ...) 20 | } 21 | \arguments{ 22 | \item{object}{name of a 'growthrates' object for which prediction is desired.} 23 | 24 | \item{\dots}{additional arguments affecting the predictions produced.} 25 | 26 | \item{newdata}{an optional data frame with column 'time' for new time steps with 27 | which to predict.} 28 | 29 | \item{type}{type of predict. Can be \code{'exponential'} or \code{'spline'} for \code{fit_spline}, 30 | resp. \code{'exponential'} or \code{'no_lag'} for \code{fit_easylinear}.} 31 | } 32 | \description{ 33 | Class-specific methods of package \pkg{growthrates} to make predictions. 34 | } 35 | \details{ 36 | The implementation of the predict methods is still experimental and under discussion. 37 | } 38 | \examples{ 39 | 40 | data(bactgrowth) 41 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 42 | 43 | ## get table from single experiment 44 | dat <- splitted.data[[1]] 45 | 46 | ## --- linear fit ----------------------------------------------------------- 47 | fit <- fit_easylinear(dat$time, dat$value) 48 | 49 | plot(fit) 50 | pr <- predict(fit) 51 | lines(pr[,1:2], col="blue", lwd=2, lty="dashed") 52 | 53 | pr <- predict(fit, newdata=list(time=seq(2, 6, .1)), type="no_lag") 54 | lines(pr[,1:2], col="magenta") 55 | 56 | 57 | ## --- spline fit ----------------------------------------------------------- 58 | fit1 <- fit_spline(dat$time, dat$value, spar=0.5) 59 | coef(fit1) 60 | summary(fit1) 61 | 62 | plot(fit1) 63 | pr <- predict(fit1) 64 | lines(pr[,1:2], lwd=2, col="blue", lty="dashed") 65 | pr <- predict(fit1, newdata=list(time=2:10), type="spline") 66 | lines(pr[,1:2], lwd=2, col="cyan") 67 | 68 | 69 | ## --- nonlinear fit -------------------------------------------------------- 70 | dat <- splitted.data[["T:0:2"]] 71 | 72 | p <- c(y0 = 0.02, mumax = .5, K = 0.05, h0 = 1) 73 | fit2 <- fit_growthmodel(grow_baranyi, p=p, time=dat$time, y=dat$value) 74 | 75 | ## prediction for given data 76 | predict(fit2) 77 | 78 | ## prediction for new data 79 | pr <- predict(fit2, newdata=data.frame(time=seq(0, 50, 0.1))) 80 | 81 | plot(fit2, xlim=c(0, 50)) 82 | lines(pr[, c("time", "y")], lty="dashed", col="red") 83 | } 84 | \seealso{ 85 | \code{\link{methods}}, \code{\link{predict.smooth.spline}}, 86 | \code{\link{predict.lm}}, \code{\link{predict.nls}} 87 | } 88 | -------------------------------------------------------------------------------- /man/growthrates-package.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/growthrates-package.R 3 | \docType{package} 4 | \name{growthrates-package} 5 | \alias{growthrates-package} 6 | \alias{growthrates} 7 | \title{\packageTitle{growthrates}} 8 | \description{ 9 | \packageDescription{growthrates} 10 | 11 | The package contains basically three methods: 12 | \itemize{ 13 | \item fit a linear regression to a subset of data with the steepest 14 | log-linear increase (a method, similar to Hall et al., 2013), 15 | \item fit parametric nonlinear models to the complete data set, where the 16 | model functions can be given either in closed form or as numerically 17 | solved (system of) differential equation(s), 18 | \item use maximum of the 1st derivative of a smoothing spline with 19 | log-transformed y-values (similar to Kahm et al., 2010). 20 | } 21 | 22 | The package can fit data sets of single experiments or complete series 23 | containing multiple data sets. Included are functions for extracting 24 | estimates and for plotting. The package supports growth models given as 25 | numerically solved differential equations. Multi-core computation is used to 26 | speed up fitting of parametric models. 27 | } 28 | \examples{ 29 | 30 | data(bactgrowth) 31 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 32 | 33 | ## get table from single experiment 34 | dat <- splitted.data[["D:0:1"]] 35 | 36 | fit1 <- fit_spline(dat$time, dat$value) 37 | plot(fit1, log="y") 38 | plot(fit1) 39 | 40 | ## derive start parameters from spline fit 41 | p <- coef(fit1) 42 | 43 | ## subset of first 10 data 44 | first10 <- dat[1:10, ] 45 | fit2 <- fit_growthmodel(grow_exponential, p=p, time=first10$time, y=first10$value) 46 | 47 | ## use parameters from spline fit and take K from the data maximum 48 | p <- c(coef(fit1), K = max(dat$value)) 49 | fit3 <- fit_growthmodel(grow_logistic, p=p, time=dat$time, y=dat$value, transform="log") 50 | 51 | plot(fit1) 52 | lines(fit2, col="green") 53 | lines(fit3, col="red") 54 | 55 | } 56 | \references{ 57 | Hall, B. G., Acar, H. and Barlow, M. 2013. Growth Rates Made Easy. 58 | Mol. Biol. Evol. 31, 232-238, \doi{10.1093/molbev/mst197} 59 | 60 | Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 61 | 2010. grofit: Fitting Biological Growth Curves with R. 62 | Journal of Statistical Software, 33(7), 1-21, 63 | \doi{10.18637/jss.v033.i07} 64 | 65 | Soetaert, K. and Petzoldt, T. 2010. Inverse Modelling, Sensitivity and 66 | Monte Carlo Analysis in R Using Package FME. 67 | Journal of Statistical Software, 33(3), 1-28, 68 | \doi{10.18637/jss.v033.i03} 69 | 70 | Soetaert, K., Petzoldt, T. Setzer, R. W. 2010. Solving Differential Equations 71 | in R: Package deSolve. Journal of Statistical Software, 33(9), 1-25, 72 | \doi{10.18637/jss.v033.i09} 73 | } 74 | \seealso{ 75 | \code{\link{fit_easylinear}}, \code{\link{fit_spline}}, \code{\link{fit_growthmodel}}, 76 | \code{\link{all_easylinear}}, \code{\link{all_splines}}, \code{\link{all_growthmodels}} 77 | } 78 | \author{ 79 | Thomas Petzoldt 80 | } 81 | \keyword{package} 82 | -------------------------------------------------------------------------------- /R/parse_formula_nonlin.R: -------------------------------------------------------------------------------- 1 | #' Simple Formula Interface for Grouped Nonlinear Functions 2 | #' 3 | #' This simple formula interface handles formulae of the form 4 | #' \code{dependent ~ FUN(independent, parms) | group1 + group2 + ...}. 5 | #' 6 | #' This function is used by \code{\link{all_growthmodels}} and normally not 7 | #' called for the user. 8 | #' 9 | #' @param formula a model formula specifying dependent and 10 | #' independent variables, nonlinear model and grouping variables in the form: 11 | #' \code{dependent ~ FUN(independent, parms) | group1 + group2 + ...}. 12 | #' FUN can be a name of an existing growth model (e.g. \code{grow_logistic}) 13 | #' or a valid user-defined function (see \code{\link{growthmodel}}). 14 | #' 15 | #' @return a list with the elements \code{FUN}, \code{valuevar}, \code{timevar}, 16 | #' and \code{groups} 17 | #' 18 | #' @seealso \code{\link{multisplit}}, \code{\link{split}}, \code{\link{parse_formula}} 19 | #' 20 | #' @examples 21 | #' 22 | #' ret <- parse_formula_nonlin(y ~ f(x, parms) | a + b + c) 23 | #' 24 | #' @keywords internal 25 | #' 26 | #' @export 27 | #' 28 | parse_formula_nonlin <- function(formula) { 29 | 30 | form <- as.formula(formula) 31 | 32 | valuevar <- as.character(form[[2]]) # dependent variable 33 | RHS <- form[[3]] # f(time, parms) | group1 + group2 + ... 34 | 35 | rhs <- as.character(RHS) 36 | 37 | if (rhs[1] == "|") { # with grouping 38 | 39 | FUN <- gsub("^\\s+|\\s+$", "", rhs[[2]]) # trim 40 | 41 | ## example: length(grep("^.*[(].*[)]$", "test(x, y)")) 42 | 43 | if (length(grep("^.*[(].*[)]$", FUN)) < 1) { # no nonlinear part 44 | timevar <- rhs[2] 45 | FUN1 <- FUN2 <- NULL 46 | } else { # with nonlinear part 47 | 48 | FUN1 <- parse(text = FUN) # full expression 49 | #FUN2 <- parse(text = gsub("[(].*", "", FUN)) # function name only 50 | FUN2 <- gsub("[(].*", "", FUN) # as character 51 | 52 | vars <- all.vars(FUN1) 53 | if (length(vars) != 2) 54 | stop ("Nonlinear part of the formula should be in the form FUN(time, parms)") 55 | timevar <- vars[1] 56 | # vars[2] is just a dummy placeholder 57 | } 58 | 59 | groups <- gsub("[*:]", "+", rhs[3]) # convert "*" or ":" to "+" 60 | groups <- unlist(strsplit(groups, "[+]")) # split right hand side 61 | groups <- gsub("^\\s+|\\s+$", "", groups) # trim 62 | 63 | } else { # no grouping 64 | 65 | if (length(rhs) == 1) { # rhs has only independend variable 66 | timevar <- rhs[1] 67 | FUN1 <- FUN2 <- NULL 68 | groups <- NULL 69 | } else { # rhs contains nonlinear function 70 | timevar <- rhs[2] 71 | FUN <- rhs[1] 72 | FUN1 <- parse(text = FUN) 73 | FUN2 <- gsub("[(].*", "", FUN) 74 | groups <- NULL 75 | } 76 | } 77 | 78 | list(FUN1 = FUN1, 79 | FUN2 = FUN2, 80 | valuevar = valuevar, 81 | timevar = timevar, 82 | groups = groups) 83 | } 84 | -------------------------------------------------------------------------------- /man/grow_huang.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_huang.R 3 | \name{grow_huang} 4 | \alias{grow_huang} 5 | \title{Growth Model According to Huang} 6 | \usage{ 7 | grow_huang(time, parms) 8 | } 9 | \arguments{ 10 | \item{time}{vector of time steps (independent variable).} 11 | 12 | \item{parms}{named parameter vector of Huang's growth model with: 13 | \itemize{ 14 | \item \code{y0} initial value of abundance, 15 | \item \code{mumax} maximum growth rate (1/time), 16 | \item \code{K} carrying capacity (max. total concentration of cells), 17 | \item \code{alpha} shape parameter determining the curvature, 18 | \item \code{lambda} parameter determining the lag time. 19 | 20 | }} 21 | } 22 | \value{ 23 | vector of dependent variable (\code{y}). 24 | } 25 | \description{ 26 | Huangs growth model written as analytical solution of the differential equations. 27 | } 28 | \details{ 29 | The version of the equation used in this package has the following form: 30 | \deqn{B = time + 1/alpha * log((1+exp(-alpha * (time - lambda)))/(1 + exp(alpha * lambda)))} 31 | \deqn{log(y) = log(y0) + log(K) - log(y0 + (K - y0) * exp(-mumax * B))} 32 | 33 | In contrast to the original publication, all parameters related to population 34 | abundance (y, y0, K) are given as untransformed values. 35 | They are not log-transformed.\cr 36 | In general, using log-transformed parameters would indeed be a good idea to 37 | avoid the need of constained optimization, but tests showed that 38 | box-constrained optimization worked resonably well. 39 | Therefore, handling of optionally log-transformed parameters was removed 40 | from the package to avoid confusion. If you want to discuss this, please 41 | let me know. 42 | } 43 | \examples{ 44 | 45 | time <- seq(0, 30, length=200) 46 | y <- grow_huang(time, c(y0=0.01, mumax=.1, K=0.1, alpha=1.5, lambda=3))[,"y"] 47 | plot(time, y, type="l") 48 | plot(time, y, type="l", log="y") 49 | 50 | } 51 | \references{ 52 | Huang, Lihan (2008) Growth kinetics of Listeria monocytogenes in broth and 53 | beef frankfurters - determination of lag phase duration and exponential 54 | growth rate under isothermal conditions. Journal of Food Science 73(5), 55 | E235 -- E242. \doi{10.1111/j.1750-3841.2008.00785.x} 56 | 57 | Huang, Lihan (2011) A new mechanistic growth model for simultaneous 58 | determination of lag phase duration and exponential growth rate and a new 59 | Belehdradek-type model for evaluating the effect of temperature on growth rate. 60 | Food Microbiology 28, 770 -- 776. \doi{10.1016/j.fm.2010.05.019} 61 | 62 | Huang, Lihan (2013) Introduction to USDA Integrated Pathogen Modeling 63 | Program (IPMP). Residue Chemistry and Predictive Microbiology Research 64 | Unit. USDA Agricultural Research Service. 65 | } 66 | \seealso{ 67 | Other growth models: 68 | \code{\link{grow_baranyi}()}, 69 | \code{\link{grow_exponential}()}, 70 | \code{\link{grow_gompertz}()}, 71 | \code{\link{grow_gompertz2}()}, 72 | \code{\link{grow_logistic}()}, 73 | \code{\link{grow_richards}()}, 74 | \code{\link{growthmodel}}, 75 | \code{\link{ode_genlogistic}()}, 76 | \code{\link{ode_twostep}()} 77 | } 78 | \concept{growth models} 79 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CRAN_Status_Badge](https://www.r-pkg.org/badges/version/growthrates)](https://cran.r-project.org/package=growthrates) 2 | [![Downloads](https://cranlogs.r-pkg.org/badges/growthrates)](https://cran.r-project.org/package=growthrates) 3 | 4 | # R package growthrates 5 | 6 | ## Estimate Growth Rates from Experimental Data 7 | 8 | The population growth rate is the main indicator of population 9 | fitness. This **R** package provides a collection of methods to 10 | determine growth rates from experimental data, in particular from 11 | batch experiments and microwell plate reader trials. 12 | 13 | ## Overview 14 | 15 | The package contains basically three methods: 16 | 17 | * fit a linear regression to a subset of data with the steepest 18 | log-linear increase (a method, similar to Hall et al., 2014), 19 | 20 | * fit parametric nonlinear models to the complete data set, where the 21 | model functions can be given either in closed form or as numerically 22 | solved (system of) differential equation(s), 23 | 24 | * use maximum of the 1st derivative of a smoothing spline with 25 | log-transformed y-values (similar to Kahm et al., 2010). 26 | 27 | The package can fit data sets of single experiments or complete series 28 | containing multiple data sets. Included are functions for extracting 29 | estimates and for plotting. The package supports growth models given 30 | as numerically solved differential equations. Multi-core computation 31 | is used to speed up fitting of parametric models. 32 | 33 | ## Documentation 34 | 35 | * [Introduction to the main functions](https://tpetzoldt.github.io/growthrates/articles/Introduction.html) 36 | 37 | * [Writing user defined functions](https://tpetzoldt.github.io/growthrates/articles/User_models.html) 38 | 39 | 40 | ## Download and Installation 41 | 42 | ### Release version (recommended) 43 | 44 | The package is available on [CRAN](https://cran.r-project.org/package=growthrates). 45 | Install it from within **R** or **RStudio** as usual or with: 46 | 47 | 48 | ```R 49 | install.packages("growthrates") 50 | ``` 51 | 52 | ### Development version 53 | 54 | Install with package devtools: 55 | 56 | ```R 57 | install.packages("devtools") 58 | library(devtools) 59 | install_github("tpetzoldt/growthrates") 60 | ``` 61 | 62 | 63 | 64 | 65 | ## References 66 | 67 | 68 | Hall, B. G., H. Acar, A. Nandipati, and M. Barlow. 2014. Growth Rates Made 69 | Easy. Mol. Biol. Evol. 31: 232-38. https://dx.doi.org/10.1093/molbev/mst187 70 | 71 | Kahm, Matthias, Guido Hasenbrink, Hella Lichtenberg-Frate, Jost 72 | Ludwig, and Maik Kschischo. 2010. grofit: Fitting Biological Growth 73 | Curves with R. Journal of Statistical Software 33 (7): 74 | 1-21. https://dx.doi.org/10.18637/jss.v033.i07 75 | 76 | R Core Team. 2015. R: A Language and Environment for Statistical 77 | Computing. Vienna, Austria: R Foundation for Statistical 78 | Computing. https://www.R-project.org/ 79 | 80 | Soetaert, Karline, and Thomas Petzoldt. 2010. Inverse Modelling, 81 | Sensitivity and Monte Carlo Analysis in R Using Package FME. Journal 82 | of Statistical Software 33 (3): 83 | 1-28. https://dx.doi.org/10.18637/jss.v033.i03 84 | 85 | Soetaert, Karline, Thomas Petzoldt, and R. Woodrow 86 | Setzer. 2010. Solving Differential Equations in R: Package 87 | deSolve. Journal of Statistical Software 33 (9): 88 | 1-25. https://dx.doi.org/10.18637/jss.v033.i09 89 | 90 | ## Author 91 | 92 | [tpetzoldt](https://github.com/tpetzoldt) 93 | -------------------------------------------------------------------------------- /R/grow_huang.R: -------------------------------------------------------------------------------- 1 | #' Growth Model According to Huang 2 | #' 3 | #' Huangs growth model written as analytical solution of the differential equations. 4 | #' 5 | #' The version of the equation used in this package has the following form: 6 | #' \deqn{B = time + 1/alpha * log((1+exp(-alpha * (time - lambda)))/(1 + exp(alpha * lambda)))} 7 | #' \deqn{log(y) = log(y0) + log(K) - log(y0 + (K - y0) * exp(-mumax * B))} 8 | #' 9 | #' In contrast to the original publication, all parameters related to population 10 | #' abundance (y, y0, K) are given as untransformed values. 11 | #' They are not log-transformed.\cr 12 | #' In general, using log-transformed parameters would indeed be a good idea to 13 | #' avoid the need of constained optimization, but tests showed that 14 | #' box-constrained optimization worked resonably well. 15 | #' Therefore, handling of optionally log-transformed parameters was removed 16 | #' from the package to avoid confusion. If you want to discuss this, please 17 | #' let me know. 18 | #' 19 | #' @param time vector of time steps (independent variable). 20 | #' @param parms named parameter vector of Huang's growth model with: 21 | #' \itemize{ 22 | #' \item \code{y0} initial value of abundance, 23 | #' \item \code{mumax} maximum growth rate (1/time), 24 | #' \item \code{K} carrying capacity (max. total concentration of cells), 25 | #' \item \code{alpha} shape parameter determining the curvature, 26 | #' \item \code{lambda} parameter determining the lag time. 27 | #' 28 | #' } 29 | #' 30 | #' @return vector of dependent variable (\code{y}). 31 | #' 32 | #' 33 | #' @references 34 | #' 35 | #' Huang, Lihan (2008) Growth kinetics of Listeria monocytogenes in broth and 36 | #' beef frankfurters - determination of lag phase duration and exponential 37 | #' growth rate under isothermal conditions. Journal of Food Science 73(5), 38 | #' E235 -- E242. \doi{10.1111/j.1750-3841.2008.00785.x} 39 | #' 40 | #' Huang, Lihan (2011) A new mechanistic growth model for simultaneous 41 | #' determination of lag phase duration and exponential growth rate and a new 42 | #' Belehdradek-type model for evaluating the effect of temperature on growth rate. 43 | #' Food Microbiology 28, 770 -- 776. \doi{10.1016/j.fm.2010.05.019} 44 | #' 45 | #' Huang, Lihan (2013) Introduction to USDA Integrated Pathogen Modeling 46 | #' Program (IPMP). Residue Chemistry and Predictive Microbiology Research 47 | #' Unit. USDA Agricultural Research Service. 48 | #' 49 | #' 50 | #' 51 | #' @examples 52 | #' 53 | #' time <- seq(0, 30, length=200) 54 | #' y <- grow_huang(time, c(y0=0.01, mumax=.1, K=0.1, alpha=1.5, lambda=3))[,"y"] 55 | #' plot(time, y, type="l") 56 | #' plot(time, y, type="l", log="y") 57 | #' 58 | #' @family growth models 59 | #' 60 | #' @rdname grow_huang 61 | #' @export 62 | #' 63 | grow_huang <- function(time, parms) { 64 | with(as.list(parms), { 65 | B <- time + 1/alpha * log((1+exp(-alpha * (time - lambda)))/(1 + exp(alpha * lambda))) 66 | #log_y <- y0 + K - log(exp(y0) + (exp(K) - exp(y0)) * exp(-mumax * B)) 67 | log_y <- log(y0) + log(K) - log(y0 + (K - y0) * exp(-mumax * B)) 68 | return(as.matrix(data.frame(time = time, y = exp(log_y)))) 69 | }) 70 | } 71 | ## attach names of parameters as attributes 72 | attr(grow_huang, "fname") <- c("grow_huang") 73 | attr(grow_huang, "pnames") <- c("y0", "mumax", "K", "alpha", "lambda") 74 | class(grow_huang) <- c("growthmodel", "function") 75 | -------------------------------------------------------------------------------- /man/methods.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/methods.R 3 | \name{rsquared,growthrates_fit-method} 4 | \alias{rsquared,growthrates_fit-method} 5 | \alias{obs,growthrates_fit-method} 6 | \alias{coef,growthrates_fit-method} 7 | \alias{coef,easylinear_fit-method} 8 | \alias{coef,smooth.spline_fit-method} 9 | \alias{deviance,growthrates_fit-method} 10 | \alias{summary,growthrates_fit-method} 11 | \alias{summary,nonlinear_fit-method} 12 | \alias{residuals,growthrates_fit-method} 13 | \alias{df.residual,growthrates_fit-method} 14 | \alias{summary,smooth.spline_fit-method} 15 | \alias{df.residual,smooth.spline_fit-method} 16 | \alias{deviance,smooth.spline_fit-method} 17 | \alias{coef,multiple_fits-method} 18 | \alias{rsquared,multiple_fits-method} 19 | \alias{deviance,multiple_fits-method} 20 | \alias{results,multiple_fits-method} 21 | \alias{results,multiple_easylinear_fits-method} 22 | \alias{summary,multiple_fits-method} 23 | \alias{residuals,multiple_fits-method} 24 | \title{Accessor Methods of Package \pkg{growthrates}.} 25 | \usage{ 26 | \S4method{rsquared}{growthrates_fit}(object, ...) 27 | 28 | \S4method{obs}{growthrates_fit}(object, ...) 29 | 30 | \S4method{coef}{growthrates_fit}(object, extended = FALSE, ...) 31 | 32 | \S4method{coef}{easylinear_fit}(object, ...) 33 | 34 | \S4method{coef}{smooth.spline_fit}(object, extended = FALSE, ...) 35 | 36 | \S4method{deviance}{growthrates_fit}(object, ...) 37 | 38 | \S4method{summary}{growthrates_fit}(object, ...) 39 | 40 | \S4method{summary}{nonlinear_fit}(object, cov = TRUE, ...) 41 | 42 | \S4method{residuals}{growthrates_fit}(object, ...) 43 | 44 | \S4method{df.residual}{growthrates_fit}(object, ...) 45 | 46 | \S4method{summary}{smooth.spline_fit}(object, cov = TRUE, ...) 47 | 48 | \S4method{df.residual}{smooth.spline_fit}(object, ...) 49 | 50 | \S4method{deviance}{smooth.spline_fit}(object, ...) 51 | 52 | \S4method{coef}{multiple_fits}(object, ...) 53 | 54 | \S4method{rsquared}{multiple_fits}(object, ...) 55 | 56 | \S4method{deviance}{multiple_fits}(object, ...) 57 | 58 | \S4method{results}{multiple_fits}(object, ...) 59 | 60 | \S4method{results}{multiple_easylinear_fits}(object, ...) 61 | 62 | \S4method{summary}{multiple_fits}(object, ...) 63 | 64 | \S4method{residuals}{multiple_fits}(object, ...) 65 | } 66 | \arguments{ 67 | \item{object}{name of a 'growthrate' object.} 68 | 69 | \item{\dots}{other arguments passed to the methods.} 70 | 71 | \item{extended}{boolean if extended set of parameters shoild be printed} 72 | 73 | \item{cov}{boolean if the covariance matrix should be printed.} 74 | } 75 | \description{ 76 | Functions to access the results of fitted growthrate objects: \code{summary}, 77 | \code{coef}, \code{rsquared}, \code{deviance}, \code{residuals}, 78 | \code{df.residual}, \code{obs}, \code{results}. 79 | } 80 | \examples{ 81 | 82 | data(bactgrowth) 83 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 84 | 85 | ## get table from single experiment 86 | dat <- splitted.data[[10]] 87 | 88 | fit1 <- fit_spline(dat$time, dat$value, spar=0.5) 89 | coef(fit1) 90 | summary(fit1) 91 | 92 | ## derive start parameters from spline fit 93 | p <- c(coef(fit1), K = max(dat$value)) 94 | fit2 <- fit_growthmodel(grow_logistic, p=p, time=dat$time, y=dat$value, transform="log") 95 | coef(fit2) 96 | rsquared(fit2) 97 | deviance(fit2) 98 | 99 | summary(fit2) 100 | 101 | plot(residuals(fit2) ~ obs(fit2)[,2]) 102 | 103 | 104 | } 105 | -------------------------------------------------------------------------------- /R/growthrates-package.R: -------------------------------------------------------------------------------- 1 | #' \packageTitle{growthrates} 2 | #' 3 | #' @description 4 | #' \packageDescription{growthrates} 5 | #' 6 | #' The package contains basically three methods: 7 | #' \itemize{ 8 | #' \item fit a linear regression to a subset of data with the steepest 9 | #' log-linear increase (a method, similar to Hall et al., 2013), 10 | #' \item fit parametric nonlinear models to the complete data set, where the 11 | #' model functions can be given either in closed form or as numerically 12 | #' solved (system of) differential equation(s), 13 | #' \item use maximum of the 1st derivative of a smoothing spline with 14 | #' log-transformed y-values (similar to Kahm et al., 2010). 15 | #' } 16 | #' 17 | #' The package can fit data sets of single experiments or complete series 18 | #' containing multiple data sets. Included are functions for extracting 19 | #' estimates and for plotting. The package supports growth models given as 20 | #' numerically solved differential equations. Multi-core computation is used to 21 | #' speed up fitting of parametric models. 22 | #' 23 | #' @name growthrates-package 24 | #' @aliases growthrates growthrates-package 25 | #' @author Thomas Petzoldt 26 | #' 27 | #' @seealso \code{\link{fit_easylinear}}, \code{\link{fit_spline}}, \code{\link{fit_growthmodel}}, 28 | #' \code{\link{all_easylinear}}, \code{\link{all_splines}}, \code{\link{all_growthmodels}} 29 | #' 30 | #' 31 | #' @references 32 | #' 33 | #' Hall, B. G., Acar, H. and Barlow, M. 2013. Growth Rates Made Easy. 34 | #' Mol. Biol. Evol. 31, 232-238, \doi{10.1093/molbev/mst197} 35 | #' 36 | #' Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 37 | #' 2010. grofit: Fitting Biological Growth Curves with R. 38 | #' Journal of Statistical Software, 33(7), 1-21, 39 | #' \doi{10.18637/jss.v033.i07} 40 | #' 41 | #' Soetaert, K. and Petzoldt, T. 2010. Inverse Modelling, Sensitivity and 42 | #' Monte Carlo Analysis in R Using Package FME. 43 | #' Journal of Statistical Software, 33(3), 1-28, 44 | #' \doi{10.18637/jss.v033.i03} 45 | #' 46 | #' Soetaert, K., Petzoldt, T. Setzer, R. W. 2010. Solving Differential Equations 47 | #' in R: Package deSolve. Journal of Statistical Software, 33(9), 1-25, 48 | #' \doi{10.18637/jss.v033.i09} 49 | #' 50 | #' 51 | #' 52 | #' @keywords package 53 | #' @examples 54 | #' 55 | #' data(bactgrowth) 56 | #' splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 57 | #' 58 | #' ## get table from single experiment 59 | #' dat <- splitted.data[["D:0:1"]] 60 | #' 61 | #' fit1 <- fit_spline(dat$time, dat$value) 62 | #' plot(fit1, log="y") 63 | #' plot(fit1) 64 | #' 65 | #' ## derive start parameters from spline fit 66 | #' p <- coef(fit1) 67 | #' 68 | #' ## subset of first 10 data 69 | #' first10 <- dat[1:10, ] 70 | #' fit2 <- fit_growthmodel(grow_exponential, p=p, time=first10$time, y=first10$value) 71 | #' 72 | #' ## use parameters from spline fit and take K from the data maximum 73 | #' p <- c(coef(fit1), K = max(dat$value)) 74 | #' fit3 <- fit_growthmodel(grow_logistic, p=p, time=dat$time, y=dat$value, transform="log") 75 | #' 76 | #' plot(fit1) 77 | #' lines(fit2, col="green") 78 | #' lines(fit3, col="red") 79 | #' 80 | #' @useDynLib growthrates 81 | #' 82 | #' @import stats graphics methods FME lattice 83 | #' @importFrom deSolve ode 84 | #' @importFrom utils type.convert 85 | #' @import parallel 86 | ## @importFrom parallel makeCluster stopCluster parLapply 87 | #' @export 88 | "_PACKAGE" 89 | 90 | -------------------------------------------------------------------------------- /man/plot.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/plot.R 3 | \name{plot} 4 | \alias{plot} 5 | \alias{plot,nonlinear_fit,missing-method} 6 | \alias{lines,nonlinear_fit-method} 7 | \alias{plot,easylinear_fit,missing-method} 8 | \alias{plot,smooth.spline_fit,missing-method} 9 | \alias{lines,easylinear_fit-method} 10 | \alias{plot,multiple_fits,missing-method} 11 | \title{Plot Model Fits} 12 | \usage{ 13 | \S4method{plot}{nonlinear_fit,missing}(x, y, log = "", which = c("fit", "diagnostics"), ...) 14 | 15 | \S4method{lines}{nonlinear_fit}(x, ...) 16 | 17 | \S4method{plot}{easylinear_fit,missing}(x, y, log = "", which = c("fit", "diagnostics"), ...) 18 | 19 | \S4method{plot}{smooth.spline_fit,missing}(x, y, ...) 20 | 21 | \S4method{lines}{easylinear_fit}(x, ...) 22 | 23 | \S4method{plot}{multiple_fits,missing}(x, y, ...) 24 | } 25 | \arguments{ 26 | \item{x}{an object returned by a model fitting function of package 27 | \pkg{growthrates}, that can contain one or multiple fits.} 28 | 29 | \item{y}{(ignored) for compatibility with the default plot method.} 30 | 31 | \item{log}{a character string which contains \code{"y"} if the y axis is to 32 | be logarithmic.} 33 | 34 | \item{which}{either \code{"fit"} (default) or \code{"diagnostics"}.} 35 | 36 | \item{\dots}{other arguments pased to the plotting methods, 37 | see \code{\link{plot.default}} and \code{\link{par}}.} 38 | } 39 | \description{ 40 | Methods to plot growth model fits together with the data and, alternatively, 41 | plot diagnostics 42 | } 43 | \details{ 44 | The plot methods detect automatically which type of plot is 45 | appropriate, depending on the class of \code{x} and can plot either one 46 | single model fit or a complete series (multiple fits). In the latter case 47 | it may be wise to redirect the graphics to an external file (e.g. a pdf) 48 | and / or to use tomething like \code{par(mfrow=c(3,3))}. 49 | 50 | The \code{lines}-method is currently only available for single fits. 51 | 52 | If you need more control, you can of course also write own plotting functions. 53 | } 54 | \examples{ 55 | 56 | data(bactgrowth) 57 | splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 58 | 59 | ## get table from single experiment 60 | dat <- splitted.data[["D:0:1"]] 61 | 62 | fit1 <- fit_spline(dat$time, dat$value) 63 | plot(fit1, log="y") 64 | plot(fit1) 65 | 66 | ## derive start parameters from spline fit 67 | p <- coef(fit1) 68 | 69 | ## subset of first 10 data 70 | first10 <- dat[1:10, ] 71 | fit2 <- fit_growthmodel(grow_exponential, p=p, time=first10$time, y=first10$value) 72 | 73 | p <- c(coef(fit1), K = max(dat$value)) 74 | fit3 <- fit_growthmodel(grow_logistic, p=p, time=dat$time, y=dat$value, transform="log") 75 | 76 | plot(fit1) 77 | lines(fit2, col="green") 78 | lines(fit3, col="red") 79 | 80 | 81 | all.fits <- all_splines(value ~ time | strain + conc + replicate, data = bactgrowth) 82 | par(mfrow=c(3,3)) 83 | plot(all.fits) 84 | 85 | ## it is also possible to plot a single fit or a subset of the fits 86 | par(mfrow=c(1,1)) 87 | plot(all.fits[["D:0:1"]]) 88 | par(mfrow=c(2,2)) 89 | plot(all.fits[1:4]) 90 | 91 | ## plot only the 'R' strain 92 | # par(mfrow=c(4, 6)) 93 | plot(all.fits[grep("R:", names(all.fits))]) 94 | 95 | } 96 | \seealso{ 97 | \code{\link{plot.default}}, \code{\link{par}}, 98 | \code{\link{fit_growthmodel}}, \code{\link{fit_easylinear}}, 99 | \code{\link{all_growthmodels}}, \code{\link{all_easylinear}} 100 | } 101 | -------------------------------------------------------------------------------- /man/grow_genlogistic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_genlogistic.R 3 | \name{ode_genlogistic} 4 | \alias{ode_genlogistic} 5 | \alias{grow_genlogistic} 6 | \title{Generalized Logistic Growth Model} 7 | \usage{ 8 | ode_genlogistic(time, y, parms, ...) 9 | 10 | grow_genlogistic(time, parms, ...) 11 | } 12 | \arguments{ 13 | \item{time}{vector of simulation time steps} 14 | 15 | \item{y}{named vector with initial value of the system (e.g. cell concentration)} 16 | 17 | \item{parms}{parameters of the generalized logistic growth model 18 | \itemize{ 19 | \item \code{mumax} maximum growth rate (1/time) 20 | \item \code{K} carrying capacity (max. abundance) 21 | \item \code{alpha, beta, gamma} parameters determining the shape of growth. 22 | Setting all values to one returns the ordinary logistic function. 23 | }} 24 | 25 | \item{\dots}{additional parameters passed to the \code{ode}-function.} 26 | } 27 | \value{ 28 | For \code{ode_genlogistic}: matrix containing the simulation outputs. 29 | The return value of has also class \code{deSolve}. 30 | 31 | For \code{grow_genlogistic}: vector of dependent variable (\code{y}). 32 | 33 | \itemize{ 34 | \item \code{time} time of the simulation 35 | \item \code{y} abundance of organisms 36 | } 37 | } 38 | \description{ 39 | Generalized logistic growth model solved as differential equation. 40 | } 41 | \details{ 42 | The model is given as its first derivative: 43 | 44 | \deqn{dy/dt = mumax * y^alpha * (1-(y/K)^beta)^gamma} 45 | 46 | that is then numerically integrated ('simulated') according to time (t). 47 | 48 | The generalized logistic according to Tsoularis (2001) is a flexible 49 | model that covers exponential and logistic growth, Richards, Gompertz, von 50 | Bertalanffy, and some more as special cases. 51 | 52 | The differential equation is solved numerically, where function 53 | \code{ode_genlogistic} is the differential equation, and 54 | \code{grow_genlogistic} runs a numerical simulation over time. 55 | 56 | The default version \code{grow_genlogistic} is run directly as compiled code, 57 | whereas the R versions \code{ode_logistic} is 58 | provided for testing by the user. 59 | } 60 | \examples{ 61 | 62 | time <- seq(0, 30, length=200) 63 | parms <- c(mumax=0.5, K=10, alpha=1, beta=1, gamma=1) 64 | y0 <- c(y=.1) 65 | out <- ode(y0, time, ode_genlogistic, parms) 66 | plot(out) 67 | 68 | out2 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=2, beta=1, gamma=1)) 69 | out3 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=2, gamma=1)) 70 | out4 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=1, gamma=2)) 71 | out5 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=.5, beta=1, gamma=1)) 72 | out6 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=.5, gamma=1)) 73 | out7 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.3, K=10, alpha=1, beta=1, gamma=.5)) 74 | plot(out, out2, out3, out4, out5, out6, out7) 75 | 76 | ## growth with lag (cf. log_y) 77 | plot(ode(y0, time, ode_genlogistic, parms = c(mumax=1, K=10, alpha=2, beta=.8, gamma=5))) 78 | 79 | 80 | } 81 | \references{ 82 | Tsoularis, A. (2001) Analysis of Logistic Growth Models. 83 | Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 84 | } 85 | \seealso{ 86 | Other growth models: 87 | \code{\link{grow_baranyi}()}, 88 | \code{\link{grow_exponential}()}, 89 | \code{\link{grow_gompertz}()}, 90 | \code{\link{grow_gompertz2}()}, 91 | \code{\link{grow_huang}()}, 92 | \code{\link{grow_logistic}()}, 93 | \code{\link{grow_richards}()}, 94 | \code{\link{growthmodel}}, 95 | \code{\link{ode_twostep}()} 96 | } 97 | \concept{growth models} 98 | -------------------------------------------------------------------------------- /man/grow_twostep.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/grow_twostep.R 3 | \name{ode_twostep} 4 | \alias{ode_twostep} 5 | \alias{grow_twostep} 6 | \title{Twostep Growth Model} 7 | \usage{ 8 | ode_twostep(time, y, parms, ...) 9 | 10 | grow_twostep(time, parms, ...) 11 | } 12 | \arguments{ 13 | \item{time}{actual time (for the ode) resp. vector of simulation time steps.} 14 | 15 | \item{y}{named vector with state of the system 16 | (yi, ya: abundance of inactive and active organisms, e.g. 17 | concentration of inactive resp. active cells).} 18 | 19 | \item{parms}{parameters of the two-step growth model: 20 | \itemize{ 21 | \item \code{yi, ya} initial abundance of active and inactive organisms, 22 | \item \code{kw} activation (``wakeup'') constant (1/time), 23 | \item \code{mumax} maximum growth rate (1/time), 24 | \item \code{K} carrying capacity (max. abundance). 25 | }} 26 | 27 | \item{\dots}{placeholder for additional parameters (for user-extended versions of this function)} 28 | } 29 | \value{ 30 | For \code{ode_twostep}: matrix containing the simulation outputs. 31 | The return value of has also class \code{deSolve}. 32 | 33 | For \code{grow_twostep}: vector of dependent variable (\code{y}): 34 | 35 | \itemize{ 36 | \item \code{time} time of the simulation 37 | \item \code{yi} concentration of inactive cells 38 | \item \code{ya} concentration of active cells 39 | \item \code{y} total cell concentration 40 | } 41 | } 42 | \description{ 43 | System of two differential equations describing bacterial growth as two-step 44 | process of activation (or adaptation) and growth. 45 | } 46 | \details{ 47 | The model is given as a system of two differential equations: 48 | 49 | \deqn{dy_i/dt = -kw * yi} 50 | \deqn{dy_a/dt = kw * yi + mumax * (1 - (yi + ya)/K) * ya} 51 | 52 | that are then numerically integrated ('simulated') according to time (t). The 53 | model assumes that the population consists of active (\eqn{y_a}) and inactive 54 | (\eqn{y_i}) cells so that the observed abundance is (\eqn{y = y_i + y_a}). 55 | Adapting inactive cells change to the active state with a first order 'wakeup' 56 | rate (\eqn{kw}). 57 | 58 | Function \code{ode_twostep} is the system of differential equations, 59 | whereas \code{grow_twostep} runs a numerical simulation over time. 60 | 61 | A similar two-compartment model, but without the logistic term, was discussed by Baranyi (1998). 62 | } 63 | \examples{ 64 | 65 | time <- seq(0, 30, length=200) 66 | parms <- c(kw = 0.1, mumax=0.2, K=0.1) 67 | y0 <- c(yi=0.01, ya=0.0) 68 | out <- ode(y0, time, ode_twostep, parms) 69 | 70 | plot(out) 71 | 72 | o <- grow_twostep(0:100, c(yi=0.01, ya=0.0, kw = 0.1, mumax=0.2, K=0.1)) 73 | plot(o) 74 | 75 | } 76 | \references{ 77 | Baranyi, J. (1998). Comparison of stochastic and deterministic concepts of bacterial lag. 78 | J. heor. Biol. 192, 403--408. 79 | } 80 | \seealso{ 81 | Other growth models: 82 | \code{\link{grow_baranyi}()}, 83 | \code{\link{grow_exponential}()}, 84 | \code{\link{grow_gompertz}()}, 85 | \code{\link{grow_gompertz2}()}, 86 | \code{\link{grow_huang}()}, 87 | \code{\link{grow_logistic}()}, 88 | \code{\link{grow_richards}()}, 89 | \code{\link{growthmodel}}, 90 | \code{\link{ode_genlogistic}()} 91 | 92 | Other growth models: 93 | \code{\link{grow_baranyi}()}, 94 | \code{\link{grow_exponential}()}, 95 | \code{\link{grow_gompertz}()}, 96 | \code{\link{grow_gompertz2}()}, 97 | \code{\link{grow_huang}()}, 98 | \code{\link{grow_logistic}()}, 99 | \code{\link{grow_richards}()}, 100 | \code{\link{growthmodel}}, 101 | \code{\link{ode_genlogistic}()} 102 | } 103 | \concept{growth models} 104 | -------------------------------------------------------------------------------- /inst/doc/examples/example_ggplot.R: -------------------------------------------------------------------------------- 1 | ## ============================================================================= 2 | ## This script demonstrates how to 3 | ## - use ggplot 4 | ## - compare multiple fits with ggplot 5 | ## 6 | ## Author: Thomas Petzoldt, TU Dresden 7 | ## License: GPL >= 2, https://www.gnu.org/licenses/ 8 | ## Please cite our work when using this package. 9 | ## ============================================================================= 10 | 11 | library("growthrates") 12 | library("reshape2") 13 | library("ggplot2") 14 | library("dplyr") 15 | 16 | ## use built-in example data of the package 17 | data(bactgrowth) 18 | 19 | ## use a subset to make testing easier 20 | dataset <- bactgrowth[(bactgrowth$conc < 0.5) & bactgrowth$strain %in% c("D", "T"), ] 21 | 22 | ## uncomment this for the full data set 23 | #dataset <- bactgrowth 24 | 25 | ## start values for the most complex model 26 | ## if names or values of start values differ too much, 27 | ## define p, lower, upper separately for each model 28 | p <- c(y0 = 0.03, mumax = .1, K = 0.1, h0 = 1) 29 | lower <- c(y0 = 0.001, mumax = 1e-2, K = 0.005, h0 = 0) 30 | upper <- c(y0 = 0.1, mumax = 1, K = 0.5, h0 = 10) 31 | 32 | ## Fit exponential model 33 | many_exponential <- all_growthmodels( 34 | value ~ grow_exponential(time, parms) | strain + replicate + conc, 35 | data = dataset, 36 | p = p, lower = lower, upper = upper, which=c("y0", "mumax"), 37 | log = "y", ncores = 4) 38 | 39 | ## Fit logistic model 40 | many_logistic <- all_growthmodels( 41 | value ~ grow_logistic(time, parms) | strain + replicate + conc, 42 | data = dataset, 43 | p = p, lower = lower, upper = upper, which=c("y0", "mumax", "K"), 44 | log = "y", ncores = 4) 45 | 46 | 47 | ## Fit Baranyi model 48 | many_baranyi <- all_growthmodels( 49 | value ~ grow_baranyi(time, parms) | strain + replicate + conc, 50 | data = dataset, 51 | p = p, lower = lower, upper = upper, 52 | log = "y", ncores = 4) 53 | 54 | 55 | ## do some data mangling 56 | ## something like this may eventually become part of the package 57 | tidy_predict <- function(obj, time=NULL, model="model") { 58 | if (is.null(time)) { 59 | pr <- predict(obj) 60 | } else { 61 | pr <- predict(obj, newdata=data.frame(time=time)) 62 | } 63 | arr <- simplify2array(pr, higher=TRUE) 64 | values <- melt(arr[,2,], value.name = "value") 65 | time <- melt(arr[,1,], value.name = "value")[, "value"] 66 | grp <- as.data.frame(t(simplify2array(strsplit(as.character(values$Var2), ":"))), 67 | stringsAsFactors=FALSE) 68 | names(grp) <- obj@grouping 69 | data.frame(model=model, id=as.character(values$Var2), 70 | grp, time=time, value=values[, "value"]) 71 | } 72 | 73 | ## This defines the range and how smooth the curves should be 74 | time_steps <- seq(0, 40, by=0.5) 75 | 76 | tidy_exponential <- tidy_predict(many_exponential, time=time_steps, model="exponential") 77 | tidy_logistic <- tidy_predict(many_logistic, time=time_steps, model="logistic") 78 | tidy_baranyi <- tidy_predict(many_baranyi, time=time_steps, model="Baranyi") 79 | 80 | ## combine data row-wise 81 | tidy_fits <- rbind(tidy_exponential, tidy_logistic, tidy_baranyi) 82 | 83 | 84 | ## linear y scale 85 | ggplot(tidy_fits, aes(time, value)) + 86 | geom_point(data=dataset) + 87 | geom_line(aes(color=model)) + 88 | facet_grid(as.numeric(conc) ~ strain + replicate) 89 | 90 | ## log y scale 91 | ggplot(tidy_fits, aes(time, value)) + 92 | geom_point(data=dataset) + 93 | geom_line(aes(color=model)) + 94 | facet_grid(as.numeric(conc) ~ strain + replicate) + 95 | scale_y_log10() 96 | 97 | -------------------------------------------------------------------------------- /R/all_easylinear.R: -------------------------------------------------------------------------------- 1 | #' Easy Growth Rates Fit to data Frame 2 | #' 3 | #' Determine maximum growth rates from log-linear part of the growth curve for 4 | #' a series of experiments. 5 | #' 6 | #' @param formula model formula specifying dependent, independent and grouping 7 | #' variables in the form: 8 | #' \code{dependent ~ independent | group1 + group2 + \dots}. 9 | #' @param data data frame of observational data. 10 | #' @param time character vectors with name independent variabl.e. 11 | #' @param y character vector with name of dependent variable 12 | #' @param grouping model formula or character vector of criteria defining 13 | #' subsets in the data frame. 14 | #' @param h with of the window (number of data). 15 | #' @param quota part of window fits considered for the overall linear fit 16 | #' (relative to max. growth rate). 17 | #' @param subset a specification of the rows to be used: defaults to all rows. 18 | #' @param \dots generic parameters, reserved for future extensions. 19 | #' 20 | #' @return object with parameters of all fits. 21 | #' 22 | #' @references Hall, BG., Acar, H, Nandipati, A and Barlow, M (2014) Growth Rates Made Easy. 23 | #' Mol. Biol. Evol. 31: 232-38, \doi{10.1093/molbev/mst187} 24 | #' 25 | #' @family fitting functions 26 | #' 27 | #' @examples 28 | #' 29 | #' \donttest{ 30 | #' library("growthrates") 31 | #' L <- all_easylinear(value ~ time | strain + conc + replicate, data=bactgrowth) 32 | #' summary(L) 33 | #' coef(L) 34 | #' rsquared(L) 35 | #' 36 | #' results <- results(L) 37 | #' 38 | #' library(lattice) 39 | #' xyplot(mumax ~ conc|strain, data=results) 40 | #' } 41 | #' 42 | #' @rdname all_easylinear 43 | #' @export 44 | #' 45 | all_easylinear <- function(...) UseMethod("all_easylinear") 46 | 47 | 48 | #' @rdname all_easylinear 49 | #' @export 50 | #' 51 | all_easylinear.formula <- function(formula, data, h = 5, quota = 0.95, 52 | subset = NULL, ...) { 53 | 54 | ## force data frame if user enters a tibble 55 | if (inherits(data, "tbl_df")) data <- as.data.frame(data) 56 | 57 | X <- get_all_vars(formula, data) 58 | if (!is.null(subset)) X <- X[subset, ] 59 | all_easylinear.data.frame(data = X, grouping = formula, h = h, quota = quota) 60 | } 61 | 62 | 63 | #' @rdname all_easylinear 64 | #' @export 65 | #' 66 | all_easylinear.data.frame <- 67 | function(data, grouping, time = "time", y = "value", h = 5, quota = 0.95, ...) { 68 | 69 | ## force data frame if user enters a tibble 70 | if (inherits(data, "tbl_df")) data <- as.data.frame(data) 71 | 72 | splitted.data <- multisplit(data, grouping) 73 | 74 | ## todo: consider to attach parsed formula as attr to splitted.data 75 | if (inherits(grouping, "formula")) { 76 | p <- parse_formula(grouping) 77 | time <- p$timevar 78 | y <- p$valuevar 79 | grouping <- p$groups 80 | } 81 | 82 | ## suppress warnings, esp. in case of "perfect fit" 83 | #fits <- lapply(splitted.data, 84 | # function(tmp) 85 | # suppressWarnings(fit_easylinear( 86 | # tmp[,time], tmp[,y], h = h, quota = quota 87 | # ))) 88 | 89 | ## suppress only "perfect fit" warning 90 | fits <- lapply(splitted.data, 91 | function(tmp) { 92 | withCallingHandlers({ 93 | fit_easylinear(tmp[,time], tmp[,y], h = h, quota = quota) 94 | }, warning = function(w) { 95 | if (startsWith(conditionMessage(w), 96 | "essentially perfect fit")) invokeRestart("muffleWarning") 97 | }) 98 | }) 99 | 100 | new("multiple_easylinear_fits", fits = fits, grouping = grouping) 101 | } 102 | -------------------------------------------------------------------------------- /man/all_splines.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/all_splines.R 3 | \name{all_splines} 4 | \alias{all_splines} 5 | \alias{all_splines.formula} 6 | \alias{all_splines.data.frame} 7 | \title{Fit Exponential Growth Model with Smoothing Spline} 8 | \usage{ 9 | all_splines(...) 10 | 11 | \method{all_splines}{formula}(formula, data = NULL, optgrid = 50, subset = NULL, ...) 12 | 13 | \method{all_splines}{data.frame}( 14 | data, 15 | grouping = NULL, 16 | time = "time", 17 | y = "value", 18 | optgrid = 50, 19 | ... 20 | ) 21 | } 22 | \arguments{ 23 | \item{\dots}{generic parameters, including parameters passed to \code{\link{smooth.spline}}, see details.} 24 | 25 | \item{formula}{model formula specifying dependent, independent and grouping 26 | variables in the form: 27 | \code{dependent ~ independent | group1 + group2 + \dots}.} 28 | 29 | \item{data}{data frame of observational data.} 30 | 31 | \item{optgrid}{number of steps on the x-axis used for searching the maximum 32 | of the first derivative of the spline. 33 | The default should work in most cases, as long as the data are equally spaced. 34 | A smaller number may lead to non-detectable speed-up, but has the risk that 35 | the search is trapped in a local minimum.} 36 | 37 | \item{subset}{a specification of the rows to be used: defaults to all rows.} 38 | 39 | \item{grouping}{vector of grouping variables defining subsets in the data frame.} 40 | 41 | \item{time}{character vectors with name independent variable.} 42 | 43 | \item{y}{character vector with name of dependent variable.} 44 | } 45 | \value{ 46 | object with parameters of the fit. 47 | } 48 | \description{ 49 | Determine maximum growth rates from log-linear part of the growth curve for 50 | a series of experiments by using smoothing splines. 51 | } 52 | \details{ 53 | The method was inspired by an algorithm of Kahm et al. (2010), 54 | with different settings and assumptions. In the moment, spline fitting 55 | is always done with log-transformed data, assuming exponential growth 56 | at the time point of the maximum of its first derivative. 57 | 58 | All the hard work is done by function \code{\link{smooth.spline}} from package 59 | \pkg{stats}, that is highly user configurable. Normally, smoothness is 60 | automatically determined via cross-validation. This works well in many cases, 61 | whereas manual adjustment is required otherwise, e.g. by setting \code{spar} 62 | to a fixed value \eqn{[0,1]} that also disables cross-validation. 63 | A typical case where cross validation does not work is, if time dependent 64 | measurements are taken as pseudoreplicates from the same experimental unit. 65 | } 66 | \examples{ 67 | 68 | data(bactgrowth) 69 | L <- all_splines(value ~ time | strain + conc + replicate, 70 | data = bactgrowth, spar = 0.5) 71 | 72 | #par(mfrow=c(4, 3)) 73 | plot(L) 74 | results <- results(L) 75 | xyplot(mumax ~ log(conc + 1)|strain, data=results) 76 | 77 | ## fit splines at lower grouping levels 78 | L2 <- all_splines(value ~ time | conc + strain, 79 | data = bactgrowth, spar = 0.5) 80 | plot(L2) 81 | 82 | ## total data set without any grouping 83 | L3 <- all_splines(value ~ time, 84 | data = bactgrowth, spar = 0.5) 85 | #par(mfrow=c(1, 1)) 86 | plot(L3) 87 | 88 | } 89 | \references{ 90 | Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 91 | 2010. grofit: Fitting Biological Growth Curves with R. 92 | Journal of Statistical Software, 33(7), 1-21, 93 | \doi{10.18637/jss.v033.i07} 94 | } 95 | \seealso{ 96 | Other fitting functions: 97 | \code{\link{all_easylinear}()}, 98 | \code{\link{all_growthmodels}()}, 99 | \code{\link{fit_easylinear}()}, 100 | \code{\link{fit_growthmodel}()}, 101 | \code{\link{fit_spline}()} 102 | } 103 | \concept{fitting functions} 104 | -------------------------------------------------------------------------------- /R/grow_twostep.R: -------------------------------------------------------------------------------- 1 | #' Twostep Growth Model 2 | #' 3 | #' System of two differential equations describing bacterial growth as two-step 4 | #' process of activation (or adaptation) and growth. 5 | #' 6 | #' The model is given as a system of two differential equations: 7 | #' 8 | #' \deqn{dy_i/dt = -kw * yi} 9 | #' \deqn{dy_a/dt = kw * yi + mumax * (1 - (yi + ya)/K) * ya} 10 | #' 11 | #' that are then numerically integrated ('simulated') according to time (t). The 12 | #' model assumes that the population consists of active (\eqn{y_a}) and inactive 13 | #' (\eqn{y_i}) cells so that the observed abundance is (\eqn{y = y_i + y_a}). 14 | #' Adapting inactive cells change to the active state with a first order 'wakeup' 15 | #' rate (\eqn{kw}). 16 | #' 17 | #' @param time actual time (for the ode) resp. vector of simulation time steps. 18 | #' @param y named vector with state of the system 19 | #' (yi, ya: abundance of inactive and active organisms, e.g. 20 | #' concentration of inactive resp. active cells). 21 | #' @param parms parameters of the two-step growth model: 22 | #' \itemize{ 23 | #' \item \code{yi, ya} initial abundance of active and inactive organisms, 24 | #' \item \code{kw} activation (``wakeup'') constant (1/time), 25 | #' \item \code{mumax} maximum growth rate (1/time), 26 | #' \item \code{K} carrying capacity (max. abundance). 27 | #' } 28 | #' @param \dots placeholder for additional parameters (for user-extended versions of this function) 29 | #' 30 | #' @return 31 | #' 32 | #' For \code{ode_twostep}: matrix containing the simulation outputs. 33 | #' The return value of has also class \code{deSolve}. 34 | #' 35 | #' For \code{grow_twostep}: vector of dependent variable (\code{y}): 36 | #' 37 | #' \itemize{ 38 | #' \item \code{time} time of the simulation 39 | #' \item \code{yi} concentration of inactive cells 40 | #' \item \code{ya} concentration of active cells 41 | #' \item \code{y} total cell concentration 42 | #' } 43 | #' 44 | #' @details Function \code{ode_twostep} is the system of differential equations, 45 | #' whereas \code{grow_twostep} runs a numerical simulation over time. 46 | #' 47 | #' A similar two-compartment model, but without the logistic term, was discussed by Baranyi (1998). 48 | #' 49 | #' 50 | #' @references 51 | #' 52 | #' Baranyi, J. (1998). Comparison of stochastic and deterministic concepts of bacterial lag. 53 | #' J. heor. Biol. 192, 403--408. 54 | #' 55 | #' @examples 56 | #' 57 | #' time <- seq(0, 30, length=200) 58 | #' parms <- c(kw = 0.1, mumax=0.2, K=0.1) 59 | #' y0 <- c(yi=0.01, ya=0.0) 60 | #' out <- ode(y0, time, ode_twostep, parms) 61 | #' 62 | #' plot(out) 63 | #' 64 | #' o <- grow_twostep(0:100, c(yi=0.01, ya=0.0, kw = 0.1, mumax=0.2, K=0.1)) 65 | #' plot(o) 66 | #' 67 | #' @family growth models 68 | #' 69 | #' @rdname grow_twostep 70 | #' @export ode_twostep 71 | #' 72 | ode_twostep <- function (time, y, parms, ...) { 73 | ## the differential equations 74 | with(as.list(c(parms, y)), { 75 | dyi <- -kw * yi 76 | dya <- kw * yi + mumax * (1 - (yi + ya)/K) * ya 77 | list(c(dyi, dya), y = unname(yi + ya)) 78 | }) 79 | } 80 | 81 | ## @rdname grow_twostep 82 | ## @export grow_twostep.R 83 | ## 84 | #grow_twostep.R <- function(time, parms, ...) { 85 | # ## assign parameters and solve differential equations 86 | # y0 <- parms[c("yi", "ya")] 87 | # parms <- parms[c("kw", "mumax", "K")] 88 | # out <- ode(y0, time, ode_twostep, parms, ...) 89 | #} 90 | 91 | #' @family growth models 92 | #' 93 | #' @rdname grow_twostep 94 | #' @export 95 | #' 96 | grow_twostep <- function(time, parms, ...) { 97 | ## assign parameters and solve differential equations 98 | #cat("compiled code running\n") 99 | y0 <- parms[c("yi", "ya")] 100 | parms <- parms[c("kw", "mumax", "K")] 101 | #cat(y0, "\n") 102 | #cat(parms, "\n") 103 | out <- ode(y0, time, func = "d_twostep", parms = parms, 104 | dllname = "growthrates", 105 | initfunc = "ini_twostep", nout = 1, outnames=c("y"), ...) 106 | 107 | } 108 | ## attach names of parameters as attributes 109 | attr(grow_twostep, "fname") <- c("grow_twostep") 110 | attr(grow_twostep, "pnames") <- c("yi","ya", "kw", "mumax", "K") 111 | class(grow_twostep) <- c("growthmodel", "function") 112 | 113 | 114 | -------------------------------------------------------------------------------- /R/aac_classes.R: -------------------------------------------------------------------------------- 1 | ### ============================================================================ 2 | ### The S4 classes 3 | ### ============================================================================ 4 | 5 | ### ---------------------------------------------------------------------------- 6 | ### Helper Classes 7 | ### ---------------------------------------------------------------------------- 8 | 9 | #' @include aaa_growthmodel-class.R aab_growthmodel-constructor.R 10 | 11 | setOldClass(c("modFit", "summary.modFit")) # classes from package FME 12 | 13 | setOldClass("smooth.spline") 14 | 15 | 16 | ### ---------------------------------------------------------------------------- 17 | ### S4 Classes Representing Single Fits 18 | ### ---------------------------------------------------------------------------- 19 | 20 | #' S4 Classes of Package \pkg{growthrates} 21 | #' 22 | #' 23 | #' \code{growthrates_fit}: top-level class representing a growthrates fit with 24 | #' any method. 25 | #' 26 | #' @slot FUN model function used. 27 | #' @slot fit results of the model fit. 28 | #' @slot obs observation data used for model fitting. 29 | #' @slot rsquared coefficient of determination. 30 | #' 31 | #' @rdname growthrates-classes 32 | #' @exportClass growthrates_fit 33 | #' 34 | setClass("growthrates_fit", 35 | representation( 36 | FUN = "function_growthmodel", 37 | fit = "ANY", 38 | obs = "data.frame", 39 | rsquared = "numeric" 40 | ) 41 | ) 42 | 43 | 44 | #' \code{nonlinear_fit}: single nonlinear growthrates fit with package FME. 45 | #' 46 | #' @slot par parameters of the fit. 47 | #' @rdname growthrates-classes 48 | #' @exportClass nonlinear_fit 49 | #' 50 | setClass("nonlinear_fit", 51 | representation( 52 | fit = "modFit", 53 | par = "numeric" ## fitted and fixed parms 54 | ), 55 | contains = "growthrates_fit" 56 | ) 57 | 58 | 59 | #' \code{easylinear_fit}: single fit from the ``growthrates made easy''-method. 60 | #' 61 | #' @slot ndx index values of the time points used (for \code{easylinear_fit}). 62 | #' @rdname growthrates-classes 63 | #' @exportClass easylinear_fit 64 | #' 65 | setClass("easylinear_fit", 66 | representation( 67 | fit = "lm_or_NULL", 68 | par = "numeric", 69 | ndx = "numeric" 70 | ), 71 | contains = "growthrates_fit" 72 | ) 73 | 74 | #' \code{spline_fit}: single fit with (optionally cross-validated) smoothing splines. 75 | #' 76 | #' @slot xy x and y values at the maximum of the spline. 77 | #' @rdname growthrates-classes 78 | #' @exportClass smooth.spline_fit 79 | #' 80 | setClass("smooth.spline_fit", 81 | representation( 82 | fit = "smooth.spline", 83 | xy = "numeric", 84 | par = "numeric" 85 | ), 86 | contains = "growthrates_fit" 87 | ) 88 | 89 | 90 | 91 | ### ---------------------------------------------------------------------------- 92 | ### S4 Classes Representing Multiple Fits 93 | ### ---------------------------------------------------------------------------- 94 | 95 | 96 | #' \code{multiple_fits}: top-level class representing multiple fits with 97 | #' any method. 98 | #' 99 | #' @rdname growthrates-classes 100 | #' @aliases multiple_fits 101 | #' @exportClass multiple_fits 102 | #' 103 | setClass("multiple_fits", 104 | representation( 105 | fits = "list", 106 | grouping = "character" 107 | ) 108 | ) 109 | 110 | 111 | #' \code{multiple_easylinear_fits}: class representing multiple fits with 112 | #' the ``growthrates made easy''-method. 113 | #' 114 | #' @rdname growthrates-classes 115 | #' @exportClass multiple_easylinear_fits 116 | #' 117 | setClass("multiple_easylinear_fits", 118 | contains = "multiple_fits" 119 | ) 120 | 121 | 122 | #' \code{multiple_nonlinear_fits}: class representing multiple nonlinear fits. 123 | #' 124 | #' @rdname growthrates-classes 125 | #' @exportClass multiple_nonlinear_fits 126 | #' 127 | setClass("multiple_nonlinear_fits", 128 | contains = "multiple_fits" 129 | ) 130 | 131 | #' \code{multiple_smooth.spline_fits}: class representing multiple smooth.spline fits. 132 | #' 133 | #' @rdname growthrates-classes 134 | #' @exportClass multiple_smooth.spline_fits 135 | #' 136 | setClass("multiple_smooth.spline_fits", 137 | contains = "multiple_fits" 138 | ) 139 | 140 | -------------------------------------------------------------------------------- /R/fit_spline.R: -------------------------------------------------------------------------------- 1 | #' Fit Exponential Growth Model with Smoothing Spline 2 | #' 3 | #' Determine maximum growth rates from the first derivative of a smoothing spline. 4 | #' 5 | #' 6 | #' @param time vector of independent variable. 7 | #' @param y vector of dependent variable (concentration of organisms). 8 | #' @param optgrid number of steps on the x-axis used for the optimum search . 9 | #' algorithm. The default should work in most cases, as long as the data are equally spaced. 10 | #' A smaller number may lead to non-detectable speed-up, but has the risk that 11 | #' the search gets trapped in a local minimum. 12 | #' @param \dots other parameters passed to \code{\link{smooth.spline}}, see details. 13 | #' 14 | #' @return object with parameters of the fit 15 | #' 16 | #' @details The method was inspired by an algorithm of Kahm et al. (2010), 17 | #' with different settings and assumptions. In the moment, spline fitting 18 | #' is always done with log-transformed data, assuming exponential growth 19 | #' at the time point of the maximum of the first derivative of the spline fit. 20 | #' 21 | #' All the hard work is done by function \code{\link{smooth.spline}} from package 22 | #' \pkg{stats}, that is highly user configurable. Normally, smoothness is 23 | #' automatically determined via cross-validation. This works well in many cases, 24 | #' whereas manual adjustment is required otherwise, e.g. by setting \code{spar} 25 | #' to a fixed value \eqn{[0, 1]} that also disables cross-validation. 26 | #' 27 | #' @references 28 | #' 29 | #' Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 30 | #' 2010. grofit: Fitting Biological Growth Curves with R. 31 | #' Journal of Statistical Software, 33(7), 1-21, 32 | #' \doi{10.18637/jss.v033.i07} 33 | #' 34 | #' @family fitting functions 35 | #' 36 | #' @examples 37 | #' 38 | #' data(bactgrowth) 39 | #' splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 40 | #' 41 | #' dat <- splitted.data[[2]] 42 | #' time <- dat$time 43 | #' y <- dat$value 44 | #' 45 | #' ## automatic smoothing with cv 46 | #' res <- fit_spline(time, y) 47 | #' 48 | #' plot(res, log="y") 49 | #' plot(res) 50 | #' coef(res) 51 | #' 52 | #' ## a more difficult data set 53 | #' dat <- splitted.data[[56]] 54 | #' time <- dat$time 55 | #' y <- dat$value 56 | #' 57 | #' ## default parameters 58 | #' res <- fit_spline(time, y) 59 | #' plot(res, log="y") 60 | #' 61 | #' ## small optgrid, trapped in local minimum 62 | #' res <- fit_spline(time, y, optgrid=5) 63 | #' plot(res, log="y") 64 | #' 65 | #' ## manually selected smoothing parameter 66 | #' res <- fit_spline(time, y, spar=.5) 67 | #' plot(res, log="y") 68 | #' plot(res, ylim=c(0.005, 0.03)) 69 | #' 70 | #' 71 | #' @rdname fit_spline 72 | #' @export fit_spline 73 | #' 74 | fit_spline <- function(time, y, optgrid = length(time), ...) { 75 | 76 | #if (any(duplicated(time))) stop("x variable must not contain duplicated values") 77 | 78 | obs <- data.frame(time = time, y = y) 79 | ylog <- log(obs$y) 80 | 81 | ## fit smoothing spline with cross validation by default 82 | spl <- smooth.spline(time, ylog, ...) 83 | 84 | xnew <- seq(min(time), max(time), length.out = optgrid) 85 | delta.xnew <- diff(xnew)[1] 86 | 87 | ynew <- predict(spl, x=xnew)$y 88 | ## 1st derivative for n = optgrid candidate values 89 | dspl <- predict(spl, x=xnew, deriv=1) 90 | 91 | ## find approximate maximum of 1st derivative 92 | tmax <- dspl$x[which.max(dspl$y)] 93 | 94 | ## post-optimize maximum 95 | optmax <- function(x) predict(spl, x = x, deriv = 1)$y 96 | 97 | ## set search interval to tmax + 2 * delta.xnew 98 | ## but limited by [min, max] of time 99 | interval <- tmax + c(-2, 2) * delta.xnew 100 | interval[1] <- max(min(time), interval[1]) 101 | interval[2] <- min(max(time), interval[2]) 102 | 103 | tmax2 <- optimize(f = optmax, interval = interval, 104 | maximum = TRUE)$maximum 105 | 106 | ## predict x, y and derivative at the maximum 107 | xy <- predict(spl, x = tmax2) 108 | dy <- predict(spl, x = tmax2, deriv = 1L)$y 109 | 110 | px <- xy$x; py <- xy$y 111 | 112 | ## Note: r2 applies to log transformed data 113 | r2 <- 1 - var(residuals(spl))/var(ylog) 114 | 115 | new("smooth.spline_fit", fit = spl, 116 | FUN = grow_exponential, 117 | par = c(y0 = exp(py)/exp((dy * px)), mumax = dy), 118 | xy = c(px, exp(py)), obs=obs, rsquared = r2) 119 | } 120 | -------------------------------------------------------------------------------- /R/multisplit.R: -------------------------------------------------------------------------------- 1 | #' Split Data Frame into Multiple Groups 2 | #' 3 | #' A data frame is split into a list of data subsets defined by multiple groups. 4 | #' 5 | #' @param data data frame, matrix or vector containing several subsets of data 6 | #' @param grouping either a character vector containing the names of the grouping variables 7 | #' or a model formula specifying dependent, 8 | #' independent and grouping variables in the form: 9 | #' \code{dependent ~ independent | group1 + group2 + ...}. 10 | #' It may also be a factor or list of factors as in \code{\link{split}}. 11 | #' @param drop if drop is TRUE, unused factor levels are dropped from the result. 12 | #' The default is to drop all factor levels. 13 | #' @param sep string to construct the new level labels by joining the 14 | #' constituent ones. 15 | #' @param \dots other parameters passed to \code{\link{split}}, see details. 16 | #' 17 | #' @details This function is wrapper around \code{\link{split}} with 18 | #' different defaults, slightly different behavior, and methods for additional 19 | #' argument classes. \code{multisplit} returns always a data frame. 20 | #' 21 | #' @return list containing data frames of the data subsets as its elements. 22 | #' The components of the list are named by their grouping levels. 23 | #' 24 | #' @seealso \code{\link{split}} 25 | #' 26 | #' @examples 27 | #' 28 | #' 29 | #'data(bactgrowth) 30 | #' 31 | #'## simple method 32 | #'spl <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 33 | #' 34 | #'## preferred method 35 | #'spl <- multisplit(bactgrowth, value ~ time | strain + conc + replicate) 36 | #' 37 | #'## show what is in one data set 38 | #'spl[[1]] 39 | #'summary(spl[[1]]) 40 | #' 41 | #'## use factor combination 42 | #'spl[["D:0:1"]] 43 | #'summary(spl[["D:0:1"]]) 44 | #' 45 | #' 46 | #'lapply(spl, FUN=function(x) 47 | #' plot(x$time, x$value, 48 | #' main=paste(x[1, "strain"], x[1, "conc"], x[1, "replicate"], sep=":"))) 49 | #' 50 | #' 51 | #' @rdname multisplit 52 | #' @keywords internal 53 | #' @exportMethod multisplit 54 | #' 55 | setMethod("multisplit", c("data.frame", "formula"), 56 | function(data, grouping, drop = TRUE, sep=":", ...) { 57 | if (missing(grouping) || (length(grouping) != 3L)) 58 | stop("'grouping' missing or incorrect") 59 | 60 | if (is.matrix(data)) 61 | data <- as.data.frame(data) 62 | if (!is.data.frame(data)) 63 | stop("'data' must be a data frame or matrix") 64 | 65 | p <- parse_formula(grouping) 66 | nm <- names(data) 67 | if (is.na(p$groups[1])) 68 | stop("grouping variable(s) missing") 69 | if (!all(c(p$valuevar, p$timevar) %in% names(data))) 70 | stop("dependent and independent variables must be column names of data") 71 | if (!all(p$groups %in% names(data))) 72 | stop("all grouping criteria must be column names of data") 73 | 74 | split(data[c(p$timevar, p$valuevar)], data[p$groups], 75 | drop = drop, sep = sep) 76 | }) 77 | 78 | 79 | #' @rdname multisplit 80 | #' @keywords internal 81 | #' @exportMethod multisplit 82 | #' 83 | setMethod("multisplit", c("data.frame", "character"), 84 | function(data, grouping, drop = TRUE, sep = ":", ...) { 85 | if (!all(grouping %in% names(data))) 86 | stop("Not all groups found in data frame") 87 | split(data, data[, grouping], drop = drop, sep = sep) 88 | }) 89 | 90 | #' @rdname multisplit 91 | #' @keywords internal 92 | #' @exportMethod multisplit 93 | #' 94 | setMethod("multisplit", c("data.frame", "factor"), 95 | function(data, grouping, drop = TRUE, sep = ":", ...) { 96 | split(data, list(grouping), drop = drop, sep = sep, ...) 97 | }) 98 | 99 | #' @rdname multisplit 100 | #' @keywords internal 101 | #' @exportMethod multisplit 102 | #' 103 | setMethod("multisplit", c("data.frame", "list"), 104 | function(data, grouping, drop = TRUE, sep = ":", ...) { 105 | split(data, grouping, drop = drop, sep = sep, ...) 106 | }) 107 | 108 | #' @rdname multisplit 109 | #' @keywords internal 110 | #' @exportMethod multisplit 111 | #' 112 | setMethod("multisplit", c("ANY", "ANY"), 113 | function(data, grouping, drop = TRUE, sep = ":", ...) { 114 | data <- as.data.frame(data) 115 | multisplit(data, grouping, drop = drop, sep = sep, ...) 116 | }) 117 | 118 | -------------------------------------------------------------------------------- /man/all_growthmodels.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/all_growthmodels.R 3 | \name{all_growthmodels} 4 | \alias{all_growthmodels} 5 | \alias{all_growthmodels.formula} 6 | \alias{all_growthmodels.function} 7 | \title{Fit Nonlinear Growth Models to Data Frame} 8 | \usage{ 9 | all_growthmodels(...) 10 | 11 | \method{all_growthmodels}{formula}( 12 | formula, 13 | data, 14 | p, 15 | lower = -Inf, 16 | upper = Inf, 17 | which = names(p), 18 | FUN = NULL, 19 | method = "Marq", 20 | transform = c("none", "log"), 21 | ..., 22 | subset = NULL, 23 | ncores = detectCores(logical = FALSE) 24 | ) 25 | 26 | \method{all_growthmodels}{`function`}( 27 | FUN, 28 | p, 29 | data, 30 | grouping = NULL, 31 | time = "time", 32 | y = "value", 33 | lower = -Inf, 34 | upper = Inf, 35 | which = names(p), 36 | method = "Marq", 37 | transform = c("none", "log"), 38 | ..., 39 | ncores = detectCores(logical = FALSE) 40 | ) 41 | } 42 | \arguments{ 43 | \item{\dots}{generic parameters, including parameters passed to the optimizer.} 44 | 45 | \item{formula}{model formula specifying dependent, independent and grouping 46 | variables in the form: 47 | \code{dependent ~ independent | group1 + group2 + \dots}.} 48 | 49 | \item{data}{data frame of observational data.} 50 | 51 | \item{p}{named vector of start parameters and initial values of the growth model.} 52 | 53 | \item{lower}{lower bound of the parameter vector.} 54 | 55 | \item{upper}{upper bound of the parameter vector.} 56 | 57 | \item{which}{vector of parameter names that are to be fitted.} 58 | 59 | \item{FUN}{function of growth model to be fitted.} 60 | 61 | \item{method}{character vector specifying the optimization algorithm.} 62 | 63 | \item{transform}{fit model to non-transformed or log-transformed data.} 64 | 65 | \item{subset}{a specification of the rows to be used: defaults to all rows.} 66 | 67 | \item{ncores}{number of CPU cores used for parallel computation. The number 68 | of real cores is detected automatically by default, 69 | but fort debugging purposes it could be wise to set \code{ncores = 1}. 70 | Usage of logical (hyperthreading) cores does not speed up computation.} 71 | 72 | \item{grouping}{vector of grouping variables defining subsets in the data frame.} 73 | 74 | \item{time}{character vector with name of independent variable.} 75 | 76 | \item{y}{character vector with name of dependent variable.} 77 | } 78 | \value{ 79 | object containing the parameters of all fits. 80 | } 81 | \description{ 82 | Determine maximum growth rates by nonlinear fits for 83 | a series of experiments. 84 | } 85 | \examples{ 86 | 87 | data(bactgrowth) 88 | splitted.data <- multisplit(value ~ time | strain + conc + replicate, 89 | data = bactgrowth) 90 | 91 | ## show which experiments are in splitted.data 92 | names(splitted.data) 93 | 94 | ## get table from single experiment 95 | dat <- splitted.data[["D:0:1"]] 96 | 97 | fit0 <- fit_spline(dat$time, dat$value) 98 | 99 | fit1 <- all_splines(value ~ time | strain + conc + replicate, 100 | data = bactgrowth, spar = 0.5) 101 | 102 | \donttest{ 103 | ## these examples require some CPU power and may take a bit longer 104 | 105 | ## initial parameters 106 | p <- c(coef(fit0), K = max(dat$value)) 107 | 108 | ## avoid negative parameters 109 | lower = c(y0 = 0, mumax = 0, K = 0) 110 | 111 | ## fit all models 112 | fit2 <- all_growthmodels(value ~ time | strain + conc + replicate, 113 | data = bactgrowth, FUN=grow_logistic, 114 | p = p, lower = lower, ncores = 2) 115 | 116 | results1 <- results(fit1) 117 | results2 <- results(fit2) 118 | plot(results1$mumax, results2$mumax, xlab="smooth splines", ylab="logistic") 119 | 120 | ## experimental: nonlinear model as part of the formula 121 | 122 | fit3 <- all_growthmodels( 123 | value ~ grow_logistic(time, parms) | strain + conc + replicate, 124 | data = bactgrowth, p = p, lower = lower, ncores = 2) 125 | 126 | ## this allows also to fit to the 'global' data set or any subsets 127 | fit4 <- all_growthmodels( 128 | value ~ grow_logistic(time, parms), 129 | data = bactgrowth, p = p, lower = lower, ncores = 1) 130 | plot(fit4) 131 | 132 | fit5 <- all_growthmodels( 133 | value ~ grow_logistic(time, parms) | strain + conc, 134 | data = bactgrowth, p = p, lower = lower, ncores = 2) 135 | plot(fit5) 136 | } 137 | 138 | } 139 | \seealso{ 140 | Other fitting functions: 141 | \code{\link{all_easylinear}()}, 142 | \code{\link{all_splines}()}, 143 | \code{\link{fit_easylinear}()}, 144 | \code{\link{fit_growthmodel}()}, 145 | \code{\link{fit_spline}()} 146 | } 147 | \concept{fitting functions} 148 | -------------------------------------------------------------------------------- /R/fit_easylinear.R: -------------------------------------------------------------------------------- 1 | #' Fit Exponential Growth Model with a Heuristic Linear Method 2 | #' 3 | #' Determine maximum growth rates from the log-linear part of a growth curve using 4 | #' a heuristic approach similar to the ``growth rates made easy''-method of 5 | #' Hall et al. (2013). 6 | #' 7 | #' The algorithm works as follows: 8 | #' \enumerate{ 9 | #' \item Fit linear regressions to all subsets of \code{h} consecutive data 10 | #' points. If for example \eqn{h=5}, fit a linear regression to points 11 | #' 1 \dots 5, 2 \dots 6, 3\dots 7 and so on. The method seeks the highest 12 | #' rate of exponential growth, so the dependent variable is of course 13 | #' log-transformed. 14 | #' \item Find the subset with the highest slope \eqn{b_{max}}{b_max} and 15 | #' include also the data points of adjacent subsets that have a slope of 16 | #' at least \eqn{quota \cdot b_{max}}{quota * b_max}, 17 | #' e.g. all data sets that have at least 95\% of the maximum slope. 18 | #' \item Fit a new linear model to the extended data window identified in step 2. 19 | #' } 20 | #' 21 | #' @param time vector of independent variable. 22 | #' @param y vector of dependent variable (concentration of organisms). 23 | #' @param h width of the window (number of data). 24 | #' @param quota part of window fits considered for the overall linear fit 25 | #' (relative to max. growth rate) 26 | #' 27 | #' @return object with parameters of the fit. The lag time is currently estimated 28 | #' as the intersection between the fit and the horizontal line with \eqn{y=y_0}, 29 | #' where \code{y0} is the first value of the dependent variable. The intersection 30 | #' of the fit with the abscissa is indicated as \code{y0_lm} (lm for linear model). 31 | #' These identifieres and their assumptions may change in future versions. 32 | #' 33 | #' @references Hall, BG., Acar, H, Nandipati, A and Barlow, M (2014) Growth Rates Made Easy. 34 | #' Mol. Biol. Evol. 31: 232-38, \doi{10.1093/molbev/mst187} 35 | #' 36 | #' @family fitting functions 37 | #' 38 | #' @examples 39 | #' data(bactgrowth) 40 | #' 41 | #' splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 42 | #' dat <- splitted.data[[1]] 43 | #' 44 | #' plot(value ~ time, data=dat) 45 | #' fit <- fit_easylinear(dat$time, dat$value) 46 | #' 47 | #' plot(fit) 48 | #' plot(fit, log="y") 49 | #' plot(fit, which="diagnostics") 50 | #' 51 | #' fitx <- fit_easylinear(dat$time, dat$value, h=8, quota=0.95) 52 | #' 53 | #' plot(fit, log="y") 54 | #' lines(fitx, pch="+", col="blue") 55 | #' 56 | #' plot(fit) 57 | #' lines(fitx, pch="+", col="blue") 58 | #' 59 | #' 60 | #' @rdname fit_easylinear 61 | #' @export fit_easylinear 62 | #' 63 | fit_easylinear <- function(time, y, h = 5, quota = 0.95) { 64 | 65 | if (any(duplicated(time))) stop("time variable must not contain duplicated values") 66 | if (any(y <= 0)) stop("dependent variable y must be positive") 67 | if ((h < 2) | (h > length(time)-1)) stop("h must be > 1 and < N") 68 | 69 | obs <- data.frame(time, y) 70 | obs$ylog <- log(obs$y) 71 | 72 | ## number of values 73 | N <- nrow(obs) 74 | 75 | ## repeat for all windows and save results in 'ret' 76 | ret <- matrix(0, nrow = N - h, ncol = 6) 77 | for(i in 1:(N - h)) { 78 | ret[i, ] <- c(i, with(obs, lm_parms(lm_window(time, ylog, i0 = i, h = h)))) 79 | } 80 | 81 | ## indices of windows with high growth rate 82 | slope.quota <- quota * max(ret[ ,3]) # "3" is slope (b) because "i" is 1st 83 | candidates <- which(ret[ ,3] >= slope.quota) 84 | 85 | if(length(candidates) > 0) { 86 | tp <- seq(min(candidates), max(candidates) + h-1) 87 | m <- lm_window(obs$time, obs$ylog, min(tp), length(tp)) 88 | p <- c(lm_parms(m), n=length(tp)) 89 | 90 | ## estimate lag phase 91 | ## todo: determine y0_data as average or median from multipe values 92 | y0_data <- y[1] 93 | y0_lm <- unname(exp(coef(m)[1])) 94 | mumax <- unname(coef(m)[2]) 95 | 96 | lag <- (log(y0_data) - log(y0_lm)) / mumax 97 | 98 | } else { 99 | warning(paste("no positively growing segment of length h >=", h, " found")) 100 | ## set crude defaults, check this 101 | y0_data <- y[1] 102 | y0_lm <- mean(y) # check this 103 | mumax <- NA 104 | lag <- NA 105 | tp <- 1:length(y) 106 | m <- NULL 107 | p <- c(a=y0_lm, b=NA, se=NA, r2=NA, cv=NA, n=NA) 108 | } 109 | 110 | obj <- new("easylinear_fit", 111 | FUN = grow_exponential, fit = m, 112 | par = c(y0 = y0_data, y0_lm = y0_lm, mumax = mumax, lag = lag), 113 | ndx = tp, 114 | obs = data.frame(time = obs$time, y = obs$y), 115 | rsquared = p["r2"]) 116 | invisible(obj) 117 | } 118 | -------------------------------------------------------------------------------- /R/grow_genlogistic.R: -------------------------------------------------------------------------------- 1 | #' Generalized Logistic Growth Model 2 | #' 3 | #' Generalized logistic growth model solved as differential equation. 4 | #' 5 | #' The model is given as its first derivative: 6 | #' 7 | #' \deqn{dy/dt = mumax * y^alpha * (1-(y/K)^beta)^gamma} 8 | #' 9 | #' that is then numerically integrated ('simulated') according to time (t). 10 | #' 11 | #' @param time vector of simulation time steps 12 | #' @param y named vector with initial value of the system (e.g. cell concentration) 13 | #' @param parms parameters of the generalized logistic growth model 14 | #' \itemize{ 15 | #' \item \code{mumax} maximum growth rate (1/time) 16 | #' \item \code{K} carrying capacity (max. abundance) 17 | #' \item \code{alpha, beta, gamma} parameters determining the shape of growth. 18 | #' Setting all values to one returns the ordinary logistic function. 19 | #' } 20 | #' @param \dots additional parameters passed to the \code{ode}-function. 21 | #' 22 | #' @return 23 | #' 24 | #' For \code{ode_genlogistic}: matrix containing the simulation outputs. 25 | #' The return value of has also class \code{deSolve}. 26 | #' 27 | #' For \code{grow_genlogistic}: vector of dependent variable (\code{y}). 28 | #' 29 | #' \itemize{ 30 | #' \item \code{time} time of the simulation 31 | #' \item \code{y} abundance of organisms 32 | #' } 33 | #' 34 | #' @details The generalized logistic according to Tsoularis (2001) is a flexible 35 | #' model that covers exponential and logistic growth, Richards, Gompertz, von 36 | #' Bertalanffy, and some more as special cases. 37 | #' 38 | #' The differential equation is solved numerically, where function 39 | #' \code{ode_genlogistic} is the differential equation, and 40 | #' \code{grow_genlogistic} runs a numerical simulation over time. 41 | #' 42 | #' The default version \code{grow_genlogistic} is run directly as compiled code, 43 | #' whereas the R versions \code{ode_logistic} is 44 | #' provided for testing by the user. 45 | #' 46 | #' @references 47 | #' Tsoularis, A. (2001) Analysis of Logistic Growth Models. 48 | #' Res. Lett. Inf. Math. Sci, (2001) 2, 23-46. 49 | #' 50 | #' @examples 51 | #' 52 | #' time <- seq(0, 30, length=200) 53 | #' parms <- c(mumax=0.5, K=10, alpha=1, beta=1, gamma=1) 54 | #' y0 <- c(y=.1) 55 | #' out <- ode(y0, time, ode_genlogistic, parms) 56 | #' plot(out) 57 | #' 58 | #' out2 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=2, beta=1, gamma=1)) 59 | #' out3 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=2, gamma=1)) 60 | #' out4 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=1, gamma=2)) 61 | #' out5 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=.5, beta=1, gamma=1)) 62 | #' out6 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.2, K=10, alpha=1, beta=.5, gamma=1)) 63 | #' out7 <- ode(y0, time, ode_genlogistic, parms = c(mumax=0.3, K=10, alpha=1, beta=1, gamma=.5)) 64 | #' plot(out, out2, out3, out4, out5, out6, out7) 65 | #' 66 | #' ## growth with lag (cf. log_y) 67 | #' plot(ode(y0, time, ode_genlogistic, parms = c(mumax=1, K=10, alpha=2, beta=.8, gamma=5))) 68 | #' 69 | #' 70 | #' @family growth models 71 | #' 72 | #' @rdname grow_genlogistic 73 | #' @export ode_genlogistic 74 | #' 75 | ode_genlogistic <- function (time, y, parms, ...) { 76 | ## the differential equations 77 | with(as.list(c(parms)), { 78 | dy <- mumax * y^alpha * (1-(y/K)^beta)^gamma 79 | list(dy) 80 | }) 81 | } 82 | 83 | # @rdname grow_genlogistic 84 | # @export grow_genlogistic.R 85 | # 86 | # grow_genlogistic.R <- function(time, parms, ...) { 87 | # ## assign parameters and solve differential equations 88 | # y0 <- c(y = unname(parms[c("y0")])) 89 | # parms <- parms[c("mumax", "K", "alpha", "beta", "gamma")] 90 | # out <- as.matrix(ode(y0, time, ode_genlogistic, parms, ...)) 91 | # } 92 | 93 | #' @rdname grow_genlogistic 94 | #' @export grow_genlogistic 95 | #' 96 | grow_genlogistic <- function(time, parms, ...) { 97 | ## assign parameters and solve differential equations 98 | #cat("compiled code running\n") 99 | y0 <- c(y = unname(parms[c("y0")])) 100 | parms <- parms[c("mumax", "K", "alpha", "beta", "gamma")] 101 | #cat(y0, "\n") 102 | #cat(parms, "\n") 103 | out <- ode(y0, time, func = "d_genlogistic", parms = parms, 104 | dllname = "growthrates", 105 | initfunc = "ini_genlogistic", nout = 0, ...) 106 | #cbind(out, log_y = log(out[,"y"])) 107 | out 108 | } 109 | ## attach names of parameters as attributes 110 | attr(grow_genlogistic, "fname") <- c("grow_genlogistic") 111 | attr(grow_genlogistic, "pnames") <- c("y0", "mumax", "K", "alpha", "beta", "gamma") 112 | class(grow_genlogistic) <- c("growthmodel", "function") 113 | 114 | 115 | -------------------------------------------------------------------------------- /R/all_splines.R: -------------------------------------------------------------------------------- 1 | #' Fit Exponential Growth Model with Smoothing Spline 2 | #' 3 | #' Determine maximum growth rates from log-linear part of the growth curve for 4 | #' a series of experiments by using smoothing splines. 5 | #' 6 | #' @param formula model formula specifying dependent, independent and grouping 7 | #' variables in the form: 8 | #' \code{dependent ~ independent | group1 + group2 + \dots}. 9 | #' @param data data frame of observational data. 10 | #' @param grouping vector of grouping variables defining subsets in the data frame. 11 | #' @param time character vectors with name independent variable. 12 | #' @param y character vector with name of dependent variable. 13 | #' @param optgrid number of steps on the x-axis used for searching the maximum 14 | #' of the first derivative of the spline. 15 | #' The default should work in most cases, as long as the data are equally spaced. 16 | #' A smaller number may lead to non-detectable speed-up, but has the risk that 17 | #' the search is trapped in a local minimum. 18 | #' @param subset a specification of the rows to be used: defaults to all rows. 19 | #' @param \dots generic parameters, including parameters passed to \code{\link{smooth.spline}}, see details. 20 | #' 21 | #' @return object with parameters of the fit. 22 | #' 23 | #' @details The method was inspired by an algorithm of Kahm et al. (2010), 24 | #' with different settings and assumptions. In the moment, spline fitting 25 | #' is always done with log-transformed data, assuming exponential growth 26 | #' at the time point of the maximum of its first derivative. 27 | #' 28 | #' All the hard work is done by function \code{\link{smooth.spline}} from package 29 | #' \pkg{stats}, that is highly user configurable. Normally, smoothness is 30 | #' automatically determined via cross-validation. This works well in many cases, 31 | #' whereas manual adjustment is required otherwise, e.g. by setting \code{spar} 32 | #' to a fixed value \eqn{[0,1]} that also disables cross-validation. 33 | #' A typical case where cross validation does not work is, if time dependent 34 | #' measurements are taken as pseudoreplicates from the same experimental unit. 35 | #' 36 | #' @references 37 | #' 38 | #' Kahm, M., Hasenbrink, G., Lichtenberg-Frate, H., Ludwig, J., Kschischo, M. 39 | #' 2010. grofit: Fitting Biological Growth Curves with R. 40 | #' Journal of Statistical Software, 33(7), 1-21, 41 | #' \doi{10.18637/jss.v033.i07} 42 | #' 43 | #' @family fitting functions 44 | #' 45 | #' @examples 46 | #' 47 | #' data(bactgrowth) 48 | #' L <- all_splines(value ~ time | strain + conc + replicate, 49 | #' data = bactgrowth, spar = 0.5) 50 | #' 51 | #' #par(mfrow=c(4, 3)) 52 | #' plot(L) 53 | #' results <- results(L) 54 | #' xyplot(mumax ~ log(conc + 1)|strain, data=results) 55 | #' 56 | #' ## fit splines at lower grouping levels 57 | #' L2 <- all_splines(value ~ time | conc + strain, 58 | #' data = bactgrowth, spar = 0.5) 59 | #' plot(L2) 60 | #' 61 | #' ## total data set without any grouping 62 | #' L3 <- all_splines(value ~ time, 63 | #' data = bactgrowth, spar = 0.5) 64 | #' #par(mfrow=c(1, 1)) 65 | #' plot(L3) 66 | #' 67 | #' @rdname all_splines 68 | #' @export 69 | #' 70 | all_splines <- function(...) UseMethod("all_splines") 71 | 72 | #' @rdname all_splines 73 | #' @export 74 | #' 75 | all_splines.formula <- function(formula, data=NULL, optgrid = 50, subset=NULL, ...) { 76 | 77 | ## force data frame if user enters a tibble 78 | if (inherits(data, "tbl_df")) data <- as.data.frame(data) 79 | 80 | dataset_name <- deparse(substitute(data)) # name of the dataset in the call 81 | X <- get_all_vars(formula, data) 82 | attr(X, "dataset_name") <- dataset_name 83 | 84 | if (!is.null(subset)) X <- X[subset, ] 85 | all_splines.data.frame(data = X, grouping = formula, optgrid = optgrid, ...) 86 | } 87 | 88 | #' @rdname all_splines 89 | #' @export 90 | #' 91 | all_splines.data.frame <- 92 | function(data, grouping=NULL, time = "time", y = "value", optgrid = 50, ...) { 93 | 94 | ## force data frame if user enters a tibble 95 | if (inherits(data, "tbl_df")) data <- as.data.frame(data) 96 | 97 | ## remember name of data set 98 | if (is.null(attr(data, "dataset_name"))) { # inherited from former method ? 99 | dataset_name <- deparse(substitute(data)) # get new one 100 | } else { 101 | dataset_name <- attr(data, "dataset_name") # take old one 102 | } 103 | 104 | ## check and parse grouping if formula 105 | if (inherits(grouping, "formula")) { 106 | parsed <- parse_formula(grouping) 107 | time <- parsed$timevar 108 | y <- parsed$valuevar 109 | grouping <- parsed$groups 110 | } 111 | 112 | ## missing groups => complete data handled as one group 113 | if (is.null(grouping)) { 114 | splitted.data <- list(data) 115 | names(splitted.data) <- dataset_name 116 | ndata <- 1 117 | } else { 118 | splitted.data <- multisplit(data, grouping) 119 | ndata <- length(splitted.data) 120 | } 121 | 122 | #splitted.data <- multisplit(data, grouping) 123 | 124 | ## supress warnings, esp. in case of "perfect fit" 125 | fits <- lapply(splitted.data, 126 | function(tmp) 127 | suppressWarnings(fit_spline(tmp[,time], tmp[,y], 128 | optgrid = optgrid, ...))) 129 | 130 | ## one fit without grouping 131 | if (is.null(grouping)) grouping <- dataset_name 132 | 133 | new("multiple_smooth.spline_fits", fits = fits, grouping = grouping) 134 | } 135 | -------------------------------------------------------------------------------- /R/predict.R: -------------------------------------------------------------------------------- 1 | ## ------------------------------------------------------------- 2 | ## predict-methods for top-level class growthrates_fit 3 | ## ------------------------------------------------------------- 4 | 5 | 6 | #' Model Predictions for \pkg{growthrates} Fits 7 | #' 8 | #' Class-specific methods of package \pkg{growthrates} to make predictions. 9 | #' 10 | #' @param object name of a 'growthrates' object for which prediction is desired. 11 | #' @param newdata an optional data frame with column 'time' for new time steps with 12 | #' which to predict. 13 | #' @param type type of predict. Can be \code{'exponential'} or \code{'spline'} for \code{fit_spline}, 14 | #' resp. \code{'exponential'} or \code{'no_lag'} for \code{fit_easylinear}. 15 | #' @param \dots additional arguments affecting the predictions produced. 16 | #' 17 | #' @rdname predict 18 | #' @export predict 19 | #' @exportMethod predict 20 | #' 21 | #' @details The implementation of the predict methods is still experimental and under discussion. 22 | #' 23 | #' @seealso \code{\link{methods}}, \code{\link{predict.smooth.spline}}, 24 | #' \code{\link{predict.lm}}, \code{\link{predict.nls}} 25 | 26 | #' 27 | #' @examples 28 | #' 29 | #' data(bactgrowth) 30 | #' splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 31 | #' 32 | #' ## get table from single experiment 33 | #' dat <- splitted.data[[1]] 34 | #' 35 | #' ## --- linear fit ----------------------------------------------------------- 36 | #' fit <- fit_easylinear(dat$time, dat$value) 37 | #' 38 | #' plot(fit) 39 | #' pr <- predict(fit) 40 | #' lines(pr[,1:2], col="blue", lwd=2, lty="dashed") 41 | #' 42 | #' pr <- predict(fit, newdata=list(time=seq(2, 6, .1)), type="no_lag") 43 | #' lines(pr[,1:2], col="magenta") 44 | #' 45 | #' 46 | #' ## --- spline fit ----------------------------------------------------------- 47 | #' fit1 <- fit_spline(dat$time, dat$value, spar=0.5) 48 | #' coef(fit1) 49 | #' summary(fit1) 50 | #' 51 | #' plot(fit1) 52 | #' pr <- predict(fit1) 53 | #' lines(pr[,1:2], lwd=2, col="blue", lty="dashed") 54 | #' pr <- predict(fit1, newdata=list(time=2:10), type="spline") 55 | #' lines(pr[,1:2], lwd=2, col="cyan") 56 | #' 57 | #' 58 | #' ## --- nonlinear fit -------------------------------------------------------- 59 | #' dat <- splitted.data[["T:0:2"]] 60 | #' 61 | #' p <- c(y0 = 0.02, mumax = .5, K = 0.05, h0 = 1) 62 | #' fit2 <- fit_growthmodel(grow_baranyi, p=p, time=dat$time, y=dat$value) 63 | #' 64 | #' ## prediction for given data 65 | #' predict(fit2) 66 | #' 67 | #' ## prediction for new data 68 | #' pr <- predict(fit2, newdata=data.frame(time=seq(0, 50, 0.1))) 69 | #' 70 | #' plot(fit2, xlim=c(0, 50)) 71 | #' lines(pr[, c("time", "y")], lty="dashed", col="red") 72 | #' @exportMethod predict 73 | #' 74 | setMethod("predict", "growthrates_fit", 75 | function(object, ...) { 76 | cat("class: ", class(object), "\n") 77 | cat("Sorry, this case is not yet implemented.") 78 | } 79 | ) 80 | 81 | 82 | #' @rdname predict 83 | #' @exportMethod predict 84 | #' 85 | setMethod("predict", "smooth.spline_fit", 86 | function(object, newdata=NULL, ..., type=c("exponential", "spline")) { 87 | ## todo: implement two kinds of predict 88 | ## 1) raw: residuals of the underlying spline method 89 | ## 2) model: residuals of the (exponential) growth function 90 | 91 | type <- match.arg(type) 92 | 93 | if (is.null(newdata)){ 94 | newdata <- list(time = object@obs$time) 95 | } 96 | if (type=="spline"){ 97 | ## residuals of the spline 98 | xy <- stats::predict(object@fit$fit, x = newdata$time, ...) # calls predict.smooth.spline 99 | xy <- data.frame( 100 | time = xy$x, 101 | y = exp(xy$y) 102 | ) 103 | } else { # exponential 104 | xy <- object@FUN(newdata$time, coef(object))[,1:2] 105 | 106 | } 107 | xy 108 | } 109 | ) 110 | 111 | #' @rdname predict 112 | #' @exportMethod predict 113 | #' 114 | setMethod("predict", "easylinear_fit", 115 | function(object, newdata=NULL, ..., type=c("exponential", "no_lag")) { 116 | 117 | type <- match.arg(type) 118 | 119 | if (is.null(newdata)){ 120 | newdata <- list(time = object@obs$time) 121 | } 122 | 123 | x <- newdata$time 124 | p <- coef(object) 125 | 126 | if (type == "no_lag") { 127 | y <- p["y0"] * exp(x * p["mumax"]) 128 | } else { # "exponential" 129 | y <- p["y0_lm"] * exp(x * p["mumax"]) 130 | } 131 | data.frame( 132 | time = x, 133 | y = y 134 | ) 135 | } 136 | ) 137 | 138 | 139 | 140 | 141 | #' @rdname predict 142 | #' @exportMethod predict 143 | #' 144 | setMethod("predict", "nonlinear_fit", 145 | function(object, newdata, ...) { 146 | ## todo: check other options (e.g. log) 147 | obs <- obs(object) 148 | 149 | if (missing(newdata) || is.null(newdata)) { 150 | time <- obs$time 151 | } else { 152 | time <- newdata$time 153 | } 154 | y <- object@FUN(time, coef(object)) 155 | 156 | ## todo: check type of return parameters for consistency with 157 | ## other functions, especially "residuals" 158 | ## now it returns a data frame with time, y 159 | y 160 | } 161 | ) 162 | 163 | 164 | 165 | #' @rdname predict 166 | #' @exportMethod predict 167 | #' 168 | setMethod("predict", "multiple_fits", 169 | function(object, ...) { 170 | lapply(object@fits, predict, ...) 171 | } 172 | ) 173 | 174 | -------------------------------------------------------------------------------- /R/fit_growthmodel.R: -------------------------------------------------------------------------------- 1 | #' Fit Nonlinear Parametric Growth Model 2 | #' 3 | #' Determine maximum growth rates by fitting nonlinear models. 4 | #' 5 | #' 6 | #' @param FUN function of growth model to be fitted. 7 | #' @param p named vector of start parameters and initial values of the growth model. 8 | #' @param time vector of independent variable. 9 | #' @param y vector of dependent variable (concentration of organisms). 10 | #' @param lower lower bound of the parameter vector (optional). 11 | #' @param upper upper bound of the parameter vector (optional). 12 | #' @param which vector of parameter names that are to be fitted. 13 | #' @param method character vector specifying the optimization algorithm (see \code{\link[FME]{modFit}}). 14 | #' @param transform fit model to non-transformed or log-transformed data. 15 | #' @param control A list of control parameters for the optimizers. See Details. 16 | #' @param \dots additional parameters passed to the optimizer. 17 | #' 18 | #' @return object with parameters of the fit. 19 | #' 20 | #' @details This function calls \code{\link[FME]{modFit}} from package \pkg{FME}. 21 | #' Syntax of control parameters and available options may differ, depending 22 | #' on the optimizer used, except \code{control=list(trace=...)} that switches 23 | #' tracing on and off for all methods and is either \code{TRUE}, or \code{FALSE}, 24 | #' or an integer value like 0, 1, 2, 3, depending on the optimizer. 25 | #' 26 | #' @family fitting functions 27 | #' @seealso \code{\link[FME]{modFit}} about constrained fitting of models to data 28 | #' 29 | #' @examples 30 | #' 31 | #' data(bactgrowth) 32 | #' splitted.data <- multisplit(bactgrowth, c("strain", "conc", "replicate")) 33 | #' 34 | #' ## get one element either by index or by name 35 | #' dat <- splitted.data[[1]] 36 | #' dat <- splitted.data[["D:0:1"]] 37 | #' 38 | #' p <- c(y0 = 0.01, mumax = 0.2, K = 0.1) 39 | #' 40 | #' ## unconstraied fitting 41 | #' fit1 <- fit_growthmodel(FUN = grow_logistic, p = p, dat$time, dat$value) 42 | #' coef(fit1) 43 | #' summary(fit1) 44 | #' 45 | #' ## optional box-constraints 46 | #' lower <- c(y0 = 1e-6, mumax = 0, K = 0) 47 | #' upper <- c(y0 = 0.05, mumax = 5, K = 0.5) 48 | #' fit1 <- fit_growthmodel( 49 | #' FUN = grow_logistic, p = p, dat$time, dat$value, 50 | #' lower = lower, upper = upper) 51 | #' 52 | #' plot(fit1, log="y") 53 | #' 54 | #' @export fit_growthmodel 55 | #' 56 | fit_growthmodel <- function(FUN, p, time, y, lower = -Inf, upper = Inf, 57 | which = names(p), 58 | method = "Marq", transform=c("none", "log"), 59 | control = NULL, ...) { 60 | 61 | transform <- match.arg(transform) 62 | 63 | #if (any(duplicated(time))) 64 | # stop("x variable must not contain duplicated values") 65 | 66 | ## split parameter vector in fitted and non-fitted 67 | parnames <- names(p) 68 | 69 | if (length(union(parnames, c(parnames, which))) > length(parnames)) 70 | warning("Names in 'which' that do not occur in p") 71 | 72 | fixed.p <- p[setdiff(parnames, which)] 73 | parms <- p[intersect(parnames, which)] 74 | 75 | ## todo: check case of lower and upper = 1 76 | if (length(fixed.p) & (length(lower) > 1)) { 77 | lower = lower[intersect(parnames, which)] 78 | } 79 | if (length(fixed.p) & (length(upper) > 1)) { 80 | upper = upper[intersect(parnames, which)] 81 | } 82 | 83 | if(!length(parms)) stop("No fitting parameters given. 'which' is empty or wrong") 84 | 85 | ## create data frame with names matching between model and data 86 | if (transform == "log") { 87 | #obs <- data.frame(time = time, y=y, log_y = log(y)) ## wrong! 88 | obs <- data.frame(time = time, log_y = log(y)) 89 | } else { 90 | obs <- data.frame(time = time, y = y) 91 | } 92 | 93 | ## handle trace. this control parameter is named inconsistently between the 94 | ## methods, so we try to make this unique. other controls are even more diverse 95 | 96 | ## optim-methods: "Nelder-Mead", "BFGS", "CG", "L-BFGS-B", "SANN" 97 | ## "trace" as integer: leave as is 98 | 99 | ## nlminb, "Port": trace as integer: leave as is 100 | 101 | ## nlm, "Newton": print.level as integer 102 | if (method %in% c("Newton")) 103 | control <- renameListElement(control, "trace", "print.level") 104 | 105 | ## nls.lm, "Marq": nprint as integer 106 | if (method %in% c("Marq")) 107 | control <- renameListElement(control, "trace", "nprint") 108 | 109 | ## minqa, "bobyqa": iprint can be 0, 1, 2, 3 110 | if (method %in% c("bobyqa")) 111 | control <- renameListElement(control, "trace", "iprint") 112 | 113 | ## FME, "Pseudo": verbose = TRUE/FALSE 114 | if (method %in% c("Pseudo")) 115 | control <- renameListElement(control, "trace", "verbose") 116 | 117 | ## fit model; log-transformed data need box constraints 118 | ## todo: redefine functions to allow unconstrained log-transformed parameters 119 | fit <- modFit(f = cost, p = parms, FUN = FUN, obs = obs, 120 | lower = lower, upper = upper, 121 | method = method, fixed.p = fixed.p, control = control, transform = transform, ...) 122 | 123 | parms <- coef(fit) 124 | out.fit <- FUN(obs$time, c(parms, fixed.p)) 125 | 126 | 127 | ## Note r2 in case of log-transformed values 128 | if (transform == "log") { 129 | yobs <- obs$log_y 130 | } else { 131 | yobs <- obs$y 132 | } 133 | 134 | r2 <- 1 - var(residuals(fit))/var(yobs) 135 | RSS <- sum(residuals(fit)^2) 136 | #cat(coef(fit), "r2=", r2, "\n") 137 | 138 | #return(list(fit=fit, out = out.fit, coef = coef(fit), obs=obs, RSS=RSS, r2=r2)) 139 | 140 | ## par contains fitted and fixed parameters in the original order 141 | obj <- new("nonlinear_fit", FUN = FUN, fit = fit, obs = obs, 142 | par = c(parms, fixed.p)[parnames], rsquared = r2) 143 | invisible(obj) 144 | } 145 | -------------------------------------------------------------------------------- /man/extcoef_logistic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/extcoef_logistic.R 3 | \name{extcoef_logistic} 4 | \alias{extcoef_logistic} 5 | \title{Extended Set of Coefficients of a Logistic Growth Model} 6 | \usage{ 7 | extcoef_logistic(object, quantile = 0.95, time = NULL, ...) 8 | } 9 | \arguments{ 10 | \item{object}{model object fited by \code{fit_growthmodel}} 11 | 12 | \item{quantile}{fraction of the capacity parameter (\code{K}) for the quantile method} 13 | 14 | \item{time}{2-valued vector of the search interval for the independent 15 | variable (\code{time}). 16 | Note: this needs to be set this manually if saturation is not 17 | reached within the observation time period taken from the data.} 18 | 19 | \item{...}{reserved for future extensions} 20 | } 21 | \value{ 22 | vector that contains the fitted parameters and some 23 | derived characteristics (extended parameters) of the logistic 24 | function. 25 | } 26 | \description{ 27 | Estimate model-specific derived parameters of the logistic growth 28 | model 29 | } 30 | \details{ 31 | This function returns the estimated parameters of a logistic growth model 32 | (\code{y0}, \code{mumax}, \code{K}) and a series of estimates for the time 33 | of approximate saturation. 34 | The estimates are defined as follows: 35 | \itemize{ 36 | \item \code{turnpoint}: time of turnpoint (50\% saturation) 37 | \item \code{sat1}: time of the minimum of the 2nd derivative 38 | \item \code{sat2}: time of the intercept between the steepest increase 39 | (the tangent at \code{mumax}) and the carrying capacity \code{K} 40 | \item \code{sat3}: time when a quantile of \code{K} (default 0.95) 41 | is reached 42 | } 43 | 44 | This function is normally not directly called by the user. 45 | It is usually called indirectly from \code{coef} or \code{results} if 46 | \code{extended=TRUE}. 47 | } 48 | \note{ 49 | The estimates for the turnpoint and the time of approximate saturation 50 | (\code{sat1}, \code{sat2}, \code{sat3}) may be unreliable, if saturation 51 | is not reached within the observation time period. See example below. 52 | A set of extended parameters exists currently only for the standard logistic 53 | growth model (\code{grow_logistic}). 54 | The code and naming of the parameters is preliminary and may change in 55 | future versions. 56 | } 57 | \examples{ 58 | 59 | ## ========================================================================= 60 | ## The 'extended parameters' are usually derived 61 | ## ========================================================================= 62 | 63 | data(antibiotic) 64 | 65 | ## fit a logistic model to a single data set 66 | dat <- subset(antibiotic, conc==0.078 & repl=="R4") 67 | 68 | parms <- c(y0=0.01, mumax=0.2, K=0.5) 69 | fit <- fit_growthmodel(grow_logistic, parms, dat$time, dat$value) 70 | coef(fit, extended=TRUE) 71 | 72 | ## fit the logistic to all data sets 73 | myData <- subset(antibiotic, repl=="R3") 74 | parms <- c(y0=0.01, mumax=0.2, K=0.5) 75 | all <- all_growthmodels(value ~ time | conc, 76 | data = myData, FUN=grow_logistic, 77 | p = parms, ncores = 2) 78 | 79 | 80 | # par(mfrow=c(3,4)) 81 | plot(all) 82 | results(all, extended=TRUE) 83 | ## we see that the the last 3 series (10...12) do not go into saturation 84 | ## within the observation time period. 85 | 86 | ## We can try to extend the search range: 87 | results(all[10:12], extended=TRUE, time=c(0, 5000)) 88 | 89 | 90 | ## ========================================================================= 91 | ## visualisation how the 'extended parameters' are derived 92 | ## ========================================================================= 93 | 94 | # Derivatives of the logistic: 95 | # The 1st and 2nd derivatives are internal functions of the package. 96 | # They are used here for the visualisation of the algorithm. 97 | 98 | deriv1 <- function(time, y0, mumax, K) { 99 | ret <- (K*mumax*y0*(K - y0)*exp(mumax * time))/ 100 | ((K + y0 * (exp(mumax * time) - 1))^2) 101 | unname(ret) 102 | } 103 | 104 | deriv2 <- function(time, y0, mumax, K) { 105 | ret <- -(K * mumax^2 * y0 * (K - y0) * exp(mumax * time) * 106 | (-K + y0 * exp(mumax * time) + y0))/ 107 | (K + y0 * (exp(mumax * time) - 1))^3 108 | unname(ret) 109 | } 110 | ## ========================================================================= 111 | 112 | data(bactgrowth) 113 | ## extract one growth experiment by name 114 | dat <- multisplit(bactgrowth, c("strain", "conc", "replicate"))[["D:0:1"]] 115 | 116 | 117 | ## unconstraied fitting 118 | p <- c(y0 = 0.01, mumax = 0.2, K = 0.1) # start parameters 119 | fit1 <- fit_growthmodel(FUN = grow_logistic, p = p, dat$time, dat$value) 120 | summary(fit1) 121 | p <- coef(fit1, extended=TRUE) 122 | 123 | ## copy parameters to separate variables to improve readability ------------ 124 | y0 <- p["y0"] 125 | mumax <- p["mumax"] 126 | K <- p["K"] 127 | turnpoint <- p["turnpoint"] 128 | sat1 <- p["sat1"] # 2nd derivative 129 | sat2 <- p["sat2"] # intercept between steepest increase and K 130 | sat3 <- p["sat3"] # a given quantile of K, default 95\\% 131 | 132 | ## show saturation values in growth curve and 1st and 2nd derivatives ------ 133 | opar <- par(no.readonly=TRUE) 134 | par(mfrow=c(3, 1), mar=c(4,4,0.2,0)) 135 | plot(fit1) 136 | 137 | ## 95\% saturation 138 | abline(h=0.95*K, col="magenta", lty="dashed") 139 | 140 | ## Intercept between steepest increase and 100\% saturation 141 | b <- deriv1(turnpoint, y0, mumax, K) 142 | a <- K/2 - b*turnpoint 143 | abline(a=a, b=b, col="orange", lty="dashed") 144 | abline(h=K, col="orange", lty="dashed") 145 | points(sat2, K, pch=16, col="orange") 146 | points(turnpoint, K/2, pch=16, col="blue") 147 | 148 | ## sat2 is the minimum of the 2nd derivative 149 | abline(v=c(turnpoint, sat1, sat2, sat3), 150 | col=c("blue", "grey", "orange", "magenta"), lty="dashed") 151 | 152 | ## plot the derivatives 153 | with(dat, plot(time, deriv1(time, y0, mumax, K), type="l", ylab="y'")) 154 | abline(v=c(turnpoint, sat1), col=c("blue", "grey"), lty="dashed") 155 | 156 | with(dat, plot(time, deriv2(time, y0, mumax, K), type="l", ylab="y''")) 157 | abline(v=sat1, col="grey", lty="dashed") 158 | par(opar) 159 | 160 | } 161 | --------------------------------------------------------------------------------