Conditional_effects() producing different Expected Values than fitted()

I’m debating whether to plot predictor effects manually or with conditional_effects(), and I’ve made the disturbing discovery that fitted.brmsfit(), which I use to calculate predictor effects manually, produces different expected values than conditional_effects() on the default setting of method = "fitted". It is disturbing because now I don’t know which expected values are correct. It’s a categorical model. Here’s how to reproduce the phenomenon with simple categorical data:


Cores = 4

model <- brm(Species ~ ., family = categorical, seed = 2023,
  prior = prior(normal(0, 1)), data = iris, backend = "cmdstanr", 
  cores = Cores, chains = Cores/2, threads = threading(Cores/2))

NewData <-
  lapply(lapply(iris[,names(iris) != "Species"], mean), rep, 2))

Predictor <- "Sepal.Length"
NewData[,Predictor] <- range(iris[,Predictor])

Fitted <- fitted(model, newdata = NewData)[,1,]

CondEff <- conditional_effects(model, Predictor, conditions = NewData[1,], categorical = T)

Fitted.CondEff <- with(CondEff[[1]], sapply(levels(cats__), function(x) estimate__[cats__ == x][c(1, 100)]))

list(Fitted, Fitted.CondEff)

     P(Y = setosa) P(Y = versicolor) P(Y = virginica)
[1,]    0.33536731         0.6051630       0.05946970
[2,]    0.04619066         0.9315982       0.02221116

         setosa versicolor   virginica
[1,] 0.30030614  0.6249712 0.037091023
[2,] 0.01700964  0.9663580 0.008161822

Hence, the mind boggles. Which set of expected values can I trust?

Just a quick thought:
My understanding is that fitted gives you model predictions, given input data, while conditional_effects gives you conditional or marginal effects. The difference is that for the conditional effects all parameters not part of the condition you investigate are set to their mean/reference category.

Besides that, the docu of conditional_effectsmethod parameter reads

Method used to obtain predictions. Can be set to "posterior_epred" (the default), "posterior_predict" , or "posterior_linpred" . For more details, see the respective function documentations.

Such that “fitted” doesn’t seem to be a valid option.

I don’t think they are supposed to do the same thing.

ps. Your code doesn’t run:

Error: The following priors do not correspond to any model parameter:
b ~ normal(0, 1)

I also think you can’t use threading and multiple cores per chain simultaneously.

just a quick addition: fitted is an outdated name for posterior_epred actually. however conditional effects uses robust = TRUE so computes the posterior median as point summary by default instead of the posterior mean.


Oh! It was just because of mean vs median. Now everything replicates again. Thanks!

Afterthought: A major source of my confusion was that when I Google conditional_effects(), the first search result (which I mistook for accurate) was this outdated helpfile for the function. In that version, method = "fitted" is specified as the alternative to “predict”…

Not sure why the replication code wouldn’t work though. I’m using brms 2.19.0 with cmdstan 2.32.0 and cmrstanr 0.5.3, and everything runs without issues.