Skip to content

Commit f058b58

Browse files
CopilotDominiqueMakowskistrengejacke
authored
Fix check_predictions() for brms models with dec() responses (Wiener / RT-choice) (#972)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: DominiqueMakowski <8875533+DominiqueMakowski@users.noreply.github.com> Co-authored-by: Daniel <mail@danielluedecke.de> Co-authored-by: strengejacke <26301769+strengejacke@users.noreply.github.com>
1 parent c1660cf commit f058b58

4 files changed

Lines changed: 104 additions & 5 deletions

File tree

‎DESCRIPTION‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Type: Package
22
Package: performance
33
Title: Assessment of Regression Models Performance
4-
Version: 0.18.2.16
4+
Version: 0.18.2.17
55
Authors@R:
66
c(person(given = "Daniel",
77
family = "Lüdecke",

‎NEWS.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# performance (devel)
22

3+
## Bug fixes
4+
5+
* Fixed issue in `check_predictions()` for *brms* models with `dec()` response
6+
(Wiener and RT-choice families); the RT part of the model is now checked.
7+
38
## Changes
49

510
* `check_outliers()` now computes the default threshold for Cook's distance as

‎R/check_predictions.R‎

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,12 @@ check_predictions.stanreg <- function(
225225
...
226226
)
227227

228+
# Wiener / RT-choice models predict two components; check the RTs
229+
is_rt_model <- isTRUE(minfo$is_wiener) || isTRUE(minfo$is_rtchoice)
230+
if (is_rt_model && "Component" %in% colnames(out)) {
231+
out <- out[out$Component == "rt", , drop = FALSE]
232+
}
233+
228234
iter_columns <- startsWith(colnames(out), "iter_")
229235
if (!any(iter_columns)) {
230236
insight::format_error(
@@ -247,10 +253,14 @@ check_predictions.stanreg <- function(
247253

248254
response <- insight::get_response(model)
249255
if (is.data.frame(response)) {
250-
response <- eval(
251-
str2lang(insight::find_response(model)),
252-
envir = insight::get_response(model)
253-
)
256+
if (is_rt_model) {
257+
response <- response[[insight::find_response(model)[1]]]
258+
} else {
259+
response <- eval(
260+
str2lang(insight::find_response(model)),
261+
envir = response
262+
)
263+
}
254264
}
255265
out$y <- response
256266

‎tests/testthat/test-check_predictions.R‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,3 +317,87 @@ withr::with_environment(
317317
})
318318
}
319319
)
320+
321+
test_that("check_predictions, brms-wiener", {
322+
skip_on_cran()
323+
skip_if_not_installed("curl")
324+
skip_if_offline()
325+
skip_if_not_installed("httr2")
326+
skip_if_not_installed("rstan")
327+
skip_if_not_installed("RWiener")
328+
329+
m <- insight::download_model("brms_wiener_1")
330+
skip_if(is.null(m))
331+
332+
old_opt <- getOption("wiener_backend")
333+
options(wiener_backend = "rtdists")
334+
on.exit(options(wiener_backend = old_opt))
335+
expect_no_error({
336+
out <- check_predictions(m)
337+
})
338+
# fmt: skip
339+
expect_named(
340+
out,
341+
c(
342+
"sim_1", "sim_2", "sim_3", "sim_4", "sim_5", "sim_6", "sim_7",
343+
"sim_8", "sim_9", "sim_10", "sim_11", "sim_12", "sim_13", "sim_14",
344+
"sim_15", "sim_16", "sim_17", "sim_18", "sim_19", "sim_20", "sim_21",
345+
"sim_22", "sim_23", "sim_24", "sim_25", "sim_26", "sim_27", "sim_28",
346+
"sim_29", "sim_30", "sim_31", "sim_32", "sim_33", "sim_34", "sim_35",
347+
"sim_36", "sim_37", "sim_38", "sim_39", "sim_40", "sim_41", "sim_42",
348+
"sim_43", "sim_44", "sim_45", "sim_46", "sim_47", "sim_48", "sim_49",
349+
"sim_50", "y"
350+
)
351+
)
352+
})
353+
354+
355+
test_that("check_predictions, RT-choice models keep RT predictions only (mocked)", {
356+
skip_if_not_installed("modelbased", minimum_version = "0.12.0")
357+
358+
n <- 4
359+
iter <- 3
360+
rt_draws <- matrix(seq_len(n * iter), nrow = n)
361+
choice_draws <- matrix(-seq_len(n * iter), nrow = n)
362+
colnames(rt_draws) <- colnames(choice_draws) <- paste0("iter_", seq_len(iter))
363+
preds <- rbind(
364+
data.frame(Component = "rt", rt_draws),
365+
data.frame(Component = "response", choice_draws)
366+
)
367+
colnames(preds) <- c("Component", paste0("iter_", seq_len(iter)))
368+
observed <- data.frame(rt = c(0.5, 0.6, 0.7, 0.8), resp = c(1, 0, 1, 1))
369+
370+
for (flag in c("is_wiener", "is_rtchoice")) {
371+
info <- list(
372+
is_wiener = FALSE,
373+
is_rtchoice = FALSE,
374+
is_bernoulli = FALSE,
375+
is_count = FALSE,
376+
is_ordinal = FALSE,
377+
is_categorical = FALSE,
378+
is_multinomial = FALSE
379+
)
380+
info[[flag]] <- TRUE
381+
local_mocked_bindings(
382+
model_info = function(...) info,
383+
find_terms = function(...) list(response = "rt"),
384+
find_response = function(...) c("rt", "resp"),
385+
get_response = function(...) observed,
386+
.package = "insight"
387+
)
388+
local_mocked_bindings(
389+
estimate_prediction = function(...) preds,
390+
.package = "modelbased"
391+
)
392+
model <- structure(list(), class = c("brmsfit", "stanreg"))
393+
out <- check_predictions(model, iterations = iter)
394+
expect_s3_class(out, "performance_pp_check")
395+
expect_equal(nrow(out), n)
396+
expect_named(out, c(paste0("sim_", seq_len(iter)), "y"))
397+
expect_equal(
398+
unname(as.matrix(out[paste0("sim_", seq_len(iter))])),
399+
unname(rt_draws)
400+
)
401+
expect_equal(out$y, observed$rt)
402+
}
403+
})

0 commit comments

Comments
 (0)