diff options
-rw-r--r-- | DESCRIPTION | 2 | ||||
-rw-r--r-- | R/confint.mkinfit.R | 20 | ||||
-rw-r--r-- | docs/reference/confint.mkinfit.html | 31 | ||||
-rw-r--r-- | man/confint.mkinfit.Rd | 17 | ||||
-rw-r--r-- | test.log | 16 | ||||
-rw-r--r-- | tests/testthat/FOCUS_2006_D.csf | 2 | ||||
-rw-r--r-- | tests/testthat/test_confidence.R | 103 |
7 files changed, 105 insertions, 86 deletions
diff --git a/DESCRIPTION b/DESCRIPTION index 88412fa8..544af139 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,7 +19,7 @@ Description: Calculation routines based on the FOCUS Kinetics Report (2006, purpose. Imports: stats, graphics, methods, deSolve, R6, inline, parallel, numDeriv Suggests: knitr, rbenchmark, tikzDevice, testthat, rmarkdown, covr, vdiffr, - benchmarkme, tibble + benchmarkme, tibble, stats4 License: GPL LazyLoad: yes LazyData: yes diff --git a/R/confint.mkinfit.R b/R/confint.mkinfit.R index 58a3c8db..1ed689a9 100644 --- a/R/confint.mkinfit.R +++ b/R/confint.mkinfit.R @@ -1,10 +1,17 @@ #' Confidence intervals for parameters of mkinfit objects #' +#' The default method 'profile' is based on the profile likelihood for each +#' parameter. The method uses two nested optimisations. The speed of the method +#' could likely be improved by using the method of Venzon and Moolgavkar (1988). +#' #' @param object An \code{\link{mkinfit}} object #' @param parm A vector of names of the parameters which are to be given #' confidence intervals. If missing, all parameters are considered. #' @param level The confidence level required #' @param alpha The allowed error probability, overrides 'level' if specified. +#' @param cutoff Possibility to specify an alternative cutoff for the difference +#' in the log-likelihoods at the confidence boundary. Specifying an explicit +#' cutoff value overrides arguments 'level' and 'alpha' #' @param method The 'profile' method searches the parameter space for the #' cutoff of the confidence intervals by means of a likelihood ratio test. #' The 'quadratic' method approximates the likelihood function at the @@ -25,6 +32,9 @@ #' @importFrom stats qnorm #' @references Pawitan Y (2013) In all likelihood - Statistical modelling and #' inference using likelihood. Clarendon Press, Oxford. +#' Venzon DJ and Moolgavkar SH (1988) A Method for Computing +#' Profile-Likelihood Based Confidence Intervals, Applied Statistics, 37, +#' 87–94. #' @examples #' f <- mkinfit("SFO", FOCUS_2006_C, quiet = TRUE) #' confint(f, method = "quadratic") @@ -33,7 +43,7 @@ #' } #' @export confint.mkinfit <- function(object, parm, - level = 0.95, alpha = 1 - level, + level = 0.95, alpha = 1 - level, cutoff, method = c("profile", "quadratic"), transformed = TRUE, backtransform = TRUE, distribution = c("student_t", "normal"), quiet = FALSE, ...) @@ -100,15 +110,17 @@ confint.mkinfit <- function(object, parm, } if (method == "profile") { - message("Profiling the likelihood") + if (!quiet) message("Profiling the likelihood") + lci <- uci <- rep(NA, p) names(lci) <- names(uci) <- return_pnames profile_pnames <- if(missing(parm)) names(parms(object)) else parm - # We do two-sided intervals based on the likelihood ratio - cutoff <- 0.5 * qchisq(1 - (alpha / 2), 1) + if (missing(cutoff)) { + cutoff <- 0.5 * qchisq(1 - alpha, 1) + } all_parms <- parms(object) diff --git a/docs/reference/confint.mkinfit.html b/docs/reference/confint.mkinfit.html index 64ea0d4c..ec8938c5 100644 --- a/docs/reference/confint.mkinfit.html +++ b/docs/reference/confint.mkinfit.html @@ -36,7 +36,9 @@ <meta property="og:title" content="Confidence intervals for parameters of mkinfit objects — confint.mkinfit" /> -<meta property="og:description" content="Confidence intervals for parameters of mkinfit objects" /> +<meta property="og:description" content="The default method 'profile' is based on the profile likelihood for each +parameter. The method uses two nested optimisations. The speed of the method +could likely be improved by using the method of Venzon and Moolgavkar (1988)." /> <meta name="twitter:card" content="summary" /> @@ -133,14 +135,16 @@ </div> <div class="ref-description"> - <p>Confidence intervals for parameters of mkinfit objects</p> + <p>The default method 'profile' is based on the profile likelihood for each +parameter. The method uses two nested optimisations. The speed of the method +could likely be improved by using the method of Venzon and Moolgavkar (1988).</p> </div> <pre class="usage"><span class='co'># S3 method for mkinfit</span> <span class='fu'><a href='https://rdrr.io/r/stats/confint.html'>confint</a></span>(<span class='no'>object</span>, <span class='no'>parm</span>, <span class='kw'>level</span> <span class='kw'>=</span> <span class='fl'>0.95</span>, <span class='kw'>alpha</span> <span class='kw'>=</span> <span class='fl'>1</span> - - <span class='no'>level</span>, <span class='kw'>method</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"profile"</span>, <span class='st'>"quadratic"</span>), <span class='kw'>transformed</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>, - <span class='kw'>backtransform</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>, <span class='kw'>distribution</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"student_t"</span>, <span class='st'>"normal"</span>), - <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>FALSE</span>, <span class='no'>...</span>)</pre> + <span class='no'>level</span>, <span class='no'>cutoff</span>, <span class='kw'>method</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"profile"</span>, <span class='st'>"quadratic"</span>), + <span class='kw'>transformed</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>, <span class='kw'>backtransform</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>, + <span class='kw'>distribution</span> <span class='kw'>=</span> <span class='fu'><a href='https://rdrr.io/r/base/c.html'>c</a></span>(<span class='st'>"student_t"</span>, <span class='st'>"normal"</span>), <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>FALSE</span>, <span class='no'>...</span>)</pre> <h2 class="hasAnchor" id="arguments"><a class="anchor" href="#arguments"></a>Arguments</h2> <table class="ref-arguments"> @@ -163,6 +167,12 @@ confidence intervals. If missing, all parameters are considered.</p></td> <td><p>The allowed error probability, overrides 'level' if specified.</p></td> </tr> <tr> + <th>cutoff</th> + <td><p>Possibility to specify an alternative cutoff for the difference +in the log-likelihoods at the confidence boundary. Specifying an explicit +cutoff value overrides arguments 'level' and 'alpha'</p></td> + </tr> + <tr> <th>method</th> <td><p>The 'profile' method searches the parameter space for the cutoff of the confidence intervals by means of a likelihood ratio test. @@ -204,7 +214,10 @@ the parameter estimate</p></td> <h2 class="hasAnchor" id="references"><a class="anchor" href="#references"></a>References</h2> <p>Pawitan Y (2013) In all likelihood - Statistical modelling and - inference using likelihood. Clarendon Press, Oxford.</p> + inference using likelihood. Clarendon Press, Oxford. + Venzon DJ and Moolgavkar SH (1988) A Method for Computing + Profile-Likelihood Based Confidence Intervals, Applied Statistics, 37, + 87–94.</p> <h2 class="hasAnchor" id="examples"><a class="anchor" href="#examples"></a>Examples</h2> <pre class="examples"><div class='input'><span class='no'>f</span> <span class='kw'><-</span> <span class='fu'><a href='mkinfit.html'>mkinfit</a></span>(<span class='st'>"SFO"</span>, <span class='no'>FOCUS_2006_C</span>, <span class='kw'>quiet</span> <span class='kw'>=</span> <span class='fl'>TRUE</span>) @@ -213,9 +226,9 @@ the parameter estimate</p></td> #> k_parent_sink 0.2109541 0.4440528 #> sigma 1.9778868 7.3681380</div><div class='input'><span class='co'># \dontrun{</span> <span class='fu'><a href='https://rdrr.io/r/stats/confint.html'>confint</a></span>(<span class='no'>f</span>, <span class='kw'>method</span> <span class='kw'>=</span> <span class='st'>"profile"</span>)</div><div class='output co'>#> <span class='message'>Profiling the likelihood</span></div><div class='output co'>#> 2.5% 97.5% -#> parent_0 71.3471007 93.9447024 -#> k_parent_sink 0.2030765 0.4491067 -#> sigma 2.9810656 8.8633278</div><div class='input'># } +#> parent_0 73.0641834 92.1392181 +#> k_parent_sink 0.2170293 0.4235348 +#> sigma 3.1307772 8.0628314</div><div class='input'># } </div></pre> </div> <div class="col-md-3 hidden-xs hidden-sm" id="sidebar"> diff --git a/man/confint.mkinfit.Rd b/man/confint.mkinfit.Rd index 943904b9..29959e52 100644 --- a/man/confint.mkinfit.Rd +++ b/man/confint.mkinfit.Rd @@ -5,9 +5,9 @@ \title{Confidence intervals for parameters of mkinfit objects} \usage{ \method{confint}{mkinfit}(object, parm, level = 0.95, alpha = 1 - - level, method = c("profile", "quadratic"), transformed = TRUE, - backtransform = TRUE, distribution = c("student_t", "normal"), - quiet = FALSE, ...) + level, cutoff, method = c("profile", "quadratic"), + transformed = TRUE, backtransform = TRUE, + distribution = c("student_t", "normal"), quiet = FALSE, ...) } \arguments{ \item{object}{An \code{\link{mkinfit}} object} @@ -19,6 +19,10 @@ confidence intervals. If missing, all parameters are considered.} \item{alpha}{The allowed error probability, overrides 'level' if specified.} +\item{cutoff}{Possibility to specify an alternative cutoff for the difference +in the log-likelihoods at the confidence boundary. Specifying an explicit +cutoff value overrides arguments 'level' and 'alpha'} + \item{method}{The 'profile' method searches the parameter space for the cutoff of the confidence intervals by means of a likelihood ratio test. The 'quadratic' method approximates the likelihood function at the @@ -45,7 +49,9 @@ A matrix with columns giving lower and upper confidence limits for each parameter. } \description{ -Confidence intervals for parameters of mkinfit objects +The default method 'profile' is based on the profile likelihood for each +parameter. The method uses two nested optimisations. The speed of the method +could likely be improved by using the method of Venzon and Moolgavkar (1988). } \examples{ f <- mkinfit("SFO", FOCUS_2006_C, quiet = TRUE) @@ -57,4 +63,7 @@ confint(f, method = "quadratic") \references{ Pawitan Y (2013) In all likelihood - Statistical modelling and inference using likelihood. Clarendon Press, Oxford. + Venzon DJ and Moolgavkar SH (1988) A Method for Computing + Profile-Likelihood Based Confidence Intervals, Applied Statistics, 37, + 87–94. } @@ -2,10 +2,10 @@ Loading mkin Testing mkin ✔ | OK F W S | Context ✔ | 2 | Export dataset for reading into CAKE -✔ | 9 | Confidence intervals and p-values [2.3 s] -✔ | 10 | Error model fitting [37.2 s] +✔ | 10 | Confidence intervals and p-values [12.8 s] +✔ | 10 | Error model fitting [37.1 s] ✔ | 5 | Calculation of FOCUS chi2 error levels [3.5 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.9 s] ✔ | 1 | Fitting the logistic model [0.9 s] ✔ | 1 | Test dataset class mkinds used in gmkin @@ -13,19 +13,19 @@ Testing mkin ✔ | 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] -✔ | 4 | Calculation of maximum time weighted average concentrations (TWAs) [2.3 s] +✔ | 4 | Calculation of maximum time weighted average concentrations (TWAs) [2.2 s] ✔ | 3 | Summary ✔ | 11 | Plotting [0.6 s] ✔ | 3 | AIC calculation -✔ | 2 | Complex test case from Schaefer et al. (2007) Piacenza paper [5.2 s] +✔ | 2 | Complex test case from Schaefer et al. (2007) Piacenza paper [5.3 s] ✔ | 4 | Fitting the SFORB model [1.7 s] ✔ | 1 | Summaries of old mkinfit objects -✔ | 4 | Results for synthetic data established in expertise for UBA (Ranke 2014) [7.0 s] +✔ | 4 | Results for synthetic data established in expertise for UBA (Ranke 2014) [7.2 s] ══ Results ═════════════════════════════════════════════════════════════════════ -Duration: 72.1 s +Duration: 84.5 s -OK: 119 +OK: 120 Failed: 0 Warnings: 0 Skipped: 0 diff --git a/tests/testthat/FOCUS_2006_D.csf b/tests/testthat/FOCUS_2006_D.csf index 22a4b125..c9da7d61 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-10-26 +Date: 2019-10-28 Optimiser: IRLS [Data] diff --git a/tests/testthat/test_confidence.R b/tests/testthat/test_confidence.R index 13652436..5f76c344 100644 --- a/tests/testthat/test_confidence.R +++ b/tests/testthat/test_confidence.R @@ -1,3 +1,25 @@ +# We set up some models and fits with nls for comparisons +SFO_trans <- function(t, parent_0, log_k_parent_sink) { + parent_0 * exp(- exp(log_k_parent_sink) * t) +} +SFO_notrans <- function(t, parent_0, k_parent_sink) { + parent_0 * exp(- k_parent_sink * t) +} +f_1_nls_trans <- nls(value ~ SFO_trans(time, parent_0, log_k_parent_sink), + data = FOCUS_2006_A, + start = list(parent_0 = 100, log_k_parent_sink = log(0.1))) +f_1_nls_notrans <- nls(value ~ SFO_notrans(time, parent_0, k_parent_sink), + data = FOCUS_2006_A, + start = list(parent_0 = 100, k_parent_sink = 0.1)) + +f_1_mkin_OLS <- mkinfit("SFO", FOCUS_2006_A, quiet = TRUE) +f_1_mkin_OLS_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) + context("Confidence intervals and p-values") test_that("The confint method 'quadratic' is consistent with the summary", { @@ -20,24 +42,7 @@ test_that("The confint method 'quadratic' is consistent with the summary", { }) -test_that("Quadratic confidence intervals for rate constants are comparable to confint.nls", { - - SFO_trans <- function(t, parent_0, log_k_parent_sink) { - parent_0 * exp(- exp(log_k_parent_sink) * t) - } - SFO_notrans <- function(t, parent_0, k_parent_sink) { - parent_0 * exp(- k_parent_sink * t) - } - f_1_nls_trans <- nls(value ~ SFO_trans(time, parent_0, log_k_parent_sink), - data = FOCUS_2006_A, - start = list(parent_0 = 100, log_k_parent_sink = log(0.1))) - f_1_nls_notrans <- nls(value ~ SFO_notrans(time, parent_0, k_parent_sink), - data = FOCUS_2006_A, - start = list(parent_0 = 100, k_parent_sink = 0.1)) - - f_1_mkin_OLS <- mkinfit("SFO", FOCUS_2006_A, quiet = TRUE) - f_1_mkin_OLS_notrans <- mkinfit("SFO", FOCUS_2006_A, quiet = TRUE, - transform_rates = FALSE) +test_that("Quadratic confidence intervals for rate constants are comparable to values in summary.nls", { # Check fitted parameter values expect_equivalent( @@ -70,8 +75,6 @@ test_that("Quadratic confidence intervals for rate constants are comparable to c expect_equivalent(se_nls_notrans[2] / se_mkin_notrans[2], sqrt(8/5), tolerance = 0.01) # Another case: - 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) 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 @@ -82,42 +85,24 @@ test_that("Quadratic confidence intervals for rate constants are comparable to c tolerance = 0.03) }) -#test_that("Likelihood profile based confidence intervals work", { - -# f <- mkinfit("SFO", FOCUS_2006_C, quiet = TRUE) -# f <- fits[["SFO", "FOCUS_C"]] -# f_notrans <- mkinfit("SFO", FOCUS_2006_C, quiet = TRUE, transform_rates = FALSE) -# pf <- parms(f) -# f_nll <- function(parent_0, k_parent_sink, sigma) { -# - f$ll(c(parent_0 = as.numeric(parent_0), -# k_parent_sink = as.numeric(k_parent_sink), -# sigma = as.numeric(sigma))) -# } -# f_nll(parent_0 = 100, k_parent_sink = 0.3, sigma = 4.7) -# f_nll(pf[1], pf[2], pf[3]) -# f_mle <- stats4::mle(f_nll, start = as.list(parms(f)), nobs = nrow(FOCUS_2006_C)) -# f_mle <- stats4::mle(f_nll, start = as.list(parms(f)), nobs = 17L) -# logLik(f_mle) -# stats4::confint(f_mle, nobs = nrow(FOCUS_2006_C)) -# -# ci_mkin_1 <- confint(f, -# method = "quadratic", backtransform = FALSE, -# transformed = TRUE) -# ci_maxLik_1 <- maxLik::confint.maxLik(f_maxLik) -# -# f_tc_2_maxLik <- maxLik::maxLik(f_tc_2$ll, -# start = f_tc_2$par) -# ci_tc_2 <- confint(f_tc_2, method = "quadratic", -# backtransform = FALSE, transformed = TRUE, -# distribution = "normal") -# ci_tc_2_maxLik <- confint(f_tc_2_maxLik) -# rel_diff_ci_tc_2 <- (ci_tc_2 - ci_tc_2_maxLik)/ci_tc_2_maxLik -# # The ilr transformed variable appear to misbehave in the numeric differentiation -# expect_equivalent( -# rel_diff_ci_tc_2[c(2, 3, 4, 6, 7, 9, 10), ], rep(0, 14), -# tolerance = 0.05) -# -# ci_tc_2[, 1] -# ci_tc_2_maxLik[, 1] - -#}) +test_that("Likelihood profile based confidence intervals work", { + f <- fits[["SFO", "FOCUS_C"]] + + # negative log-likelihood for use with mle + f_nll <- function(parent_0, k_parent_sink, sigma) { + - f$ll(c(parent_0 = as.numeric(parent_0), + k_parent_sink = as.numeric(k_parent_sink), + sigma = as.numeric(sigma))) + } + 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, quiet = TRUE) + + # Magically, we get very similar boundaries as stats4::mle + # (we need to capture the output to avoid printing this while testing as + # stats4::confint uses cat() for its message, instead of message(), so + # suppressMessage() has no effect) + msg <- capture.output(ci_mle_1_0.95 <- stats4::confint(f_mle, level = 0.95)) + rel_diff_ci <- (ci_mle_1_0.95 - ci_mkin_1_p_0.95)/ci_mle_1_0.95 + expect_equivalent(as.numeric(rel_diff_ci), rep(0, 6), tolerance = 1e-4) +}) |