From a70556c44db9f0ffebf444987caa92471a81eb86 Mon Sep 17 00:00:00 2001 From: Jeffrey Girard Date: Sat, 17 Oct 2026 15:51:19 -0500 Subject: [PATCH] Split response names with dots in contrasts of multivariate brms models brms removes dots from response names (Sepal.Length becomes SepalLength) and uses these names in the contrast labels, so .split_contrast_response_levels() did not find the response and kept the labels unsplit. It now also searches for the names of insight::find_response(), which hold the brms names. --- DESCRIPTION | 2 +- NEWS.md | 6 ++++++ R/format.R | 10 +++++++++- tests/testthat/test-brms.R | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 0f86fd806..8525efd09 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,7 +1,7 @@ Type: Package Package: modelbased Title: Estimation of Model-Based Predictions, Contrasts and Means -Version: 0.17.0.16 +Version: 0.17.0.17 Authors@R: c(person(given = "Dominique", family = "Makowski", diff --git a/NEWS.md b/NEWS.md index 3e940b581..fa0e9f3b8 100644 --- a/NEWS.md +++ b/NEWS.md @@ -140,6 +140,12 @@ `estimate_contrasts()` now uses the names of the response variables, not their observed values, to find the response in the contrast labels. +* `estimate_contrasts()` now returns separate `Response1` and `Response2` + columns for multivariate *brms* models with dots in the response names, for + example `mvbind(Sepal.Length, Petal.Length) ~ Species`. Before, the response + names stayed in the `Level1` and `Level2` columns, because *brms* removes the + dots from the response names. + * The `grid` argument of `visualisation_recipe()` and `plot()` now changes the facet grid of plots with four or more `by` variables. Before, these functions applied the `facet` argument to the facet grid instead. If `facet` was not diff --git a/R/format.R b/R/format.R index ca0370bfe..87a9556ea 100644 --- a/R/format.R +++ b/R/format.R @@ -646,7 +646,15 @@ format.marginaleffects_contrasts <- function( response_levels <- unique(as.character(response)) } if (is_multivariate) { - response_levels <- unique(c(response_levels, insight::find_response(model))) + # brms drops dots from response names (e.g., "Sepal.Length" becomes + # "SepalLength"), and uses these names in the contrast labels. The names + # of `find_response()` hold these brms names + model_response <- insight::find_response(model) + response_levels <- unique(c( + response_levels, + model_response, + names(model_response) + )) } if (!length(response_levels)) { return(params) diff --git a/tests/testthat/test-brms.R b/tests/testthat/test-brms.R index 681bb014e..246fb0085 100644 --- a/tests/testthat/test-brms.R +++ b/tests/testthat/test-brms.R @@ -117,6 +117,27 @@ test_that("estimate_contrasts - brms, multivariate, many observations", { }) +test_that("estimate_contrasts - brms, multivariate, response names with dots", { + # brms drops the dot from response names ("Sepal.Length" -> "SepalLength"), + # and the contrast labels use the brms names + m <- suppressWarnings(brms::brm( + brms::mvbind(Sepal.Length, Petal.Length) ~ Species, + data = iris, + refresh = 0, + iter = 500, + chains = 1, + seed = 123 + )) + + out <- estimate_contrasts(m, "Species") + expect_identical(nrow(out), 15L) + expect_true(all(c("Response1", "Response2") %in% colnames(out))) + expect_identical(levels(out$Response1), c("SepalLength", "PetalLength")) + expect_true(all(as.character(out$Level1) %in% levels(iris$Species))) + expect_true(all(as.character(out$Level2) %in% levels(iris$Species))) +}) + + test_that("estimate_means - brms, Wiener", { skip_if_not_installed("curl") skip_if_offline()