From 31f1596798275c460eb0e3743fa8e63fd08d0efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sun, 7 May 2023 20:03:44 -0400 Subject: [PATCH 01/16] add effect size (cohen's d) to estimate_contrasts --- DESCRIPTION | 2 +- R/estimate_contrasts.R | 25 ++++++++++++++++++++++++ tests/testthat/test-estimate_contrasts.R | 22 ++++++++++----------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index fed248f48..c1acc50b5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,7 +68,7 @@ VignetteBuilder: knitr Encoding: UTF-8 Language: en-US -RoxygenNote: 7.2.3.9000 +RoxygenNote: 7.2.3 Config/testthat/edition: 3 Config/testthat/parallel: true Roxygen: list(markdown = TRUE) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index f5aed15b5..fe3aa59bb 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -135,6 +135,31 @@ estimate_contrasts <- function(model, contrasts$contrast <- NULL contrasts <- cbind(level_cols, contrasts) + # Add effect size (Cohen's d) + if (is.null(contrast) && is.null(fixed) && is.null(at)) { + dat <- insight::get_data(model) + resp <- insight::find_response(model) + + if (is.numeric(dat[[resp]])) { + dat <- datawizard::data_select(dat, c(resp, info$contrast)) + + list.dat <- lapply(seq_len(nrow(contrasts)), function(i) { + log.vec <- which(dat[[info$contrast]] == unlist(info$misc$orig.grid)[[i]]) + dat.temp <- datawizard::data_filter(dat, log.vec) + }) + list.dat <- stats::setNames(list.dat, unique(unlist(level_cols))) + + eff <- lapply(seq_len(nrow(contrasts)), function(i) { + effectsize::cohens_d(x = list.dat[[contrasts$Level1[i]]][[resp]], + y = list.dat[[contrasts$Level2[i]]][[resp]], + verbose = FALSE) + }) + + eff <- do.call(rbind, eff) + names(eff)[-1] <- paste0("Cohens_d_", names(eff)[-1]) + contrasts <- cbind(contrasts, eff) + } + } # Table formatting attr(contrasts, "table_title") <- c("Marginal Contrasts Analysis", "blue") diff --git a/tests/testthat/test-estimate_contrasts.R b/tests/testthat/test-estimate_contrasts.R index 9a2db2b0c..dd505878c 100644 --- a/tests/testthat/test-estimate_contrasts.R +++ b/tests/testthat/test-estimate_contrasts.R @@ -8,7 +8,7 @@ test_that("estimate_contrasts - Frequentist", { model <- lm(Sepal.Width ~ Species, data = dat) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) estim <- suppressMessages(estimate_contrasts(model, at = "Species=c('versicolor', 'virginica')")) expect_equal(dim(estim), c(1, 9)) @@ -21,16 +21,16 @@ test_that("estimate_contrasts - Frequentist", { model <- lm(Sepal.Width ~ Species * fac, data = dat) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) estim <- suppressMessages(estimate_contrasts(model, levels = "Species")) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) estim <- suppressMessages(estimate_contrasts(model, fixed = "fac")) expect_equal(dim(estim), c(3, 10)) # One factor and one continuous model <- lm(Sepal.Width ~ Species * Petal.Width, data = iris) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) estim <- suppressMessages(estimate_contrasts(model, fixed = "Petal.Width")) expect_equal(dim(estim), c(3, 10)) estim <- suppressMessages(estimate_contrasts(model, at = "Petal.Width", length = 4)) @@ -84,7 +84,7 @@ test_that("estimate_contrasts - Frequentist", { model <- lme4::lmer(Sepal.Width ~ Species + (1 | Petal.Length_factor), data = data) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) # GLM - binomial @@ -107,7 +107,7 @@ test_that("estimate_contrasts - Frequentist", { model <- glm(counts ~ treatment, data = dat, family = poisson()) estim <- suppressMessages(estimate_contrasts(model, transform = "response")) - expect_equal(dim(estim), c(3, 9)) + expect_equal(dim(estim), c(3, 13)) }) @@ -145,7 +145,7 @@ test_that("estimate_contrasts - Bayesian", { ) ) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 7)) + expect_equal(dim(estim), c(3, 11)) estim <- suppressMessages(estimate_contrasts(model, fixed = "Petal.Width")) expect_equal(dim(estim), c(3, 8)) estim <- suppressMessages(estimate_contrasts(model, at = "Petal.Width", length = 4)) @@ -161,14 +161,14 @@ test_that("estimate_contrasts - Bayesian", { )) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 7)) + expect_equal(dim(estim), c(3, 11)) estim <- suppressMessages(estimate_contrasts(model, transform = "response")) - expect_equal(dim(estim), c(3, 7)) + expect_equal(dim(estim), c(3, 11)) estim <- suppressWarnings(suppressMessages(estimate_contrasts(model, test = "bf"))) - expect_equal(dim(estim), c(3, 6)) + expect_equal(dim(estim), c(3, 10)) estim <- suppressWarnings(suppressMessages(estimate_contrasts(model, transform = "response", test = "bf"))) - expect_equal(dim(estim), c(3, 6)) + expect_equal(dim(estim), c(3, 10)) }) From d439676627e8d6476ce8799b5a86e55b0672b186 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sat, 1 Jul 2023 16:25:35 -0400 Subject: [PATCH 02/16] `estimate_contrasts`: now supports optional standardized effect sizes, one of "none" (default), "emmeans", or "bootES" --- DESCRIPTION | 8 ++- NEWS.md | 2 + R/estimate_contrasts.R | 66 +++++++++++++++++------- man/estimate_contrasts.Rd | 11 ++++ man/modelbased-package.Rd | 1 + tests/testthat/test-estimate_contrasts.R | 22 ++++---- 6 files changed, 79 insertions(+), 31 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index c1acc50b5..5d328649b 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,7 +22,12 @@ Authors@R: family = "Patil", role = "aut", email = "patilindrajeet.science@gmail.com", - comment = c(ORCID = "0000-0003-1995-6531", Twitter = "@patilindrajeets"))) + comment = c(ORCID = "0000-0003-1995-6531", Twitter = "@patilindrajeets")), + person(given = "Rémi", + family = "Thériault", + role = c("aut"), + email = "remi.theriault@mail.mcgill.ca", + comment = c(ORCID = "0000-0003-4315-6788", Twitter = "@rempsyc"))) Maintainer: Dominique Makowski Description: Implements a general interface for model-based estimations for a wide variety of models (see list of supported models using the @@ -63,6 +68,7 @@ Suggests: rstanarm, rtdists, see (>= 0.7.4), + bootES, testthat VignetteBuilder: knitr diff --git a/NEWS.md b/NEWS.md index f18b73e76..7f94f96af 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,5 +1,7 @@ # modelbased (development version) +- `estimate_contrasts`: now supports optional standardized effect sizes, one of "none" (default), "emmeans", or "bootES" (#227, @rempsyc). + # modelbased 0.8.6 ## Breaking Changes diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index fe3aa59bb..353fd7ccd 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -11,6 +11,13 @@ #' "bonferroni", "BH", "BY", "fdr" or "none". See the p-value adjustment #' section in the `emmeans::test` documentation. #' @param adjust Deprecated in favour of `p_adjust`. +#' @param effectsize Desired measure of standardized effect size, one of "none" +#' (default), "emmeans", "bootES". +#' @param bootES_type Specifies the type of effect-size measure to +#' estimate when using `effectsize = "bootES"`. One of `c("unstandardized", +#' "cohens.d", "hedges.g", "cohens.d.sigma", "r", "akp.robust.d")`. See` +#' effect.type` argument of [bootES::bootES] for details. +#' @param bootstraps The number of bootstrap resamples to perform. #' #' @inherit estimate_slopes details #' @@ -83,6 +90,9 @@ estimate_contrasts <- function(model, p_adjust = "holm", method = "pairwise", adjust = NULL, + effectsize = "none", + bootstraps = 200, + bootES_type = "cohens.d", ...) { # Deprecation if (!is.null(adjust)) { @@ -135,30 +145,48 @@ estimate_contrasts <- function(model, contrasts$contrast <- NULL contrasts <- cbind(level_cols, contrasts) - # Add effect size (Cohen's d) - if (is.null(contrast) && is.null(fixed) && is.null(at)) { - dat <- insight::get_data(model) - resp <- insight::find_response(model) + # Add standardized effect size + if (!effectsize %in% c("none", "emmeans", "bootES")) { + message("Unsupported effect size '", effectsize, "', returning none.") + } + + if (effectsize == "emmeans") { + eff <- emmeans::eff_size( + estimated, sigma = stats::sigma(model), + edf = stats::df.residual(model), method = "identity") + eff <- as.data.frame(eff) + eff <- eff[c(2, 5:6)] + names(eff) <- c("effect_size", "es_CI_low", "es_CI_high") + contrasts <- cbind(contrasts, eff) - if (is.numeric(dat[[resp]])) { - dat <- datawizard::data_select(dat, c(resp, info$contrast)) + } else if (effectsize == "bootES") { + insight::check_if_installed("bootES") + dat <- insight::get_data(model) + resp <- insight::find_response(model) + group <- names(estimated@model.info$xlev) + contrast <- estimated@misc$con.coef - list.dat <- lapply(seq_len(nrow(contrasts)), function(i) { - log.vec <- which(dat[[info$contrast]] == unlist(info$misc$orig.grid)[[i]]) - dat.temp <- datawizard::data_filter(dat, log.vec) - }) - list.dat <- stats::setNames(list.dat, unique(unlist(level_cols))) + contrast <- lapply(seq_len(nrow(contrast)), function(x) { + contrast[x, ] + }) - eff <- lapply(seq_len(nrow(contrasts)), function(i) { - effectsize::cohens_d(x = list.dat[[contrasts$Level1[i]]][[resp]], - y = list.dat[[contrasts$Level2[i]]][[resp]], - verbose = FALSE) - }) + es.lists <- lapply(contrast, function(x) { + y <- bootES::bootES( + data = stats::na.omit(insight::get_data(model)), + R = bootstraps, + data.col = insight::find_response(model), + group.col = insight::find_predictors(model)[[1]], + contrast = x, + effect.type = bootES_type + ) + y <- as.data.frame(summary(y))}) + + eff <- do.call(rbind, es.lists) + eff <- eff[1:3] + names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), + paste0(bootES_type, "es_CI_high")) - eff <- do.call(rbind, eff) - names(eff)[-1] <- paste0("Cohens_d_", names(eff)[-1]) contrasts <- cbind(contrasts, eff) - } } # Table formatting diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 250bf0168..4abb4624f 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -14,6 +14,9 @@ estimate_contrasts( p_adjust = "holm", method = "pairwise", adjust = NULL, + effectsize = "none", + bootstraps = 200, + bootES_type = "cohens.d", ... ) } @@ -53,6 +56,14 @@ section in the \code{emmeans::test} documentation.} \item{adjust}{Deprecated in favour of \code{p_adjust}.} +\item{effectsize}{Desired measure of standardized effect size, one of "none" +(default), "emmeans", "bootES".} + +\item{bootstraps}{The number of bootstrap resamples to perform.} + +\item{bootES_type}{Specifies the type of effect-size measure to +estimate when using \code{effectsize = "bootES"}. One of \code{c("unstandardized", "cohens.d", "hedges.g", "cohens.d.sigma", "r", "akp.robust.d")}. See\code{ effect.type} argument of \link[bootES:bootES]{bootES::bootES} for details.} + \item{...}{Other arguments passed for instance to \code{\link[insight:get_datagrid]{insight::get_datagrid()}}.} } \value{ diff --git a/man/modelbased-package.Rd b/man/modelbased-package.Rd index 6ad16b23b..a38d0a764 100644 --- a/man/modelbased-package.Rd +++ b/man/modelbased-package.Rd @@ -29,6 +29,7 @@ Authors: \item Daniel Lüdecke \email{d.luedecke@uke.de} (\href{https://orcid.org/0000-0002-8895-3206}{ORCID}) (@strengejacke) \item Mattan S. Ben-Shachar \email{matanshm@post.bgu.ac.il} (\href{https://orcid.org/0000-0002-4287-4801}{ORCID}) (@mattansb) \item Indrajeet Patil \email{patilindrajeet.science@gmail.com} (\href{https://orcid.org/0000-0003-1995-6531}{ORCID}) (@patilindrajeets) + \item Rémi Thériault \email{remi.theriault@mail.mcgill.ca} (\href{https://orcid.org/0000-0003-4315-6788}{ORCID}) (@rempsyc) } } diff --git a/tests/testthat/test-estimate_contrasts.R b/tests/testthat/test-estimate_contrasts.R index dd505878c..9a2db2b0c 100644 --- a/tests/testthat/test-estimate_contrasts.R +++ b/tests/testthat/test-estimate_contrasts.R @@ -8,7 +8,7 @@ test_that("estimate_contrasts - Frequentist", { model <- lm(Sepal.Width ~ Species, data = dat) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) estim <- suppressMessages(estimate_contrasts(model, at = "Species=c('versicolor', 'virginica')")) expect_equal(dim(estim), c(1, 9)) @@ -21,16 +21,16 @@ test_that("estimate_contrasts - Frequentist", { model <- lm(Sepal.Width ~ Species * fac, data = dat) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) estim <- suppressMessages(estimate_contrasts(model, levels = "Species")) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) estim <- suppressMessages(estimate_contrasts(model, fixed = "fac")) expect_equal(dim(estim), c(3, 10)) # One factor and one continuous model <- lm(Sepal.Width ~ Species * Petal.Width, data = iris) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) estim <- suppressMessages(estimate_contrasts(model, fixed = "Petal.Width")) expect_equal(dim(estim), c(3, 10)) estim <- suppressMessages(estimate_contrasts(model, at = "Petal.Width", length = 4)) @@ -84,7 +84,7 @@ test_that("estimate_contrasts - Frequentist", { model <- lme4::lmer(Sepal.Width ~ Species + (1 | Petal.Length_factor), data = data) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) # GLM - binomial @@ -107,7 +107,7 @@ test_that("estimate_contrasts - Frequentist", { model <- glm(counts ~ treatment, data = dat, family = poisson()) estim <- suppressMessages(estimate_contrasts(model, transform = "response")) - expect_equal(dim(estim), c(3, 13)) + expect_equal(dim(estim), c(3, 9)) }) @@ -145,7 +145,7 @@ test_that("estimate_contrasts - Bayesian", { ) ) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 11)) + expect_equal(dim(estim), c(3, 7)) estim <- suppressMessages(estimate_contrasts(model, fixed = "Petal.Width")) expect_equal(dim(estim), c(3, 8)) estim <- suppressMessages(estimate_contrasts(model, at = "Petal.Width", length = 4)) @@ -161,14 +161,14 @@ test_that("estimate_contrasts - Bayesian", { )) estim <- suppressMessages(estimate_contrasts(model)) - expect_equal(dim(estim), c(3, 11)) + expect_equal(dim(estim), c(3, 7)) estim <- suppressMessages(estimate_contrasts(model, transform = "response")) - expect_equal(dim(estim), c(3, 11)) + expect_equal(dim(estim), c(3, 7)) estim <- suppressWarnings(suppressMessages(estimate_contrasts(model, test = "bf"))) - expect_equal(dim(estim), c(3, 10)) + expect_equal(dim(estim), c(3, 6)) estim <- suppressWarnings(suppressMessages(estimate_contrasts(model, transform = "response", test = "bf"))) - expect_equal(dim(estim), c(3, 10)) + expect_equal(dim(estim), c(3, 6)) }) From eadb7987f6bcb3dd0b6c96efabaf750ad0cd2733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sat, 1 Jul 2023 21:25:43 -0400 Subject: [PATCH 03/16] update docs [skip ci] --- DESCRIPTION | 2 +- R/estimate_contrasts.R | 16 ++++++++++++++++ man/estimate_contrasts.Rd | 18 ++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index 5d328649b..340aef2d5 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: modelbased Title: Estimation of Model-Based Predictions, Contrasts and Means -Version: 0.8.6.3 +Version: 0.8.6.4 Authors@R: c(person(given = "Dominique", family = "Makowski", diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 353fd7ccd..2b9a7f3d2 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -21,6 +21,22 @@ #' #' @inherit estimate_slopes details #' +#' @section Effect Size: By default, `estimate_contrasts` reports no standardized effect size +#' on purpose. Should one request one, some things are to keep in mind. As the +#' authors of `emmeans` write, "There is substantial disagreement among +#' practitioners on what is the appropriate sigma to use in computing effect +#' sizes; or, indeed, whether any effect-size measure is appropriate for some +#' situations. The user is completely responsible for specifying appropriate +#' parameters (or for failing to do so)." +#' +#' In particular, effect size methods `"emmeans"` and `"bootES"` do not correct +#' for covariates in the model, so should probably only be used when there is +#' just one categorical predictor (with however many levels). If there are +#' multiple predictors or any covariates, it is important to re-compute sigma +#' adding back in the response variance associated with the variables that +#' aren't part of the contrast (or else the Cohen's *d* scale does not really +#' make sense). +#' #' @examplesIf require("emmeans", quietly = TRUE) #' # Basic usage #' model <- lm(Sepal.Width ~ Species, data = iris) diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 4abb4624f..b88f2318b 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -130,6 +130,24 @@ these levels (using \code{\link[=estimate_contrasts]{estimate_contrasts()}}). Fi the effect of x averaged over all conditions, or instead within each condition (\code{using [estimate_slopes]}). } +\section{Effect Size}{ + By default, \code{estimate_contrasts} reports no standardized effect size +on purpose. Should one request one, some things are to keep in mind. As the +authors of \code{emmeans} write, "There is substantial disagreement among +practitioners on what is the appropriate sigma to use in computing effect +sizes; or, indeed, whether any effect-size measure is appropriate for some +situations. The user is completely responsible for specifying appropriate +parameters (or for failing to do so)." + +In particular, effect size methods \code{"emmeans"} and \code{"bootES"} do not correct +for covariates in the model, so should probably only be used when there is +just one categorical predictor (with however many levels). If there are +multiple predictors or any covariates, it is important to re-compute sigma +adding back in the response variance associated with the variables that +aren't part of the contrast (or else the Cohen's \emph{d} scale does not really +make sense). +} + \examples{ \dontshow{if (require("emmeans", quietly = TRUE)) (if (getRversion() >= "3.4") withAutoprint else force)(\{ # examplesIf} # Basic usage From 7d0c5bc07792ef96d8449d1fd2bb2cce112f9edf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:24:58 -0400 Subject: [PATCH 04/16] add method "marginal" --- R/estimate_contrasts.R | 63 +++++++++++++++++++++++++++------------ man/estimate_contrasts.Rd | 43 ++++++++++++++++---------- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 2b9a7f3d2..e008c7a78 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -12,7 +12,7 @@ #' section in the `emmeans::test` documentation. #' @param adjust Deprecated in favour of `p_adjust`. #' @param effectsize Desired measure of standardized effect size, one of "none" -#' (default), "emmeans", "bootES". +#' (default), "emmeans", "marginal", or "bootES". #' @param bootES_type Specifies the type of effect-size measure to #' estimate when using `effectsize = "bootES"`. One of `c("unstandardized", #' "cohens.d", "hedges.g", "cohens.d.sigma", "r", "akp.robust.d")`. See` @@ -21,21 +21,34 @@ #' #' @inherit estimate_slopes details #' -#' @section Effect Size: By default, `estimate_contrasts` reports no standardized effect size -#' on purpose. Should one request one, some things are to keep in mind. As the -#' authors of `emmeans` write, "There is substantial disagreement among -#' practitioners on what is the appropriate sigma to use in computing effect -#' sizes; or, indeed, whether any effect-size measure is appropriate for some -#' situations. The user is completely responsible for specifying appropriate -#' parameters (or for failing to do so)." +#' @section Effect Size: By default, `estimate_contrasts` reports no +#' standardized effect size on purpose. Should one request one, some things +#' are to keep in mind. As the authors of `emmeans` write, "There is +#' substantial disagreement among practitioners on what is the appropriate +#' sigma to use in computing effect sizes; or, indeed, whether any effect-size +#' measure is appropriate for some situations. The user is completely +#' responsible for specifying appropriate parameters (or for failing to do +#' so)." #' -#' In particular, effect size methods `"emmeans"` and `"bootES"` do not correct +#' In particular, effect size method `"bootES"` does not correct #' for covariates in the model, so should probably only be used when there is -#' just one categorical predictor (with however many levels). If there are -#' multiple predictors or any covariates, it is important to re-compute sigma -#' adding back in the response variance associated with the variables that -#' aren't part of the contrast (or else the Cohen's *d* scale does not really -#' make sense). +#' just one categorical predictor (with however many levels). Some believe that +#' if there are multiple predictors or any covariates, it is important to +#' re-compute sigma adding back in the response variance associated with the +#' variables that aren't part of the contrast. +#' +#' Note also that standardizing the components of a composite variable will +#' probably lead to nonsense results. +#' +#' `effectsize = "emmeans"` uses [emmeans::eff_size] with +#' `sigma = stats::sigma(model)`, `edf = stats::df.residual(model)` and +#' `method = "identity")`. +#' +#' `effectsize = "marginal"` uses the following formula to compute effect +#' size: `d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)`. +#' +#' `effectsize = "bootES"` uses bootstrapping (defaults to a low value of +#' 200) through [bootES::bootES]. Does not adjust for covariates. #' #' @examplesIf require("emmeans", quietly = TRUE) #' # Basic usage @@ -162,7 +175,7 @@ estimate_contrasts <- function(model, contrasts <- cbind(level_cols, contrasts) # Add standardized effect size - if (!effectsize %in% c("none", "emmeans", "bootES")) { + if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { message("Unsupported effect size '", effectsize, "', returning none.") } @@ -175,7 +188,17 @@ estimate_contrasts <- function(model, names(eff) <- c("effect_size", "es_CI_low", "es_CI_high") contrasts <- cbind(contrasts, eff) + } else if (effectsize == "marginal") { + # d_adj <- t * se_b / sigma * sqrt(1 - R2_cov) + R2_cov <- summary(model)$r.squared + d_adj <- contrasts$t * contrasts$SE / sigma(model) * sqrt(1 - R2_cov) + contrasts <- cbind(contrasts, marginal_d = d_adj) + } else if (effectsize == "bootES") { + if (bootstraps < 500) { + message("Number of bootstraps probably too low. Consider increasing it.") + } + insight::check_if_installed("bootES") dat <- insight::get_data(model) resp <- insight::find_response(model) @@ -183,15 +206,17 @@ estimate_contrasts <- function(model, contrast <- estimated@misc$con.coef contrast <- lapply(seq_len(nrow(contrast)), function(x) { - contrast[x, ] + z <- contrast[x, ] + names(z) <- levels(as.factor(dat[[group]])) + z }) es.lists <- lapply(contrast, function(x) { y <- bootES::bootES( - data = stats::na.omit(insight::get_data(model)), + data = stats::na.omit(dat), R = bootstraps, - data.col = insight::find_response(model), - group.col = insight::find_predictors(model)[[1]], + data.col = resp, + group.col = group, contrast = x, effect.type = bootES_type ) diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index b88f2318b..ac08d3e5a 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -57,7 +57,7 @@ section in the \code{emmeans::test} documentation.} \item{adjust}{Deprecated in favour of \code{p_adjust}.} \item{effectsize}{Desired measure of standardized effect size, one of "none" -(default), "emmeans", "bootES".} +(default), "emmeans", "marginal", or "bootES".} \item{bootstraps}{The number of bootstrap resamples to perform.} @@ -131,21 +131,34 @@ the effect of x averaged over all conditions, or instead within each condition (\code{using [estimate_slopes]}). } \section{Effect Size}{ - By default, \code{estimate_contrasts} reports no standardized effect size -on purpose. Should one request one, some things are to keep in mind. As the -authors of \code{emmeans} write, "There is substantial disagreement among -practitioners on what is the appropriate sigma to use in computing effect -sizes; or, indeed, whether any effect-size measure is appropriate for some -situations. The user is completely responsible for specifying appropriate -parameters (or for failing to do so)." - -In particular, effect size methods \code{"emmeans"} and \code{"bootES"} do not correct + By default, \code{estimate_contrasts} reports no +standardized effect size on purpose. Should one request one, some things +are to keep in mind. As the authors of \code{emmeans} write, "There is +substantial disagreement among practitioners on what is the appropriate +sigma to use in computing effect sizes; or, indeed, whether any effect-size +measure is appropriate for some situations. The user is completely +responsible for specifying appropriate parameters (or for failing to do +so)." + +In particular, effect size method \code{"bootES"} does not correct for covariates in the model, so should probably only be used when there is -just one categorical predictor (with however many levels). If there are -multiple predictors or any covariates, it is important to re-compute sigma -adding back in the response variance associated with the variables that -aren't part of the contrast (or else the Cohen's \emph{d} scale does not really -make sense). +just one categorical predictor (with however many levels). Some believe that +if there are multiple predictors or any covariates, it is important to +re-compute sigma adding back in the response variance associated with the +variables that aren't part of the contrast. + +Note also that standardizing the components of a composite variable will +probably lead to nonsense results. + +\code{effectsize = "emmeans"} uses \link[emmeans:eff_size]{emmeans::eff_size} with +\code{sigma = stats::sigma(model)}, \code{edf = stats::df.residual(model)} and +\verb{method = "identity")}. + +\code{effectsize = "marginal"} uses the following formula to compute effect +size: \code{d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)}. + +\code{effectsize = "bootES"} uses bootstrapping (defaults to a low value of +200) through \link[bootES:bootES]{bootES::bootES}. Does not adjust for covariates. } \examples{ From 219935d241eff95fd2767c35e49f3bcfa39c79cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sun, 2 Jul 2023 14:40:03 -0400 Subject: [PATCH 05/16] Buff details of methods [skip ci] --- R/estimate_contrasts.R | 16 ++++++++++------ man/estimate_contrasts.Rd | 16 ++++++++++------ 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index e008c7a78..dad2ec3f6 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -37,18 +37,22 @@ #' re-compute sigma adding back in the response variance associated with the #' variables that aren't part of the contrast. #' -#' Note also that standardizing the components of a composite variable will -#' probably lead to nonsense results. -#' #' `effectsize = "emmeans"` uses [emmeans::eff_size] with #' `sigma = stats::sigma(model)`, `edf = stats::df.residual(model)` and -#' `method = "identity")`. +#' `method = "identity")`. This standardizes using the MSE (sigma). This works +#' when the contrasts are the only predictors in the model, but not when there +#' are covariates. The response variance accounted for by the covariates should +#' not be removed from the SD used to standardize. Otherwise, _d_ will be +#' overestimated. #' #' `effectsize = "marginal"` uses the following formula to compute effect -#' size: `d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)`. +#' size: `d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)`. This standardized +#' using the response SD with only the between-groups variance on the focal +#' factor/contrast removed. This allows for groups to be equated on their +#' covariates, but creates an appropriate scale for standardizing the response. #' #' `effectsize = "bootES"` uses bootstrapping (defaults to a low value of -#' 200) through [bootES::bootES]. Does not adjust for covariates. +#' 200) through [bootES::bootES]. Adjust for contrasts, but not for covariates. #' #' @examplesIf require("emmeans", quietly = TRUE) #' # Basic usage diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index ac08d3e5a..7d666b6d3 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -147,18 +147,22 @@ if there are multiple predictors or any covariates, it is important to re-compute sigma adding back in the response variance associated with the variables that aren't part of the contrast. -Note also that standardizing the components of a composite variable will -probably lead to nonsense results. - \code{effectsize = "emmeans"} uses \link[emmeans:eff_size]{emmeans::eff_size} with \code{sigma = stats::sigma(model)}, \code{edf = stats::df.residual(model)} and -\verb{method = "identity")}. +\verb{method = "identity")}. This standardizes using the MSE (sigma). This works +when the contrasts are the only predictors in the model, but not when there +are covariates. The response variance accounted for by the covariates should +not be removed from the SD used to standardize. Otherwise, \emph{d} will be +overestimated. \code{effectsize = "marginal"} uses the following formula to compute effect -size: \code{d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)}. +size: \code{d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)}. This standardized +using the response SD with only the between-groups variance on the focal +factor/contrast removed. This allows for groups to be equated on their +covariates, but creates an appropriate scale for standardizing the response. \code{effectsize = "bootES"} uses bootstrapping (defaults to a low value of -200) through \link[bootES:bootES]{bootES::bootES}. Does not adjust for covariates. +200) through \link[bootES:bootES]{bootES::bootES}. Adjust for contrasts, but not for covariates. } \examples{ From 719f546ee1468147e61c6eea24895942adc770b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sat, 5 Aug 2023 14:50:23 -0400 Subject: [PATCH 06/16] update docs [skip ci] --- R/estimate_contrasts.R | 10 +++++----- man/estimate_contrasts.Rd | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index dad2ec3f6..7ce53b985 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -39,11 +39,11 @@ #' #' `effectsize = "emmeans"` uses [emmeans::eff_size] with #' `sigma = stats::sigma(model)`, `edf = stats::df.residual(model)` and -#' `method = "identity")`. This standardizes using the MSE (sigma). This works -#' when the contrasts are the only predictors in the model, but not when there -#' are covariates. The response variance accounted for by the covariates should -#' not be removed from the SD used to standardize. Otherwise, _d_ will be -#' overestimated. +#' `method = "identity")`. This standardizes using the MSE (sigma). Some believe +#' this works when the contrasts are the only predictors in the model, but not +#' when there are covariates. The response variance accounted for by the +#' covariates should not be removed from the SD used to standardize. Otherwise, +#' _d_ will be overestimated. #' #' `effectsize = "marginal"` uses the following formula to compute effect #' size: `d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)`. This standardized diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 7d666b6d3..52e8f1126 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -149,11 +149,11 @@ variables that aren't part of the contrast. \code{effectsize = "emmeans"} uses \link[emmeans:eff_size]{emmeans::eff_size} with \code{sigma = stats::sigma(model)}, \code{edf = stats::df.residual(model)} and -\verb{method = "identity")}. This standardizes using the MSE (sigma). This works -when the contrasts are the only predictors in the model, but not when there -are covariates. The response variance accounted for by the covariates should -not be removed from the SD used to standardize. Otherwise, \emph{d} will be -overestimated. +\verb{method = "identity")}. This standardizes using the MSE (sigma). Some believe +this works when the contrasts are the only predictors in the model, but not +when there are covariates. The response variance accounted for by the +covariates should not be removed from the SD used to standardize. Otherwise, +\emph{d} will be overestimated. \code{effectsize = "marginal"} uses the following formula to compute effect size: \code{d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)}. This standardized From 0c22e29fd7bdef9103b340f9d05451a27e28a529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sat, 5 Aug 2023 16:47:11 -0400 Subject: [PATCH 07/16] rewrite as "difference * (1- R2)/ sigma", rename as "partial_effect_size" [skip ci] --- R/estimate_contrasts.R | 12 +++++++----- man/estimate_contrasts.Rd | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 7ce53b985..04ed00a32 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -46,7 +46,7 @@ #' _d_ will be overestimated. #' #' `effectsize = "marginal"` uses the following formula to compute effect -#' size: `d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)`. This standardized +#' size: `d_adj <- difference * (1- R2)/ sigma`. This standardized #' using the response SD with only the between-groups variance on the focal #' factor/contrast removed. This allows for groups to be equated on their #' covariates, but creates an appropriate scale for standardizing the response. @@ -189,13 +189,15 @@ estimate_contrasts <- function(model, edf = stats::df.residual(model), method = "identity") eff <- as.data.frame(eff) eff <- eff[c(2, 5:6)] - names(eff) <- c("effect_size", "es_CI_low", "es_CI_high") + names(eff) <- c("partial_effect_size", "es_CI_low", "es_CI_high") contrasts <- cbind(contrasts, eff) } else if (effectsize == "marginal") { - # d_adj <- t * se_b / sigma * sqrt(1 - R2_cov) - R2_cov <- summary(model)$r.squared - d_adj <- contrasts$t * contrasts$SE / sigma(model) * sqrt(1 - R2_cov) + # Original: d_adj <- t * se_b / sigma * sqrt(1 - R2_cov) + # d_adj <- contrasts$t * contrasts$SE / sigma(model) * sqrt(1 - R2) + # New: d_adj <- difference * (1- R2)/ sigma + R2 <- summary(model)$r.squared + d_adj <- contrasts$Difference * (1 - R2) / sigma(model) contrasts <- cbind(contrasts, marginal_d = d_adj) } else if (effectsize == "bootES") { diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 52e8f1126..870699b92 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -156,7 +156,7 @@ covariates should not be removed from the SD used to standardize. Otherwise, \emph{d} will be overestimated. \code{effectsize = "marginal"} uses the following formula to compute effect -size: \code{d_adj <- t * se_b / sigma * sqrt(1 - R2_cov)}. This standardized +size: \code{d_adj <- difference * (1- R2)/ sigma}. This standardized using the response SD with only the between-groups variance on the focal factor/contrast removed. This allows for groups to be equated on their covariates, but creates an appropriate scale for standardizing the response. From a3aad0d521e7ee514df61f764bed5af42aa0ee47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Sun, 6 Aug 2023 14:13:54 -0400 Subject: [PATCH 08/16] rewrite output column as "partial_d" when using emmeans [skip ci] --- R/estimate_contrasts.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 04ed00a32..fe144d420 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -189,7 +189,7 @@ estimate_contrasts <- function(model, edf = stats::df.residual(model), method = "identity") eff <- as.data.frame(eff) eff <- eff[c(2, 5:6)] - names(eff) <- c("partial_effect_size", "es_CI_low", "es_CI_high") + names(eff) <- c("partial_d", "es_CI_low", "es_CI_high") contrasts <- cbind(contrasts, eff) } else if (effectsize == "marginal") { From d1e0fadcc5517a14dafe426db35eb988738c8017 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 19 Dec 2024 13:52:49 +0100 Subject: [PATCH 09/16] descr --- DESCRIPTION | 7 ++++--- R/estimate_contrasts.R | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index e9a39afdf..e1ce0c8d6 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -2,7 +2,8 @@ Type: Package Package: modelbased Title: Estimation of Model-Based Predictions, Contrasts and Means Version: 0.8.9.2 -Authors@R: c(person(given = "Dominique", +Authors@R: + c(person(given = "Dominique", family = "Makowski", role = c("aut", "cre"), email = "dom.makowski@gmail.com", @@ -22,9 +23,9 @@ Authors@R: c(person(given = "Dominique", role = "aut", email = "patilindrajeet.science@gmail.com", comment = c(ORCID = "0000-0003-1995-6531")), - person(given = "Rémi", + person(given = "Rémi", family = "Thériault", - role = c("aut"), + role = c("aut"), email = "remi.theriault@mail.mcgill.ca", comment = c(ORCID = "0000-0003-4315-6788"))) Maintainer: Dominique Makowski diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 6aa824f7e..c5ff83dcc 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -38,7 +38,7 @@ #' #' `effectsize = "emmeans"` uses [emmeans::eff_size] with #' `sigma = stats::sigma(model)`, `edf = stats::df.residual(model)` and -#' `method = "identity")`. This standardizes using the MSE (sigma). Some believe +#' `method = "identity"`. This standardizes using the MSE (sigma). Some believe #' this works when the contrasts are the only predictors in the model, but not #' when there are covariates. The response variance accounted for by the #' covariates should not be removed from the SD used to standardize. Otherwise, @@ -133,7 +133,7 @@ estimate_contrasts <- function(model, adjust = p_adjust, ... ) - out <- .format_emmeans_contrasts(model, estimated, ci, transform, p_adjust, ...) + out <- .format_emmeans_contrasts(model, estimated, ci, transform, p_adjust, effectsize, ...) info <- attributes(estimated) } else { # Marginalmeans ------------------------------------------------------------ @@ -181,7 +181,7 @@ estimate_contrasts <- function(model, # Table formatting emmeans ---------------------------------------------------- -.format_emmeans_contrasts <- function(model, estimated, ci, transform, p_adjust, ...) { +.format_emmeans_contrasts <- function(model, estimated, ci, transform, p_adjust, effectsize, ...) { # Summarize and clean if (insight::model_info(model)$is_bayesian) { out <- cbind(estimated@grid, bayestestR::describe_posterior(estimated, ci = ci, verbose = FALSE, ...)) @@ -217,7 +217,7 @@ estimate_contrasts <- function(model, # Add standardized effect size if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { message("Unsupported effect size '", effectsize, "', returning none.") - } + } if (effectsize == "emmeans") { eff <- emmeans::eff_size( From 683f2fb9331994814886b1a584235e943d9e7751 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 11 Feb 2025 07:34:57 +0100 Subject: [PATCH 10/16] fix --- NAMESPACE | 5 +-- R/estimate_contrasts.R | 65 ++------------------------------ R/estimate_contrasts_effecsize.R | 59 +++++++++++++++++++++++++++++ man/estimate_contrasts.Rd | 59 +++++++++++++++++------------ man/get_emmeans.Rd | 32 ---------------- man/modelbased-package.Rd | 1 + 6 files changed, 100 insertions(+), 121 deletions(-) create mode 100644 R/estimate_contrasts_effecsize.R diff --git a/NAMESPACE b/NAMESPACE index 969aec176..5e7114846 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,8 +3,6 @@ S3method(describe_nonlinear,data.frame) S3method(describe_nonlinear,estimate_predicted) S3method(describe_nonlinear,numeric) -S3method(estimate_contrasts,default) -S3method(estimate_contrasts,estimate_predicted) S3method(format,estimate_contrasts) S3method(format,estimate_grouplevel) S3method(format,estimate_means) @@ -58,7 +56,8 @@ S3method(visualisation_recipe,estimate_means) S3method(visualisation_recipe,estimate_predicted) S3method(visualisation_recipe,estimate_slopes) export(describe_nonlinear) -export(estimate_contrasts) +export(estimate_contrasts.default) +export(estimate_contrasts.estimate_predicted) export(estimate_expectation) export(estimate_grouplevel) export(estimate_link) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 0a70066cf..55c5ec423 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -146,10 +146,10 @@ estimate_contrasts.default <- function(model, estimate = "average", p_adjust = "none", transform = NULL, - method = "pairwise", - effectsize = "none", - bootstraps = 200, - bootES_type = "cohens.d", + method = "pairwise", + effectsize = "none", + bootstraps = 200, + bootES_type = "cohens.d", backend = getOption("modelbased_backend", "marginaleffects"), verbose = TRUE, ...) { @@ -213,60 +213,3 @@ estimate_contrasts.default <- function(model, class(out) <- c("estimate_contrasts", "see_estimate_contrasts", class(out)) out } - - # Add standardized effect size - if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { - message("Unsupported effect size '", effectsize, "', returning none.") - } - - if (effectsize == "emmeans") { - eff <- emmeans::eff_size( - estimated, sigma = stats::sigma(model), - edf = stats::df.residual(model), method = "identity") - eff <- as.data.frame(eff) - eff <- eff[c(2, 5:6)] - names(eff) <- c("partial_d", "es_CI_low", "es_CI_high") - contrasts <- cbind(contrasts, eff) - - } else if (effectsize == "marginal") { - # Original: d_adj <- t * se_b / sigma * sqrt(1 - R2_cov) - # d_adj <- contrasts$t * contrasts$SE / sigma(model) * sqrt(1 - R2) - # New: d_adj <- difference * (1- R2)/ sigma - R2 <- summary(model)$r.squared - d_adj <- contrasts$Difference * (1 - R2) / sigma(model) - contrasts <- cbind(contrasts, marginal_d = d_adj) - - } else if (effectsize == "bootES") { - if (bootstraps < 500) { - message("Number of bootstraps probably too low. Consider increasing it.") - } - - insight::check_if_installed("bootES") - dat <- insight::get_data(model) - resp <- insight::find_response(model) - group <- names(estimated@model.info$xlev) - contrast <- estimated@misc$con.coef - - contrast <- lapply(seq_len(nrow(contrast)), function(x) { - z <- contrast[x, ] - names(z) <- levels(as.factor(dat[[group]])) - z - }) - - es.lists <- lapply(contrast, function(x) { - y <- bootES::bootES( - data = stats::na.omit(dat), - R = bootstraps, - data.col = resp, - group.col = group, - contrast = x, - effect.type = bootES_type - ) - y <- as.data.frame(summary(y))}) - - eff <- do.call(rbind, es.lists) - eff <- eff[1:3] - names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), - paste0(bootES_type, "es_CI_high")) - - contrasts <- cbind(contrasts, eff) diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R new file mode 100644 index 000000000..6a0bd057e --- /dev/null +++ b/R/estimate_contrasts_effecsize.R @@ -0,0 +1,59 @@ +.estimate_contrasts_effecsize <- function() { + # Add standardized effect size + if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { + message("Unsupported effect size '", effectsize, "', returning none.") + } + + if (effectsize == "emmeans") { + eff <- emmeans::eff_size( + estimated, sigma = stats::sigma(model), + edf = stats::df.residual(model), method = "identity") + eff <- as.data.frame(eff) + eff <- eff[c(2, 5:6)] + names(eff) <- c("partial_d", "es_CI_low", "es_CI_high") + contrasts <- cbind(contrasts, eff) + + } else if (effectsize == "marginal") { + # Original: d_adj <- t * se_b / sigma * sqrt(1 - R2_cov) + # d_adj <- contrasts$t * contrasts$SE / sigma(model) * sqrt(1 - R2) + # New: d_adj <- difference * (1- R2)/ sigma + R2 <- summary(model)$r.squared + d_adj <- contrasts$Difference * (1 - R2) / sigma(model) + contrasts <- cbind(contrasts, marginal_d = d_adj) + + } else if (effectsize == "bootES") { + if (bootstraps < 500) { + message("Number of bootstraps probably too low. Consider increasing it.") + } + + insight::check_if_installed("bootES") + dat <- insight::get_data(model) + resp <- insight::find_response(model) + group <- names(estimated@model.info$xlev) + contrast <- estimated@misc$con.coef + + contrast <- lapply(seq_len(nrow(contrast)), function(x) { + z <- contrast[x, ] + names(z) <- levels(as.factor(dat[[group]])) + z + }) + + es.lists <- lapply(contrast, function(x) { + y <- bootES::bootES( + data = stats::na.omit(dat), + R = bootstraps, + data.col = resp, + group.col = group, + contrast = x, + effect.type = bootES_type + ) + y <- as.data.frame(summary(y))}) + + eff <- do.call(rbind, es.lists) + eff <- eff[1:3] + names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), + paste0(bootES_type, "es_CI_high")) + + contrasts <- cbind(contrasts, eff) + } +} diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index a208f1260..53a93ee06 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -1,24 +1,23 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/estimate_contrasts.R -\name{estimate_contrasts} -\alias{estimate_contrasts} +\name{estimate_contrasts.default} \alias{estimate_contrasts.default} \title{Estimate Marginal Contrasts} \usage{ -estimate_contrasts(model, ...) - -\method{estimate_contrasts}{default}( +estimate_contrasts.default( model, contrast = NULL, by = NULL, predict = NULL, ci = 0.95, - p_adjust = "holm", - method = "pairwise", comparison = "pairwise", estimate = "average", p_adjust = "none", transform = NULL, + method = "pairwise", + effectsize = "none", + bootstraps = 200, + bootES_type = "cohens.d", backend = getOption("modelbased_backend", "marginaleffects"), verbose = TRUE, ... @@ -27,23 +26,6 @@ estimate_contrasts(model, ...) \arguments{ \item{model}{A statistical model.} -\item{...}{Other arguments passed, for instance, to \code{\link[insight:get_datagrid]{insight::get_datagrid()}}, -to functions from the \strong{emmeans} or \strong{marginaleffects} package, or to process -Bayesian models via \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}}. Examples: -\itemize{ -\item \code{insight::get_datagrid()}: Argument such as \code{length} or \code{range} can be used -to control the (number of) representative values. -\item \strong{marginaleffects}: Internally used functions are \code{avg_predictions()} for -means and contrasts, and \code{avg_slope()} for slopes. Therefore, arguments -for instance like \code{vcov}, \code{transform}, \code{equivalence}, \code{slope} or even -\code{newdata} can be passed to those functions. -\item \strong{emmeans}: Internally used functions are \code{emmeans()} and \code{emtrends()}. -Additional arguments can be passed to these functions. -\item Bayesian models: For Bayesian models, parameters are cleaned using -\code{describe_posterior()}, thus, arguments like, for example, \code{centrality}, -\code{rope_range}, or \code{test} are passed to that function. -}} - \item{contrast}{A character vector indicating the name of the variable(s) for which to compute the contrasts.} @@ -172,6 +154,14 @@ posterior distribution, before computing summaries. Can also be \code{TRUE}, in which case \code{insight::get_transformation()} is called to determine the appropriate transformation-function.} +\item{effectsize}{Desired measure of standardized effect size, one of "none" +(default), "emmeans", "marginal", or "bootES".} + +\item{bootstraps}{The number of bootstrap resamples to perform.} + +\item{bootES_type}{Specifies the type of effect-size measure to +estimate when using \code{effectsize = "bootES"}. One of \code{c("unstandardized", "cohens.d", "hedges.g", "cohens.d.sigma", "r", "akp.robust.d")}. See\code{ effect.type} argument of \link[bootES:bootES]{bootES::bootES} for details.} + \item{backend}{Whether to use \code{"emmeans"} or \code{"marginaleffects"} as a backend. Results are usually very similar. The major difference will be found for mixed models, where \code{backend = "marginaleffects"} will also average across random @@ -183,6 +173,25 @@ You can set a default backend via \code{options()}, e.g. use \code{options(modelbased_backend = "marginaleffects")} to set \strong{marginaleffects} as default backend.} +\item{verbose}{Use \code{FALSE} to silence messages and warnings.} + +\item{...}{Other arguments passed, for instance, to \code{\link[insight:get_datagrid]{insight::get_datagrid()}}, +to functions from the \strong{emmeans} or \strong{marginaleffects} package, or to process +Bayesian models via \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}}. Examples: +\itemize{ +\item \code{insight::get_datagrid()}: Argument such as \code{length} or \code{range} can be used +to control the (number of) representative values. +\item \strong{marginaleffects}: Internally used functions are \code{avg_predictions()} for +means and contrasts, and \code{avg_slope()} for slopes. Therefore, arguments +for instance like \code{vcov}, \code{transform}, \code{equivalence}, \code{slope} or even +\code{newdata} can be passed to those functions. +\item \strong{emmeans}: Internally used functions are \code{emmeans()} and \code{emtrends()}. +Additional arguments can be passed to these functions. +\item Bayesian models: For Bayesian models, parameters are cleaned using +\code{describe_posterior()}, thus, arguments like, for example, \code{centrality}, +\code{rope_range}, or \code{test} are passed to that function. +}} +} \value{ A data frame of estimated contrasts. } @@ -262,7 +271,7 @@ variables that aren't part of the contrast. \code{effectsize = "emmeans"} uses \link[emmeans:eff_size]{emmeans::eff_size} with \code{sigma = stats::sigma(model)}, \code{edf = stats::df.residual(model)} and -\verb{method = "identity")}. This standardizes using the MSE (sigma). Some believe +\code{method = "identity"}. This standardizes using the MSE (sigma). Some believe this works when the contrasts are the only predictors in the model, but not when there are covariates. The response variance accounted for by the covariates should not be removed from the SD used to standardize. Otherwise, diff --git a/man/get_emmeans.Rd b/man/get_emmeans.Rd index 6cb6fea16..54256f21a 100644 --- a/man/get_emmeans.Rd +++ b/man/get_emmeans.Rd @@ -71,9 +71,6 @@ get_marginaltrends( \arguments{ \item{model}{A statistical model.} -\item{contrast}{A character vector indicating the name of the variable(s) -for which to compute the contrasts.} - \item{by}{The (focal) predictor variable(s) at which to evaluate the desired effect / mean / contrasts. Other predictors of the model that are not included here will be collapsed and "averaged" over (the effect will be @@ -111,35 +108,6 @@ packages), for instance when using complex formulae in \code{brms} models, the \code{predict} argument can take the value of the parameter you want to estimate, for instance \code{"sigma"}, \code{"kappa"}, etc.} -\item{comparison}{Specify the type of contrasts or tests that should be -carried out. -\itemize{ -\item When \code{backend = "emmeans"}, can be one of \code{"pairwise"}, \code{"poly"}, -\code{"consec"}, \code{"eff"}, \code{"del.eff"}, \code{"mean_chg"}, \code{"trt.vs.ctrl"}, -\code{"dunnett"}, \code{"wtcon"} and some more. See also \code{method} argument in -\link[emmeans:contrast]{emmeans::contrast} and the \code{?emmeans::emmc-functions}. -\item For \code{backend = "marginaleffects"}, can be a numeric value, vector, or -matrix, a string equation specifying the hypothesis to test, a string -naming the comparison method, a formula, or a function. Strings, string -equations and formula are probably the most common options and described -below. For other options and detailed descriptions of those options, see -also \link[marginaleffects:comparisons]{marginaleffects::comparisons} and -\href{https://marginaleffects.com/bonus/hypothesis.html}{this website}. -\itemize{ -\item String: One of \code{"pairwise"}, \code{"reference"}, \code{"sequential"}, \code{"meandev"} -\code{"meanotherdev"}, \code{"poly"}, \code{"helmert"}, or \code{"trt_vs_ctrl"}. -\item String equation: To identify parameters from the output, either specify -the term name, or \code{"b1"}, \code{"b2"} etc. to indicate rows, e.g.:\code{"hp = drat"}, -\code{"b1 = b2"}, or \code{"b1 + b2 + b3 = 0"}. -\item Formula: A formula like \code{comparison ~ pairs | group}, where the left-hand -side indicates the type of comparison (\code{difference} or \code{ratio}), the -right-hand side determines the pairs of estimates to compare (\code{reference}, -\code{sequential}, \code{meandev}, etc., see string-options). Optionally, comparisons -can be carried out within subsets by indicating the grouping variable -after a vertical bar ( \code{|}). -} -}} - \item{transform}{A function applied to predictions and confidence intervals to (back-) transform results, which can be useful in case the regression model has a transformed response variable (e.g., \code{lm(log(y) ~ x)}). For diff --git a/man/modelbased-package.Rd b/man/modelbased-package.Rd index 6c3cc50d5..421afb7cc 100644 --- a/man/modelbased-package.Rd +++ b/man/modelbased-package.Rd @@ -28,6 +28,7 @@ Authors: \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}) + \item Rémi Thériault \email{remi.theriault@mail.mcgill.ca} (\href{https://orcid.org/0000-0003-4315-6788}{ORCID}) } } From 5f8f6255a847f2a49d1c415315385a54d8d4ea0d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 11 Feb 2025 07:43:48 +0100 Subject: [PATCH 11/16] fix --- NAMESPACE | 5 ++-- R/estimate_contrasts.R | 5 ++++ R/estimate_contrasts_effecsize.R | 2 +- man/estimate_contrasts.Rd | 41 +++++++++++++++++--------------- man/get_emmeans.Rd | 32 +++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 5e7114846..969aec176 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -3,6 +3,8 @@ S3method(describe_nonlinear,data.frame) S3method(describe_nonlinear,estimate_predicted) S3method(describe_nonlinear,numeric) +S3method(estimate_contrasts,default) +S3method(estimate_contrasts,estimate_predicted) S3method(format,estimate_contrasts) S3method(format,estimate_grouplevel) S3method(format,estimate_means) @@ -56,8 +58,7 @@ S3method(visualisation_recipe,estimate_means) S3method(visualisation_recipe,estimate_predicted) S3method(visualisation_recipe,estimate_slopes) export(describe_nonlinear) -export(estimate_contrasts.default) -export(estimate_contrasts.estimate_predicted) +export(estimate_contrasts) export(estimate_expectation) export(estimate_grouplevel) export(estimate_link) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 55c5ec423..9bb362999 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -135,6 +135,11 @@ #' #' @return A data frame of estimated contrasts. #' @export +estimate_contrasts <- function(model, ...) { + UseMethod("estimate_contrasts") +} + + #' @rdname estimate_contrasts #' @export estimate_contrasts.default <- function(model, diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R index 6a0bd057e..bcd7145e8 100644 --- a/R/estimate_contrasts_effecsize.R +++ b/R/estimate_contrasts_effecsize.R @@ -1,7 +1,7 @@ .estimate_contrasts_effecsize <- function() { # Add standardized effect size if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { - message("Unsupported effect size '", effectsize, "', returning none.") + insight::format_alert("Unsupported effect size '", effectsize, "', returning none.") } if (effectsize == "emmeans") { diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 53a93ee06..2ea0e8d1c 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -1,10 +1,13 @@ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/estimate_contrasts.R -\name{estimate_contrasts.default} +\name{estimate_contrasts} +\alias{estimate_contrasts} \alias{estimate_contrasts.default} \title{Estimate Marginal Contrasts} \usage{ -estimate_contrasts.default( +estimate_contrasts(model, ...) + +\method{estimate_contrasts}{default}( model, contrast = NULL, by = NULL, @@ -26,6 +29,23 @@ estimate_contrasts.default( \arguments{ \item{model}{A statistical model.} +\item{...}{Other arguments passed, for instance, to \code{\link[insight:get_datagrid]{insight::get_datagrid()}}, +to functions from the \strong{emmeans} or \strong{marginaleffects} package, or to process +Bayesian models via \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}}. Examples: +\itemize{ +\item \code{insight::get_datagrid()}: Argument such as \code{length} or \code{range} can be used +to control the (number of) representative values. +\item \strong{marginaleffects}: Internally used functions are \code{avg_predictions()} for +means and contrasts, and \code{avg_slope()} for slopes. Therefore, arguments +for instance like \code{vcov}, \code{transform}, \code{equivalence}, \code{slope} or even +\code{newdata} can be passed to those functions. +\item \strong{emmeans}: Internally used functions are \code{emmeans()} and \code{emtrends()}. +Additional arguments can be passed to these functions. +\item Bayesian models: For Bayesian models, parameters are cleaned using +\code{describe_posterior()}, thus, arguments like, for example, \code{centrality}, +\code{rope_range}, or \code{test} are passed to that function. +}} + \item{contrast}{A character vector indicating the name of the variable(s) for which to compute the contrasts.} @@ -174,23 +194,6 @@ You can set a default backend via \code{options()}, e.g. use as default backend.} \item{verbose}{Use \code{FALSE} to silence messages and warnings.} - -\item{...}{Other arguments passed, for instance, to \code{\link[insight:get_datagrid]{insight::get_datagrid()}}, -to functions from the \strong{emmeans} or \strong{marginaleffects} package, or to process -Bayesian models via \code{\link[bayestestR:describe_posterior]{bayestestR::describe_posterior()}}. Examples: -\itemize{ -\item \code{insight::get_datagrid()}: Argument such as \code{length} or \code{range} can be used -to control the (number of) representative values. -\item \strong{marginaleffects}: Internally used functions are \code{avg_predictions()} for -means and contrasts, and \code{avg_slope()} for slopes. Therefore, arguments -for instance like \code{vcov}, \code{transform}, \code{equivalence}, \code{slope} or even -\code{newdata} can be passed to those functions. -\item \strong{emmeans}: Internally used functions are \code{emmeans()} and \code{emtrends()}. -Additional arguments can be passed to these functions. -\item Bayesian models: For Bayesian models, parameters are cleaned using -\code{describe_posterior()}, thus, arguments like, for example, \code{centrality}, -\code{rope_range}, or \code{test} are passed to that function. -}} } \value{ A data frame of estimated contrasts. diff --git a/man/get_emmeans.Rd b/man/get_emmeans.Rd index 54256f21a..6cb6fea16 100644 --- a/man/get_emmeans.Rd +++ b/man/get_emmeans.Rd @@ -71,6 +71,9 @@ get_marginaltrends( \arguments{ \item{model}{A statistical model.} +\item{contrast}{A character vector indicating the name of the variable(s) +for which to compute the contrasts.} + \item{by}{The (focal) predictor variable(s) at which to evaluate the desired effect / mean / contrasts. Other predictors of the model that are not included here will be collapsed and "averaged" over (the effect will be @@ -108,6 +111,35 @@ packages), for instance when using complex formulae in \code{brms} models, the \code{predict} argument can take the value of the parameter you want to estimate, for instance \code{"sigma"}, \code{"kappa"}, etc.} +\item{comparison}{Specify the type of contrasts or tests that should be +carried out. +\itemize{ +\item When \code{backend = "emmeans"}, can be one of \code{"pairwise"}, \code{"poly"}, +\code{"consec"}, \code{"eff"}, \code{"del.eff"}, \code{"mean_chg"}, \code{"trt.vs.ctrl"}, +\code{"dunnett"}, \code{"wtcon"} and some more. See also \code{method} argument in +\link[emmeans:contrast]{emmeans::contrast} and the \code{?emmeans::emmc-functions}. +\item For \code{backend = "marginaleffects"}, can be a numeric value, vector, or +matrix, a string equation specifying the hypothesis to test, a string +naming the comparison method, a formula, or a function. Strings, string +equations and formula are probably the most common options and described +below. For other options and detailed descriptions of those options, see +also \link[marginaleffects:comparisons]{marginaleffects::comparisons} and +\href{https://marginaleffects.com/bonus/hypothesis.html}{this website}. +\itemize{ +\item String: One of \code{"pairwise"}, \code{"reference"}, \code{"sequential"}, \code{"meandev"} +\code{"meanotherdev"}, \code{"poly"}, \code{"helmert"}, or \code{"trt_vs_ctrl"}. +\item String equation: To identify parameters from the output, either specify +the term name, or \code{"b1"}, \code{"b2"} etc. to indicate rows, e.g.:\code{"hp = drat"}, +\code{"b1 = b2"}, or \code{"b1 + b2 + b3 = 0"}. +\item Formula: A formula like \code{comparison ~ pairs | group}, where the left-hand +side indicates the type of comparison (\code{difference} or \code{ratio}), the +right-hand side determines the pairs of estimates to compare (\code{reference}, +\code{sequential}, \code{meandev}, etc., see string-options). Optionally, comparisons +can be carried out within subsets by indicating the grouping variable +after a vertical bar ( \code{|}). +} +}} + \item{transform}{A function applied to predictions and confidence intervals to (back-) transform results, which can be useful in case the regression model has a transformed response variable (e.g., \code{lm(log(y) ~ x)}). For From 0f6ee129fc1245ea2af938c68b362479fc1065a0 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 11 Feb 2025 07:45:44 +0100 Subject: [PATCH 12/16] fix --- R/estimate_contrasts_effecsize.R | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R index bcd7145e8..70320515b 100644 --- a/R/estimate_contrasts_effecsize.R +++ b/R/estimate_contrasts_effecsize.R @@ -1,8 +1,6 @@ -.estimate_contrasts_effecsize <- function() { +.estimate_contrasts_effecsize <- function(effectsize = "none") { # Add standardized effect size - if (!effectsize %in% c("none", "emmeans", "marginal", "bootES")) { - insight::format_alert("Unsupported effect size '", effectsize, "', returning none.") - } + insight::validate_argument(effectsize, c("none", "emmeans", "marginal", "bootES")) if (effectsize == "emmeans") { eff <- emmeans::eff_size( From 9dbcbc6dd10f400470985aee2ef18455d07fb625 Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 11 Feb 2025 07:49:38 +0100 Subject: [PATCH 13/16] use insight functions --- R/estimate_contrasts_effecsize.R | 80 +++++++++++++++++--------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R index 70320515b..ebc1169fe 100644 --- a/R/estimate_contrasts_effecsize.R +++ b/R/estimate_contrasts_effecsize.R @@ -1,11 +1,14 @@ -.estimate_contrasts_effecsize <- function(effectsize = "none") { +.estimate_contrasts_effecsize <- function(contrasts, effectsize = "none") { # Add standardized effect size insight::validate_argument(effectsize, c("none", "emmeans", "marginal", "bootES")) if (effectsize == "emmeans") { eff <- emmeans::eff_size( - estimated, sigma = stats::sigma(model), - edf = stats::df.residual(model), method = "identity") + estimated, + sigma = insight::get_sigma(model, no_recursion = TRUE), + edf = insight::get_df(model), + method = "identity" + ) eff <- as.data.frame(eff) eff <- eff[c(2, 5:6)] names(eff) <- c("partial_d", "es_CI_low", "es_CI_high") @@ -19,39 +22,40 @@ d_adj <- contrasts$Difference * (1 - R2) / sigma(model) contrasts <- cbind(contrasts, marginal_d = d_adj) - } else if (effectsize == "bootES") { - if (bootstraps < 500) { - message("Number of bootstraps probably too low. Consider increasing it.") - } - - insight::check_if_installed("bootES") - dat <- insight::get_data(model) - resp <- insight::find_response(model) - group <- names(estimated@model.info$xlev) - contrast <- estimated@misc$con.coef - - contrast <- lapply(seq_len(nrow(contrast)), function(x) { - z <- contrast[x, ] - names(z) <- levels(as.factor(dat[[group]])) - z - }) - - es.lists <- lapply(contrast, function(x) { - y <- bootES::bootES( - data = stats::na.omit(dat), - R = bootstraps, - data.col = resp, - group.col = group, - contrast = x, - effect.type = bootES_type - ) - y <- as.data.frame(summary(y))}) - - eff <- do.call(rbind, es.lists) - eff <- eff[1:3] - names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), - paste0(bootES_type, "es_CI_high")) - - contrasts <- cbind(contrasts, eff) - } + } else if (effectsize == "bootES") { + if (bootstraps < 500) { + insight::format_alert("Number of bootstraps probably too low. Consider increasing it.") + } + + insight::check_if_installed("bootES") + dat <- insight::get_data(model) + resp <- insight::find_response(model) + group <- names(estimated@model.info$xlev) + contrast <- estimated@misc$con.coef + + contrast <- lapply(seq_len(nrow(contrast)), function(x) { + z <- contrast[x, ] + names(z) <- levels(as.factor(dat[[group]])) + z + }) + + es.lists <- lapply(contrast, function(x) { + y <- bootES::bootES( + data = stats::na.omit(dat), + R = bootstraps, + data.col = resp, + group.col = group, + contrast = x, + effect.type = bootES_type + ) + y <- as.data.frame(summary(y)) + }) + + eff <- do.call(rbind, es.lists) + eff <- eff[1:3] + names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), paste0(bootES_type, "es_CI_high")) + + contrasts <- cbind(contrasts, eff) + } + contrasts } From 0140e206859c6282b3048df87dfd656879ae5f5f Mon Sep 17 00:00:00 2001 From: rempsyc Date: Tue, 11 Feb 2025 12:28:01 -0500 Subject: [PATCH 14/16] typos, document --- R/estimate_contrasts.R | 4 +-- man/estimate_contrasts.Rd | 4 +-- man/print.estimate_contrasts.Rd | 47 --------------------------------- 3 files changed, 4 insertions(+), 51 deletions(-) diff --git a/R/estimate_contrasts.R b/R/estimate_contrasts.R index 9bb362999..567582fa8 100644 --- a/R/estimate_contrasts.R +++ b/R/estimate_contrasts.R @@ -74,13 +74,13 @@ #' _d_ will be overestimated. #' #' `effectsize = "marginal"` uses the following formula to compute effect -#' size: `d_adj <- difference * (1- R2)/ sigma`. This standardized +#' size: `d_adj <- difference * (1- R2)/ sigma`. This standardizes #' using the response SD with only the between-groups variance on the focal #' factor/contrast removed. This allows for groups to be equated on their #' covariates, but creates an appropriate scale for standardizing the response. #' #' `effectsize = "bootES"` uses bootstrapping (defaults to a low value of -#' 200) through [bootES::bootES]. Adjust for contrasts, but not for covariates. +#' 200) through [bootES::bootES]. Adjusts for contrasts, but not for covariates. #' #' @examplesIf all(insight::check_if_installed(c("lme4", "marginaleffects", "rstanarm"), quietly = TRUE)) #' \dontrun{ diff --git a/man/estimate_contrasts.Rd b/man/estimate_contrasts.Rd index 2ea0e8d1c..608f6aaba 100644 --- a/man/estimate_contrasts.Rd +++ b/man/estimate_contrasts.Rd @@ -281,13 +281,13 @@ covariates should not be removed from the SD used to standardize. Otherwise, \emph{d} will be overestimated. \code{effectsize = "marginal"} uses the following formula to compute effect -size: \code{d_adj <- difference * (1- R2)/ sigma}. This standardized +size: \code{d_adj <- difference * (1- R2)/ sigma}. This standardizes using the response SD with only the between-groups variance on the focal factor/contrast removed. This allows for groups to be equated on their covariates, but creates an appropriate scale for standardizing the response. \code{effectsize = "bootES"} uses bootstrapping (defaults to a low value of -200) through \link[bootES:bootES]{bootES::bootES}. Adjust for contrasts, but not for covariates. +200) through \link[bootES:bootES]{bootES::bootES}. Adjusts for contrasts, but not for covariates. } \examples{ diff --git a/man/print.estimate_contrasts.Rd b/man/print.estimate_contrasts.Rd index 129e3ffeb..5722719b6 100644 --- a/man/print.estimate_contrasts.Rd +++ b/man/print.estimate_contrasts.Rd @@ -15,53 +15,6 @@ \arguments{ \item{x}{An object returned by the different \verb{estimate_*()} functions.} -\item{select}{Determines which columns are printed and the table layout. -There are two options for this argument: -\itemize{ -\item \strong{A string expression with layout pattern} - -\code{select} is a string with "tokens" enclosed in braces. These tokens will be -replaced by their associated columns, where the selected columns will be -collapsed into one column. Following tokens are replaced by the related -coefficients or statistics: \code{{estimate}}, \code{{se}}, \code{{ci}} (or \code{{ci_low}} and -\code{{ci_high}}), \code{{p}}, \code{{pd}} and \code{{stars}}. The token \code{{ci}} will be replaced -by \verb{{ci_low}, {ci_high}}. Example: \code{select = "{estimate}{stars} ({ci})"} - -It is possible to create multiple columns as well. A \code{|} separates values -into new cells/columns. Example: \code{select = "{estimate} ({ci})|{p}"}. -\item \strong{A string indicating a pre-defined layout} - -\code{select} can be one of the following string values, to create one of the -following pre-defined column layouts: -\itemize{ -\item \code{"minimal"}: Estimates, confidence intervals and numeric p-values, in two -columns. This is equivalent to \code{select = "{estimate} ({ci})|{p}"}. -\item \code{"short"}: Estimate, standard errors and numeric p-values, in two columns. -This is equivalent to \code{select = "{estimate} ({se})|{p}"}. -\item \code{"ci"}: Estimates and confidence intervals, no asterisks for p-values. -This is equivalent to \code{select = "{estimate} ({ci})"}. -\item \code{"se"}: Estimates and standard errors, no asterisks for p-values. This is -equivalent to \code{select = "{estimate} ({se})"}. -\item \code{"ci_p"}: Estimates, confidence intervals and asterisks for p-values. This -is equivalent to \code{select = "{estimate}{stars} ({ci})"}. -\item \code{"se_p"}: Estimates, standard errors and asterisks for p-values. This is -equivalent to \code{select = "{estimate}{stars} ({se})"}.. -} -} - -Using \code{select} to define columns will re-order columns and remove all columns -related to uncertainty (standard errors, confidence intervals), test statistics, -and p-values (and similar, like \code{pd} or \code{BF} for Bayesian models), because -these are assumed to be included or intentionally excluded when using \code{select}. -The new column order will be: Parameter columns first, followed by the "glue" -columns, followed by all remaining columns. If further columns should also be -placed first, add those as \code{focal_terms} attributes to \code{x}. I.e., following -columns are considers as "parameter columns" and placed first: -\code{c(easystats_columns("parameter"), attributes(x)$focal_terms)}. - -\strong{Note:} glue-like syntax is still experimental in the case of more complex models -(like mixed models) and may not return expected results.} - \item{include_grid}{Logical, if \code{TRUE}, the data grid is included in the table output. Only applies to prediction-functions like \code{estimate_relation()} or \code{estimate_link()}.} From f2d742e021831205d484fbf27c9d5b2336891a62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:23:55 -0500 Subject: [PATCH 15/16] Update R/estimate_contrasts_effecsize.R Co-authored-by: Dominique Makowski --- R/estimate_contrasts_effecsize.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R index ebc1169fe..ebf98ca72 100644 --- a/R/estimate_contrasts_effecsize.R +++ b/R/estimate_contrasts_effecsize.R @@ -53,7 +53,7 @@ eff <- do.call(rbind, es.lists) eff <- eff[1:3] - names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), paste0(bootES_type, "es_CI_high")) + names(eff) <- c(bootES_type, paste0(bootES_type, "_CI_low"), paste0(bootES_type, "_CI_high")) contrasts <- cbind(contrasts, eff) } From 4ff060fa563666624adb7197a35959e8900a86be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Th=C3=A9riault?= <13123390+rempsyc@users.noreply.github.com> Date: Tue, 11 Feb 2025 16:27:08 -0500 Subject: [PATCH 16/16] Update R/estimate_contrasts_effecsize.R Co-authored-by: Dominique Makowski --- R/estimate_contrasts_effecsize.R | 3 --- 1 file changed, 3 deletions(-) diff --git a/R/estimate_contrasts_effecsize.R b/R/estimate_contrasts_effecsize.R index ebf98ca72..a3987dde8 100644 --- a/R/estimate_contrasts_effecsize.R +++ b/R/estimate_contrasts_effecsize.R @@ -23,9 +23,6 @@ contrasts <- cbind(contrasts, marginal_d = d_adj) } else if (effectsize == "bootES") { - if (bootstraps < 500) { - insight::format_alert("Number of bootstraps probably too low. Consider increasing it.") - } insight::check_if_installed("bootES") dat <- insight::get_data(model)