From 6f2c54181855bc9116f2112a4c9d3f67cd3b2273 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 19 Oct 2026 11:15:09 -0500 Subject: [PATCH 01/10] Add tests for zero-inflation rows in glmmTMB bootstrap output --- tests/testthat/test-bootstrap_parameters.R | 132 +++++++++++++++++++++ 1 file changed, 132 insertions(+) diff --git a/tests/testthat/test-bootstrap_parameters.R b/tests/testthat/test-bootstrap_parameters.R index cfd43299f..4cccaf0a0 100644 --- a/tests/testthat/test-bootstrap_parameters.R +++ b/tests/testthat/test-bootstrap_parameters.R @@ -403,3 +403,135 @@ test_that("bootstrap_model parametric errors for responses that are not a data c ) ) }) + +.fit_zi_glmmTMB <- function(ziformula = ~mined, ...) { + data(Salamanders, package = "glmmTMB") + glmmTMB::glmmTMB( + count ~ mined + (1 | site), + ziformula = ziformula, + family = poisson, + data = Salamanders, + ... + ) +} + +.fit_zi_disp_glmmTMB <- function() { + data(Salamanders, package = "glmmTMB") + glmmTMB::glmmTMB( + count ~ mined + (1 | site), + ziformula = ~mined, + dispformula = ~mined, + family = glmmTMB::nbinom2, + data = Salamanders + ) +} + +test_that("bootstrap_model names zero-inflation columns of glmmTMB models", { + skip_if_not_installed("glmmTMB") + skip_if_not_installed("lme4") + zi_names <- c("(Intercept)", "minedno", "(Intercept)_zi", "minedno_zi") + + set.seed(123) + expect_named(bootstrap_model(.fit_zi_glmmTMB(), iterations = 5), zi_names) + set.seed(123) + expect_named(bootstrap_model(.fit_zi_disp_glmmTMB(), iterations = 5), zi_names) + set.seed(123) + expect_named( + bootstrap_model(.fit_zi_glmmTMB(ziformula = ~cover), iterations = 5), + c("(Intercept)", "minedno", "(Intercept)_zi", "cover_zi") + ) + + m_cond <- .fit_zi_glmmTMB(ziformula = ~0) + set.seed(123) + expect_named( + bootstrap_model(m_cond, iterations = 5), + insight::find_parameters(m_cond, effects = "fixed")$conditional + ) + data(sleepstudy, package = "lme4") + m_lmer <- lme4::lmer(Reaction ~ Days + (1 | Subject), data = sleepstudy) + set.seed(123) + expect_named( + bootstrap_model(m_lmer, iterations = 5), + insight::find_parameters(m_lmer, effects = "fixed")$conditional + ) +}) + +test_that("bootstrap_parameters returns components of glmmTMB models", { + skip_if_not_installed("glmmTMB") + for (m in list(.fit_zi_glmmTMB(), .fit_zi_disp_glmmTMB())) { + params <- insight::get_parameters(m) + params <- params[params$Component != "dispersion", ] + set.seed(123) + out <- list( + bootstrap_parameters(m, iterations = 20), + model_parameters(m, bootstrap = TRUE, iterations = 20, effects = "fixed"), + model_parameters( + m, + bootstrap = TRUE, + iterations = 20, + effects = "fixed", + exponentiate = TRUE + ) + ) + for (x in out) { + expect_identical(x$Parameter, params$Parameter) + expect_identical(x$Component, params$Component) + expect_false(anyNA(x$Coefficient)) + } + } + + set.seed(123) + mp <- model_parameters( + .fit_zi_glmmTMB(), + bootstrap = TRUE, + iterations = 20, + effects = "fixed" + ) + printed <- capture.output(print(mp)) + zi_start <- grep("# Zero-Inflation", printed, fixed = TRUE) + expect_length(zi_start, 1) + zi_rows <- printed[seq_along(printed) > min(zi_start, Inf)] + expect_length(grep("^\\(Intercept\\)\\s+\\|", zi_rows), 1) + expect_length(grep("^mined \\[no\\]\\s+\\|", zi_rows), 1) +}) + +test_that("bootstrap_parameters matches each glmmTMB draw to its parameter", { + skip_if_not_installed("glmmTMB") + m <- .fit_zi_glmmTMB(ziformula = ~cover) + estimates <- insight::get_parameters(m)$Estimate + set.seed(123) + out <- bootstrap_parameters(m, iterations = 50) + distances <- abs(outer(out$Coefficient, estimates, "-")) + closest <- apply(distances, 1, function(d) which.min(d)[1]) + expect_identical(closest, seq_along(estimates)) +}) + +test_that("model_parameters with bootstrap uses component for glmmTMB", { + skip_if_not_installed("glmmTMB") + m <- .fit_zi_glmmTMB() + for (comp in c("conditional", "zi")) { + expected <- model_parameters(m, component = comp, effects = "fixed") + set.seed(123) + out <- model_parameters( + m, + bootstrap = TRUE, + iterations = 20, + component = comp, + effects = "fixed" + ) + expect_identical(out$Parameter, expected$Parameter) + expect_identical(out$Component, expected$Component) + } +}) + +test_that("model_parameters with bootstrap prints a bootstrap footer for glmmTMB", { + skip_if_not_installed("glmmTMB") + for (m in list(.fit_zi_glmmTMB(), .fit_zi_glmmTMB(ziformula = ~0))) { + set.seed(123) + mp <- model_parameters(m, bootstrap = TRUE, iterations = 20, effects = "fixed") + messages <- capture_messages(printed <- capture.output(print(mp))) + printed <- paste(c(printed, messages), collapse = " ") + expect_match(printed, "bootstrap", fixed = TRUE) + expect_no_match(printed, "Wald", fixed = TRUE) + } +}) From 18dcde34f8c462d0620d788071af1764f1beb926 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 19 Oct 2026 11:26:38 -0500 Subject: [PATCH 02/10] Name zero-inflation parameters of glmmTMB models in bootstrap output (#1284) --- DESCRIPTION | 2 +- NEWS.md | 11 ++++++++ R/bootstrap_model.R | 7 ++++- R/bootstrap_parameters.R | 21 +++++++++++++++ R/methods_glmmTMB.R | 31 +++++++++++++++++++--- tests/testthat/test-bootstrap_parameters.R | 19 +++++++++---- 6 files changed, 80 insertions(+), 11 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c8a9b7ab6..fab3d0b69 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: parameters Title: Processing of Model Parameters -Version: 0.30.0.10 +Version: 0.30.0.11 Authors@R: c(person(given = "Daniel", family = "Lüdecke", diff --git a/NEWS.md b/NEWS.md index 5f719ab10..779f1e7fd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -39,6 +39,17 @@ per row. In the second kind, the response in the formula is not one column of the data, for example `log(y)` or `cbind()`. +* `bootstrap_model()`, `bootstrap_parameters()` and `model_parameters()` with + `bootstrap = TRUE` now name the zero-inflation parameters of *glmmTMB* + models and show them in their own component. Before this fix, these rows + were named `NA` or had no name. In `bootstrap_model()`, the columns of the + zero-inflation parameters get the suffix `_zi`, as in `simulate_model()`. + `model_parameters()` with `bootstrap = TRUE` now uses the `component` + argument, and `component = "dispersion"` gives an error. Its output no longer + has a dispersion row, because the bootstrap draws have no dispersion + parameters. The footer now says that the intervals are bootstrap intervals + (#1284). + * The `p_adjust` argument in `model_parameters()` now accepts the methods from `stats::p.adjust.methods` in any letter case, for example `"bh"`. Before, this failed with an error. An invalid value for `p_adjust` no longer shows the diff --git a/R/bootstrap_model.R b/R/bootstrap_model.R index f985572ee..9539612ec 100644 --- a/R/bootstrap_model.R +++ b/R/bootstrap_model.R @@ -552,7 +552,12 @@ bootstrap_model.merMod <- function( out <- as.data.frame(results$t) out <- out[stats::complete.cases(out), ] - names(out) <- insight::find_parameters(model, effects = "fixed")$conditional + # zero-inflation parameters get the suffix "_zi", as in simulate_model() + fixed_names <- insight::find_parameters(model, effects = "fixed") + names(out) <- c( + fixed_names$conditional, + if (length(fixed_names$zero_inflated)) paste0(fixed_names$zero_inflated, "_zi") + ) class(out) <- unique(c("bootstrap_model", "see_bootstrap_model", class(out))) attr(out, "original_model") <- model out diff --git a/R/bootstrap_parameters.R b/R/bootstrap_parameters.R index c5c529d62..a3758b69b 100644 --- a/R/bootstrap_parameters.R +++ b/R/bootstrap_parameters.R @@ -98,6 +98,7 @@ bootstrap_parameters.bootstrap_model <- function( ci_method = ci_method, ... ) + out <- .add_component_bootstrap_glmmTMB(out, attr(model, "original_model")) class(out) <- c("bootstrap_parameters", "parameters_model", class(out)) attr(out, "boot_samples") <- model @@ -111,6 +112,26 @@ model_parameters.bootstrap_model <- bootstrap_parameters.bootstrap_model # utilities -------------------------------------------------------------------- +# bootstrap_model() names the zero-inflation draws of glmmTMB models +# "_zi". Restore the names and add the component, as in model_parameters() +.add_component_bootstrap_glmmTMB <- function(params, model) { + if (!inherits(model, "glmmTMB")) { + return(params) + } + fixed_names <- insight::find_parameters(model, effects = "fixed") + if (!length(fixed_names$zero_inflated)) { + return(params) + } + draw_names <- c(fixed_names$conditional, paste0(fixed_names$zero_inflated, "_zi")) + idx <- match(params$Parameter, draw_names) + params$Parameter <- c(fixed_names$conditional, fixed_names$zero_inflated)[idx] + params$Component <- rep( + c("conditional", "zero_inflated"), + c(length(fixed_names$conditional), length(fixed_names$zero_inflated)) + )[idx] + params +} + #' @keywords internal .summary_bootstrap <- function(data, test, centrality, ci, ci_method, ...) { # Is the p-value requested? diff --git a/R/methods_glmmTMB.R b/R/methods_glmmTMB.R index a5c8fdaea..c76015420 100644 --- a/R/methods_glmmTMB.R +++ b/R/methods_glmmTMB.R @@ -262,8 +262,21 @@ model_parameters.glmmTMB <- function( if (effects %in% c("fixed", "all")) { if (bootstrap) { + if (component == "dispersion") { + insight::format_error( + "Bootstrapping does not return dispersion parameters. Use `component = \"all\"`, `\"conditional\"` or `\"zi\"`." # nolint + ) + } # bootstrapped parameters params <- bootstrap_parameters(model, iterations = iterations, ci = ci, ...) + # keep requested component, and drop the component column for + # the conditional component, as for the regular parameters + if (!is.null(params$Component)) { + params <- .filter_component(params, component) + if (component == "conditional") { + params$Component <- NULL + } + } if (effects != "fixed") { effects <- "fixed" if (verbose) { @@ -297,10 +310,13 @@ model_parameters.glmmTMB <- function( params <- do.call(".extract_parameters_generic", fun_args) } - # add dispersion parameter - out <- .add_dispersion_param_glmmTMB(model, params, effects, component, ci, verbose) - params <- out$params - dispersion_param <- out$dispersion_param + # add dispersion parameter. bootstrapped draws have no dispersion + # parameters, so we don't add a row with Wald intervals for them + if (!bootstrap) { + out <- .add_dispersion_param_glmmTMB(model, params, effects, component, ci, verbose) + params <- out$params + dispersion_param <- out$dispersion_param + } # exponentiate coefficients and SE/CI, if requested params <- .exponentiate_parameters(params, model, exponentiate) @@ -344,11 +360,18 @@ model_parameters.glmmTMB <- function( attributes(params) <- utils::modifyList(att, attributes(params)) } + # bootstrapped intervals are quantiles of the draws + if (bootstrap) { + ci_method <- "quantile" + } + params <- .add_model_parameters_attributes( params, model, ci = ci, exponentiate, + bootstrap = bootstrap, + iterations = iterations, ci_method = ci_method, p_adjust = p_adjust, verbose = verbose, diff --git a/tests/testthat/test-bootstrap_parameters.R b/tests/testthat/test-bootstrap_parameters.R index 4cccaf0a0..d4112be78 100644 --- a/tests/testthat/test-bootstrap_parameters.R +++ b/tests/testthat/test-bootstrap_parameters.R @@ -498,12 +498,17 @@ test_that("bootstrap_parameters returns components of glmmTMB models", { test_that("bootstrap_parameters matches each glmmTMB draw to its parameter", { skip_if_not_installed("glmmTMB") m <- .fit_zi_glmmTMB(ziformula = ~cover) - estimates <- insight::get_parameters(m)$Estimate + params <- insight::get_parameters(m) set.seed(123) out <- bootstrap_parameters(m, iterations = 50) - distances <- abs(outer(out$Coefficient, estimates, "-")) + own <- match( + paste(out$Parameter, out$Component), + paste(params$Parameter, params$Component) + ) + distances <- abs(outer(out$Coefficient, params$Estimate, "-")) closest <- apply(distances, 1, function(d) which.min(d)[1]) - expect_identical(closest, seq_along(estimates)) + expect_identical(closest, own) + expect_false(anyNA(own)) }) test_that("model_parameters with bootstrap uses component for glmmTMB", { @@ -522,6 +527,10 @@ test_that("model_parameters with bootstrap uses component for glmmTMB", { expect_identical(out$Parameter, expected$Parameter) expect_identical(out$Component, expected$Component) } + expect_error( + model_parameters(m, bootstrap = TRUE, iterations = 5, component = "dispersion"), + "Bootstrapping\\s+does\\s+not\\s+return\\s+dispersion\\s+parameters" + ) }) test_that("model_parameters with bootstrap prints a bootstrap footer for glmmTMB", { @@ -529,8 +538,8 @@ test_that("model_parameters with bootstrap prints a bootstrap footer for glmmTMB for (m in list(.fit_zi_glmmTMB(), .fit_zi_glmmTMB(ziformula = ~0))) { set.seed(123) mp <- model_parameters(m, bootstrap = TRUE, iterations = 20, effects = "fixed") - messages <- capture_messages(printed <- capture.output(print(mp))) - printed <- paste(c(printed, messages), collapse = " ") + printed <- evaluate_promise(print(mp)) + printed <- paste(c(printed$output, printed$messages), collapse = " ") expect_match(printed, "bootstrap", fixed = TRUE) expect_no_match(printed, "Wald", fixed = TRUE) } From 251e764677b6dfe2234d3ac114a584abc0cdeb98 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 19 Oct 2026 11:29:12 -0500 Subject: [PATCH 03/10] Make the NEWS entry for glmmTMB bootstrap output more exact --- NEWS.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/NEWS.md b/NEWS.md index 779f1e7fd..48cb9f620 100644 --- a/NEWS.md +++ b/NEWS.md @@ -42,13 +42,15 @@ * `bootstrap_model()`, `bootstrap_parameters()` and `model_parameters()` with `bootstrap = TRUE` now name the zero-inflation parameters of *glmmTMB* models and show them in their own component. Before this fix, these rows - were named `NA` or had no name. In `bootstrap_model()`, the columns of the - zero-inflation parameters get the suffix `_zi`, as in `simulate_model()`. + were named `NA` or `NA.1` or had no name, and one zero-inflation estimate + was missing. In `bootstrap_model()`, the columns of the zero-inflation + parameters get the suffix `_zi`, as in `simulate_model()`. `model_parameters()` with `bootstrap = TRUE` now uses the `component` - argument, and `component = "dispersion"` gives an error. Its output no longer - has a dispersion row, because the bootstrap draws have no dispersion - parameters. The footer now says that the intervals are bootstrap intervals - (#1284). + argument. For models with a zero-inflation or dispersion formula, + `component = "dispersion"` gives an error, because the bootstrap draws have + no dispersion parameters. For the same reason, the output no longer has a + dispersion row with Wald intervals. The footer now says that the intervals + are bootstrap intervals (#1284). * The `p_adjust` argument in `model_parameters()` now accepts the methods from `stats::p.adjust.methods` in any letter case, for example `"bh"`. Before, From 07d342bb5e4432431824da93c4a61695182f131f Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 19 Oct 2026 11:39:15 -0500 Subject: [PATCH 04/10] Keep added columns in glmmTMB bootstrap summaries and document the dispersion limit --- R/bootstrap_parameters.R | 10 +++++++--- R/methods_glmmTMB.R | 4 ++++ man/model_parameters.glmmTMB.Rd | 4 ++++ tests/testthat/test-bootstrap_parameters.R | 19 +++++++++++++++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/R/bootstrap_parameters.R b/R/bootstrap_parameters.R index a3758b69b..a059b1d6f 100644 --- a/R/bootstrap_parameters.R +++ b/R/bootstrap_parameters.R @@ -123,12 +123,16 @@ model_parameters.bootstrap_model <- bootstrap_parameters.bootstrap_model return(params) } draw_names <- c(fixed_names$conditional, paste0(fixed_names$zero_inflated, "_zi")) + # columns that users added to the draws, like linear combinations, keep + # their names idx <- match(params$Parameter, draw_names) - params$Parameter <- c(fixed_names$conditional, fixed_names$zero_inflated)[idx] - params$Component <- rep( + found <- !is.na(idx) + params$Parameter[found] <- c(fixed_names$conditional, fixed_names$zero_inflated)[idx[found]] + params$Component <- "conditional" + params$Component[found] <- rep( c("conditional", "zero_inflated"), c(length(fixed_names$conditional), length(fixed_names$zero_inflated)) - )[idx] + )[idx[found]] params } diff --git a/R/methods_glmmTMB.R b/R/methods_glmmTMB.R index c76015420..604b6f3cb 100644 --- a/R/methods_glmmTMB.R +++ b/R/methods_glmmTMB.R @@ -132,6 +132,10 @@ #' For models where the dispersion parameter and the residual variance are #' the same, only the residual variance is shown in the output. #' +#' With `bootstrap = TRUE`, the output has no dispersion parameters, because +#' the bootstrapped draws include only the conditional and zero-inflation +#' parameters. `component = "dispersion"` then gives an error. +#' #' @seealso [insight::standardize_names()] to #' rename columns into a consistent, standardized naming scheme. #' diff --git a/man/model_parameters.glmmTMB.Rd b/man/model_parameters.glmmTMB.Rd index ec60d4f7d..fb9cb4189 100644 --- a/man/model_parameters.glmmTMB.Rd +++ b/man/model_parameters.glmmTMB.Rd @@ -305,6 +305,10 @@ sigma(model)^2 # 0.09902987 For models where the dispersion parameter and the residual variance are the same, only the residual variance is shown in the output. + +With \code{bootstrap = TRUE}, the output has no dispersion parameters, because +the bootstrapped draws include only the conditional and zero-inflation +parameters. \code{component = "dispersion"} then gives an error. } \section{Model components}{ diff --git a/tests/testthat/test-bootstrap_parameters.R b/tests/testthat/test-bootstrap_parameters.R index d4112be78..0efc584d0 100644 --- a/tests/testthat/test-bootstrap_parameters.R +++ b/tests/testthat/test-bootstrap_parameters.R @@ -480,6 +480,8 @@ test_that("bootstrap_parameters returns components of glmmTMB models", { } } + skip_if_not_installed("withr") + withr::local_options(width = 300) set.seed(123) mp <- model_parameters( .fit_zi_glmmTMB(), @@ -495,6 +497,23 @@ test_that("bootstrap_parameters returns components of glmmTMB models", { expect_length(grep("^mined \\[no\\]\\s+\\|", zi_rows), 1) }) +test_that("bootstrap_parameters keeps added columns of glmmTMB draws", { + skip_if_not_installed("glmmTMB") + set.seed(123) + b <- bootstrap_model(.fit_zi_glmmTMB(), iterations = 5) + b$lin_comb <- b$minedno - b$minedno_zi + out <- bootstrap_parameters(b) + expect_identical( + out$Parameter, + c("(Intercept)", "minedno", "(Intercept)", "minedno", "lin_comb") + ) + expect_identical( + out$Component, + c("conditional", "conditional", "zero_inflated", "zero_inflated", "conditional") + ) + expect_equal(out$Coefficient[5], median(b$lin_comb), tolerance = 1e-10) +}) + test_that("bootstrap_parameters matches each glmmTMB draw to its parameter", { skip_if_not_installed("glmmTMB") m <- .fit_zi_glmmTMB(ziformula = ~cover) From 95a57fecb1afeda1128442d0dbe29bfac4134ee9 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Mon, 19 Oct 2026 13:58:23 -0500 Subject: [PATCH 05/10] Shorten a line in the glmmTMB bootstrap summary for air --- R/bootstrap_parameters.R | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/R/bootstrap_parameters.R b/R/bootstrap_parameters.R index a059b1d6f..f11b880c0 100644 --- a/R/bootstrap_parameters.R +++ b/R/bootstrap_parameters.R @@ -127,7 +127,8 @@ model_parameters.bootstrap_model <- bootstrap_parameters.bootstrap_model # their names idx <- match(params$Parameter, draw_names) found <- !is.na(idx) - params$Parameter[found] <- c(fixed_names$conditional, fixed_names$zero_inflated)[idx[found]] + fixed_parameters <- c(fixed_names$conditional, fixed_names$zero_inflated) + params$Parameter[found] <- fixed_parameters[idx[found]] params$Component <- "conditional" params$Component[found] <- rep( c("conditional", "zero_inflated"), From ddaaa4bb121b868c7c26fdaa5db954ed86e60433 Mon Sep 17 00:00:00 2001 From: "Mattan S. Ben-Shachar" <35330040+mattansb@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:01:47 +0300 Subject: [PATCH 06/10] Apply suggestion from @mattansb --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index fab3d0b69..582502ba3 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: parameters Title: Processing of Model Parameters -Version: 0.30.0.11 +Version: 0.30.0.12 Authors@R: c(person(given = "Daniel", family = "Lüdecke", From 99c2ce39178b8680292e9f7cf80e73889b687cd1 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Wed, 21 Oct 2026 13:39:11 -0500 Subject: [PATCH 07/10] shorten NEWS entry, docs and comments --- NEWS.md | 16 ++++------------ R/bootstrap_parameters.R | 6 ++---- R/methods_glmmTMB.R | 8 +++----- man/model_parameters.glmmTMB.Rd | 5 ++--- 4 files changed, 11 insertions(+), 24 deletions(-) diff --git a/NEWS.md b/NEWS.md index e1198d892..984f7d7dd 100644 --- a/NEWS.md +++ b/NEWS.md @@ -48,18 +48,10 @@ per row. In the second kind, the response in the formula is not one column of the data, for example `log(y)` or `cbind()`. -* `bootstrap_model()`, `bootstrap_parameters()` and `model_parameters()` with - `bootstrap = TRUE` now name the zero-inflation parameters of *glmmTMB* - models and show them in their own component. Before this fix, these rows - were named `NA` or `NA.1` or had no name, and one zero-inflation estimate - was missing. In `bootstrap_model()`, the columns of the zero-inflation - parameters get the suffix `_zi`, as in `simulate_model()`. - `model_parameters()` with `bootstrap = TRUE` now uses the `component` - argument. For models with a zero-inflation or dispersion formula, - `component = "dispersion"` gives an error, because the bootstrap draws have - no dispersion parameters. For the same reason, the output no longer has a - dispersion row with Wald intervals. The footer now says that the intervals - are bootstrap intervals (#1284). +* Fixed issue where `bootstrap_parameters()` and `model_parameters()` with + `bootstrap = TRUE` mislabeled the zero-inflation parameters of *glmmTMB* + models. `bootstrap_model()` now adds the suffix `_zi` to these parameters, + and bootstrapped output no longer includes dispersion parameters (#1284). * The `p_adjust` argument in `model_parameters()` now accepts the methods from `stats::p.adjust.methods` in any letter case, for example `"bh"`. Before, diff --git a/R/bootstrap_parameters.R b/R/bootstrap_parameters.R index f11b880c0..607d3d66b 100644 --- a/R/bootstrap_parameters.R +++ b/R/bootstrap_parameters.R @@ -112,8 +112,7 @@ model_parameters.bootstrap_model <- bootstrap_parameters.bootstrap_model # utilities -------------------------------------------------------------------- -# bootstrap_model() names the zero-inflation draws of glmmTMB models -# "_zi". Restore the names and add the component, as in model_parameters() +# restore names of zero-inflation parameters ("_zi" suffix) and add component .add_component_bootstrap_glmmTMB <- function(params, model) { if (!inherits(model, "glmmTMB")) { return(params) @@ -123,8 +122,7 @@ model_parameters.bootstrap_model <- bootstrap_parameters.bootstrap_model return(params) } draw_names <- c(fixed_names$conditional, paste0(fixed_names$zero_inflated, "_zi")) - # columns that users added to the draws, like linear combinations, keep - # their names + # user-added columns (e.g. linear combinations) keep their names idx <- match(params$Parameter, draw_names) found <- !is.na(idx) fixed_parameters <- c(fixed_names$conditional, fixed_names$zero_inflated) diff --git a/R/methods_glmmTMB.R b/R/methods_glmmTMB.R index 604b6f3cb..7a03a3e9d 100644 --- a/R/methods_glmmTMB.R +++ b/R/methods_glmmTMB.R @@ -132,9 +132,8 @@ #' For models where the dispersion parameter and the residual variance are #' the same, only the residual variance is shown in the output. #' -#' With `bootstrap = TRUE`, the output has no dispersion parameters, because -#' the bootstrapped draws include only the conditional and zero-inflation -#' parameters. `component = "dispersion"` then gives an error. +#' With `bootstrap = TRUE`, dispersion parameters are not included in the +#' output. #' #' @seealso [insight::standardize_names()] to #' rename columns into a consistent, standardized naming scheme. @@ -314,8 +313,7 @@ model_parameters.glmmTMB <- function( params <- do.call(".extract_parameters_generic", fun_args) } - # add dispersion parameter. bootstrapped draws have no dispersion - # parameters, so we don't add a row with Wald intervals for them + # add dispersion parameter (not for bootstrapped draws, which have none) if (!bootstrap) { out <- .add_dispersion_param_glmmTMB(model, params, effects, component, ci, verbose) params <- out$params diff --git a/man/model_parameters.glmmTMB.Rd b/man/model_parameters.glmmTMB.Rd index d340312e9..9b8c0f394 100644 --- a/man/model_parameters.glmmTMB.Rd +++ b/man/model_parameters.glmmTMB.Rd @@ -306,9 +306,8 @@ sigma(model)^2 # 0.09902987 For models where the dispersion parameter and the residual variance are the same, only the residual variance is shown in the output. -With \code{bootstrap = TRUE}, the output has no dispersion parameters, because -the bootstrapped draws include only the conditional and zero-inflation -parameters. \code{component = "dispersion"} then gives an error. +With \code{bootstrap = TRUE}, dispersion parameters are not included in the +output. } \section{Model components}{ From d9376b33ceae267a2de5fcf9de82ba65ca869091 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Wed, 21 Oct 2026 21:20:35 -0500 Subject: [PATCH 08/10] Trim tests --- tests/testthat/test-bootstrap_parameters.R | 171 ++++++--------------- 1 file changed, 47 insertions(+), 124 deletions(-) diff --git a/tests/testthat/test-bootstrap_parameters.R b/tests/testthat/test-bootstrap_parameters.R index 0efc584d0..d5a6e1a51 100644 --- a/tests/testthat/test-bootstrap_parameters.R +++ b/tests/testthat/test-bootstrap_parameters.R @@ -19,8 +19,26 @@ test_that("bootstrap_model intercept-only", { expect_equal( as.numeric(out), c( - 6.3, 4.8, 7, 6, 6.3, 6.7, 6.6, 6.6, 6.1, 6.1, 6.3, 6.3, 6.1, - 7.2, 5.9, 5.6, 5.7, 6.8, 6.7, 6.4 + 6.3, + 4.8, + 7, + 6, + 6.3, + 6.7, + 6.6, + 6.6, + 6.1, + 6.1, + 6.3, + 6.3, + 6.1, + 7.2, + 5.9, + 5.6, + 5.7, + 6.8, + 6.7, + 6.4 ), tolerance = 1e-2 ) @@ -404,103 +422,18 @@ test_that("bootstrap_model parametric errors for responses that are not a data c ) }) -.fit_zi_glmmTMB <- function(ziformula = ~mined, ...) { - data(Salamanders, package = "glmmTMB") - glmmTMB::glmmTMB( - count ~ mined + (1 | site), - ziformula = ziformula, - family = poisson, - data = Salamanders, - ... - ) -} - -.fit_zi_disp_glmmTMB <- function() { +test_that("bootstrap_parameters returns zero-inflation components of glmmTMB models", { + skip_if_not_installed("glmmTMB") data(Salamanders, package = "glmmTMB") - glmmTMB::glmmTMB( + m <- glmmTMB::glmmTMB( count ~ mined + (1 | site), ziformula = ~mined, - dispformula = ~mined, - family = glmmTMB::nbinom2, + family = poisson, data = Salamanders ) -} - -test_that("bootstrap_model names zero-inflation columns of glmmTMB models", { - skip_if_not_installed("glmmTMB") - skip_if_not_installed("lme4") - zi_names <- c("(Intercept)", "minedno", "(Intercept)_zi", "minedno_zi") - set.seed(123) - expect_named(bootstrap_model(.fit_zi_glmmTMB(), iterations = 5), zi_names) - set.seed(123) - expect_named(bootstrap_model(.fit_zi_disp_glmmTMB(), iterations = 5), zi_names) - set.seed(123) - expect_named( - bootstrap_model(.fit_zi_glmmTMB(ziformula = ~cover), iterations = 5), - c("(Intercept)", "minedno", "(Intercept)_zi", "cover_zi") - ) - - m_cond <- .fit_zi_glmmTMB(ziformula = ~0) - set.seed(123) - expect_named( - bootstrap_model(m_cond, iterations = 5), - insight::find_parameters(m_cond, effects = "fixed")$conditional - ) - data(sleepstudy, package = "lme4") - m_lmer <- lme4::lmer(Reaction ~ Days + (1 | Subject), data = sleepstudy) - set.seed(123) - expect_named( - bootstrap_model(m_lmer, iterations = 5), - insight::find_parameters(m_lmer, effects = "fixed")$conditional - ) -}) - -test_that("bootstrap_parameters returns components of glmmTMB models", { - skip_if_not_installed("glmmTMB") - for (m in list(.fit_zi_glmmTMB(), .fit_zi_disp_glmmTMB())) { - params <- insight::get_parameters(m) - params <- params[params$Component != "dispersion", ] - set.seed(123) - out <- list( - bootstrap_parameters(m, iterations = 20), - model_parameters(m, bootstrap = TRUE, iterations = 20, effects = "fixed"), - model_parameters( - m, - bootstrap = TRUE, - iterations = 20, - effects = "fixed", - exponentiate = TRUE - ) - ) - for (x in out) { - expect_identical(x$Parameter, params$Parameter) - expect_identical(x$Component, params$Component) - expect_false(anyNA(x$Coefficient)) - } - } - - skip_if_not_installed("withr") - withr::local_options(width = 300) - set.seed(123) - mp <- model_parameters( - .fit_zi_glmmTMB(), - bootstrap = TRUE, - iterations = 20, - effects = "fixed" - ) - printed <- capture.output(print(mp)) - zi_start <- grep("# Zero-Inflation", printed, fixed = TRUE) - expect_length(zi_start, 1) - zi_rows <- printed[seq_along(printed) > min(zi_start, Inf)] - expect_length(grep("^\\(Intercept\\)\\s+\\|", zi_rows), 1) - expect_length(grep("^mined \\[no\\]\\s+\\|", zi_rows), 1) -}) - -test_that("bootstrap_parameters keeps added columns of glmmTMB draws", { - skip_if_not_installed("glmmTMB") - set.seed(123) - b <- bootstrap_model(.fit_zi_glmmTMB(), iterations = 5) + b <- bootstrap_model(m, iterations = 5) + expect_named(b, c("(Intercept)", "minedno", "(Intercept)_zi", "minedno_zi")) b$lin_comb <- b$minedno - b$minedno_zi out <- bootstrap_parameters(b) expect_identical( @@ -511,55 +444,45 @@ test_that("bootstrap_parameters keeps added columns of glmmTMB draws", { out$Component, c("conditional", "conditional", "zero_inflated", "zero_inflated", "conditional") ) - expect_equal(out$Coefficient[5], median(b$lin_comb), tolerance = 1e-10) -}) - -test_that("bootstrap_parameters matches each glmmTMB draw to its parameter", { - skip_if_not_installed("glmmTMB") - m <- .fit_zi_glmmTMB(ziformula = ~cover) - params <- insight::get_parameters(m) - set.seed(123) - out <- bootstrap_parameters(m, iterations = 50) - own <- match( - paste(out$Parameter, out$Component), - paste(params$Parameter, params$Component) - ) - distances <- abs(outer(out$Coefficient, params$Estimate, "-")) - closest <- apply(distances, 1, function(d) which.min(d)[1]) - expect_identical(closest, own) - expect_false(anyNA(own)) + estimates <- insight::get_parameters(m)$Estimate + expect_lt(max(abs(out$Coefficient[1:4] - estimates)), 0.5) }) test_that("model_parameters with bootstrap uses component for glmmTMB", { skip_if_not_installed("glmmTMB") - m <- .fit_zi_glmmTMB() - for (comp in c("conditional", "zi")) { + data(Salamanders, package = "glmmTMB") + m <- glmmTMB::glmmTMB( + count ~ mined + (1 | site), + ziformula = ~mined, + dispformula = ~mined, + family = glmmTMB::nbinom2, + data = Salamanders + ) + for (comp in c("all", "conditional", "zi")) { expected <- model_parameters(m, component = comp, effects = "fixed") + if (comp == "all") { + expected <- expected[expected$Component != "dispersion", ] + } set.seed(123) out <- model_parameters( m, bootstrap = TRUE, - iterations = 20, + iterations = 5, component = comp, effects = "fixed" ) expect_identical(out$Parameter, expected$Parameter) expect_identical(out$Component, expected$Component) } + printed <- evaluate_promise(print(out)) + expect_match(paste(printed$messages, collapse = " "), "bootstrap", fixed = TRUE) expect_error( model_parameters(m, bootstrap = TRUE, iterations = 5, component = "dispersion"), "Bootstrapping\\s+does\\s+not\\s+return\\s+dispersion\\s+parameters" ) -}) -test_that("model_parameters with bootstrap prints a bootstrap footer for glmmTMB", { - skip_if_not_installed("glmmTMB") - for (m in list(.fit_zi_glmmTMB(), .fit_zi_glmmTMB(ziformula = ~0))) { - set.seed(123) - mp <- model_parameters(m, bootstrap = TRUE, iterations = 20, effects = "fixed") - printed <- evaluate_promise(print(mp)) - printed <- paste(c(printed$output, printed$messages), collapse = " ") - expect_match(printed, "bootstrap", fixed = TRUE) - expect_no_match(printed, "Wald", fixed = TRUE) - } + m <- glmmTMB::glmmTMB(count ~ mined + (1 | site), family = poisson, data = Salamanders) + set.seed(123) + out <- model_parameters(m, bootstrap = TRUE, iterations = 5, effects = "fixed") + expect_identical(out$Parameter, c("(Intercept)", "minedno")) }) From d95582e67efe12eb64bd1fb10b10c80fcf36742a Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Wed, 21 Oct 2026 21:20:56 -0500 Subject: [PATCH 09/10] Restore formatting of an unchanged test --- tests/testthat/test-bootstrap_parameters.R | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/tests/testthat/test-bootstrap_parameters.R b/tests/testthat/test-bootstrap_parameters.R index d5a6e1a51..1ba451bbd 100644 --- a/tests/testthat/test-bootstrap_parameters.R +++ b/tests/testthat/test-bootstrap_parameters.R @@ -19,26 +19,8 @@ test_that("bootstrap_model intercept-only", { expect_equal( as.numeric(out), c( - 6.3, - 4.8, - 7, - 6, - 6.3, - 6.7, - 6.6, - 6.6, - 6.1, - 6.1, - 6.3, - 6.3, - 6.1, - 7.2, - 5.9, - 5.6, - 5.7, - 6.8, - 6.7, - 6.4 + 6.3, 4.8, 7, 6, 6.3, 6.7, 6.6, 6.6, 6.1, 6.1, 6.3, 6.3, 6.1, + 7.2, 5.9, 5.6, 5.7, 6.8, 6.7, 6.4 ), tolerance = 1e-2 ) From d20dca25d7db60b68c634d3ffe56dc3d1016198d Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Thu, 29 Oct 2026 11:09:37 +0100 Subject: [PATCH 10/10] Set Version to 0.30.0.13 --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index d7c30cb7e..f42bc344d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: parameters Title: Processing of Model Parameters -Version: 0.30.0.12 +Version: 0.30.0.13 Authors@R: c(person(given = "Daniel", family = "Lüdecke",