<- '
m1 # Outer Model
X =~ x1 + x2 + x3
Y =~ y1 + y2 + y3
Z =~ z1 + z2 + z3
# Inner model
Y ~ X + Z + X:Z
'
<- modsem(m1, oneInt)
est1 cat(est1$syntax)
#> X =~ x1
#> X =~ x2
#> X =~ x3
#> Y =~ y1
#> Y =~ y2
#> Y =~ y3
#> Z =~ z1
#> Z =~ z2
#> Z =~ z3
#> Y ~ X
#> Y ~ Z
#> Y ~ XZ
#> XZ =~ 1*x1z1
#> XZ =~ x2z1
#> XZ =~ x3z1
#> XZ =~ x1z2
#> XZ =~ x2z2
#> XZ =~ x3z2
#> XZ =~ x1z3
#> XZ =~ x2z3
#> XZ =~ x3z3
#> x1z1 ~~ 0*x2z2
#> x1z1 ~~ 0*x2z3
#> x1z1 ~~ 0*x3z2
#> x1z1 ~~ 0*x3z3
#> x1z2 ~~ 0*x2z1
#> x1z2 ~~ 0*x2z3
#> x1z2 ~~ 0*x3z1
#> x1z2 ~~ 0*x3z3
#> x1z3 ~~ 0*x2z1
#> x1z3 ~~ 0*x2z2
#> x1z3 ~~ 0*x3z1
#> x1z3 ~~ 0*x3z2
#> x2z1 ~~ 0*x3z2
#> x2z1 ~~ 0*x3z3
#> x2z2 ~~ 0*x3z1
#> x2z2 ~~ 0*x3z3
#> x2z3 ~~ 0*x3z1
#> x2z3 ~~ 0*x3z2
#> x1z1 ~~ x1z2
#> x1z1 ~~ x1z3
#> x1z1 ~~ x2z1
#> x1z1 ~~ x3z1
#> x1z2 ~~ x1z3
#> x1z2 ~~ x2z2
#> x1z2 ~~ x3z2
#> x1z3 ~~ x2z3
#> x1z3 ~~ x3z3
#> x2z1 ~~ x2z2
#> x2z1 ~~ x2z3
#> x2z1 ~~ x3z1
#> x2z2 ~~ x2z3
#> x2z2 ~~ x3z2
#> x2z3 ~~ x3z3
#> x3z1 ~~ x3z2
#> x3z1 ~~ x3z3
#> x3z2 ~~ x3z3
5 customizing interaction terms
By default, modsem()
creates product indicators for you, based on the interaction specified in your model. Behind the scenes we can see that modsem()
creates in total 9 variables (product indicators) used as the indicators for your latent product.
Whilst this often is sufficient, you might want some control over how these indicators are created. In general, modsem()
has two mechanisms for giving control over the creating of indicator products: 1. By specifying the measurement model of your latent product your self, and 2. By using the mean() and sum() function, collectively known as parceling operations.
5.1 Specifying The Measurement Model
By default, modsem()
creates all possible combinations of different product indicators. However, another common approach is to match the indicators by order. For example, let’s say you have an interaction between the laten variables X and Z: ‘X =~ x1 + x2’ and ‘Z =~ z1 + z2’. By default you would get ‘XZ =~ x1z1 + x1z2 + x2z1 + x2z2’. If you wanted to use the matching approach you would want to get ‘XZ =~ x1z1 + x2z2’ instead. To achieve this you can use the ‘match = TRUE’ argument.
<- '
m2 # Outer Model
X =~ x1 + x2
Y =~ y1 + y2
Z =~ z1 + z2
# Inner model
Y ~ X + Z + X:Z
'
<- modsem(m2, oneInt, match = TRUE)
est2 summary(est2)
#> modsem:
#> Method = dblcent
#> lavaan 0.6-18 ended normally after 41 iterations
#>
#> Estimator ML
#> Optimization method NLMINB
#> Number of model parameters 22
#>
#> Number of observations 2000
#>
#> Model Test User Model:
#>
#> Test statistic 11.355
#> Degrees of freedom 14
#> P-value (Chi-square) 0.658
#>
#> Parameter Estimates:
#>
#> Standard errors Standard
#> Information Expected
#> Information saturated (h1) model Structured
#>
#> Latent Variables:
#> Estimate Std.Err z-value P(>|z|)
#> X =~
#> x1 1.000
#> x2 0.819 0.021 38.127 0.000
#> Y =~
#> y1 1.000
#> y2 0.807 0.010 82.495 0.000
#> Z =~
#> z1 1.000
#> z2 0.836 0.024 35.392 0.000
#> XZ =~
#> x1z1 1.000
#> x2z2 0.645 0.024 26.904 0.000
#>
#> Regressions:
#> Estimate Std.Err z-value P(>|z|)
#> Y ~
#> X 0.688 0.029 23.366 0.000
#> Z 0.576 0.029 20.173 0.000
#> XZ 0.706 0.032 22.405 0.000
#>
#> Covariances:
#> Estimate Std.Err z-value P(>|z|)
#> .x1z1 ~~
#> .x2z2 0.000
#> X ~~
#> Z 0.202 0.025 8.182 0.000
#> XZ 0.003 0.026 0.119 0.905
#> Z ~~
#> XZ 0.042 0.026 1.621 0.105
#>
#> Variances:
#> Estimate Std.Err z-value P(>|z|)
#> .x1 0.179 0.022 8.029 0.000
#> .x2 0.151 0.015 9.956 0.000
#> .y1 0.184 0.021 8.577 0.000
#> .y2 0.136 0.014 9.663 0.000
#> .z1 0.197 0.025 7.802 0.000
#> .z2 0.138 0.018 7.831 0.000
#> .x1z1 0.319 0.035 9.141 0.000
#> .x2z2 0.244 0.016 15.369 0.000
#> X 0.962 0.042 23.120 0.000
#> .Y 0.964 0.042 23.110 0.000
#> Z 0.987 0.044 22.260 0.000
#> XZ 1.041 0.054 19.441 0.000
5.2 More complicated models
I you want even more control you can use the get_pi_syntax()
and get_pi_data()
functions, such that you can extract the modified syntax and data from modsem, and alter them accordingly. This can be particularly useful in cases where you want to estimate a model using a feature in lavaan, which isn’t available in modsem. For example, (as of yet) the syntax for both ordered- and multigroup models isn’t as flexible as in lavaan. Thus you can modify the auto-generated syntax (with the altered dataset) from modsem to suit your needs.
<- '
m3 # Outer Model
X =~ x1 + x2
Y =~ y1 + y2
Z =~ z1 + z2
# Inner model
Y ~ X + Z + X:Z
'
<- get_pi_syntax(m3)
syntax cat(syntax)
#> X =~ x1
#> X =~ x2
#> Y =~ y1
#> Y =~ y2
#> Z =~ z1
#> Z =~ z2
#> Y ~ X
#> Y ~ Z
#> Y ~ XZ
#> XZ =~ 1*x1z1
#> XZ =~ x2z1
#> XZ =~ x1z2
#> XZ =~ x2z2
#> x1z1 ~~ 0*x2z2
#> x1z2 ~~ 0*x2z1
#> x1z1 ~~ x1z2
#> x1z1 ~~ x2z1
#> x1z2 ~~ x2z2
#> x2z1 ~~ x2z2
<- get_pi_data(m3, oneInt)
data head(data)
#> x1 x2 y1 y2 z1 z2 x1z1
#> 1 2.4345722 1.3578655 1.4526897 0.9560888 0.8184825 1.60708140 -0.4823019
#> 2 0.2472734 0.2723201 0.5496756 0.7115311 3.6649148 2.60983102 -2.2680403
#> 3 -1.3647759 -0.5628205 -0.9835467 -0.6697747 1.7249386 2.10981827 -1.9137416
#> 4 3.0432836 2.2153763 6.4641465 4.7805981 2.5697116 3.26335379 2.9385205
#> 5 2.8148841 2.7029616 2.2860280 2.1457643 0.3467850 0.07164577 -1.4009548
#> 6 -0.5453450 -0.7530642 1.1294876 1.1998472 -0.2362958 0.60252657 1.7465860
#> x2z1 x1z2 x2z2
#> 1 -0.1884837 0.3929380 -0.0730934
#> 2 -2.6637694 -1.2630544 -1.4547433
#> 3 -1.4299711 -2.3329864 -1.7383407
#> 4 1.3971422 3.9837389 1.9273102
#> 5 -1.1495704 -2.2058995 -1.8169042
#> 6 2.2950753 0.7717365 1.0568143