11 Fundamentals of Conditional Process Analysis

Thus far in this book, mediation and moderation have been treated as distinct, separate, and independent concepts with different analytical procedures and interpretations. Yet processes modeled with mediation analysis likely are contingent and hence moderated, in that they operate differently for different people or in different contexts or circumstances. A more complete analysis, therefore, should attempt to model the mechanisms at work linking \(X\) to \(Y\) while simultaneously allowing those effects to be contingent on context, circumstance, or individual differences. (p. 395)

11.1 Examples of conditional process models in the literature

You can look up the various examples in the literature on your own. The main point is

moderation can be combined with mediation in a number of different ways. But these examples [we skipped for the sake of brevity] only scratch the surface of what is possible. Think about the number of possibilities when you increase the number of mediators, distinguish between moderation of paths in a parallel versus serial multiple mediator model, or allow for multiple moderators of different paths or the same path, and so forth. The possibilities are nearly endless. But regardless of the configuration of moderated paths or complexity of the model, conditional process analysis involves the estimation and interpretation of direct and indirect effects, just as in a simple mediation analysis. However, when causal effects in a mediation model are moderated, they will be conditional on those moderators. Thus, an understanding of the concepts of the conditional direct effect and the conditional indirect effect is required before one should attempt to under- take a conditional process analysis. (p. 401, emphasis in the original)

11.2 Conditional direct and indirect effects

When a direct or indirect effect is conditional, analysis and interpretation of the results of the modeling process should be based on a formal estimate of and inference about conditional direct and/or conditional in- direct effects. In this section, [Hayes illustrated] the computation of conditional direct and indirect effects for example models that combine moderation and mediation. (p. 403)

11.3 Example: Hiding your feelings from your work team

Here we load a couple necessary packages, load the data, and take a glimpse().

## Observations: 60
## Variables: 4
## $ dysfunc <dbl> -0.23, -0.13, 0.00, -0.33, 0.39, 1.02, -0.35, -0.23, 0.39, -0.08, -0.23, 0.09, -0.29, -0.06…
## $ negtone <dbl> -0.51, 0.22, -0.08, -0.11, -0.48, 0.72, -0.18, -0.13, 0.52, -0.26, 1.08, 0.53, -0.19, 0.15,…
## $ negexp  <dbl> -0.49, -0.49, 0.84, 0.84, 0.17, -0.82, -0.66, -0.16, -0.16, -0.16, -0.16, 0.50, 0.84, 0.50,…
## $ perform <dbl> 0.12, 0.52, -0.08, -0.08, 0.12, 1.12, -0.28, 0.32, -1.08, -0.28, -1.08, -0.28, -0.28, -0.88…

Load the brms package.

Recall that we fit mediation models with brms using multivariate syntax. In previous attempts, we’ve defined and saved the model components outside of the brm() function and then plugged then into brm() using their identifier. Just to shake things up a bit, we’ll just do all the steps right in brm(), this time.

##  Family: MV(gaussian, gaussian) 
##   Links: mu = identity; sigma = identity
##          mu = identity; sigma = identity 
## Formula: negtone ~ 1 + dysfunc 
##          perform ~ 1 + dysfunc + negtone + negexp + negtone:negexp 
##    Data: teams (Number of observations: 60) 
## Samples: 4 chains, each with iter = 2000; warmup = 1000; thin = 1;
##          total post-warmup samples = 4000
## 
## Population-Level Effects: 
##                        Estimate Est.Error l-95% CI u-95% CI  Rhat Bulk_ESS Tail_ESS
## negtone_Intercept         0.025     0.063   -0.104    0.150 1.002     6391     2962
## perform_Intercept        -0.012     0.060   -0.129    0.104 1.001     6329     2866
## negtone_dysfunc           0.622     0.172    0.274    0.963 1.002     6698     2815
## perform_dysfunc           0.367     0.182    0.008    0.721 1.000     4817     3385
## perform_negtone          -0.438     0.135   -0.701   -0.166 1.002     4929     3156
## perform_negexp           -0.019     0.120   -0.254    0.212 1.001     5368     3143
## perform_negtone:negexp   -0.516     0.245   -1.004   -0.029 1.000     4989     3347
## 
## Family Specific Parameters: 
##               Estimate Est.Error l-95% CI u-95% CI  Rhat Bulk_ESS Tail_ESS
## sigma_negtone    0.487     0.047    0.406    0.590 1.000     6107     2860
## sigma_perform    0.460     0.045    0.380    0.562 1.000     5601     3187
## 
## Samples were drawn using sampling(NUTS). For each parameter, Eff.Sample 
## is a crude measure of effective sample size, and Rhat is the potential 
## scale reduction factor on split chains (at convergence, Rhat = 1).

Our model summary coheres nicely with Table 11.1 and the formulas on page 409. Here are the \(R^2\) distribution summaries.

