13.2 Looking at the components of the indirect effect of \(X\)

13.2.1 Examining the first stage of the mediation process.

When making a newdata object to feed into fitted() with more complicated models, it can be useful to review the model formula like so:

model1$formula
## respappr ~ 1 + D1 + D2 + sexism + D1:sexism + D2:sexism 
## liking ~ 1 + D1 + D2 + respappr + sexism + D1:sexism + D2:sexism

Now we’ll prep for and make our version of Figure 13.3.

nd <-
  tibble(D1 = rep(c(1/3, -2/3, 1/3), each = 30),
         D2 = rep(c(1/2, 0, -1/2), each = 30),
         sexism = rep(seq(from = 3.5, to = 6.5, length.out = 30),
                      times = 3))

model1_fitted <-
  fitted(model1, 
       newdata = nd,
       resp = "respappr") %>% 
  as_tibble() %>% 
  bind_cols(nd) %>% 
  mutate(condition = ifelse(D2 == 0, "No Protest",
                            ifelse(D2 == -1/2, "Individual Protest", "Collective Protest"))) %>% 
  mutate(condition = factor(condition, levels = c("No Protest", "Individual Protest", "Collective Protest")))

protest <-
  protest %>% 
  mutate(condition = ifelse(protest == 0, "No Protest",
                            ifelse(protest == 1, "Individual Protest", "Collective Protest"))) %>% 
  mutate(condition = factor(condition, levels = c("No Protest", "Individual Protest", "Collective Protest")))

model1_fitted %>% 
  ggplot(aes(x = sexism, group = condition)) +
  geom_ribbon(aes(ymin = Q2.5, ymax = Q97.5),
              linetype = 3, color = "white", fill = "transparent") +
  geom_line(aes(y = Estimate), color = "white") +
  geom_point(data = protest, aes(x = sexism, y = respappr),
             color = "red", size = 2/3) +
  coord_cartesian(xlim = 4:6) +
  labs(x = expression(paste("Perceived Pervasiveness of Sex Discrimination in Society (", italic(W), ")")),
       y = expression(paste("Perceived Appropriateness of Response (", italic(M), ")"))) +
  theme_black() +
  theme(panel.grid = element_blank()) +
  facet_wrap(~condition)

In order to get the \(\Delta R^2\) distribution analogous to the change in \(R^2\) \(F\)-test Hayes discussed on page 482, we’ll have to first refit the model without the interaction for the \(M\) criterion. Here are the sub-models.

m_model <- bf(respappr ~ 1 + D1 + D2 + sexism)
y_model <- bf(liking   ~ 1 + D1 + D2 + respappr + sexism + D1:sexism + D2:sexism)

Now we fit model2.

model2 <-
  brm(data = protest, family = gaussian,
      m_model + y_model + set_rescor(FALSE),
      chains = 4, cores = 4)

With model2 in hand, we’re ready to compare \(R^2\) distributions.

R2s <-
  bayes_R2(model1, resp = "respappr", summary = F) %>% 
  as_tibble() %>% 
  rename(model1 = R2_respappr) %>% 
  bind_cols(
    bayes_R2(model2, resp = "respappr", summary = F) %>% 
      as_tibble() %>% 
      rename(model2 = R2_respappr)
  ) %>% 
  mutate(difference = model1 - model2) 

R2s %>% 
  ggplot(aes(x = difference)) +
  geom_halfeyeh(aes(y = 0), fill = "grey50", color = "white",
                point_interval = median_qi, .prob = 0.95) +
  scale_x_continuous(breaks = median_qi(R2s$difference, .prob = .95)[1, 1:3],
                     labels = median_qi(R2s$difference, .prob = .95)[1, 1:3] %>% round(2)) +
  scale_y_continuous(NULL, breaks = NULL) +
  xlab(expression(paste(Delta, italic(R)^2))) +
  theme_black() +
  theme(panel.grid = element_blank())

And we might also compare the models by their information criteria.

loo(model1, model2)
##                  LOOIC    SE
## model1          760.10 29.56
## model2          765.47 29.71
## model1 - model2  -5.37  7.96
waic(model1, model2)
##                   WAIC    SE
## model1          759.74 29.47
## model2          765.32 29.71
## model1 - model2  -5.58  7.96

The Bayesian \(R^2\), the LOO-CV, and the WAIC all suggest there’s little difference between the two models with respect to their predictive utility. In such a case, I’d lean on theory to choose between them. If inclined, one could also do Bayesian model averaging.

Within our Bayesian modeling paradigm, we don’t have a direct analogue to the \(F\)-tests Hayes presented on page 483. But a little fitted() and follow-up wrangling will give us some difference scores.

# we need new `nd` data
nd <-
  tibble(D1 = rep(c(1/3, -2/3, 1/3), each = 3),
         D2 = rep(c(1/2, 0, -1/2), each = 3),
         sexism = rep(c(4.250, 5.120, 5.896), times = 3))

