From 7b2f69cc95b4ed96f803e5ee9a3186714024281d Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 3 Jun 2026 14:58:10 +0200 Subject: [PATCH 1/3] `estimate_contrasts()` gets a `post_process` argument` (multi-step comparisons) --- DESCRIPTION | 4 +- NEWS.md | 3 + R/estimate_contrasts.R | 34 ++++++++ R/get_marginalcontrasts.R | 3 + R/get_marginalmeans.R | 83 ++++++++++++++++++- R/get_marginaltrends.R | 62 ++++++++++---- R/utils.R | 4 +- man/estimate_contrasts.Rd | 34 ++++++++ man/get_emmeans.Rd | 6 ++ man/modelbased-package.Rd | 1 + man/reexports.Rd | 4 +- .../test-estimate_contrasts_post_process.R | 39 +++++++++ 12 files changed, 252 insertions(+), 25 deletions(-) create mode 100644 tests/testthat/test-estimate_contrasts_post_process.R diff --git a/DESCRIPTION b/DESCRIPTION index 19e339cb4..9806d965a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: modelbased Title: Estimation of Model-Based Predictions, Contrasts and Means -Version: 0.15.0.2 +Version: 0.15.0.3 Authors@R: c(person(given = "Dominique", family = "Makowski", @@ -114,10 +114,10 @@ VignetteBuilder: knitr Encoding: UTF-8 Language: en-US -RoxygenNote: 7.3.3 Config/testthat/edition: 3 Config/testthat/parallel: true Roxygen: list(markdown = TRUE) Config/Needs/check: stan-dev/cmdstanr Config/Needs/website: easystats/easystatstemplate LazyData: true +Config/roxygen2/version: 8.0.0 diff --git a/NEWS.md b/NEWS.md index 5915fffe3..771345e08 100644 --- a/NEWS.md +++ b/NEWS.md @@ -5,6 +5,9 @@ * Informative error message when the `trend` variable in `estimate_slopes()` is not numeric and `backend = "emmeans"`. +* `estimate_contrasts()` gets a `post_process` argument, to process subsequent + comparisons. It allows for complex, multi-step comparisons. + # modelbased 0.15.0 ## Breaking Changes diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index a65d95670..7861863ed 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -77,6 +77,10 @@ #' `difference` or `ratio`, or skipped). #' * If contrasts should be calculated (or grouped by) factors, `comparison` #' can also be a matrix that specifies factor contrasts (see 'Examples'). +#' @param post_process Optional formula, or list of formulas, to process subsequent +#' comparisons. It allows for complex, multi-step comparisons. After the initial +#' comparison in `comparison` is completed, the results are then post-processed +#' using the specified formula(s). #' @param effectsize Desired measure of standardized effect size, one of #' `"emmeans"`, `"marginal"`, or `"boot"`. Default is `NULL`, i.e. no effect #' size will be computed. @@ -368,6 +372,34 @@ #' # are differences in time trends of context effects statistically significant #' # between education levels? #' estimate_contrasts(model, c("phq4_within", "phq4_between", "education")) +#' +#' # Post-processing of multiple comparisons --------------------- +#' # Caution! Don't expect this example to be meaningful! # It is +#' # just to demonstrate the usage of the `post_process` argument. +#' # ------------------------------------------------------------- +#' data("qol_cancer", package = "parameters") +#' model <- lme4::lmer(QoL ~ time * education + (1 + time | ID), data = qol_cancer) +#' +#' # contrasts (pairwise comparisons) by timepoints - the default +#' estimate_contrasts(model, "education=c('low', 'mid')", by = "time") +#' +#' # contrasts (pairwise comparisons) by timepoints, the default for `comparison` +#' # additionally, we compare the differences of these contrasts across timepoints +#' # against the reference time point (contrasts at times 2 and 3 against +#' # contrasts at time 1) +#' estimate_contrasts(model, +#' contrasts = "education=c('low', 'mid')", +#' by = "time", +#' post_process = ~reference +#' ) +#' +#' # multiple post-processing steps - same as before, but calculates +#' # poly-contrasts in addition to reference contrasts +#' estimate_contrasts(model, +#' contrasts = "education=c('low', 'mid')", +#' by = "time", +#' post_process = list(~reference, ~poly) +#' ) #' } #' #' @return A data frame of estimated contrasts. @@ -389,6 +421,7 @@ estimate_contrasts.default <- function( estimate = NULL, p_adjust = "none", transform = NULL, + post_process = NULL, keep_iterations = FALSE, effectsize = NULL, iterations = 200, @@ -441,6 +474,7 @@ estimate_contrasts.default <- function( ci = ci, estimate = estimate, transform = transform, + post_process = post_process, keep_iterations = keep_iterations, verbose = verbose, ... diff --git a/R/get_marginalcontrasts.R b/R/get_marginalcontrasts.R index 1c49b7799..825246c65 100644 --- a/R/get_marginalcontrasts.R +++ b/R/get_marginalcontrasts.R @@ -9,6 +9,7 @@ get_marginalcontrasts <- function( comparison = "pairwise", estimate = NULL, transform = NULL, + post_process = NULL, p_adjust = "none", keep_iterations = FALSE, verbose = TRUE, @@ -123,6 +124,7 @@ get_marginalcontrasts <- function( hypothesis = my_args$comparison_slopes, backend = "marginaleffects", transform = transform, + post_process = post_process, keep_iterations = keep_iterations, verbose = verbose, ... @@ -139,6 +141,7 @@ get_marginalcontrasts <- function( backend = "marginaleffects", estimate = estimate, transform = transform, + post_process = post_process, keep_iterations = keep_iterations, verbose = verbose, .joint_test = my_args$joint_test, diff --git a/R/get_marginalmeans.R b/R/get_marginalmeans.R index fce5f4621..6754937c5 100644 --- a/R/get_marginalmeans.R +++ b/R/get_marginalmeans.R @@ -40,6 +40,7 @@ get_marginalmeans <- function( dots <- list(...) comparison <- dots$hypothesis + post_process <- dots$post_process joint_test <- isTRUE(dots$.joint_test) # validate input @@ -86,7 +87,7 @@ get_marginalmeans <- function( # fmt: skip dots[c( "by", "conf_level", "type", "digits", "bias_correction", "sigma", - "offset", ".joint_test" + "offset", ".joint_test", "post_process" )] <- NULL # model df - can be passed via `...` @@ -184,7 +185,7 @@ get_marginalmeans <- function( # we can use this function for contrasts as well, # just need to add "hypothesis" argument - means <- .call_marginaleffects(fun_args) + means <- .call_marginaleffects(fun_args, post_process, verbose) vcov_means <- .safe(stats::vcov(means)) # intermediate step: joint tests -------------------------------------------- @@ -253,9 +254,14 @@ get_marginalmeans <- function( # call marginaleffects and process potential errors --------------------------- -.call_marginaleffects <- function(fun_args, type = "means") { +.call_marginaleffects <- function(fun_args, post_process = NULL, verbose = TRUE) { out <- tryCatch( - suppressWarnings(do.call(marginaleffects::avg_predictions, fun_args)), + { + # call marginaleffects + result <- suppressWarnings(do.call(marginaleffects::avg_predictions, fun_args)) + # process subsequential comparisons, if any + .post_process_comparisons(result, post_process, verbose) + }, error = function(e) e ) @@ -272,6 +278,75 @@ get_marginalmeans <- function( out } +# process subsequential comparisons --------------------------- + +.post_process_comparisons <- function(out, post_process = NULL, verbose = TRUE) { + if (!is.null(post_process)) { + # coerce to list + if (!is.list(post_process)) { + post_process <- list(post_process) + } + # save temporary parameter names + temp_params <- .safe(out$hypothesis) + # parameter names from groups + group_params <- .safe(out[seq_len(which(colnames(out) == "estimate") - 1)[-1]]) + # check + for (i in seq_along(post_process)) { + if (verbose) { + insight::format_alert(paste0( + "Post-processing ", + deparse(post_process[[i]]), + "..." + )) + } + out <- suppressWarnings(marginaleffects::hypotheses( + out, + hypothesis = post_process[[i]] + )) + temp_params <- .update_post_process_params(out, temp_params, group_params) + } + # check if extractinb parameters worked + if (length(temp_params) == nrow(out)) { + out$hypothesis <- temp_params + } + } + out +} + +.update_post_process_params <- function(out, temp_params, group_params) { + if (is.null(temp_params)) { + return(NULL) + } + .safe( + { + # clean current parameter names + temp_params <- gsub("(", "", gsub(")", "", temp_params, fixed = TRUE), fixed = TRUE) + # extract rownumbers (from b-cofficients) from current output + lhs <- as.numeric(gsub("\\(b(\\d+)\\) - \\(b(\\d+)\\)", "\\1", out$hypothesis)) + rhs <- as.numeric(gsub("\\(b(\\d+)\\) - \\(b(\\d+)\\)", "\\2", out$hypothesis)) + # update current parameter names + if (all(temp_params[lhs] == temp_params[rhs])) { + temp_params <- temp_params[lhs] + } else { + temp_params <- paste(temp_params[lhs], "-", temp_params[rhs]) + } + if (!is.null(group_params)) { + for (i in colnames(group_params)) { + temp_params <- paste0( + temp_params, + ", ", + group_params[lhs, i], + " - ", + group_params[rhs, i] + ) + } + } + temp_params + }, + quietly = TRUE + ) +} + # create data grid for marginal means ---------------------------------------- # diff --git a/R/get_marginaltrends.R b/R/get_marginaltrends.R index 52dee9bae..c12f7c13e 100644 --- a/R/get_marginaltrends.R +++ b/R/get_marginaltrends.R @@ -84,7 +84,7 @@ get_marginaltrends <- function( } # remove user-arguments from "..." that will be used when calling marginaleffects - dots[c("by", "conf_level", "digits")] <- NULL + dots[c("by", "conf_level", "digits", "post_process")] <- NULL # handle weights - argument is named "wts" in marginal effects if (!is.null(dots$weights)) { @@ -136,6 +136,10 @@ get_marginaltrends <- function( estimated <- suppressWarnings(do.call(marginaleffects::avg_slopes, fun_args)) vcov_slopes <- .safe(stats::vcov(estimated)) + # any subsequent comparisons? ----------------------------------------------- + # --------------------------------------------------------------------------- + estimated <- .post_process_comparisons(estimated, myargs$post_process) + # Fourth step: back-transform response -------------------------------------- # --------------------------------------------------------------------------- @@ -199,11 +203,13 @@ get_marginaltrends <- function( # ========================================================================= #' @keywords internal -.guess_marginaltrends_arguments <- function(model, - trend = NULL, - by = NULL, - verbose = TRUE, - ...) { +.guess_marginaltrends_arguments <- function( + model, + trend = NULL, + by = NULL, + verbose = TRUE, + ... +) { # Gather info model_data <- insight::get_data(model, verbose = FALSE) predictors <- intersect( @@ -215,10 +221,16 @@ get_marginaltrends <- function( if (is.null(trend)) { trend <- predictors[sapply(model_data[predictors], is.numeric)][1] if (!length(trend) || is.na(trend)) { - insight::format_error("Model contains no numeric predictor. Please specify `trend`.") + insight::format_error( + "Model contains no numeric predictor. Please specify `trend`." + ) } if (verbose) { - insight::format_alert(paste0("No numeric variable was specified for slope estimation. Selecting `trend = \"", trend, "\"`.")) # nolint + insight::format_alert(paste0( + "No numeric variable was specified for slope estimation. Selecting `trend = \"", + trend, + "\"`." + )) # nolint } } @@ -226,8 +238,16 @@ get_marginaltrends <- function( if (length(trend) > 1) { if (verbose) { insight::format_alert(paste0( - "More than one numeric variable was selected for slope estimation. Keeping only `", trend[1], "`. ", # nolint - "If you want to estimate the slope of `", trend[1], "` at different values of `", trend[2], "`, use `by=\"", trend[2], "\"` instead." # nolint + "More than one numeric variable was selected for slope estimation. Keeping only `", + trend[1], + "`. ", # nolint + "If you want to estimate the slope of `", + trend[1], + "` at different values of `", + trend[2], + "`, use `by=\"", + trend[2], + "\"` instead." # nolint )) } trend <- trend[1] @@ -246,18 +266,28 @@ get_marginaltrends <- function( if (!is.null(by) && !is.null(range) && startsWith(by, trend)) { insight::format_error( paste0( - "To calculate average marginal effects over a range of `", trend, "` ", - "values, use `trend=\"", trend, "=seq(1, 3, 0.1)\"` (or similar) and omit `", - trend, "` from the `by` argument." + "To calculate average marginal effects over a range of `", + trend, + "` ", + "values, use `trend=\"", + trend, + "=seq(1, 3, 0.1)\"` (or similar) and omit `", + trend, + "` from the `by` argument." ), paste0( - "To get marginal effects at specific `", trend, "` values, use `trend=\"", - trend, "\"` along with `by=\"", trend, "=c(1, 3, 5)\"`." + "To get marginal effects at specific `", + trend, + "` values, use `trend=\"", + trend, + "\"` along with `by=\"", + trend, + "=c(1, 3, 5)\"`." ) ) } - list(trend = trend, range = range, by = NULL) + list(trend = trend, range = range, by = NULL, post_process = dots$post_process) } diff --git a/R/utils.R b/R/utils.R index 8ad63009d..2ccdfc55f 100644 --- a/R/utils.R +++ b/R/utils.R @@ -138,9 +138,11 @@ #' @keywords internal #' @noRd -.safe <- function(code, on_error = NULL) { +.safe <- function(code, on_error = NULL, quietly = FALSE) { if (isTRUE(getOption("easystats_errors", FALSE)) && is.null(on_error)) { code + } else if (quietly) { + suppressWarnings(tryCatch(code, error = function(e) on_error)) } else { tryCatch(code, error = function(e) on_error) } diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 553944c65..b47e27b56 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -17,6 +17,7 @@ estimate_contrasts(model, ...) estimate = NULL, p_adjust = "none", transform = NULL, + post_process = NULL, keep_iterations = FALSE, effectsize = NULL, iterations = 200, @@ -240,6 +241,11 @@ which case \code{insight::get_transformation()} is called to determine the appropriate transformation-function. Note that no standard errors are returned when transformations are applied.} +\item{post_process}{Optional formula, or list of formulas, to process subsequent +comparisons. It allows for complex, multi-step comparisons. After the initial +comparison in \code{comparison} is completed, the results are then post-processed +using the specified formula(s).} + \item{keep_iterations}{If \code{TRUE}, will keep all iterations (draws) of bootstrapped or Bayesian models. They will be added as additional columns named \code{iter_1}, \code{iter_2}, and so on. If \code{keep_iterations} is a positive @@ -686,6 +692,34 @@ estimate_contrasts(model, c("phq4_within", "phq4_between"), by = "education") # are differences in time trends of context effects statistically significant # between education levels? estimate_contrasts(model, c("phq4_within", "phq4_between", "education")) + +# Post-processing of multiple comparisons --------------------- +# Caution! Don't expect this example to be meaningful! # It is +# just to demonstrate the usage of the `post_process` argument. +# ------------------------------------------------------------- +data("qol_cancer", package = "parameters") +model <- lme4::lmer(QoL ~ time * education + (1 + time | ID), data = qol_cancer) + +# contrasts (pairwise comparisons) by timepoints - the default +estimate_contrasts(model, "education=c('low', 'mid')", by = "time") + +# contrasts (pairwise comparisons) by timepoints, the default for `comparison` +# additionally, we compare the differences of these contrasts across timepoints +# against the reference time point (contrasts at times 2 and 3 against +# contrasts at time 1) +estimate_contrasts(model, + contrasts = "education=c('low', 'mid')", + by = "time", + post_process = ~reference +) + +# multiple post-processing steps - same as before, but calculates +# poly-contrasts in addition to reference contrasts +estimate_contrasts(model, + contrasts = "education=c('low', 'mid')", + by = "time", + post_process = list(~reference, ~poly) +) } \dontshow{\}) # examplesIf} } diff --git a/man/get_emmeans.Rd b/man/get_emmeans.Rd index 2be80529a..0b116caf8 100644 --- a/man/get_emmeans.Rd +++ b/man/get_emmeans.Rd @@ -50,6 +50,7 @@ get_marginalcontrasts( comparison = "pairwise", estimate = NULL, transform = NULL, + post_process = NULL, p_adjust = "none", keep_iterations = FALSE, verbose = TRUE, @@ -301,6 +302,11 @@ which case \code{insight::get_transformation()} is called to determine the appropriate transformation-function. Note that no standard errors are returned when transformations are applied.} +\item{post_process}{Optional formula, or list of formulas, to process subsequent +comparisons. It allows for complex, multi-step comparisons. After the initial +comparison in \code{comparison} is completed, the results are then post-processed +using the specified formula(s).} + \item{p_adjust}{The p-values adjustment method for frequentist multiple comparisons. For \code{estimate_slopes()}, multiple comparison only occurs for Johnson-Neyman intervals, i.e. in case of interactions with two numeric diff --git a/man/modelbased-package.Rd b/man/modelbased-package.Rd index 7630714ac..0797d2e6e 100644 --- a/man/modelbased-package.Rd +++ b/man/modelbased-package.Rd @@ -25,6 +25,7 @@ Useful links: Authors: \itemize{ + \item Dominique Makowski \email{officialeasystats@gmail.com} (\href{https://orcid.org/0000-0001-5375-9967}{ORCID}) \item Daniel Lüdecke \email{d.luedecke@uke.de} (\href{https://orcid.org/0000-0002-8895-3206}{ORCID}) \item Mattan S. Ben-Shachar \email{matanshm@post.bgu.ac.il} (\href{https://orcid.org/0000-0002-4287-4801}{ORCID}) \item Indrajeet Patil \email{patilindrajeet.science@gmail.com} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID}) diff --git a/man/reexports.Rd b/man/reexports.Rd index ff5cbfba0..beb6f51ed 100644 --- a/man/reexports.Rd +++ b/man/reexports.Rd @@ -16,8 +16,8 @@ These objects are imported from other packages. Follow the links below to see their documentation. \describe{ - \item{datawizard}{\code{\link[datawizard]{standardize}}, \code{\link[datawizard:standardize]{unstandardize}}, \code{\link[datawizard]{visualisation_recipe}}} + \item{datawizard}{\code{\link[datawizard:standardize]{standardize()}}, \code{\link[datawizard:unstandardize]{unstandardize()}}, \code{\link[datawizard:visualisation_recipe]{visualisation_recipe()}}} - \item{insight}{\code{\link[insight]{display}}, \code{\link[insight:display]{print_html}}, \code{\link[insight:display]{print_md}}} + \item{insight}{\code{\link[insight:display]{display()}}, \code{\link[insight:print_html]{print_html()}}, \code{\link[insight:print_md]{print_md()}}} }} diff --git a/tests/testthat/test-estimate_contrasts_post_process.R b/tests/testthat/test-estimate_contrasts_post_process.R new file mode 100644 index 000000000..f134ed0ce --- /dev/null +++ b/tests/testthat/test-estimate_contrasts_post_process.R @@ -0,0 +1,39 @@ +skip_on_cran() +skip_on_os("mac") +skip_if(getRversion() < "4.5.0") +skip_if_not_installed("marginaleffects", minimum_version = "0.29.0") +skip_if_not_installed("lme4") +skip_if_not_installed("datawizard") +skip_if_not_installed("parameters") + +test_that("estimate_contrast, post_process", { + data("qol_cancer", package = "parameters") + model <- lme4::lmer(QoL ~ time * education + (1 + time | ID), data = qol_cancer) + + out1 <- estimate_contrasts(model, "education=c('low', 'mid')", by = "time") + expect_named( + out1, + c("Level1", "Level2", "time", "Difference", "SE", "CI_low", "CI_high", "t", "df", "p") + ) + expect_equal(out1$Difference, c(6.0324, 8.8203, 11.6082), tolerance = 1e-3) + + out2 <- estimate_contrasts( + model, + "education=c('low', 'mid')", + by = "time", + post_process = ~reference + ) + expect_named(out2, c("Parameter", "Difference", "SE", "CI_low", "CI_high", "z", "p")) + expect_equal(out2$Difference, c(2.7879, 5.5758), tolerance = 1e-3, ignore_attr = TRUE) + expect_identical(out2$Parameter, c("mid - low, 2 - 1", "mid - low, 3 - 1")) + + out3 <- estimate_contrasts( + model, + "education=c('low', 'mid')", + by = "time", + post_process = list(~reference, ~poly) + ) + expect_named(out3, c("Parameter", "Difference", "SE", "CI_low", "CI_high", "z", "p")) + expect_equal(out3$Difference, 1.9713, tolerance = 1e-3, ignore_attr = TRUE) + expect_identical(out3$Parameter, "Linear") +}) From c511230d290037b43add957c5e5b6196dfc898dc Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 3 Jun 2026 15:02:53 +0200 Subject: [PATCH 2/3] docs --- R/estimate_contrasts.R | 9 +++++---- man/estimate_contrasts.Rd | 9 +++++---- man/get_emmeans.Rd | 9 +++++---- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 7861863ed..a83c0bda4 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -77,10 +77,11 @@ #' `difference` or `ratio`, or skipped). #' * If contrasts should be calculated (or grouped by) factors, `comparison` #' can also be a matrix that specifies factor contrasts (see 'Examples'). -#' @param post_process Optional formula, or list of formulas, to process subsequent -#' comparisons. It allows for complex, multi-step comparisons. After the initial -#' comparison in `comparison` is completed, the results are then post-processed -#' using the specified formula(s). +#' @param post_process Optional formula, character string or function (see +#' `comparison`), or a list of formulas, string or functions, to process +#' subsequent, multi-step comparisons. After the initial comparison in +#' `comparison` is completed, the results are then post-processed using the +#' specified post-process tests. See 'Exmaples'. #' @param effectsize Desired measure of standardized effect size, one of #' `"emmeans"`, `"marginal"`, or `"boot"`. Default is `NULL`, i.e. no effect #' size will be computed. diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index b47e27b56..4e034150e 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -241,10 +241,11 @@ which case \code{insight::get_transformation()} is called to determine the appropriate transformation-function. Note that no standard errors are returned when transformations are applied.} -\item{post_process}{Optional formula, or list of formulas, to process subsequent -comparisons. It allows for complex, multi-step comparisons. After the initial -comparison in \code{comparison} is completed, the results are then post-processed -using the specified formula(s).} +\item{post_process}{Optional formula, character string or function (see +\code{comparison}), or a list of formulas, string or functions, to process +subsequent, multi-step comparisons. After the initial comparison in +\code{comparison} is completed, the results are then post-processed using the +specified post-process tests. See 'Exmaples'.} \item{keep_iterations}{If \code{TRUE}, will keep all iterations (draws) of bootstrapped or Bayesian models. They will be added as additional columns diff --git a/man/get_emmeans.Rd b/man/get_emmeans.Rd index 0b116caf8..6fda80514 100644 --- a/man/get_emmeans.Rd +++ b/man/get_emmeans.Rd @@ -302,10 +302,11 @@ which case \code{insight::get_transformation()} is called to determine the appropriate transformation-function. Note that no standard errors are returned when transformations are applied.} -\item{post_process}{Optional formula, or list of formulas, to process subsequent -comparisons. It allows for complex, multi-step comparisons. After the initial -comparison in \code{comparison} is completed, the results are then post-processed -using the specified formula(s).} +\item{post_process}{Optional formula, character string or function (see +\code{comparison}), or a list of formulas, string or functions, to process +subsequent, multi-step comparisons. After the initial comparison in +\code{comparison} is completed, the results are then post-processed using the +specified post-process tests. See 'Exmaples'.} \item{p_adjust}{The p-values adjustment method for frequentist multiple comparisons. For \code{estimate_slopes()}, multiple comparison only occurs for From 7434629a79bcf72a52092c0823c21ac82ce3acbf Mon Sep 17 00:00:00 2001 From: Daniel Date: Wed, 3 Jun 2026 15:48:49 +0200 Subject: [PATCH 3/3] fix --- R/get_marginaltrends.R | 1 + 1 file changed, 1 insertion(+) diff --git a/R/get_marginaltrends.R b/R/get_marginaltrends.R index c12f7c13e..c6a51fd40 100644 --- a/R/get_marginaltrends.R +++ b/R/get_marginaltrends.R @@ -211,6 +211,7 @@ get_marginaltrends <- function( ... ) { # Gather info + dots <- list(...) model_data <- insight::get_data(model, verbose = FALSE) predictors <- intersect( colnames(model_data),