##           Estimate Est.Error  Q2.5 Q97.5
## R2negtone    0.194     0.079 0.041 0.349
## R2perform    0.321     0.078 0.152 0.459

On page 410 Hayes reported two sample means. Compute them like so.

## [1] -0.008
## [1] -0.032

For our Figure 11.4 and other similar figures in this chapter, we’ll use spaghetti plots. Recall that with a spaghetti plots for linear models, we only need two values for the variable on the x-axis, rather than the typical 30+.

Here’s our Figure 11.4, which uses only the first 40 HMC iterations for the spaghetti-plot lines.

Also, the plot theme in this chapter is a nod to the style John Kruschke frequently uses in his papers and texts.

Using Hayes’s notation from the top of page 412, we can express \(M\)’s conditional effect on \(Y\) as

\[\theta_{M \rightarrow Y} = b_1 + b_3 W,\]

where \(M\) is negtone, \(Y\) is perform, and \(W\) is negexp. We can extract our posterior summaries for \(b_1\) and \(b_3\) like so.

##                          Estimate Est.Error      Q2.5       Q97.5
## perform_negtone        -0.4378715 0.1351663 -0.701215 -0.16589701
## perform_negtone:negexp -0.5157503 0.2453382 -1.004435 -0.02860408

11.4 Estimation of a conditional process model using PROCESS

We just fit the model in the last section. No need to repeat.

11.5 Quantifying and visualizing (conditional) indirect and direct effects.

The analysis presented thus far has been piecemeal, in that [Hayes] addressed how to estimate the regression coefficients for each equation in this conditional process model and how to interpret them using standard principles of regression analysis, moderation analysis, and so forth. But a complete analysis goes further by integrating the estimates of each of the effects in the model (i.e., \(X \rightarrow M, \theta_{M \rightarrow Y}\)) to yield the direct and indirect effects of \(X\) on \(Y\). That is, the individual effects as quantified with the regression coefficients (conditional or otherwise) in equations 11.10 and 11.11 are not necessarily of immediate interest or relevance. Estimating them is a means to an end. What matters is the estimation of the direct and indirect effects, for they convey information about how \(X\) influences \(Y\) directly or through a mediator and how those effects are contingent on a moderator. (pp. 417–418)

11.5.0.1 The conditional indirect effect of \(X\).

One way to make a version of Table 11.2 is to work with the posterior_samples(), simply summarizing the distributions with means.

## # A tibble: 3 x 4
## # Groups:   w [3]
##        w     a conditional_effect conditional_indirect_effect
##    <dbl> <dbl>              <dbl>                       <dbl>
## 1 -0.531 0.622             -0.164                      -0.103
## 2 -0.006 0.622             -0.435                      -0.271
## 3  0.6   0.622             -0.747                      -0.465

That kind of summary isn’t the most Bayesian of us.

Here the posterior distribution for each is on full display.

11.5.0.2 The direct effect.

The direct effect of \(X\) on \(Y\) (i.e., dysfunc on perform) for this model is b_perform_dysfunc in brms. Here’s how to get its summary values from posterior_summary().

##  Estimate Est.Error      Q2.5     Q97.5 
##     0.367     0.182     0.008     0.721

11.6 Statistical inference

11.6.1 Inference about the direct effect.

We’ve already been expressing undertainty in terms of percentile-based 95% intervals and histograms. Here’s a plot of the direct effect, b_perform_dysfunc.

Since we’re plotting in a style similar to Kruschke, we switched from emphasizing the posterior mean or median to marking off the posterior mode, which is Kruschkes’ preferred measure of central tendency. We also ditched our typical percentile-based 95% intervals for highest posterior density intervals. The stat_pointintervalh() function from the Matthew Kay’s tidybayes package made it easy to compute those values with the point_interval = mode_hdi argument. Note how we also used tidybayes::mode_hdi() to compute those values and plug them into scale_x_continuous().

11.6.3 Probing moderation of mediation.

One of the contributions of Preacher et al. (2007) to the literature on moderated mediation analysis was their discussion of inference for conditional indirect effects. They suggested two approaches, one a normal theory-based approach that is an analogue of the Sobel test in unmoderated mediation analysis, and another based on bootstrapping. (p. 426)

One of the contributions of this project is moving away from NHST in favor of Bayesian modeling. Since we’ve already been referencing him with our plot themes, you might check out Kruschke’s textbook for more discussion on Bayes versus NHST.

11.6.3.1 Normal theory approach.

As we’re square within the Bayesian modeling paradigm, we have no need to appeal to normal theory for the posterior \(SD\)s or 95% intervals.

11.6.3.2 Bootstrap confidence intervals Two types of Bayesian credible intervals.

We produced the posterior means corresponding to those in Table 11.3 some time ago. Here they are, again, with percentile-based 95% intervals via tidybayes::mean_qi().

