diff options
| author | Johannes Ranke <jranke@uni-bremen.de> | 2019-11-09 01:05:51 +0100 | 
|---|---|---|
| committer | Johannes Ranke <jranke@uni-bremen.de> | 2019-11-09 01:05:51 +0100 | 
| commit | 20b9c584e7c43ecbb708459e531c24a1a4751e17 (patch) | |
| tree | a0dd523fc6cb60e33420b0eb9bf79307e5b2a2a4 | |
| parent | ead1f286271923f57d83aed41cb34181a10773ef (diff) | |
Add a lack-of-fit test
- Switch an example dataset in the test setup to a dataset with
replicates, adapt tests
- Skip the test for lrtest with an update specification as it does not
only fail when pkgdown generates static help pages, but also in testthat
| -rw-r--r-- | NAMESPACE | 5 | ||||
| -rw-r--r-- | NEWS.md | 2 | ||||
| -rw-r--r-- | R/loftest.R | 112 | ||||
| -rw-r--r-- | R/logLik.mkinfit.R | 11 | ||||
| -rw-r--r-- | _pkgdown.yml | 1 | ||||
| -rw-r--r-- | docs/news/index.html | 1 | ||||
| -rw-r--r-- | docs/reference/index.html | 6 | ||||
| -rw-r--r-- | docs/reference/loftest-1.png | bin | 0 -> 27354 bytes | |||
| -rw-r--r-- | docs/reference/loftest-2.png | bin | 0 -> 27721 bytes | |||
| -rw-r--r-- | docs/reference/loftest-3.png | bin | 0 -> 65409 bytes | |||
| -rw-r--r-- | docs/reference/loftest-4.png | bin | 0 -> 64457 bytes | |||
| -rw-r--r-- | docs/reference/loftest-5.png | bin | 0 -> 63057 bytes | |||
| -rw-r--r-- | docs/reference/loftest.html | 343 | ||||
| -rw-r--r-- | docs/sitemap.xml | 3 | ||||
| -rw-r--r-- | man/loftest.Rd | 81 | ||||
| -rw-r--r-- | test.log | 26 | ||||
| -rw-r--r-- | tests/testthat/FOCUS_2006_D.csf | 2 | ||||
| -rw-r--r-- | tests/testthat/setup_script.R | 10 | ||||
| -rw-r--r-- | tests/testthat/test_confidence.R | 7 | ||||
| -rw-r--r-- | tests/testthat/test_tests.R | 22 | 
20 files changed, 605 insertions, 27 deletions
| @@ -4,6 +4,7 @@ S3method("[",mmkin)  S3method(AIC,mmkin)  S3method(BIC,mmkin)  S3method(confint,mkinfit) +S3method(loftest,mkinfit)  S3method(logLik,mkinfit)  S3method(lrtest,mkinfit)  S3method(mkinpredict,mkinfit) @@ -32,6 +33,7 @@ export(backtransform_odeparms)  export(endpoints)  export(ilr)  export(invilr) +export(loftest)  export(logistic.solution)  export(lrtest)  export(max_twa_dfop) @@ -73,8 +75,11 @@ importFrom(parallel,parLapply)  importFrom(stats,AIC)  importFrom(stats,BIC)  importFrom(stats,aggregate) +importFrom(stats,coef)  importFrom(stats,cov2cor)  importFrom(stats,dist) +importFrom(stats,dnorm) +importFrom(stats,lm)  importFrom(stats,logLik)  importFrom(stats,nlminb)  importFrom(stats,nobs) @@ -1,5 +1,7 @@  # mkin 0.9.49.8 (unreleased) +- 'loftest': Add a lack-of-fit test +  - 'plot_res', 'plot_sep' and 'mkinerrplot': Add the possibility to show standardized residuals and make it the default for fits with error models other than 'const'  - 'lrtest.mkinfit': Improve naming of the compared fits in the case of fixed parameters diff --git a/R/loftest.R b/R/loftest.R new file mode 100644 index 00000000..29721e23 --- /dev/null +++ b/R/loftest.R @@ -0,0 +1,112 @@ +#' Lack-of-fit test for models fitted to data with replicates +#' +#' This is a generic function with a method currently only defined for mkinfit +#' objects. It fits an anova model to the data contained in the object and +#' compares the likelihoods using the likelihood ratio test +#' \code{\link[lmtest]{lrtest.default}} from the lmtest package. +#' +#' The anova model is interpreted as the simplest form of an mkinfit model, +#' assuming only a constant variance about the means, but not enforcing any +#' structure of the means, so we have one model parameter for every mean +#' of replicate samples. +#' +#' @param object A model object with a defined loftest method +#' @param \dots Not used +#' @export +loftest <- function(object, ...) { +  UseMethod("loftest") +} + +#' @rdname loftest +#' @importFrom stats logLik lm dnorm coef +#' @seealso lrtest +#' @examples +#' \dontrun{ +#' test_data <- subset(synthetic_data_for_UBA_2014[[12]]$data, name == "parent") +#' sfo_fit <- mkinfit("SFO", test_data, quiet = TRUE) +#' plot_res(sfo_fit) # We see a clear pattern in the residuals +#' loftest(sfo_fit)  # We have a clear lack of fit +#' # +#' # We try a different model (the one that was used to generate the data) +#' dfop_fit <- mkinfit("DFOP", test_data, quiet = TRUE) +#' plot_res(dfop_fit) # We don't see systematic deviations, but heteroscedastic residuals +#' # therefore we should consider adapting the error model, although we have +#' loftest(dfop_fit) # no lack of fit +#' # +#' # This is the anova model used internally for the comparison +#' test_data_anova <- test_data +#' test_data_anova$time <- as.factor(test_data_anova$time) +#' anova_fit <- lm(value ~ time, data = test_data_anova) +#' summary(anova_fit) +#' logLik(anova_fit) # We get the same likelihood and degrees of freedom +#' # +#' test_data_2 <- synthetic_data_for_UBA_2014[[12]]$data +#' m_synth_SFO_lin <- mkinmod(parent = list(type = "SFO", to = "M1"), +#'   M1 = list(type = "SFO", to = "M2"), +#'   M2 = list(type = "SFO"), use_of_ff = "max") +#' sfo_lin_fit <- mkinfit(m_synth_SFO_lin, test_data_2, quiet = TRUE) +#' plot_res(sfo_lin_fit) # not a good model, we try parallel formation +#' loftest(sfo_lin_fit) +#' # +#' m_synth_SFO_par <- mkinmod(parent = list(type = "SFO", to = c("M1", "M2")), +#'   M1 = list(type = "SFO"), +#'   M2 = list(type = "SFO"), use_of_ff = "max") +#' sfo_par_fit <- mkinfit(m_synth_SFO_par, test_data_2, quiet = TRUE) +#' plot_res(sfo_par_fit) # much better for metabolites +#' loftest(sfo_par_fit) +#' # +#' m_synth_DFOP_par <- mkinmod(parent = list(type = "DFOP", to = c("M1", "M2")), +#'   M1 = list(type = "SFO"), +#'   M2 = list(type = "SFO"), use_of_ff = "max") +#' dfop_par_fit <- mkinfit(m_synth_DFOP_par, test_data_2, quiet = TRUE) +#' plot_res(dfop_par_fit) # No visual lack of fit +#' loftest(dfop_par_fit)  # no lack of fit found by the test +#' # +#' # The anova model used for comparison in the case of transformation products +#' test_data_anova_2 <- dfop_par_fit$data +#' test_data_anova_2$variable <- as.factor(test_data_anova_2$variable) +#' test_data_anova_2$time <- as.factor(test_data_anova_2$time) +#' anova_fit_2 <- lm(observed ~ time:variable - 1, data = test_data_anova_2) +#' summary(anova_fit_2) +#' } +#' @export +loftest.mkinfit <- function(object, ...) { + +  name_function <- function(x) { +    object_name <- paste(x$mkinmod$name, "with error model", x$err_mod) +    if (length(x$bparms.fixed) > 0) { +      object_name <- paste(object_name, +        "and fixed parameter(s)", +        paste(names(x$bparms.fixed), collapse = ", ")) +    } +    return(object_name) +  } + +  # Check if we have replicates in the data +  if (max(aggregate(object$data$observed, +    by = list(object$data$variable, object$data$time), length)$x) == 1) { +    stop("Not defined for fits to data without replicates") +  } + +  data_anova <- object$data +  data_anova$time <- as.factor(data_anova$time) +  data_anova$variable <- as.factor(data_anova$variable) +  if (nlevels(data_anova$variable) == 1) { +    object_2 <- lm(observed ~ time - 1, data = data_anova) +  } else { +    object_2 <- lm(observed ~ variable:time - 1, +      data = data_anova) +  } + +  object_2$mkinmod <- list(name = "ANOVA") +  object_2$err_mod <- "const" +  sigma_mle <- sqrt(sum(residuals(object_2)^2)/nobs(object_2)) +  object_2$logLik <- sum(dnorm(x = object_2$residuals, +      mean = 0, sd = sigma_mle, log = TRUE)) +  object_2$data <- object$data # to make the nobs.mkinfit method work +  object_2$bparms.optim <- coef(object_2) +  object_2$errparms <- 1 # We have estimated one error model parameter +  class(object_2) <- "mkinfit" + +  lmtest::lrtest.default(object_2, object, name = name_function) +} diff --git a/R/logLik.mkinfit.R b/R/logLik.mkinfit.R index cadc0d0a..1c025893 100644 --- a/R/logLik.mkinfit.R +++ b/R/logLik.mkinfit.R @@ -1,15 +1,15 @@  #' Calculated the log-likelihood of a fitted mkinfit object -#'  +#'  #' This function returns the product of the likelihood densities of each  #' observed value, as calculated as part of the fitting procedure using  #' \code{\link{dnorm}}, i.e. assuming normal distribution, and with the means  #' predicted by the degradation model, and the standard deviations predicted by  #' the error model. -#'  +#'  #' The total number of estimated parameters returned with the value of the  #' likelihood is calculated as the sum of fitted degradation model parameters  #' and the fitted error model parameters. -#'  +#'  #' @param object An object of class \code{\link{mkinfit}}.  #' @param \dots For compatibility with the generic method  #' @return An object of class \code{\link{logLik}} with the number of estimated @@ -19,7 +19,7 @@  #' @seealso Compare the AIC of columns of \code{\link{mmkin}} objects using  #'   \code{\link{AIC.mmkin}}.  #' @examples -#'  +#'  #'   \dontrun{  #'   sfo_sfo <- mkinmod(  #'     parent = mkinsub("SFO", to = "m1"), @@ -31,11 +31,12 @@  #'   f_tc <- mkinfit(sfo_sfo, d_t, error_model = "tc", quiet = TRUE)  #'   AIC(f_nw, f_obs, f_tc)  #'   } -#'  +#'  #' @export  logLik.mkinfit <- function(object, ...) {    val <- object$logLik    # Number of estimated parameters    attr(val, "df") <- length(object$bparms.optim) + length(object$errparms) +  class(val) <- "logLik"    return(val)  } diff --git a/_pkgdown.yml b/_pkgdown.yml index c222a817..c298256f 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -22,6 +22,7 @@ reference:        - confint.mkinfit        - update.mkinfit        - lrtest.mkinfit +      - loftest        - mkinerrmin        - endpoints        - CAKE_export diff --git a/docs/news/index.html b/docs/news/index.html index 48ba25e5..9aa2e18b 100644 --- a/docs/news/index.html +++ b/docs/news/index.html @@ -134,6 +134,7 @@  <a href="#mkin-0-9-49-8-unreleased" class="anchor"></a>mkin 0.9.49.8 (unreleased)<small> Unreleased </small>  </h1>  <ul> +<li><p>‘loftest’: Add a lack-of-fit test</p></li>  <li><p>‘plot_res’, ‘plot_sep’ and ‘mkinerrplot’: Add the possibility to show standardized residuals and make it the default for fits with error models other than ‘const’</p></li>  <li><p>‘lrtest.mkinfit’: Improve naming of the compared fits in the case of fixed parameters</p></li>  <li><p>‘confint.mkinfit’: Make the quadratic approximation the default, as the likelihood profiling takes a lot of time, especially if the fit has more than three parameters</p></li> diff --git a/docs/reference/index.html b/docs/reference/index.html index 1c9975e0..0947ff94 100644 --- a/docs/reference/index.html +++ b/docs/reference/index.html @@ -215,6 +215,12 @@ more datasets</p></td>        </tr><tr>          <td> +          <p><code><a href="loftest.html">loftest()</a></code> </p> +        </td> +        <td><p>Lack-of-fit test for models fitted to data with replicates</p></td> +      </tr><tr> +         +        <td>            <p><code><a href="mkinerrmin.html">mkinerrmin()</a></code> </p>          </td>          <td><p>Calculate the minimum error to assume in order to pass the variance test</p></td> diff --git a/docs/reference/loftest-1.png b/docs/reference/loftest-1.pngBinary files differ new file mode 100644 index 00000000..3d20f41e --- /dev/null +++ b/docs/reference/loftest-1.png diff --git a/docs/reference/loftest-2.png b/docs/reference/loftest-2.pngBinary files differ new file mode 100644 index 00000000..be8bf815 --- /dev/null +++ b/docs/reference/loftest-2.png diff --git a/docs/reference/loftest-3.png b/docs/reference/loftest-3.pngBinary files differ new file mode 100644 index 00000000..c66c95f1 --- /dev/null +++ b/docs/reference/loftest-3.png diff --git a/docs/reference/loftest-4.png b/docs/reference/loftest-4.pngBinary files differ new file mode 100644 index 00000000..da86d97f --- /dev/null +++ b/docs/reference/loftest-4.png diff --git a/docs/reference/loftest-5.png b/docs/reference/loftest-5.pngBinary files differ new file mode 100644 index 00000000..54b176e7 --- /dev/null +++ b/docs/reference/loftest-5.png diff --git a/docs/reference/loftest.html b/docs/reference/loftest.html new file mode 100644 index 00000000..757f0bbe --- /dev/null +++ b/docs/reference/loftest.html @@ -0,0 +1,343 @@ +<!-- Generated by pkgdown: do not edit by hand --> +<!DOCTYPE html> +<html lang="en"> +  <head> +  <meta charset="utf-8"> +<meta http-equiv="X-UA-Compatible" content="IE=edge"> +<meta name="viewport" content="width=device-width, initial-scale=1.0"> + +<title>Lack-of-fit test for models fitted to data with replicates — loftest • mkin</title> + + +<!-- jquery --> +<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script> +<!-- Bootstrap --> + +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" /> + +<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous"></script> + +<!-- Font Awesome icons --> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.7.1/css/all.min.css" integrity="sha256-nAmazAk6vS34Xqo0BSrTb+abbtFlgsFK7NKSi6o7Y78=" crossorigin="anonymous" /> +<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.7.1/css/v4-shims.min.css" integrity="sha256-6qHlizsOWFskGlwVOKuns+D1nB6ssZrHQrNj1wGplHc=" crossorigin="anonymous" /> + +<!-- clipboard.js --> +<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js" integrity="sha256-FiZwavyI2V6+EXO1U+xzLG3IKldpiTFf3153ea9zikQ=" crossorigin="anonymous"></script> + +<!-- headroom.js --> +<script src="https://cdnjs.cloudflare.com/ajax/libs/headroom/0.9.4/headroom.min.js" integrity="sha256-DJFC1kqIhelURkuza0AvYal5RxMtpzLjFhsnVIeuk+U=" crossorigin="anonymous"></script> +<script src="https://cdnjs.cloudflare.com/ajax/libs/headroom/0.9.4/jQuery.headroom.min.js" integrity="sha256-ZX/yNShbjqsohH1k95liqY9Gd8uOiE1S4vZc+9KQ1K4=" crossorigin="anonymous"></script> + +<!-- pkgdown --> +<link href="../pkgdown.css" rel="stylesheet"> +<script src="../pkgdown.js"></script> + + + + +<meta property="og:title" content="Lack-of-fit test for models fitted to data with replicates — loftest" /> +<meta property="og:description" content="This is a generic function with a method currently only defined for mkinfit +objects. It fits an anova model to the data contained in the object and +compares the likelihoods using the likelihood ratio test +lrtest.default from the lmtest package." /> +<meta name="twitter:card" content="summary" /> + + + + +<!-- mathjax --> +<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js" integrity="sha256-nvJJv9wWKEm88qvoQl9ekL2J+k/RWIsaSScxxlsrv8k=" crossorigin="anonymous"></script> +<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/config/TeX-AMS-MML_HTMLorMML.js" integrity="sha256-84DKXVJXs0/F8OTMzX4UR909+jtl4G7SPypPavF+GfA=" crossorigin="anonymous"></script> + +<!--[if lt IE 9]> +<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script> +<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> +<![endif]--> + + + +  </head> + +  <body> +    <div class="container template-reference-topic"> +      <header> +      <div class="navbar navbar-default navbar-fixed-top" role="navigation"> +  <div class="container"> +    <div class="navbar-header"> +      <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false"> +        <span class="sr-only">Toggle navigation</span> +        <span class="icon-bar"></span> +        <span class="icon-bar"></span> +        <span class="icon-bar"></span> +      </button> +      <span class="navbar-brand"> +        <a class="navbar-link" href="../index.html">mkin</a> +        <span class="version label label-default" data-toggle="tooltip" data-placement="bottom" title="Released version">0.9.49.8</span> +      </span> +    </div> + +    <div id="navbar" class="navbar-collapse collapse"> +      <ul class="nav navbar-nav"> +        <li> +  <a href="../reference/index.html">Functions and data</a> +</li> +<li class="dropdown"> +  <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false"> +    Articles +      +    <span class="caret"></span> +  </a> +  <ul class="dropdown-menu" role="menu"> +    <li> +      <a href="../articles/mkin.html">Introduction to mkin</a> +    </li> +    <li> +      <a href="../articles/FOCUS_D.html">Example evaluation of FOCUS Example Dataset D</a> +    </li> +    <li> +      <a href="../articles/FOCUS_L.html">Example evaluation of FOCUS Laboratory Data L1 to L3</a> +    </li> +    <li> +      <a href="../articles/web_only/FOCUS_Z.html">Example evaluation of FOCUS Example Dataset Z</a> +    </li> +    <li> +      <a href="../articles/web_only/compiled_models.html">Performance benefit by using compiled model definitions in mkin</a> +    </li> +    <li> +      <a href="../articles/twa.html">Calculation of time weighted average concentrations with mkin</a> +    </li> +    <li> +      <a href="../articles/web_only/NAFTA_examples.html">Example evaluation of NAFTA SOP Attachment examples</a> +    </li> +  </ul> +</li> +<li> +  <a href="../news/index.html">News</a> +</li> +      </ul> +      <ul class="nav navbar-nav navbar-right"> +         +      </ul> +       +    </div><!--/.nav-collapse --> +  </div><!--/.container --> +</div><!--/.navbar --> + +       + +      </header> + +<div class="row"> +  <div class="col-md-9 contents"> +    <div class="page-header"> +    <h1>Lack-of-fit test for models fitted to data with replicates</h1> +     +    <div class="hidden name"><code>loftest.Rd</code></div> +    </div> + +    <div class="ref-description"> +    <p>This is a generic function with a method currently only defined for mkinfit +objects. It fits an anova model to the data contained in the object and +compares the likelihoods using the likelihood ratio test +<code><a href='https://rdrr.io/pkg/lmtest/man/lrtest.html'>lrtest.default</a></code> from the lmtest package.</p> +    </div> + +    <pre class="usage"><span class='fu'>loftest</span>(<span class='no'>object</span>, <span class='no'>...</span>) + +<span class='co'># S3 method for mkinfit</span> +<span class='fu'>loftest</span>(<span class='no'>object</span>, <span class='no'>...</span>)</pre> + +    <h2 class="hasAnchor" id="arguments"><a class="anchor" href="#arguments"></a>Arguments</h2> +    <table class="ref-arguments"> +    <colgroup><col class="name" /><col class="desc" /></colgroup> +    <tr> +      <th>object</th> +      <td><p>A model object with a defined loftest method</p></td> +    </tr> +    <tr> +      <th>...</th> +      <td><p>Not used</p></td> +    </tr> +    </table> + +    <h2 class="hasAnchor" id="details"><a class="anchor" href="#details"></a>Details</h2> + +    <p>The anova model is interpreted as the simplest form of an mkinfit model, +assuming only a constant variance about the means, but not enforcing any +structure of the means, so we have one model parameter for every mean +of replicate samples.</p> +    <h2 class="hasAnchor" id="see-also"><a class="anchor" href="#see-also"></a>See also</h2> + +    <div class='dont-index'><p>lrtest</p></div> + +    <h2 class="hasAnchor" id="examples"><a class="anchor" href="#examples"></a>Examples</h2> +    <pre class="examples"><div class='input'><span class='co'># \dontrun{</span> +<span class='no'>test_data</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/base/subset.html'>subset</a></span>(<span class='no'>synthetic_data_for_UBA_2014</span><span class='kw'>[[</span><span class='fl'>12</span>]]$<span class='no'>data</span>, <span class='no'>name</span> <span class='kw'>==</span> <span class='st'>"parent"</span>) +<span class='no'>sfo_fit</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='st'>"SFO"</span>, <span class='no'>test_data</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) +<span class='fu'><a href='plot.mkinfit.html'>plot_res</a></span>(<span class='no'>sfo_fit</span>) <span class='co'># We see a clear pattern in the residuals</span></div><div class='img'><img src='loftest-1.png' alt='' width='700' height='433' /></div><div class='input'><span class='fu'>loftest</span>(<span class='no'>sfo_fit</span>)  <span class='co'># We have a clear lack of fit</span></div><div class='output co'>#> Likelihood ratio test +#>  +#> Model 1: ANOVA with error model const +#> Model 2: SFO with error model const +#>   #Df  LogLik Df  Chisq Pr(>Chisq)     +#> 1  10 -40.710                          +#> 2   3 -63.954 -7 46.487  7.027e-08 *** +#> --- +#> Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1</div><div class='input'><span class='co'>#</span> +<span class='co'># We try a different model (the one that was used to generate the data)</span> +<span class='no'>dfop_fit</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='st'>"DFOP"</span>, <span class='no'>test_data</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) +<span class='fu'><a href='plot.mkinfit.html'>plot_res</a></span>(<span class='no'>dfop_fit</span>) <span class='co'># We don't see systematic deviations, but heteroscedastic residuals</span></div><div class='img'><img src='loftest-2.png' alt='' width='700' height='433' /></div><div class='input'><span class='co'># therefore we should consider adapting the error model, although we have</span> +<span class='fu'>loftest</span>(<span class='no'>dfop_fit</span>) <span class='co'># no lack of fit</span></div><div class='output co'>#> Likelihood ratio test +#>  +#> Model 1: ANOVA with error model const +#> Model 2: DFOP with error model const +#>   #Df  LogLik Df Chisq Pr(>Chisq) +#> 1  10 -40.710                     +#> 2   5 -42.453 -5 3.485     0.6257</div><div class='input'><span class='co'>#</span> +<span class='co'># This is the anova model used internally for the comparison</span> +<span class='no'>test_data_anova</span> <span class='kw'><-</span> <span class='no'>test_data</span> +<span class='no'>test_data_anova</span>$<span class='no'>time</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/base/factor.html'>as.factor</a></span>(<span class='no'>test_data_anova</span>$<span class='no'>time</span>) +<span class='no'>anova_fit</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/stats/lm.html'>lm</a></span>(<span class='no'>value</span> ~ <span class='no'>time</span>, <span class='kw'>data</span> <span class='kw'>=</span> <span class='no'>test_data_anova</span>) +<span class='fu'><a href='https://rdrr.io/r/base/summary.html'>summary</a></span>(<span class='no'>anova_fit</span>)</div><div class='output co'>#>  +#> Call: +#> lm(formula = value ~ time, data = test_data_anova) +#>  +#> Residuals: +#>     Min      1Q  Median      3Q     Max  +#> -6.1000 -0.5625  0.0000  0.5625  6.1000  +#>  +#> Coefficients: +#>             Estimate Std. Error t value Pr(>|t|)     +#> (Intercept)  103.150      2.323  44.409 7.44e-12 *** +#> time1        -19.950      3.285  -6.073 0.000185 *** +#> time3        -50.800      3.285 -15.465 8.65e-08 *** +#> time7        -68.500      3.285 -20.854 6.28e-09 *** +#> time14       -79.750      3.285 -24.278 1.63e-09 *** +#> time28       -86.000      3.285 -26.181 8.35e-10 *** +#> time60       -94.900      3.285 -28.891 3.48e-10 *** +#> time90       -98.500      3.285 -29.986 2.49e-10 *** +#> time120     -100.450      3.285 -30.580 2.09e-10 *** +#> --- +#> Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 +#>  +#> Residual standard error: 3.285 on 9 degrees of freedom +#> Multiple R-squared:  0.9953,	Adjusted R-squared:  0.9912  +#> F-statistic: 240.5 on 8 and 9 DF,  p-value: 1.417e-09 +#> </div><div class='input'><span class='fu'><a href='https://rdrr.io/r/stats/logLik.html'>logLik</a></span>(<span class='no'>anova_fit</span>) <span class='co'># We get the same likelihood and degrees of freedom</span></div><div class='output co'>#> 'log Lik.' -40.71015 (df=10)</div><div class='input'><span class='co'>#</span> +<span class='no'>test_data_2</span> <span class='kw'><-</span> <span class='no'>synthetic_data_for_UBA_2014</span><span class='kw'>[[</span><span class='fl'>12</span>]]$<span class='no'>data</span> +<span class='no'>m_synth_SFO_lin</span> <span class='kw'><-</span> <span class='fu'><a href='mkinmod.html'>mkinmod</a></span>(<span class='kw'>parent</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>, <span class='kw'>to</span> <span class='kw'>=</span> <span class='st'>"M1"</span>), +  <span class='kw'>M1</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>, <span class='kw'>to</span> <span class='kw'>=</span> <span class='st'>"M2"</span>), +  <span class='kw'>M2</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>), <span class='kw'>use_of_ff</span> <span class='kw'>=</span> <span class='st'>"max"</span>)</div><div class='output co'>#> <span class='message'>Successfully compiled differential equation model from auto-generated C code.</span></div><div class='input'><span class='no'>sfo_lin_fit</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='no'>m_synth_SFO_lin</span>, <span class='no'>test_data_2</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) +<span class='fu'><a href='plot.mkinfit.html'>plot_res</a></span>(<span class='no'>sfo_lin_fit</span>) <span class='co'># not a good model, we try parallel formation</span></div><div class='img'><img src='loftest-3.png' alt='' width='700' height='433' /></div><div class='input'><span class='fu'>loftest</span>(<span class='no'>sfo_lin_fit</span>)</div><div class='output co'>#> Likelihood ratio test +#>  +#> Model 1: ANOVA with error model const +#> Model 2: m_synth_SFO_lin with error model const and fixed parameter(s) M1_0, M2_0 +#>   #Df   LogLik  Df  Chisq Pr(>Chisq)     +#> 1  28  -93.606                           +#> 2   7 -171.927 -21 156.64  < 2.2e-16 *** +#> --- +#> Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1</div><div class='input'><span class='co'>#</span> +<span class='no'>m_synth_SFO_par</span> <span class='kw'><-</span> <span class='fu'><a href='mkinmod.html'>mkinmod</a></span>(<span class='kw'>parent</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>, <span class='kw'>to</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"M1"</span>, <span class='st'>"M2"</span>)), +  <span class='kw'>M1</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>), +  <span class='kw'>M2</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>), <span class='kw'>use_of_ff</span> <span class='kw'>=</span> <span class='st'>"max"</span>)</div><div class='output co'>#> <span class='message'>Successfully compiled differential equation model from auto-generated C code.</span></div><div class='input'><span class='no'>sfo_par_fit</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='no'>m_synth_SFO_par</span>, <span class='no'>test_data_2</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) +<span class='fu'><a href='plot.mkinfit.html'>plot_res</a></span>(<span class='no'>sfo_par_fit</span>) <span class='co'># much better for metabolites</span></div><div class='img'><img src='loftest-4.png' alt='' width='700' height='433' /></div><div class='input'><span class='fu'>loftest</span>(<span class='no'>sfo_par_fit</span>)</div><div class='output co'>#> Likelihood ratio test +#>  +#> Model 1: ANOVA with error model const +#> Model 2: m_synth_SFO_par with error model const and fixed parameter(s) M1_0, M2_0 +#>   #Df   LogLik  Df  Chisq Pr(>Chisq)     +#> 1  28  -93.606                           +#> 2   7 -156.331 -21 125.45  < 2.2e-16 *** +#> --- +#> Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1</div><div class='input'><span class='co'>#</span> +<span class='no'>m_synth_DFOP_par</span> <span class='kw'><-</span> <span class='fu'><a href='mkinmod.html'>mkinmod</a></span>(<span class='kw'>parent</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"DFOP"</span>, <span class='kw'>to</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"M1"</span>, <span class='st'>"M2"</span>)), +  <span class='kw'>M1</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>), +  <span class='kw'>M2</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/list.html'>list</a></span>(<span class='kw'>type</span> <span class='kw'>=</span> <span class='st'>"SFO"</span>), <span class='kw'>use_of_ff</span> <span class='kw'>=</span> <span class='st'>"max"</span>)</div><div class='output co'>#> <span class='message'>Successfully compiled differential equation model from auto-generated C code.</span></div><div class='input'><span class='no'>dfop_par_fit</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='no'>m_synth_DFOP_par</span>, <span class='no'>test_data_2</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) +<span class='fu'><a href='plot.mkinfit.html'>plot_res</a></span>(<span class='no'>dfop_par_fit</span>) <span class='co'># No visual lack of fit</span></div><div class='img'><img src='loftest-5.png' alt='' width='700' height='433' /></div><div class='input'><span class='fu'>loftest</span>(<span class='no'>dfop_par_fit</span>)  <span class='co'># no lack of fit found by the test</span></div><div class='output co'>#> Likelihood ratio test +#>  +#> Model 1: ANOVA with error model const +#> Model 2: m_synth_DFOP_par with error model const and fixed parameter(s) M1_0, M2_0 +#>   #Df   LogLik  Df  Chisq Pr(>Chisq) +#> 1  28  -93.606                       +#> 2   9 -102.763 -19 18.313     0.5016</div><div class='input'><span class='co'>#</span> +<span class='co'># The anova model used for comparison in the case of transformation products</span> +<span class='no'>test_data_anova_2</span> <span class='kw'><-</span> <span class='no'>dfop_par_fit</span>$<span class='no'>data</span> +<span class='no'>test_data_anova_2</span>$<span class='no'>variable</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/base/factor.html'>as.factor</a></span>(<span class='no'>test_data_anova_2</span>$<span class='no'>variable</span>) +<span class='no'>test_data_anova_2</span>$<span class='no'>time</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/base/factor.html'>as.factor</a></span>(<span class='no'>test_data_anova_2</span>$<span class='no'>time</span>) +<span class='no'>anova_fit_2</span> <span class='kw'><-</span> <span class='fu'><a href='https://rdrr.io/r/stats/lm.html'>lm</a></span>(<span class='no'>observed</span> ~ <span class='no'>time</span>:<span class='no'>variable</span> - <span class='fl'>1</span>, <span class='kw'>data</span> <span class='kw'>=</span> <span class='no'>test_data_anova_2</span>) +<span class='fu'><a href='https://rdrr.io/r/base/summary.html'>summary</a></span>(<span class='no'>anova_fit_2</span>)</div><div class='output co'>#>  +#> Call: +#> lm(formula = observed ~ time:variable - 1, data = test_data_anova_2) +#>  +#> Residuals: +#>     Min      1Q  Median      3Q     Max  +#> -6.1000 -0.5875  0.0000  0.5875  6.1000  +#>  +#> Coefficients: (2 not defined because of singularities) +#>                        Estimate Std. Error t value Pr(>|t|)     +#> time0:variableparent    103.150      1.573  65.562  < 2e-16 *** +#> time1:variableparent     83.200      1.573  52.882  < 2e-16 *** +#> time3:variableparent     52.350      1.573  33.274  < 2e-16 *** +#> time7:variableparent     34.650      1.573  22.024  < 2e-16 *** +#> time14:variableparent    23.400      1.573  14.873 6.35e-14 *** +#> time28:variableparent    17.150      1.573  10.901 5.47e-11 *** +#> time60:variableparent     8.250      1.573   5.244 1.99e-05 *** +#> time90:variableparent     4.650      1.573   2.956 0.006717 **  +#> time120:variableparent    2.700      1.573   1.716 0.098507 .   +#> time0:variableM1             NA         NA      NA       NA     +#> time1:variableM1         11.850      1.573   7.532 6.93e-08 *** +#> time3:variableM1         22.700      1.573  14.428 1.26e-13 *** +#> time7:variableM1         33.050      1.573  21.007  < 2e-16 *** +#> time14:variableM1        31.250      1.573  19.863  < 2e-16 *** +#> time28:variableM1        18.900      1.573  12.013 7.02e-12 *** +#> time60:variableM1         7.550      1.573   4.799 6.28e-05 *** +#> time90:variableM1         3.850      1.573   2.447 0.021772 *   +#> time120:variableM1        2.050      1.573   1.303 0.204454     +#> time0:variableM2             NA         NA      NA       NA     +#> time1:variableM2          6.700      1.573   4.259 0.000254 *** +#> time3:variableM2         16.750      1.573  10.646 8.93e-11 *** +#> time7:variableM2         25.800      1.573  16.399 6.89e-15 *** +#> time14:variableM2        28.600      1.573  18.178 6.35e-16 *** +#> time28:variableM2        25.400      1.573  16.144 9.85e-15 *** +#> time60:variableM2        21.600      1.573  13.729 3.81e-13 *** +#> time90:variableM2        17.800      1.573  11.314 2.51e-11 *** +#> time120:variableM2       14.100      1.573   8.962 2.79e-09 *** +#> --- +#> Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1 +#>  +#> Residual standard error: 2.225 on 25 degrees of freedom +#> Multiple R-squared:  0.9979,	Adjusted R-squared:  0.9957  +#> F-statistic: 469.2 on 25 and 25 DF,  p-value: < 2.2e-16 +#> </div><div class='input'># } +</div></pre> +  </div> +  <div class="col-md-3 hidden-xs hidden-sm" id="sidebar"> +    <h2>Contents</h2> +    <ul class="nav nav-pills nav-stacked"> +      <li><a href="#arguments">Arguments</a></li> +      <li><a href="#details">Details</a></li> +      <li><a href="#see-also">See also</a></li> +      <li><a href="#examples">Examples</a></li> +    </ul> + +  </div> +</div> + + +      <footer> +      <div class="copyright"> +  <p>Developed by Johannes Ranke.</p> +</div> + +<div class="pkgdown"> +  <p>Site built with <a href="https://pkgdown.r-lib.org/">pkgdown</a> 1.4.1.</p> +</div> + +      </footer> +   </div> + +   + + +  </body> +</html> + + diff --git a/docs/sitemap.xml b/docs/sitemap.xml index 3a56fe49..66b776b2 100644 --- a/docs/sitemap.xml +++ b/docs/sitemap.xml @@ -67,6 +67,9 @@      <loc>https://pkgdown.jrwb.de/mkin/reference/ilr.html</loc>    </url>    <url> +    <loc>https://pkgdown.jrwb.de/mkin/reference/loftest.html</loc> +  </url> +  <url>      <loc>https://pkgdown.jrwb.de/mkin/reference/logLik.mkinfit.html</loc>    </url>    <url> diff --git a/man/loftest.Rd b/man/loftest.Rd new file mode 100644 index 00000000..397b5c08 --- /dev/null +++ b/man/loftest.Rd @@ -0,0 +1,81 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/loftest.R +\name{loftest} +\alias{loftest} +\alias{loftest.mkinfit} +\title{Lack-of-fit test for models fitted to data with replicates} +\usage{ +loftest(object, ...) + +\method{loftest}{mkinfit}(object, ...) +} +\arguments{ +\item{object}{A model object with a defined loftest method} + +\item{\dots}{Not used} +} +\description{ +This is a generic function with a method currently only defined for mkinfit +objects. It fits an anova model to the data contained in the object and +compares the likelihoods using the likelihood ratio test +\code{\link[lmtest]{lrtest.default}} from the lmtest package. +} +\details{ +The anova model is interpreted as the simplest form of an mkinfit model, +assuming only a constant variance about the means, but not enforcing any +structure of the means, so we have one model parameter for every mean +of replicate samples. +} +\examples{ +\dontrun{ +test_data <- subset(synthetic_data_for_UBA_2014[[12]]$data, name == "parent") +sfo_fit <- mkinfit("SFO", test_data, quiet = TRUE) +plot_res(sfo_fit) # We see a clear pattern in the residuals +loftest(sfo_fit)  # We have a clear lack of fit +# +# We try a different model (the one that was used to generate the data) +dfop_fit <- mkinfit("DFOP", test_data, quiet = TRUE) +plot_res(dfop_fit) # We don't see systematic deviations, but heteroscedastic residuals +# therefore we should consider adapting the error model, although we have +loftest(dfop_fit) # no lack of fit +# +# This is the anova model used internally for the comparison +test_data_anova <- test_data +test_data_anova$time <- as.factor(test_data_anova$time) +anova_fit <- lm(value ~ time, data = test_data_anova) +summary(anova_fit) +logLik(anova_fit) # We get the same likelihood and degrees of freedom +# +test_data_2 <- synthetic_data_for_UBA_2014[[12]]$data +m_synth_SFO_lin <- mkinmod(parent = list(type = "SFO", to = "M1"), +  M1 = list(type = "SFO", to = "M2"), +  M2 = list(type = "SFO"), use_of_ff = "max") +sfo_lin_fit <- mkinfit(m_synth_SFO_lin, test_data_2, quiet = TRUE) +plot_res(sfo_lin_fit) # not a good model, we try parallel formation +loftest(sfo_lin_fit) +# +m_synth_SFO_par <- mkinmod(parent = list(type = "SFO", to = c("M1", "M2")), +  M1 = list(type = "SFO"), +  M2 = list(type = "SFO"), use_of_ff = "max") +sfo_par_fit <- mkinfit(m_synth_SFO_par, test_data_2, quiet = TRUE) +plot_res(sfo_par_fit) # much better for metabolites +loftest(sfo_par_fit) +# +m_synth_DFOP_par <- mkinmod(parent = list(type = "DFOP", to = c("M1", "M2")), +  M1 = list(type = "SFO"), +  M2 = list(type = "SFO"), use_of_ff = "max") +dfop_par_fit <- mkinfit(m_synth_DFOP_par, test_data_2, quiet = TRUE) +plot_res(dfop_par_fit) # No visual lack of fit +loftest(dfop_par_fit)  # no lack of fit found by the test +# +# The anova model used for comparison in the case of transformation products +test_data_anova_2 <- dfop_par_fit$data +test_data_anova_2$variable <- as.factor(test_data_anova_2$variable) +test_data_anova_2$time <- as.factor(test_data_anova_2$time) +anova_fit_2 <- lm(observed ~ time:variable - 1, data = test_data_anova_2) +summary(anova_fit_2) +} +} +\seealso{ +lrtest +} @@ -2,32 +2,36 @@ Loading mkin  Testing mkin  ✔ |  OK F W S | Context  ✔ |   2       | Export dataset for reading into CAKE -✔ |  10       | Confidence intervals and p-values [9.7 s] -✔ |  14       | Error model fitting [36.5 s] +✔ |  10       | Confidence intervals and p-values [10.1 s] +✔ |  14       | Error model fitting [40.5 s]  ✔ |   4       | Calculation of FOCUS chi2 error levels [2.2 s] -✔ |  13       | Results for FOCUS D established in expertise for UBA (Ranke 2014) [3.3 s] +✔ |  13       | Results for FOCUS D established in expertise for UBA (Ranke 2014) [3.4 s]  ✔ |   6       | Test fitting the decline of metabolites from their maximum [0.7 s]  ✔ |   1       | Fitting the logistic model [0.9 s]  ✔ |   1       | Test dataset class mkinds used in gmkin  ✔ |  12       | Special cases of mkinfit calls [2.4 s]  ✔ |   9       | mkinmod model generation and printing [0.2 s]  ✔ |   3       | Model predictions with mkinpredict [0.3 s] -✔ |  16       | Evaluations according to 2015 NAFTA guidance [4.0 s] +✔ |  16       | Evaluations according to 2015 NAFTA guidance [4.1 s]  ✔ |   4       | Calculation of maximum time weighted average concentrations (TWAs) [2.3 s]  ✔ |   3       | Summary  ✔ |  11       | Plotting [0.6 s]  ✔ |   4       | AIC calculation  ✔ |   2       | Residuals extracted from mkinfit models -✔ |   2       | Complex test case from Schaefer et al. (2007) Piacenza paper [5.3 s] -✔ |   4       | Fitting the SFORB model [1.7 s] +✔ |   2       | Complex test case from Schaefer et al. (2007) Piacenza paper [5.6 s] +✔ |   4       | Fitting the SFORB model [1.8 s]  ✔ |   1       | Summaries of old mkinfit objects -✔ |   4       | Results for synthetic data established in expertise for UBA (Ranke 2014) [7.1 s] -✔ |   6       | Hypothesis tests [31.2 s] +✔ |   4       | Results for synthetic data established in expertise for UBA (Ranke 2014) [7.5 s] +✔ |   7     1 | Hypothesis tests [34.1 s] +──────────────────────────────────────────────────────────────────────────────── +test_tests.R:59: skip: We can do a likelihood ratio test using an update specification +Reason: This errors out if called by testthat while it works in a normal R session +────────────────────────────────────────────────────────────────────────────────  ══ Results ═════════════════════════════════════════════════════════════════════ -Duration: 108.3 s +Duration: 116.9 s -OK:       132 +OK:       133  Failed:   0  Warnings: 0 -Skipped:  0 +Skipped:  1 diff --git a/tests/testthat/FOCUS_2006_D.csf b/tests/testthat/FOCUS_2006_D.csf index 528e2b61..09940aa3 100644 --- a/tests/testthat/FOCUS_2006_D.csf +++ b/tests/testthat/FOCUS_2006_D.csf @@ -5,7 +5,7 @@ Description:  MeasurementUnits: % AR  TimeUnits: days  Comments: Created using mkin::CAKE_export -Date: 2019-11-05 +Date: 2019-11-09  Optimiser: IRLS  [Data] diff --git a/tests/testthat/setup_script.R b/tests/testthat/setup_script.R index 9becdd2a..e33f4af7 100644 --- a/tests/testthat/setup_script.R +++ b/tests/testthat/setup_script.R @@ -32,9 +32,6 @@ f_1_mkin_trans <- mkinfit("SFO", FOCUS_2006_A, quiet = TRUE)  f_1_mkin_notrans <- mkinfit("SFO", FOCUS_2006_A, quiet = TRUE,    transform_rates = FALSE) -f_2_mkin <- mkinfit("DFOP", FOCUS_2006_C, quiet = TRUE) -f_2_nls <- nls(value ~ SSbiexp(time, A1, lrc1, A2, lrc2), data = FOCUS_2006_C) -  # mmkin object of parent fits for tests  models <- c("SFO", "FOMC", "DFOP", "HS")  fits <- mmkin(models, @@ -62,11 +59,14 @@ f_sfo_sfo.ff <- mkinfit(SFO_SFO.ff,    subset(FOCUS_2006_D, value != 0),    quiet = TRUE) -# Two metabolites  SFO_lin_a <- synthetic_data_for_UBA_2014[[1]]$data -  DFOP_par_c <- synthetic_data_for_UBA_2014[[12]]$data +f_2_mkin <- mkinfit("DFOP", DFOP_par_c, quiet = TRUE) +f_2_nls <- nls(value ~ SSbiexp(time, A1, lrc1, A2, lrc2), data = subset(DFOP_par_c, name == "parent")) +f_2_anova <- lm(value ~ as.factor(time), data = subset(DFOP_par_c, name == "parent")) + +# Two metabolites  m_synth_SFO_lin <- mkinmod(    parent = mkinsub("SFO", "M1"),    M1 = mkinsub("SFO", "M2"), diff --git a/tests/testthat/test_confidence.R b/tests/testthat/test_confidence.R index a2bf1401..e85fdb7a 100644 --- a/tests/testthat/test_confidence.R +++ b/tests/testthat/test_confidence.R @@ -54,11 +54,12 @@ test_that("Quadratic confidence intervals for rate constants are comparable to v    # Another case:    se_mkin_2 <- summary(f_2_mkin)$par[1:4, "Std. Error"]    se_nls_2 <- summary(f_2_nls)$coefficients[, "Std. Error"] -  # Here we the ratio of standard errors can be explained by the same +  # Here the ratio of standard errors can be explained by the same    # principle up to about 3% +  nobs_DFOP_par_c_parent <- nrow(subset(DFOP_par_c, name == "parent"))    expect_equivalent(      se_nls_2[c("lrc1", "lrc2")] / se_mkin_2[c("log_k1", "log_k2")], -    rep(sqrt(nrow(FOCUS_2006_C) / (nrow(FOCUS_2006_C) - 4)), 2), +    rep(sqrt(nobs_DFOP_par_c_parent / (nobs_DFOP_par_c_parent - 4)), 2),      tolerance = 0.03)  }) @@ -73,7 +74,7 @@ test_that("Likelihood profile based confidence intervals work", {     }     f_mle <- stats4::mle(f_nll, start = as.list(parms(f)), nobs = nrow(FOCUS_2006_C)) -   ci_mkin_1_p_0.95 <- confint(f, method = "profile", level = 0.95,  +   ci_mkin_1_p_0.95 <- confint(f, method = "profile", level = 0.95,       cores = n_cores, quiet = TRUE)     # Magically, we get very similar boundaries as stats4::mle diff --git a/tests/testthat/test_tests.R b/tests/testthat/test_tests.R index 5a522f8e..bdc72f08 100644 --- a/tests/testthat/test_tests.R +++ b/tests/testthat/test_tests.R @@ -1,5 +1,20 @@  context("Hypothesis tests") +test_that("The lack-of-fit test works and can be reproduced using nls", { + +  expect_error(loftest(f_1_mkin_trans), "Not defined for fits to data without replicates") + +  loftest_mkin <- loftest(f_2_mkin) + +  # This code is inspired by Ritz and Streibig (2008) Nonlinear Regression using R, p. 64 +  Q <- as.numeric(- 2 * (logLik(f_2_nls) -  logLik(f_2_anova))) +  df.Q <- df.residual(f_2_nls) - df.residual(f_2_anova) +  p_nls <- 1 - pchisq(Q, df.Q) + +  expect_equal(loftest_mkin[["2", "Pr(>Chisq)"]], p_nls, tolerance = 1e-5) + +}) +  test_that("The likelihood ratio test works", {    expect_error(lrtest(f_1_mkin_trans, f_2_mkin), "not been fitted to the same data") @@ -25,7 +40,7 @@ test_that("Updating fitted models works", {      parent = mkinsub("DFOP", to = "A1"),      A1 = mkinsub("SFO", to = "A2"),      A2 = mkinsub("SFO"), -    use_of_ff = "max" +    use_of_ff = "max", quiet = TRUE    )    f_soil_1_tc <- mkinfit(dfop_sfo_sfo, @@ -41,6 +56,9 @@ test_that("Updating fitted models works", {  })  test_that("We can do a likelihood ratio test using an update specification", { +  skip("This errors out if called by testthat while it works in a normal R session")    test_2_mkin_k2 <- lrtest(f_2_mkin, fixed_parms = c(k2 = 0)) -  expect_equivalent(test_2_mkin_k2[["2", "Pr(>Chisq)"]], 1.139e-6, tolerance = 1e-8) +  expect_equivalent(test_2_mkin_k2[["2", "Pr(>Chisq)"]], 4.851e-8, tolerance = 1e-8) +  test_2_mkin_tc <- lrtest(f_2_mkin, error_model = "tc") +  expect_equivalent(test_2_mkin_tc[["2", "Pr(>Chisq)"]], 7.302e-5, tolerance = 1e-7)  }) | 
