Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Type: Package
Package: modelbased
Title: Estimation of Model-Based Predictions, Contrasts and Means
Version: 0.14.0.9
Version: 0.14.0.10
Authors@R:
c(person(given = "Dominique",
family = "Makowski",
Expand Down
2 changes: 2 additions & 0 deletions R/estimate_contrasts.R
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@
#' between-effects, or the difference of average slopes) at different levels
#' of another variable are required, add that variable to the `contrast`
#' argument instead, e.g. `contrast = c("x_within", "x_between", "factor")`.
#' These contrasts can additionally be stratified by another variable using `by`
#' again, e.g. `contrast = c("x_within", "x_between", "factor1"), by = "factor2"`.
#' Note that when average slopes are contrasted, the `comparison` argument has
#' no effect and is always set to `"pairwise"`. See also 'Examples'.
#'
Expand Down
30 changes: 24 additions & 6 deletions R/get_contexteffects.R
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,16 @@
# contrasts at each group level
if (is.null(my_args$by)) {
comparison <- stats::as.formula("~I(diff(x))")
} else if (length(my_args$by) > 2) {
# it is not possible to have more than two by-variables for now
insight::format_error(
"It is not possible to have more than two variables in `by` when calculating contrasts of slopes."
)
} else {
comparison <- stats::as.formula(paste("~I(diff(x)) |", my_args$by))
comparison <- stats::as.formula(paste(
"~I(diff(x)) |",
paste(my_args$by, collapse = "+")
))
}

# prepare arguments
Expand Down Expand Up @@ -47,10 +55,20 @@

# pairwise comparison of context effects?
if (identical(my_args$comparison, "context_pairwise")) {
# save original levels for formatting
original_levels <- .safe(out[[my_args$by]])
# if we have more than one by-variable, use the last one for stratification
stratify_by <- my_args$by[length(my_args$by)]
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current implementation of stratify_by only selects the last element of the by vector. While the current code limits by to two variables (via the check at line 16), it is more robust and future-proof to select all variables except the first one for stratification. This ensures that if the limit is ever increased, the logic of 'contrasting the first variable and stratifying by all others' remains consistent with the formatting logic at lines 85-86.

    stratify_by <- my_args$by[-1]

# save original levels from the contrast-variable for formatting
original_levels <- .safe(out[[my_args$by[1]]])
if (length(my_args$by) > 1) {
comparison <- stats::as.formula(paste(
"~pairwise |",
paste(stratify_by, collapse = "+")
))
} else {
comparison <- ~pairwise
}
# calculate pairwise comparisons
out <- marginaleffects::hypotheses(out, hypothesis = ~pairwise)
out <- marginaleffects::hypotheses(out, hypothesis = comparison)
# format comparison levels. we first split the column with "b" parameter
# names, like "(b1) - (b2)", into two columns. Then we remove the "b" and
# just keep the number, which indicates the row.
Expand All @@ -68,8 +86,8 @@
params$Level2 <- original_levels[params$Level2]
# save attributes
att <- attributes(out)
# remove old "by" column and bind new one back to output
out[[my_args$by]] <- NULL
# remove old "by" column and bind new one with the contrast-levels back to output
out[[my_args$by[1]]] <- NULL
out <- cbind(params, out)
# add back original attributes
cn <- colnames(out)
Expand Down
11 changes: 7 additions & 4 deletions R/get_marginalcontrasts.R
Original file line number Diff line number Diff line change
Expand Up @@ -358,11 +358,14 @@ get_marginalcontrasts <- function(
}
# overwrite some of the previous arguments
context_effects <- TRUE
# if we have no "by" variable, user doesn't want to stratify, so set to
# pairwise comparisons of categorical variable
if (is.null(my_args$by) && length(my_args$contrast) > 2) {
if (length(my_args$contrast) > 2) {
# if we have more than just the slopes in "contrasts", the user wants to
# contrast by a second grouping variable - we want pairwise comparisons
comparison <- "context_pairwise"
my_args$by <- my_args$contrast[3:length(my_args$contrast)]
original_by <- my_args$by <- c(
my_args$contrast[3:length(my_args$contrast)],
my_args$by
)
my_args$contrast <- my_args$contrast[1:2]
} else {
comparison <- "context"
Expand Down
2 changes: 2 additions & 0 deletions man/estimate_contrasts.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions tests/testthat/test-estimate_contrasts_context.R
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,54 @@ test_that("estimate_contrast, context effects, linear", {
tolerance = 1e-4,
ignore_attr = TRUE
)

skip_on_os(c("mac", "linux"))
m <- lm(bill_dep ~ sex * year * (bill_len_between + bill_len_within), data = d)
out <- estimate_contrasts(
m,
c("bill_len_between", "bill_len_within"),
by = c("sex", "year")
)
expect_identical(
capture.output(out),
c(
"Marginal Contrasts Analysis",
"",
"sex | year | Difference | SE | 95% CI | z | p",
"-----------------------------------------------------------------",
"female | 2007 | 0.28 | 0.08 | [ 0.12, 0.45] | 3.41 | < .001",
"female | 2008 | 0.26 | 0.06 | [ 0.15, 0.37] | 4.47 | < .001",
"female | 2009 | 0.24 | 0.10 | [ 0.05, 0.42] | 2.48 | 0.013",
"male | 2007 | 0.46 | 0.10 | [ 0.26, 0.65] | 4.60 | < .001",
"male | 2008 | 0.28 | 0.06 | [ 0.15, 0.40] | 4.41 | < .001",
"male | 2009 | 0.10 | 0.10 | [-0.11, 0.30] | 0.93 | 0.355",
"",
"Variable predicted: bill_dep",
"Predictors contrasted: bill_len_between, bill_len_within",
"p-values are uncorrected."
)
)
out <- estimate_contrasts(
m,
c("bill_len_between", "bill_len_within", "sex"),
by = "year"
)
expect_identical(
capture.output(out),
c(
"Marginal Contrasts Analysis",
"",
"Level1 | Level2 | year | Difference | SE | 95% CI | z | p",
"--------------------------------------------------------------------------",
"male | female | 2007 | 0.17 | 0.13 | [-0.08, 0.43] | 1.33 | 0.183",
"male | female | 2008 | 0.02 | 0.09 | [-0.15, 0.18] | 0.18 | 0.853",
"male | female | 2009 | -0.14 | 0.14 | [-0.42, 0.14] | -0.99 | 0.320",
"",
"Variable predicted: bill_dep",
"Predictors contrasted: bill_len_between, bill_len_within",
"p-values are uncorrected."
)
)
})

test_that("estimate_contrast, context effects, glm", {
Expand Down
Loading