Skip to content

print() fails for estimate_means() / estimate_contrasts() on Wiener and RT-choice models #680

Closed

Description

@DominiqueMakowski

Problem

For a model that insight::model_info() flags as is_wiener (brms
wiener()) or is_rtchoice (e.g. the cogmod choice families), every
print() method takes the drift-diffusion branch and calls
.print_drift_diffusion(). That function splits the table by its Component
column, but only predictions of the response have one (estimate_prediction()
returns an rt and a response block). Estimates of a distributional
parameter, such as predict = "mu" or predict = "bs", have no Component.
They are computed correctly, and then printing them fails.

Reprex

library(brms)
options(wiener_backend = "rtdists")  # for posterior_predict(); RWiener works too

set.seed(1)
sim <- rbind(
  rtdists::rdiffusion(150, a = 1.5, v = 0.5, t0 = 0.3, z = 0.75),
  rtdists::rdiffusion(150, a = 1.5, v = 1.5, t0 = 0.3, z = 0.75)
)
d <- data.frame(
  x = rep(c("a", "b"), each = 150),
  rt = sim$rt,
  resp = as.integer(sim$response == "upper")
)

m <- brm(bf(rt | dec(resp) ~ x), data = d, family = wiener(),
         chains = 1, iter = 400, refresh = 0, seed = 1)

est <- modelbased::estimate_means(m, by = "x", predict = "mu")
as.data.frame(est)   # values are fine
est
#> Error in `$<-.data.frame`(`*tmp*`, "Component", value = ...) :
#>   replacement has 0 rows, data has 2

modelbased::estimate_contrasts(m, contrast = "x", predict = "mu")
#> Error in `$<-.data.frame`(`*tmp*`, "Component", value = ...) :
#>   replacement has 0 rows, data has 1

The same happens with predict = "bs", and on cogmod::cogmod_ddm() fits
(which model_info() flags is_rtchoice), including estimate_means() with
no predict.

Cause

print.estimate_contrasts(), R/print.R (L110 on main). Seven other print
methods are aliases of it (print.estimate_means, print.estimate_slopes,
print.summary_estimate_slopes, print.estimate_smooth,
print.estimate_predicted, print.visualisation_matrix,
print.estimate_grouplevel, L130-148), so they all fail the same way.

if (isTRUE(attr$model_info$is_wiener) || isTRUE(attr$model_info$is_rtchoice)) {
  out <- .print_drift_diffusion(out, select, ...)

Suggested fix

Take that branch only when there is something to split:

if ((isTRUE(attr$model_info$is_wiener) || isTRUE(attr$model_info$is_rtchoice)) &&
    "Component" %in% colnames(x)) {

I tested this patch on the model above. estimate_means(predict = "bs") and
estimate_contrasts(predict = "mu") print the regular table, and
estimate_prediction() still prints its two sub-tables (rt, response).

Related edge case

.print_drift_diffusion() also fails when only one component is present,
for instance after subsetting:

head(modelbased::estimate_prediction(m), 2)   # both rows are from the "rt" block
#> Error in x[[i]] <- value :
#>   attempt to select less than one element in OneIndex

With a single component, setdiff(names(out), i) is character(0), so
out[[i]][[setdiff(names(out), i)]] <- NULL fails (L202 on main). A guard
fixes it:

other <- setdiff(names(out), i)
if (length(other)) out[[i]][[other]] <- NULL

Versions

R 4.5.3, brms 2.23.1, insight 1.5.4.2, modelbased 0.17.0.6. The code above is
unchanged on main (0.17.0.13).

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions