customizing interaction terms
customizing.Rmd
By default, modsem()
creates product indicators for you
based on the interaction specified in your model. Behind the scenes,
modsem()
generates a total of 9 variables (product
indicators) that are used as the indicators for your latent product.
m1 <- '
# Outer Model
X =~ x1 + x2 + x3
Y =~ y1 + y2 + y3
Z =~ z1 + z2 + z3
# Inner model
Y ~ X + Z + X:Z
'
est1 <- modsem(m1, oneInt)
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 =~ 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
While this is often sufficient, you might want more control over how
these indicators are created. In general, modsem()
offers
two mechanisms for controlling the creation of product indicators: 1. By
specifying the measurement model for your latent product yourself. 2. By
using the mean()
and sum()
functions,
collectively known as parceling operations.
Specifying the Measurement Model
By default, modsem()
creates all possible combinations
of product indicators. However, another common approach is to match the
indicators by order. For example, let’s say you have an interaction
between the latent variables X
and Z
:
X =~ x1 + x2
and Z =~ z1 + z2
. By default, you
would get XZ =~ x1z1 + x1z2 + x2z1 + x2z2
. If you prefer to
use the matching approach, you would expect
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
'
est2 <- modsem(m2, oneInt, match = TRUE)
summary(est2)
#> modsem (version 1.0.11, approach = dblcent):
#>
#> Interaction Model Fit Measures (H1):
#> Loglikelihood 22.00
#> Akaike (AIC) 0.00
#> Bayesian (BIC) 11.35
#> Chi-square 14.00
#> Degrees of Freedom 0.66
#> P-value (Chi-square) 11472.91
#> RMSEA 28.00
#>
#> Fit Measures for Baseline Model (H0):
#> Loglikelihood -18602.38
#> Akaike (AIC) 37246.76
#> Bayesian (BIC) 37364.37
#> Chi-square 614.47
#> Degrees of Freedom 15
#> P-value (Chi-square) 0.000
#> RMSEA 0.141
#>
#> Comparative Fit to H0 (LRT test):
#> Chi-square diff 603.111
#> Degrees of freedom diff 1
#> P-value (LRT) 0.000
#>
#> R-Squared Interaction Model (H1):
#> Y 0.609
#> R-Squared Baseline Model (H0):
#> Y 0.401
#> R-Squared Change (H1 - H0):
#> Y 0.208
#>
#> lavaan 0.6-19 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
More Complicated Models
If you want even more control, you can use the
get_pi_syntax()
and get_pi_data()
functions to
extract the modified syntax and data from modsem()
,
allowing you to modify them as needed. This can be particularly useful
in cases where you want to estimate a model using a feature in
lavaan
that isn’t available in modsem()
.
For example, the syntax for ordered and multigroup models (as of now)
isn’t as flexible in modsem()
as it is in
lavaan
. You can modify the auto-generated syntax (along
with the altered dataset) from modsem()
to suit your
needs.
m3 <- '
# Outer Model
X =~ x1 + x2 + x3
Y =~ y1 + y2 + y3
Z =~ z1 + z2 + z3
# Inner model
Y ~ X + Z + X:Z
'
syntax <- get_pi_syntax(m3)
cat(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 =~ 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
data <- get_pi_data(m3, oneInt)
head(data)
#> x1 x2 x3 y1 y2 y3
#> 1 2.4345722 1.3578655 1.40700355 1.4526897 0.9560888 1.209581472
#> 2 0.2472734 0.2723201 0.58024125 0.5496756 0.7115311 -0.135737642
#> 3 -1.3647759 -0.5628205 -0.09754199 -0.9835467 -0.6697747 0.007520468
#> 4 3.0432836 2.2153763 1.60846138 6.4641465 4.7805981 5.985944583
#> 5 2.8148841 2.7029616 2.58119575 2.2860280 2.1457643 1.551767464
#> 6 -0.5453450 -0.7530642 -0.63524883 1.1294876 1.1998472 0.362109037
#> z1 z2 z3 x1z1 x2z1 x3z1 x1z2
#> 1 0.8184825 1.60708140 0.93586992 -0.4823019 -0.1884837 -0.2752638 0.3929380
#> 2 3.6649148 2.60983102 3.15244788 -2.2680403 -2.6637694 -1.0811726 -1.2630544
#> 3 1.7249386 2.10981827 0.90836245 -1.9137416 -1.4299711 -0.9068668 -2.3329864
#> 4 2.5697116 3.26335379 3.38596811 2.9385205 1.3971422 0.8925897 3.9837389
#> 5 0.3467850 0.07164577 -0.01871117 -1.4009548 -1.1495704 -1.2856163 -2.2058995
#> 6 -0.2362958 0.60252657 0.31946813 1.7465860 2.2950753 1.7584641 0.7717365
#> x2z2 x3z2 x1z3 x2z3 x3z3
#> 1 -0.0730934 0.04749982 -0.1529509 -0.1293463 -0.1414301
#> 2 -1.4547433 -0.62456185 -1.9164521 -2.2419771 -0.9099617
#> 3 -1.7383407 -1.06788128 -0.1643368 -0.1193874 -0.1439739
#> 4 1.9273102 1.26967294 4.8096462 2.3378638 1.5509349
#> 5 -1.8169042 -2.03260698 -1.8558746 -1.5219744 -1.7039365
#> 6 1.0568143 0.78918775 0.7532472 1.0412580 0.7754318
The generated syntax and data can then be used to estimate a model
using lavaan
, giving the user more control over arguments
passed onto lavaan::sem()
, and potentially the ability to
modify the the generated syntax and data.
library(lavaan)
#> This is lavaan 0.6-19
#> lavaan is FREE software! Please report any bugs.
fit <- sem(syntax, data = data, fixed.x = FALSE,
std.lv = TRUE, auto.fix.first = TRUE,
auto.var = TRUE, auto.cov.lv.x = TRUE)
summary(fit)
#> lavaan 0.6-19 ended normally after 200 iterations
#>
#> Estimator ML
#> Optimization method NLMINB
#> Number of model parameters 60
#>
#> Number of observations 2000
#>
#> Model Test User Model:
#>
#> Test statistic 122.924
#> Degrees of freedom 111
#> P-value (Chi-square) 0.207
#>
#> Parameter Estimates:
#>
#> Standard errors Standard
#> Information Expected
#> Information saturated (h1) model Structured
#>
#> Latent Variables:
#> Estimate Std.Err z-value P(>|z|)
#> X =~
#> x1 0.990 0.018 53.791 0.000
#> x2 0.796 0.016 50.482 0.000
#> x3 0.907 0.017 52.468 0.000
#> Y =~
#> y1 0.995 0.019 51.851 0.000
#> y2 0.794 0.016 50.768 0.000
#> y3 0.895 0.017 51.288 0.000
#> Z =~
#> z1 1.008 0.019 53.712 0.000
#> z2 0.818 0.016 51.138 0.000
#> z3 0.889 0.017 52.410 0.000
#> XZ =~
#> x1z1 1.022 0.021 48.007 0.000
#> x2z1 0.823 0.019 44.481 0.000
#> x3z1 0.897 0.020 45.052 0.000
#> x1z2 0.810 0.018 44.153 0.000
#> x2z2 0.661 0.016 41.964 0.000
#> x3z2 0.722 0.017 42.568 0.000
#> x1z3 0.907 0.019 47.031 0.000
#> x2z3 0.732 0.017 43.902 0.000
#> x3z3 0.799 0.018 43.671 0.000
#>
#> Regressions:
#> Estimate Std.Err z-value P(>|z|)
#> Y ~
#> X 0.671 0.031 21.829 0.000
#> Z 0.568 0.029 19.352 0.000
#> XZ 0.721 0.032 22.569 0.000
#>
#> Covariances:
#> Estimate Std.Err z-value P(>|z|)
#> .x1z1 ~~
#> .x2z2 0.000
#> .x2z3 0.000
#> .x3z2 0.000
#> .x3z3 0.000
#> .x2z1 ~~
#> .x1z2 0.000
#> .x1z2 ~~
#> .x2z3 0.000
#> .x3z1 ~~
#> .x1z2 0.000
#> .x1z2 ~~
#> .x3z3 0.000
#> .x2z1 ~~
#> .x1z3 0.000
#> .x2z2 ~~
#> .x1z3 0.000
#> .x3z1 ~~
#> .x1z3 0.000
#> .x3z2 ~~
#> .x1z3 0.000
#> .x2z1 ~~
#> .x3z2 0.000
#> .x3z3 0.000
#> .x3z1 ~~
#> .x2z2 0.000
#> .x2z2 ~~
#> .x3z3 0.000
#> .x3z1 ~~
#> .x2z3 0.000
#> .x3z2 ~~
#> .x2z3 0.000
#> .x1z1 ~~
#> .x1z2 0.115 0.008 14.802 0.000
#> .x1z3 0.114 0.008 13.947 0.000
#> .x2z1 0.125 0.008 16.095 0.000
#> .x3z1 0.140 0.009 16.135 0.000
#> .x1z2 ~~
#> .x1z3 0.103 0.007 14.675 0.000
#> .x2z2 0.128 0.006 20.850 0.000
#> .x3z2 0.146 0.007 21.243 0.000
#> .x1z3 ~~
#> .x2z3 0.116 0.007 17.818 0.000
#> .x3z3 0.135 0.007 18.335 0.000
#> .x2z1 ~~
#> .x2z2 0.135 0.006 20.905 0.000
#> .x2z3 0.145 0.007 21.145 0.000
#> .x3z1 0.114 0.007 16.058 0.000
#> .x2z2 ~~
#> .x2z3 0.117 0.006 20.419 0.000
#> .x3z2 0.116 0.006 20.586 0.000
#> .x2z3 ~~
#> .x3z3 0.109 0.006 18.059 0.000
#> .x3z1 ~~
#> .x3z2 0.138 0.007 19.331 0.000
#> .x3z3 0.158 0.008 20.269 0.000
#> .x3z2 ~~
#> .x3z3 0.131 0.007 19.958 0.000
#> X ~~
#> Z 0.201 0.023 8.786 0.000
#> XZ 0.015 0.024 0.628 0.530
#> Z ~~
#> XZ 0.060 0.024 2.462 0.014
#>
#> Variances:
#> Estimate Std.Err z-value P(>|z|)
#> .x1 0.160 0.009 17.871 0.000
#> .x2 0.162 0.007 22.969 0.000
#> .x3 0.163 0.008 20.161 0.000
#> .y1 0.159 0.009 17.896 0.000
#> .y2 0.154 0.007 22.640 0.000
#> .y3 0.164 0.008 20.698 0.000
#> .z1 0.168 0.009 18.143 0.000
#> .z2 0.158 0.007 22.264 0.000
#> .z3 0.158 0.008 20.389 0.000
#> .x1z1 0.311 0.014 22.227 0.000
#> .x2z1 0.292 0.011 27.287 0.000
#> .x3z1 0.327 0.012 26.275 0.000
#> .x1z2 0.290 0.011 26.910 0.000
#> .x2z2 0.239 0.008 29.770 0.000
#> .x3z2 0.270 0.009 29.117 0.000
#> .x1z3 0.272 0.012 23.586 0.000
#> .x2z3 0.245 0.009 27.979 0.000
#> .x3z3 0.297 0.011 28.154 0.000
#> X 1.000
#> .Y 1.000
#> Z 1.000
#> XZ 1.000