Interpretation of products of paths (and Est.) when multiple link functions are combined in a single model

I am trying to estimate a simple mediation model with random intercepts grouped by participant (id) . My mediating variable (m) is in [0,1] and my outcome variable (y) is measured on the ratio level, in range[0,35] (see toy data below). I am working in brms and have tried to solve for my use case by implementing logistic regression on my x - > m regression and ordinary regression on my x + m -> y regression.

This is the code I’ve used:

#define formulae
mod_y = bf(y ~ x + m + (1 | id))
mod_m = bf(m ~ x + (1|id), family=bernoulli(link = "logit"))

#estimate 
fit = brm(mod_y +
    mod_m + 
    set_rescor(FALSE),
    data = dat,
    warmup = 2000,
    iter = 8000, 
    chains = 2, 
    inits= "0", 
    cores=4,
    seed = 123,
    sample_prior = TRUE,
save_pars = save_pars("all"))

My question is how do I interpret the Estimate column in summary(fit). My understanding of OLS style regressions is that in this case the x -> y estimates should capture change in y as a function of change of 1 in x, whereas the x -> m estimates should capture change in the log-odds of m = 1 as a function of change of 1 in x. Is this still true in the brms output, i.e. do different rows in the Estimate column have different concrete interpretations?

As a corollary, given the model specified above, what is the correct way to concretely interpret products of paths given the estimates may be measured on different scales? For example, to test whether the indirect effect (x -> m -> y, or ab in mediation terminology) is negative I would use the method, hypothesis(fit, 'y_m*m_x< 0'). Is this still correct and how would I interpret the resulting CI in terms of unit change in y as a function of change in x?

Here’s my toy data for purposes of clarity:

dat = structure(list(id = c("2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"2552a7771658870ed27965140ecff784", "2552a7771658870ed27965140ecff784", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22", 
"0fcc6d692248cb88026632598ee93a22", "0fcc6d692248cb88026632598ee93a22"
), x = structure(c(2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 
2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 2L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L), .Label = c("A", "B"), class = "factor", contrasts = structure(c(0, 
1), .Dim = 2:1, .Dimnames = list(c("A", "B"), "B"))), m = c(0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 
1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 
0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 
1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 
0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 
1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0), y = c(26.796875, 16.953125, 
1.640625, 16.953125, 34.453125, 13.671875, 16.953125, 13.671875, 
3.828125, 18.046875, 14.765625, 10.390625, 12.578125, 13.671875, 
18.046875, 16.953125, 18.046875, 16.953125, 26.796875, 21.328125, 
12.578125, 10.390625, 16.953125, 31.171875, 13.671875, 16.953125, 
0.546875, 13.671875, 20.234375, 34.453125, 26.796875, 13.671875, 
16.953125, 16.953125, 12.578125, 27.890625, 34.453125, 16.953125, 
34.453125, 34.453125, 16.953125, 24.609375, 18.046875, 22.421875, 
16.953125, 22.421875, 16.953125, 16.953125, 26.796875, 16.953125, 
26.796875, 24.609375, 24.609375, 24.609375, 24.609375, 24.609375, 
24.609375, 24.609375, 23.515625, 24.609375, 31.171875, 24.609375, 
24.609375, 24.609375, 24.609375, 28.984375, 25.703125, 31.171875, 
31.171875, 24.609375, 18.046875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 34.453125, 34.453125, 0.546875, 
34.453125, 33.359375, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 1.640625, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 0.546875, 
0.546875, 0.546875, 0.546875, 1.640625, 1.640625)), row.names = c(1L, 
2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 
16L, 17L, 18L, 19L, 20L, 21L, 22L, 23L, 24L, 25L, 26L, 27L, 28L, 
29L, 30L, 31L, 32L, 33L, 34L, 35L, 43L, 44L, 45L, 46L, 47L, 48L, 
49L, 50L, 51L, 52L, 53L, 54L, 55L, 56L, 57L, 58L, 59L, 60L, 61L, 
62L, 63L, 64L, 65L, 66L, 67L, 68L, 69L, 70L, 71L, 72L, 73L, 74L, 
75L, 76L, 77L, 85L, 86L, 87L, 88L, 89L, 90L, 91L, 92L, 93L, 94L, 
95L, 96L, 97L, 98L, 99L, 100L, 101L, 102L, 103L, 104L, 105L, 
106L, 107L, 108L, 109L, 110L, 111L, 112L, 113L, 114L, 115L, 116L, 
117L, 118L, 119L, 127L, 128L, 129L, 130L, 131L, 132L, 133L, 134L, 
135L, 136L, 137L, 138L, 139L, 140L, 141L, 142L, 143L, 144L, 145L, 
146L, 147L, 148L, 149L, 150L, 151L, 152L, 153L, 154L, 155L, 156L, 
157L, 158L, 159L, 160L, 161L), class = "data.frame")

