Skip to contents
library(modsem)
#> This is modsem (1.0.11). Please report any bugs!

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