## # A tibble: 3 x 4
##        w `a(b1 + b3w)` .lower .upper
##    <dbl>         <dbl>  <dbl>  <dbl>
## 1 -0.531        -0.103 -0.409  0.161
## 2 -0.006        -0.271 -0.526 -0.077
## 3  0.6          -0.465 -0.823 -0.172

If we wanted to summarize those same effects with posterior modes and 95% highest posterior density intervals, instead, we’d replace our mean_qi() lnie with mode_hdi().

## # A tibble: 3 x 4
##        w `a(b1 + b3w)` .lower .upper
##    <dbl>         <dbl>  <dbl>  <dbl>
## 1 -0.531        -0.072 -0.374  0.195
## 2 -0.006        -0.261 -0.497 -0.06 
## 3  0.6          -0.479 -0.793 -0.152

And we might plot these with something like this.

This, of course, leads us right into the next section.

11.6.3.3 A Johnson-Neyman approach.

On page 429, Hayes discussed how Preacher et al. (2007)’s attempt to apply the JN technique in this context presumed

the sampling distribution of the conditional indirect effect is normal. Given that the sampling distribution of the conditional indirect effect is not normal, the approach they describe yields, at best, an approximate solution. To [Hayes’s] knowledge, no one has ever proposed a bootstrapping-based analogue of the Johnson-Neyman method for probing the moderation of an indirect effect.

However, our Bayesian HMC approach makes no such assumption. All we need to do is manipulate the posterior as usual. Here it is, this time using all 4000 iterations.

Glorious.

Session info

## R version 3.6.0 (2019-04-26)
## Platform: x86_64-apple-darwin15.6.0 (64-bit)
## Running under: macOS High Sierra 10.13.6
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/3.6/Resources/lib/libRlapack.dylib
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] tidybayes_1.1.0 brms_2.10.3     Rcpp_1.0.2      forcats_0.4.0   stringr_1.4.0   dplyr_0.8.3    
##  [7] purrr_0.3.3     readr_1.3.1     tidyr_1.0.0     tibble_2.1.3    ggplot2_3.2.1   tidyverse_1.2.1
## 
## loaded via a namespace (and not attached):
##  [1] colorspace_1.4-1          ellipsis_0.3.0            ggridges_0.5.1            rsconnect_0.8.15         
##  [5] ggstance_0.3.2            markdown_1.1              base64enc_0.1-3           rstudioapi_0.10          
##  [9] rstan_2.19.2              svUnit_0.7-12             DT_0.9                    fansi_0.4.0              
## [13] lubridate_1.7.4           xml2_1.2.0                bridgesampling_0.7-2      knitr_1.23               
## [17] shinythemes_1.1.2         zeallot_0.1.0             bayesplot_1.7.0           jsonlite_1.6             
## [21] broom_0.5.2               shiny_1.3.2               compiler_3.6.0            httr_1.4.0               
## [25] backports_1.1.5           assertthat_0.2.1          Matrix_1.2-17             lazyeval_0.2.2           
## [29] cli_1.1.0                 later_1.0.0               htmltools_0.4.0           prettyunits_1.0.2        
## [33] tools_3.6.0               igraph_1.2.4.1            coda_0.19-3               gtable_0.3.0             
## [37] glue_1.3.1.9000           reshape2_1.4.3            cellranger_1.1.0          vctrs_0.2.0              
## [41] nlme_3.1-139              crosstalk_1.0.0           xfun_0.10                 ps_1.3.0                 
## [45] rvest_0.3.4               mime_0.7                  miniUI_0.1.1.1            lifecycle_0.1.0          
## [49] gtools_3.8.1              zoo_1.8-6                 scales_1.0.0              colourpicker_1.0         
## [53] hms_0.4.2                 promises_1.1.0            Brobdingnag_1.2-6         parallel_3.6.0           
## [57] inline_0.3.15             shinystan_2.5.0           gridExtra_2.3             loo_2.1.0                
## [61] StanHeaders_2.19.0        stringi_1.4.3             dygraphs_1.1.1.6          pkgbuild_1.0.5           
## [65] rlang_0.4.1               pkgconfig_2.0.3           matrixStats_0.55.0        HDInterval_0.2.0         
## [69] evaluate_0.14             lattice_0.20-38           rstantools_2.0.0          htmlwidgets_1.5          
## [73] labeling_0.3              tidyselect_0.2.5          processx_3.4.1            plyr_1.8.4               
## [77] magrittr_1.5              R6_2.4.0                  generics_0.0.2            pillar_1.4.2             
## [81] haven_2.1.0               withr_2.1.2               xts_0.11-2                abind_1.4-5              
## [85] modelr_0.1.4              crayon_1.3.4              arrayhelpers_1.0-20160527 utf8_1.1.4               
## [89] rmarkdown_1.13            grid_3.6.0                readxl_1.3.1              callr_3.3.2              
## [93] threejs_0.3.1             digest_0.6.21             xtable_1.8-4              httpuv_1.5.2             
## [97] stats4_3.6.0              munsell_0.5.0             shinyjs_1.0