Calculating average marginal effects in models with random slopes

@jackbailey Thanks, I took a look and like your approach of doing the calculations and weighting by unique predictor combos to save time. brmsmargins matches your code in cases where they both work.
Although the original uncertainty around whether these are accurate remains for me.

dummy <- mtcars
dummy$Time <- mtcars$am
m3 <- brm(mpg ~ Time + (1 + Time | cyl), data = dummy, cores = 4)
summary(m3)

which gives:

 Family: gaussian 
  Links: mu = identity; sigma = identity 
Formula: mpg ~ Time + (1 + Time | cyl) 
   Data: dummy (Number of observations: 32) 
  Draws: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
         total post-warmup draws = 4000

Group-Level Effects: 
~cyl (Number of levels: 3) 
                    Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sd(Intercept)           5.23      2.84     1.91    12.13 1.00     1783     1967
sd(Time)                3.12      2.40     0.13     9.07 1.00     1387     1677
cor(Intercept,Time)     0.30      0.51    -0.80     0.98 1.00     2404     1988

Population-Level Effects: 
          Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
Intercept    18.86      2.77    13.13    24.55 1.00     1353     1568
Time          2.79      2.45    -2.04     7.88 1.00     1852     1738

Family Specific Parameters: 
      Estimate Est.Error l-95% CI u-95% CI Rhat Bulk_ESS Tail_ESS
sigma     3.12      0.43     2.42     4.11 1.00     3105     2706

and the AME for Time

time_ame1 <- bayes_dydx.default(m3, variable = "Time", stepsize = 1)
time_ame1$est %>% quantile(probs = c(.5, .025, .975))

which gives:

       50%       2.5%      97.5% 
 2.5753360 -0.1291799  5.3944124 

or

time_ame2 <- brmsmargins(m3,
   add = data.frame(Time = c(0, 1)),
   CI = .95, CIType = "ETI",
   contrasts = matrix(c(-1, 1)))

time_ame2$ContrastSummary

which gives:

          M      Mdn         LL       UL PercentROPE PercentMID   CI CIType
1: 2.600315 2.575336 -0.1291799 5.394412          NA         NA 0.95    ETI
   ROPE  MID      Label
1: <NA> <NA> Contrast_1

To me a notable difference is the population estimate straight from brms is:
for Time: 2.79 [-2.04, 7.88]
compared to the AME for Time: 2.58 [-0.13, 5.39]
with the latter having considerably less uncertainty

On a separate note, if you’re interested in various marginal estimates after brms models, you may find some use in the additional capacity of brmsmargins

dummy <- mtcars
dummy$Time <- mtcars$am
dummy$Tx <- mtcars$vs
m2 <- brm(mpg ~ Time + Time:Tx + (1 + Time + Time:Tx | cyl), data = dummy, cores = 4)
summary(m2)

time_ame2 <- brmsmargins(m2,
            at = expand.grid(Time = 0:1, Tx = 0:1),
            CI = .95, CIType = "ETI",
            contrasts = cbind(
              "Time 0 v 1: Ctrl" = c(-1, +1, 0, 0),
              "Time 0 v 1: Tx"   = c(0, 0, -1, +1),
              "Tx v Ctrl: Time 0" = c(-1, 0, +1, 0),
              "Tx v Ctrl: Time 1" = c(0, -1, 0, +1)))

time_ame2$ContrastSummary

which gives:

          M      Mdn        LL        UL PercentROPE PercentMID   CI CIType
1: 1.494765 1.470411 -1.666221  4.573508          NA         NA 0.95    ETI
2: 5.146171 5.184606 -7.693666 17.631245          NA         NA 0.95    ETI
3: 0.000000 0.000000  0.000000  0.000000          NA         NA 0.95    ETI
4: 3.651406 3.751300 -9.708911 15.813571          NA         NA 0.95    ETI
   ROPE  MID             Label
1: <NA> <NA>  Time 0 v 1: Ctrl
2: <NA> <NA>    Time 0 v 1: Tx
3: <NA> <NA> Tx v Ctrl: Time 0
4: <NA> <NA> Tx v Ctrl: Time 1

these are counterfactuals so what if the population was all given the control treatment at baseline, etc. Also obviously it requires some more manual specification of desired contrasts to be applied. There also is a subset argument, which I use in longitudinal studies as I do not necessarily want to upweight the sample distribution of values in people who completed more time points in a longitudinal study. Anyway see
here if interested.

1 Like