11.1 Mediation using Path models

An even more flexible approach to mediation can be taken using path models, a type of structural equation model which are covered in more detail in the next section.

Using the lavaan package, path/SEM models can specify multiple variables to be outcomes, and fit these models simultaneously. For example, we can fit both step 2 and step 3 in a single model, as in the example below:

library(lavaan)
This is lavaan 0.6-3
lavaan is BETA software! Please report any bugs.

smash.model <- '
  crashes ~ speed + lateness
  speed ~ lateness
'

smash.model.fit <- sem(smash.model, data=smash)
summary(smash.model.fit)
lavaan 0.6-3 ended normally after 19 iterations

  Optimization method                           NLMINB
  Number of free parameters                          5

  Number of observations                           200

  Estimator                                         ML
  Model Fit Test Statistic                       0.000
  Degrees of freedom                                 0
  Minimum Function Value               0.0000000000000

Parameter Estimates:

  Information                                 Expected
  Information saturated (h1) model          Structured
  Standard Errors                             Standard

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)
  crashes ~                                           
    speed             0.288    0.031    9.152    0.000
    lateness          0.297    0.095    3.111    0.002
  speed ~                                             
    lateness          0.515    0.212    2.434    0.015

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
   .crashes          18.190    1.819   10.000    0.000
   .speed            92.135    9.214   10.000    0.000

The summary output gives us coefficients which correspond to the regression coefficients in the step 2 and step 3 models — but this time, from a single model.

We can also use lavaan to compute the indirect effects by labelling the relevant parameters, using the * and := operators. See the lavaan syntax guide for mediation for more detail.

Note that the * operator does not have the same meaning as in formulas for linear models in R — in lavaan, it means ‘apply a constraint’.

smash.model <- '
  crashes ~ B*speed + C*lateness
  speed ~ A*lateness

  # computed parameters, see http://lavaan.ugent.be/tutorial/mediation.html
  indirect := A*B
  total := C + (A*B)
  proportion := indirect/total
'

smash.model.fit <- sem(smash.model, data=smash)
summary(smash.model.fit)
lavaan 0.6-3 ended normally after 19 iterations

  Optimization method                           NLMINB
  Number of free parameters                          5

  Number of observations                           200

  Estimator                                         ML
  Model Fit Test Statistic                       0.000
  Degrees of freedom                                 0
  Minimum Function Value               0.0000000000000

Parameter Estimates:

  Information                                 Expected
  Information saturated (h1) model          Structured
  Standard Errors                             Standard

Regressions:
                   Estimate  Std.Err  z-value  P(>|z|)
  crashes ~                                           
    speed      (B)    0.288    0.031    9.152    0.000
    lateness   (C)    0.297    0.095    3.111    0.002
  speed ~                                             
    lateness   (A)    0.515    0.212    2.434    0.015

Variances:
                   Estimate  Std.Err  z-value  P(>|z|)
   .crashes          18.190    1.819   10.000    0.000
   .speed            92.135    9.214   10.000    0.000

Defined Parameters:
                   Estimate  Std.Err  z-value  P(>|z|)
    indirect          0.148    0.063    2.353    0.019
    total             0.445    0.112    3.973    0.000
    proportion        0.333    0.121    2.756    0.006

We can again get a bootstrap interval for the indirect effect, and print a table of just these computed effects like so:

set.seed(1234)
smash.model.fit <- sem(smash.model, data=smash, test="bootstrap", bootstrap=100)

parameterEstimates(smash.model.fit) %>%
  filter(op == ":=") %>%
  select(label, est, contains("ci")) %>%
  pander::pander()
label est ci.lower ci.upper
indirect 0.1481 0.02472 0.2715
total 0.4448 0.2254 0.6643
proportion 0.3329 0.09614 0.5697

Comparing these results with the mediation::mediate() output, we get similar results. In both cases, it’s possible to increase the number of bootstrap resamples if needed to increase the precision of the interval (the default is 1000, but 5000 might be a good target for publication).