# this time we'll use `summary = F`
model1_fitted <-
  fitted(model1, 
       newdata = nd,
       resp = "respappr", 
       summary = F) %>% 
  as_tibble() %>% 
  gather() %>% 
  mutate(condition = rep(c("Collective Protest", "No Protest", "Individual Protest"),
                         each = 3*4000),
         sexism = rep(c(4.250, 5.120, 5.896), times = 3) %>% rep(., each = 4000),
         iter = rep(1:4000, times = 9)) %>% 
  select(-key) %>% 
  spread(key = condition, value = value) %>% 
  mutate(`Individual Protest - No Protest` = `Individual Protest` - `No Protest`,
         `Collective Protest - No Protest` = `Collective Protest` - `No Protest`,
         `Collective Protest - Individual Protest` = `Collective Protest` - `Individual Protest`)

# a tiny bit more wrangling and we're ready to plot the difference distributions
model1_fitted %>% 
  select(sexism, contains("-")) %>% 
  gather(key, value, -sexism) %>% 
  
  ggplot(aes(x = value)) +
  geom_halfeyeh(aes(y = 0), fill = "grey50", color = "white",
                point_interval = median_qi, .prob = 0.95) +
  geom_vline(xintercept = 0, color = "grey25", linetype = 2) +
  scale_y_continuous(NULL, breaks = NULL) +
  facet_grid(sexism~key) +
  theme_black() +
  theme(panel.grid = element_blank())

Now we have model1_fitted, it’s easy to get the typical numeric summaries for the differences.

model1_fitted %>% 
  select(sexism, contains("-")) %>% 
  gather(key, value, -sexism) %>% 
  group_by(key, sexism) %>% 
  summarize(mean = mean(value),
            ll = quantile(value, probs = .025),
            ul = quantile(value, probs = .975)) %>% 
  mutate_if(is.double, round, digits = 3)
## # A tibble: 9 x 5
## # Groups:   key [3]
##   key                                     sexism  mean     ll    ul
##   <chr>                                    <dbl> <dbl>  <dbl> <dbl>
## 1 Collective Protest - Individual Protest   4.25 0.636 -0.102 1.38 
## 2 Collective Protest - Individual Protest   5.12 0.424 -0.059 0.898
## 3 Collective Protest - Individual Protest   5.90 0.234 -0.418 0.879
## 4 Collective Protest - No Protest           4.25 1.02   0.348 1.72 
## 5 Collective Protest - No Protest           5.12 1.65   1.19  2.11 
## 6 Collective Protest - No Protest           5.90 2.21   1.53  2.87 
## 7 Individual Protest - No Protest           4.25 0.388 -0.34  1.16 
## 8 Individual Protest - No Protest           5.12 1.23   0.739 1.73 
## 9 Individual Protest - No Protest           5.90 1.98   1.31  2.68

The three levels of Collective Protest - Individual Protest correspond nicely with some of the analyses Hayes presented on pages 484–486. However, they don’t get at the differences Hayes expressed as \(\theta_{D_{1}\rightarrow M}\) to . For those, we’ll have to work directly with the posterior_samples().

post <- posterior_samples(model1)

post %>% 
  mutate(`Difference in how Catherine's behavior is perceived between being told she protested or not when W is 4.250` = b_respappr_D1 + `b_respappr_D1:sexism`*4.250,
         `Difference in how Catherine's behavior is perceived between being told she protested or not when W is 5.210` = b_respappr_D1 + `b_respappr_D1:sexism`*5.120,
         `Difference in how Catherine's behavior is perceived between being told she protested or not when W is 5.896` = b_respappr_D1 + `b_respappr_D1:sexism`*5.896) %>% 
  select(contains("Difference")) %>% 
  gather() %>% 
  group_by(key) %>% 
  summarize(mean = mean(value),
            ll = quantile(value, probs = .025),
            ul = quantile(value, probs = .975)) %>% 
  mutate_if(is.double, round, digits = 3)
## # A tibble: 3 x 4
##   key                                                                              mean    ll    ul
##   <chr>                                                                           <dbl> <dbl> <dbl>
## 1 Difference in how Catherine's behavior is perceived between being told she pro… 0.706 0.086  1.34
## 2 Difference in how Catherine's behavior is perceived between being told she pro… 1.44  1.02   1.85
## 3 Difference in how Catherine's behavior is perceived between being told she pro… 2.09  1.51   2.69

13.2.2 Estimating the second stage of the mediation process.

Here’s \(b\).

post %>% 
  
  ggplot(aes(x = b_liking_respappr)) +
  geom_halfeyeh(aes(y = 0), fill = "grey50", color = "white",
                point_interval = median_qi, .prob = 0.95) +
  scale_x_continuous(breaks = c(-1, median(post$b_liking_respappr), 1),
                     labels = c(-1, 
                                median(post$b_liking_respappr) %>% round(3),
                                1)) +
  scale_y_continuous(NULL, breaks = NULL) +
  coord_cartesian(xlim = -1:1) +
  xlab(expression(paste("b_liking_respappr (i.e., ", italic(b), ")"))) +
  theme_black() +
  theme(panel.grid = element_blank())