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.png Binary files differnew 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.png Binary files differnew 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.png Binary files differnew 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.png Binary files differnew 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.png Binary files differnew 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) }) |