13.5 Relative conditional direct effects
In order to get the \(R^2\) difference distribution analogous to the change in \(R^2\) \(F\)-test Hayes discussed on pages 495–496, we’ll have to first refit the model without the interaction for the \(Y\) criterion, liking
.
m_model <- bf(respappr ~ 1 + D1 + D2 + sexism + D1:sexism + D2:sexism)
y_model <- bf(liking ~ 1 + D1 + D2 + respappr + sexism)
model3 <-
brm(data = protest, family = gaussian,
m_model + y_model + set_rescor(FALSE),
chains = 4, cores = 4)
Here’s the \(\Delta R^2\) density.
R2s <-
bayes_R2(model1, resp = "liking", summary = F) %>%
as_tibble() %>%
rename(model1 = R2_liking) %>%
bind_cols(
bayes_R2(model3, resp = "liking", summary = F) %>%
as_tibble() %>%
rename(model3 = R2_liking)
) %>%
mutate(difference = model1 - model3)
R2s %>%
ggplot(aes(x = difference, y = 0)) +
geom_halfeyeh(point_interval = median_qi, .prob = c(0.95, 0.5),
fill = "grey50", color = "white") +
scale_x_continuous(breaks = c(-.5, median(R2s$difference) %>% round(2), .5)) +
scale_y_continuous(NULL, breaks = NULL) +
coord_cartesian(xlim = c(-.5, .5)) +
xlab(expression(paste(Delta, italic(R)^2))) +
theme_black() +
theme(panel.grid = element_blank())
We’ll also compare the models by their information criteria.
loo(model1, model3)
## LOOIC SE
## model1 760.10 29.56
## model3 761.61 31.10
## model1 - model3 -1.51 5.47
waic(model1, model3)
## WAIC SE
## model1 759.74 29.47
## model3 761.39 31.05
## model1 - model3 -1.65 5.46
As when we went through these steps for resp = "respappr"
, above, the Bayesian \(R^2\), the LOO-CV, and the WAIC all suggest there’s little difference between the two models with respect to predictive utility. In such a case, I’d lean on theory to choose between them. If inclined, one could also do Bayesian model averaging.
Our approach to plotting the relative conditional direct effects will mirror what we did for the relative conditional indirect effects, above. Here are the brm()
parameters that correspond to the parameter names of Hayes’s notation.
- \(c_{1}\) =
b_liking_D1
- \(c_{2}\) =
b_liking_D2
- \(c_{4}\) =
b_liking_D1:sexism
- \(c_{5}\) =
b_liking_D2:sexism
With all clear, we’re off to the races.
# c1 + c4W
D1_function <- function(w){
post$b_liking_D1 + post$`b_liking_D1:sexism`*w
}
# c2 + c5W
D2_function <- function(w){
post$b_liking_D2 + post$`b_liking_D2:sexism`*w
}
rcde_tibble <-
tibble(sexism = seq(from = 3.5, to = 6.5, length.out = 30)) %>%
group_by(sexism) %>%
mutate(`Protest vs. No Protest` = map(sexism, D1_function),
`Collective vs. Individual Protest` = map(sexism, D2_function)) %>%
unnest() %>%
ungroup() %>%
mutate(iter = rep(1:4000, times = 30)) %>%
gather(`direct effect`, value, -sexism, -iter) %>%
mutate(`direct effect` = factor(`direct effect`, levels = c("Protest vs. No Protest", "Collective vs. Individual Protest")))
head(rcde_tibble)
## # A tibble: 6 x 4
## sexism iter `direct effect` value
## <dbl> <int> <fct> <dbl>
## 1 3.5 1 Protest vs. No Protest -0.856
## 2 3.5 2 Protest vs. No Protest -0.482
## 3 3.5 3 Protest vs. No Protest -1.24
## 4 3.5 4 Protest vs. No Protest -1.23
## 5 3.5 5 Protest vs. No Protest -1.06
## 6 3.5 6 Protest vs. No Protest -0.663
Here is our variant of Figure 13.4, with respect to the relative conditional direct effects.
rcde_tibble %>%
group_by(`direct effect`, sexism) %>%
summarize(median = median(value),
ll = quantile(value, probs = .025),
ul = quantile(value, probs = .975)) %>%
ggplot(aes(x = sexism, group = `direct effect`)) +
geom_ribbon(aes(ymin = ll, ymax = ul),
color = "white", fill = "transparent", linetype = 3) +
geom_line(aes(y = median),
color = "white") +
coord_cartesian(xlim = 4:6,
ylim = c(-.6, .8)) +
labs(x = expression(paste("Perceived Pervasiveness of Sex Discrimination in Society (", italic(W), ")")),
y = "Relative Conditional Effect on Liking") +
theme_black() +
theme(panel.grid = element_blank(),
legend.position = "none") +
facet_grid(~ `direct effect`)
Holy smokes, them are some wide 95% CIs! No wonder the information criteria and \(R^2\) comparisons were so uninspiring.
Notice that the y-axis is on the parameter space. In Hayes’s Figure 13.5, the y-axis is on the liking
space, instead. When we want things in the parameter space, we work with the output of posterior_samples()
; when we want them in the criterion space, we use fitted()
.
# we need new `nd` data
nd <-
tibble(D1 = rep(c(1/3, -2/3, 1/3), each = 30),
D2 = rep(c(1/2, 0, -1/2), each = 30),
respappr = mean(protest$respappr),
sexism = seq(from = 3.5, to = 6.5, length.out = 30) %>% rep(., times = 3))
# we feed `nd` into `fitted()`
model1_fitted <-
fitted(model1,
newdata = nd,
resp = "liking",
summary = T) %>%
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")))
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 = liking),
color = "red", size = 2/3) +
coord_cartesian(xlim = 4:6,
ylim = 4:7) +
labs(x = expression(paste("Perceived Pervasiveness of Sex Discrimination in Society (", italic(W), ")")),
y = expression(paste("Evaluation of the Attorney (", italic(Y), ")"))) +
theme_black() +
theme(panel.grid = element_blank()) +
facet_wrap(~condition)
We expanded the range of the y-axis, a bit, to show more of that data (and there’s even more data outside of our expanded range). Also note how after doing so and after including the 95% CI bands, the crossing regression line effect in Hayes’s Figure 13.5 isn’t as impressive looking any more.
On pages 497–498, Hayes discussed more omnibus \(F\)-tests. Much like with the \(M\) criterion, we won’t come up with Bayesian \(F\)-tests, but we might go ahead and make pairwise comparisons at the three percentiles Hayes prefers.
# 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),
respappr = mean(protest$respappr),
sexism = rep(c(4.250, 5.120, 5.896), times = 3))
# this tie we'll use `summary = F`
model1_fitted <-
fitted(model1,
newdata = nd,
resp = "liking",
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.11 -0.713 0.487
## 2 Collective Protest - Individual Protest 5.12 -0.147 -0.539 0.254
## 3 Collective Protest - Individual Protest 5.90 -0.179 -0.72 0.361
## 4 Collective Protest - No Protest 4.25 -0.542 -1.11 0.036
## 5 Collective Protest - No Protest 5.12 -0.109 -0.569 0.338
## 6 Collective Protest - No Protest 5.90 0.277 -0.381 0.904
## 7 Individual Protest - No Protest 4.25 -0.432 -1.05 0.18
## 8 Individual Protest - No Protest 5.12 0.038 -0.373 0.465
## 9 Individual Protest - No Protest 5.90 0.456 -0.147 1.06
We don’t have \(p\)-values, but all the differences are small in magnitude and have wide 95% intervals straddling zero.
To get the difference scores Hayes presented on pages 498–500, one might:
post %>%
mutate(`Difference in liking between being told she protested or not when W is 4.250` = b_liking_D1 + `b_liking_D1:sexism`*4.250,
`Difference in liking between being told she protested or not when W is 5.120` = b_liking_D1 + `b_liking_D1:sexism`*5.120,
`Difference in liking between being told she protested or not when W is 5.896` = b_liking_D1 + `b_liking_D1:sexism`*5.896,
`Difference in liking between collective vs. individual protest when W is 4.250` = b_liking_D2 + `b_liking_D2:sexism`*4.250,
`Difference in liking between collective vs. individual protest when W is 5.120` = b_liking_D2 + `b_liking_D2:sexism`*5.120,
`Difference in liking between collective vs. individual protest when W is 5.896` = b_liking_D2 + `b_liking_D2:sexism`*5.896) %>%
select(contains("Difference in liking")) %>%
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: 6 x 4
## key mean ll ul
## <chr> <dbl> <dbl> <dbl>
## 1 Difference in liking between being told she protested or not when W is 4.250 -0.487 -1.00 0.017
## 2 Difference in liking between being told she protested or not when W is 5.120 -0.036 -0.43 0.35
## 3 Difference in liking between being told she protested or not when W is 5.896 0.367 -0.207 0.923
## 4 Difference in liking between collective vs. individual protest when W is 4.2… -0.11 -0.713 0.487
## 5 Difference in liking between collective vs. individual protest when W is 5.1… -0.147 -0.539 0.254
## 6 Difference in liking between collective vs. individual protest when W is 5.8… -0.179 -0.72 0.361