Skip to content

Commit 099bc35

Browse files
authored
Error in estimte_slopes() for emmeans-backend when trend is a factor (#619)
* Error in `estimte_slopes()` for emmeans-backend when `trend` is a factor Fixes #305 * address comments * minor formatting * add tests * Fixes #618 * conditional on emmeans * docs
1 parent 0972533 commit 099bc35

10 files changed

Lines changed: 131 additions & 38 deletions

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: modelbased
33
Title: Estimation of Model-Based Predictions, Contrasts and Means
4-
Version: 0.15.0
4+
Version: 0.15.0.1
55
Authors@R:
66
c(person(given = "Dominique",
77
family = "Makowski",

NEWS.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
# modelbased (devel)
2+
3+
## Changes
4+
5+
* Informative error message when the `trend` variable in `estimate_slopes()` is
6+
not numeric and `backend = "emmeans"`.
7+
18
# modelbased 0.15.0
29

310
## Breaking Changes

R/estimate_contrasts.R

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@
218218
#' confidence bands: Theory, implementation, and an application to SVARs.
219219
#' Journal of Applied Econometrics, 34(1), 1–17. \doi{10.1002/jae.2656}
220220
#'
221-
#' @examplesIf all(insight::check_if_installed(c("lme4", "marginaleffects", "parameters", "datawizard", "rstanarm"), quietly = TRUE))
221+
#' @examplesIf all(insight::check_if_installed(c("lme4", "emmeans", "marginaleffects", "parameters", "datawizard", "rstanarm"), quietly = TRUE))
222222
#' \dontrun{
223223
#' # Basic usage --------------------------------
224224
#' # --------------------------------------------
@@ -259,8 +259,15 @@
259259
#' # "time" only has integer values and few values, so it's treated like a factor
260260
#' estimate_contrasts(model, "time", by = "education")
261261
#'
262-
#' # we set `integer_as_continuous = TRUE` to treat integer as continuous
263-
#' estimate_contrasts(model, "time", by = "education", integer_as_continuous = 1)
262+
#' # Setting `integer_as_continuous = TRUE` treats "time" as a continuous
263+
#' # variable. This allows us to compare its average slope rather than making
264+
#' # discrete pairwise comparisons at each time point.
265+
#' estimate_contrasts(
266+
#' model,
267+
#' contrast = "time",
268+
#' by = "education",
269+
#' integer_as_continuous = TRUE
270+
#' )
264271
#'
265272
#' # pairwise comparisons for multiple groups
266273
#' estimate_contrasts(
@@ -290,6 +297,17 @@
290297
#' model <- lm(happiness ~ puppy_love * dose, data = puppy_love)
291298
#' estimate_slopes(model, "puppy_love", by = "dose", comparison = cond_tx)
292299
#'
300+
#' # Note: for the emmeans-backend, we need to use `estimate_contrasts()` for
301+
#' # the above example:
302+
#' cond_tx <- list(`no treatment` = c(1, 0, 0), treatment = c(0, 0.5, 0.5))
303+
#' estimate_contrasts(
304+
#' model,
305+
#' contrast = "puppy_love",
306+
#' by = "dose",
307+
#' comparison = cond_tx,
308+
#' backend = "emmeans"
309+
#' )
310+
#'
293311
#' # Other models (mixed, Bayesian, ...) --------
294312
#' # --------------------------------------------
295313
#' data <- iris

R/estimate_means.R

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
#' @param ... Other arguments passed, for instance, to [insight::get_datagrid()],
118118
#' to functions from the **emmeans** or **marginaleffects** package, or to process
119119
#' Bayesian models via [bayestestR::describe_posterior()]. Examples:
120-
#' - `insight::get_datagrid()`: Argument such as `length`, `digits` or `range`
120+
#' - `insight::get_datagrid()`: Arguments such as `length`, `digits` or `range`
121121
#' can be used to control the (number of) representative values. For integer
122122
#' variables, `protect_integers` modulates whether these should also be
123123
#' treated as numerics, i.e. values can have fractions or not.
@@ -134,8 +134,8 @@
134134
#' - **emmeans**: Internally used functions are `emmeans()` and `emtrends()`.
135135
#' Additional arguments can be passed to these functions.
136136
#' - Bayesian models: For Bayesian models, parameters are cleaned using
137-
#' `describe_posterior()`, thus, arguments like, for example, `centrality`,
138-
#' `rope_range`, or `test` are passed to that function.
137+
#' `bayestestR::describe_posterior()`, thus, arguments like, for example,
138+
#' `centrality`, `rope_range`, or `test` are passed to that function.
139139
#' - Especially for `estimate_contrasts()` with integer focal predictors, for
140140
#' which contrasts should be calculated, use argument `integer_as_continuous`
141141
#' to set the maximum number of unique values in an integer predictor to treat

R/get_emtrends.R

Lines changed: 42 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@
1313
#' get_emtrends(model)
1414
#' get_emtrends(model, by = "Sepal.Width")
1515
#' @export
16-
get_emtrends <- function(model,
17-
trend = NULL,
18-
by = NULL,
19-
predict = NULL,
20-
keep_iterations = FALSE,
21-
verbose = TRUE,
22-
...) {
16+
get_emtrends <- function(
17+
model,
18+
trend = NULL,
19+
by = NULL,
20+
predict = NULL,
21+
keep_iterations = FALSE,
22+
verbose = TRUE,
23+
...
24+
) {
2325
# check if available
2426
insight::check_if_installed("emmeans")
2527

@@ -38,7 +40,11 @@ get_emtrends <- function(model,
3840
))
3941

4042
# handle distributional parameters
41-
if (!is.null(predict) && inherits(model, "brmsfit") && predict %in% .brms_aux_elements(model)) {
43+
if (
44+
!is.null(predict) &&
45+
inherits(model, "brmsfit") &&
46+
predict %in% .brms_aux_elements(model)
47+
) {
4248
fun_args$dpar <- predict
4349
} else {
4450
fun_args$type <- predict
@@ -70,11 +76,13 @@ get_emtrends <- function(model,
7076
# =========================================================================
7177

7278
#' @keywords internal
73-
.guess_emtrends_arguments <- function(model,
74-
trend = NULL,
75-
by = NULL,
76-
verbose = TRUE,
77-
...) {
79+
.guess_emtrends_arguments <- function(
80+
model,
81+
trend = NULL,
82+
by = NULL,
83+
verbose = TRUE,
84+
...
85+
) {
7886
# Gather info
7987
model_data <- insight::get_data(model, verbose = FALSE)
8088
predictors <- intersect(
@@ -86,19 +94,37 @@ get_emtrends <- function(model,
8694
if (is.null(trend)) {
8795
trend <- predictors[sapply(model_data[predictors], is.numeric)][1]
8896
if (!length(trend) || is.na(trend)) {
89-
insight::format_error("Model contains no numeric predictor. Please specify `trend`.")
97+
insight::format_error(
98+
"Model contains no numeric predictor. Please specify `trend`."
99+
)
90100
}
91101
if (verbose) {
92-
insight::format_alert(paste0("No numeric variable was specified for slope estimation. Selecting `trend = \"", trend, "\"`.")) # nolint
102+
insight::format_alert(paste0(
103+
"No numeric variable was specified for slope estimation. Selecting `trend = \"",
104+
trend,
105+
"\"`."
106+
))
93107
}
94108
}
95109
if (length(trend) > 1) {
96110
trend <- trend[1]
97111
if (verbose) {
98-
insight::format_alert(paste0("More than one numeric variable was selected for slope estimation. Keeping only ", trend[1], ".")) # nolint
112+
insight::format_alert(paste0(
113+
"More than one numeric variable was selected for slope estimation. Keeping only `",
114+
trend[1],
115+
"`."
116+
))
99117
}
100118
}
101119

120+
if (trend %in% colnames(model_data) && !is.numeric(model_data[[trend]])) {
121+
insight::format_error(paste0(
122+
"Variable `",
123+
trend,
124+
"` is not numeric. Slopes can only be estimated for numeric variables when `backend = \"emmeans\"`. Please use `estimate_contrasts()` instead."
125+
))
126+
}
127+
102128
my_args <- list(trend = trend, by = by)
103129
.process_emmeans_arguments(model, args = my_args, data = model_data, ...)
104130
}

man/estimate_contrasts.Rd

Lines changed: 24 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/estimate_means.Rd

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/estimate_slopes.Rd

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/get_emmeans.Rd

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/testthat/test-estimate_slopes.R

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,30 @@ test_that("estimate_slopes", {
131131
})
132132

133133

134+
test_that("estimate_slopes, errors for emmeans when trend is non-numeric", {
135+
skip_if_not_installed("emmeans")
136+
data(iris)
137+
model <- lm(Sepal.Length ~ Species * Sepal.Width, data = iris)
138+
expect_error(
139+
estimate_slopes(model, trend = "Species", by = "Sepal.Width", backend = "emmeans"),
140+
regex = "Variable `Species` is not numeric",
141+
fixed = TRUE
142+
)
143+
model <- lm(Sepal.Length ~ Species, data = iris)
144+
expect_error(
145+
estimate_slopes(model, backend = "emmeans"),
146+
regex = "Model contains no numeric predictor",
147+
fixed = TRUE
148+
)
149+
model <- lm(Sepal.Length ~ Species * Sepal.Width, data = iris)
150+
expect_message(
151+
estimate_slopes(model, backend = "emmeans"),
152+
regex = "No numeric variable was specified",
153+
fixed = TRUE
154+
)
155+
})
156+
157+
134158
test_that("estimate_slopes, johnson-neyman p-adjust", {
135159
data(iris)
136160
model <- lm(Sepal.Width ~ Petal.Width * Petal.Length, data = iris)

0 commit comments

Comments
 (0)