The following packages are used in this section: psych, tibble, and teachingtools. tibble are part of the tidyverse family.
To make sure that the packages used in this section are installed and loaded, run the following code in your console.
xfun::pkg_attach2("psych","teachingtools","tidyverse")
For mediation analysis I’m going to give a brief tour the psych package. The psych package isn’t your only option. In fact, you don’t need a special package for doing mediation analysis, and you could in fact do it just by fitting the appropriate regression models. The only tricky thing there would be calculating the bootstrapped confidence intervals, but these can be estimated with a package called mediation. However, this is overly complicated for the casual user of mediation analysis. The psych, in contrast does it all for you and you only have to specify your X, your M, and your Y.
Mediation in the psych is done with the psych::mediate()
function. In it’s simplest form it only requires the name of your X, M, and Y variables, and the name of your data table. To see it at work, we’ll replicate the first example from Hayes (2018)1
In Chapter 3.3 Hayes (2018) introduces a data set from a study by Tal-Or, Cohen, Tsfati, and Gunther (2010).
(participants) read one of two newspaper articles describing an economic crisis that may affect the price and supply of sugar in Israel. Approximately half of the participants (n = 58) were given an article they were told would be appearing on the front page of a major Israeli newspaper (henceforth referred to as the front page condition). The remaining participants (n = 65) were given the same article but were told it would appear in the middle of an economic supplement of this newspaper (referred to here as the interior page condition).
After the participants read the article, they were asked a number of questions about their reactions to the story. Some questions asked par- ticipants how soon they planned on buying sugar and how much they intended to buy. Their responses were aggregated to form an intention to buy sugar measure (REACTION in the data file)
They were also asked questions used to quantify how much they believed that others in the community would be prompted to buy sugar as a result of exposure to the article, a measure of presumed media influence (PMI in the data file).
The aim was to:
estimate the effects of the manipulation (X […], with the front page condition coded 1 and the interior page condition coded 0) on likelihood of buying sugar (Y […]), directly as well as indirectly through presumed media influence (M […])
Let use try and replicate this analysis with psych::mediate()
.
First, we’ll take a look at our data table called pmi
:
pmi
# A tibble: 123 x 6
cond pmi import reaction gender age
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 7 6 5.25 1 51
2 0 6 1 1.25 1 40
3 1 5.5 6 5 1 26
4 0 6.5 6 2.75 0 21
5 0 6 5 2.5 1 27
6 0 5.5 1 1.25 1 25
7 0 3.5 1 1.5 0 23
8 1 6 6 4.75 1 25
9 0 4.5 6 4.25 1 22
10 0 7 6 6.25 1 24
# … with 113 more rows
The column labelled cond
will be our X, the column labelled reaction
will be our Y, and the column labelled pmi
will be our M. We’ll save the model to hayes1
,
hayes1 <- psych::mediate(x = "cond", # our X
y = "reaction", # our Y
m = "pmi", # our M
data = pmi, # our data table
n.iter = 5000, # the number of bootstrap samples (default: 5000)
plot = FALSE, # don't draw a plot (default: TRUE)
)
To view the model output we can just type in the model name hayes1
to access this.
hayes1
Mediation/Moderation Analysis
Call: psych::mediate(y = "reaction", x = "cond", m = "pmi", data = pmi,
n.iter = 5000, plot = FALSE)
The DV (Y) was reaction . The IV (X) was cond . The mediating variable(s) = pmi .
Total effect(c) of cond on reaction = 0.5 S.E. = 0.28 t = 1.79 df= 122 with p = 0.075
Direct effect (c') of cond on reaction removing pmi = 0.25 S.E. = 0.55 t = 0.96 df= 120 with p = 0.34
Indirect effect (ab) of cond on reaction through pmi = 0.24
Mean bootstrapped indirect effect = 0.24 with standard error = 0.13 Lower CI = 0 Upper CI = 0.52
R = 0.45 R2 = 0.21 F = 15.56 on 2 and 120 DF p-value: 1.31e-08
To see the longer output, specify short = FALSE in the print statement or ask for the summary
The model output will give us the statistics for the Total effect, the Direct effect, and the Indirect effect, including the bootstrapped confidence intervals for the Indirect effect.
Another way to access the information about the model is to use the generic function summary()
summary(hayes1)
Call: psych::mediate(y = "reaction", x = "cond", m = "pmi", data = pmi,
n.iter = 5000, plot = FALSE)
Direct effect estimates (traditional regression) (c')
reaction se t df Prob
Intercept 0.53 0.55 0.96 120 3.40e-01
cond 0.25 0.26 0.99 120 3.22e-01
pmi 0.51 0.10 5.22 120 7.66e-07
R = 0.45 R2 = 0.21 F = 15.56 on 2 and 120 DF p-value: 9.83e-07
Total effect estimates (c)
reaction se t df Prob
cond 0.5 0.28 1.79 122 0.0754
'a' effect estimates
pmi se t df Prob
Intercept 5.38 0.16 33.22 121 1.16e-62
cond 0.48 0.24 2.02 121 4.54e-02
'b' effect estimates
reaction se t df Prob
pmi 0.51 0.1 5.24 121 6.88e-07
'ab' effect estimates (through mediators)
reaction boot sd lower upper
cond 0.24 0.24 0.13 0 0.52
If we do want to view the model diagram we can use the mediate.diagram()
function. Compare the plot below to the one in Hayes (2018, pg 88) if you’d like.
psych::mediate.diagram(hayes1)
One big downside of the psych package is that the model object is difficult to work with. For example, let’s say that we just wanted to get the bootstrapped confidence intervals for the indirect effect, and we wanted to save that to a variable. There is unfortunately no straightforward way to do this. However, all the information is saved in a list inside the output object and you could access it by subsetting.
For example:
The mean of ab
hayes1[["boot"]][['mean']][,1]
cond
0.2433902
The sd of ab
hayes1[["boot"]][['sd']][,1]
cond
0.1305335
The bootstrapped CIs of ab
hayes1[["boot"]][['ci']][,1]
2.5% 97.5%
0.003656523 0.523096406
Another big downside of the psych package is that it isn’t that great beyond simple examples like the one I’ve demonstrated. However, if you’re just interested in doing simple mediation, then the psych package might be a more user friendly alternative to the SPSS PROCESS
macro.
For more complex scenarios the go to package is lavaan. lavaan is a package for doing SEM but obviously it can also do simple mediation analysis. However, covering lavaan would take a workshop in itself because the syntax and approach is a lot more complex. Below, just for an illustration, I’ve included an example of how to replicate with lavaan what we did in psych.
xfun::pkg_attach2("lavaan")
model <- ' # direct effect
reaction ~ c*cond
# mediator
pmi ~ a*cond
reaction ~ b*pmi
# indirect effect (a*b)
ab := a*b
# total effect
total := c + (a*b)
'
fit <- lavaan::sem(model = model, data = pmi, se = "bootstrap")
summary(fit)
lavaan 0.6-6 ended normally after 21 iterations
Estimator ML
Optimization method NLMINB
Number of free parameters 5
Number of observations 123
Model Test User Model:
Test statistic 0.000
Degrees of freedom 0
Parameter Estimates:
Standard errors Bootstrap
Number of requested bootstrap draws 1000
Number of successful bootstrap draws 986
Regressions:
Estimate Std.Err z-value P(>|z|)
reaction ~
cond (c) 0.254 0.266 0.957 0.339
pmi ~
cond (a) 0.477 0.232 2.052 0.040
reaction ~
pmi (b) 0.506 0.083 6.104 0.000
Variances:
Estimate Std.Err z-value P(>|z|)
.reaction 1.893 0.201 9.441 0.000
.pmi 1.675 0.289 5.788 0.000
Defined Parameters:
Estimate Std.Err z-value P(>|z|)
ab 0.241 0.129 1.876 0.061
total 0.496 0.288 1.718 0.086
Hayes, A. F. (2018) Introduction to Mediation, Moderation, and Conditional Process Analysis (2nd Edition), Guilford Press, London.↩︎