My understanding of OLS style regressions is that in this case the x -> y estimates should capture change in y as a function of change of 1 in x, whereas the x -> m estimates should capture change in the log-odds of m = 1 as a function of change of 1 in x . Is this still true in the brms output, i.e. do different rows in the Estimate column have different concrete interpretations?

That’s correct. The paths: x -> y and m -> y are interpreted like linear regressions, and the path x -> m is interpreted like a logistic regression (i.e., the estimates are logits and can be exponentiated to give odds ratios).

As a corollary, given the model specified above, what is the correct way to concretely interpret products of paths given the estimates may be measured on different scales? For example, to test whether the indirect effect ( x -> m -> y , or ab in mediation terminology) is negative I would use the method, hypothesis(fit, 'y_m*m_x< 0') . Is this still correct and how would I interpret the resulting CI in terms of unit change in y as a function of change in x ?

Assessing indirect effects with a categorical mediator is a tricky subject, because the outcome of the first path (x -> m) is a probability, whereas the predictor of the second path (m -> y) is the category itself. This makes it harder to assess mediation, because the mediator is assessed on a different scale in each path. There are two more complex methods for handling this: using a latent underlying variable for the binary mediator as both predictor and outcome, or using causally-defined effects (more info in this paper).

However, a simpler approximation is to standardise both paths before computing the indirect effect.

The x -> m pathway is standardised by:

b_{xm} * SD_x / SD_m

Where SD_x is the observed standard deviation of the predictor and SD_m is the standard deviation of the binary outcome when analysed with a logit link, given by:

SD_m = \sqrt{b_{xm}^2 + SD_x^2 + \pi^2/3}

The m -> y pathway is standardised similarly:

b_{my} * SD_m / SD_y

But in this case SD_m is the observed standard deviation of the binary mediator.

Once you standardise both paths, you can then compute the indirect effect as usual

Sorry for the delay, my updates must be off, but that’s very helpful, thank you!

Ok, I have tried to implement your suggestion and have realized that I may benefit from further direction, if you’d be so kind. Basically, I’m not sure what you mean by the observed deviation of the predictor, i.e. do you mean the observed deviation of the X -> M path, or the observed deviation of X?

I.e. in the context of my example which of the following is correct:

library(dplyr)
post = posterior_samples(fit) 

#option 1, deviations from the data:
sd_x = sd(as.numeric(dat$x))
sd_y = sd(dat$y)
sd_m = sd(dat$m)

opt_1 = post %>% 
    mutate(b_m_xB_std = b_m_xB*sd_x/sqrt(b_m_xB^2 + sd_x^2 + pi^2/3),
           b_y_xB_std = b_y_m*sd_m/sd_y)
  
#option 2, deviations from the posterior:
opt_2 = post %>% 
    mutate(b_m_xB_std = b_m_xB*sd(b_m_xB)/sqrt(b_m_xB^2 + sd(b_m_xB)^2 + pi^2/3),
           b_y_xB_std = b_y_m*sd(b_y_m)/sigma_y)

Thanks again!

Bump! @andrjohns ?

Oh sorry for missing this question! I meant the SD of X, as in option 1.

As a quick note, I’d probably use (pi^2)/3 in your code, just to make absolutely sure you don’t accidentally get \pi^{\frac{2}{3}}

Thanks once again!