It can be seen that lavaan has a much simpler syntax that allows to rapidly model basic SEM models. However, we were a bit unfair to OpenMx because we used a path model specification for lavaan and a matrix specification for OpenMx. The truth is that OpenMx is still probably a bit wordier than lavaan, but let's apply a path model specification in each to do a fair head-to-head comparison.
We will use the famous Holzinger-Swineford 1939 dataset here from the lavaan package to do our modeling, as follows:
hs.dat <- HolzingerSwineford1939
We will create a new dataset with a shorter name so that we don't have to keep typing HozlingerSwineford1939
.
We will learn to fit the Holzinger-Swineford model in this section. We will start by specifying the SEM model using the lavaan model
syntax:
hs.model.lavaan <- ' visual =~ x1 + x2 + x3 textual =~ x4 + x5 + x6 speed =~ x7 + x8 + x9 visual ~~ textual visual ~~ speed textual ~~ speed ' fit.hs.lavaan <- cfa(hs.model.lavaan, data=hs.dat, std.lv = TRUE) summary(fit.hs.lavaan)
Here, we add the std.lv
argument to the fit function, which fixes the variance of the latent variables to 1. We do this instead of constraining the first factor loading on each variable to 1.
Only the model coefficients are included for ease of viewing in this book. The result is shown in the following model:
> summary(fit.hs.lavaan) … Estimate Std.err Z-value P(>|z|) Latent variables: visual =~ x1 0.900 0.081 11.127 0.000 x2 0.498 0.077 6.429 0.000 x3 0.656 0.074 8.817 0.000 textual =~ x4 0.990 0.057 17.474 0.000 x5 1.102 0.063 17.576 0.000 x6 0.917 0.054 17.082 0.000 speed =~ x7 0.619 0.070 8.903 0.000 x8 0.731 0.066 11.090 0.000 x9 0.670 0.065 10.305 0.000 Covariances: visual ~~ textual 0.459 0.064 7.189 0.000 speed 0.471 0.073 6.461 0.000 textual ~~ speed 0.283 0.069 4.117 0.000
Let's compare these results with a model fit in OpenMx using the same dataset and SEM model.
The OpenMx
syntax for path specification is substantially longer and more explicit. Let's take a look at the following model:
hs.model.open.mx <- mxModel("Holzinger Swineford", type="RAM", manifestVars = names(hs.dat)[7:15], latentVars = c('visual', 'textual', 'speed'), # Create paths from latent to observed variables mxPath( from = 'visual', to = c('x1', 'x2', 'x3'), free = c(TRUE, TRUE, TRUE), values = 1 ), mxPath( from = 'textual', to = c('x4', 'x5', 'x6'), free = c(TRUE, TRUE, TRUE), values = 1 ), mxPath( from = 'speed', to = c('x7', 'x8', 'x9'), free = c(TRUE, TRUE, TRUE), values = 1 ), # Create covariances among latent variables mxPath( from = 'visual', to = 'textual', arrows=2, free=TRUE ), mxPath( from = 'visual', to = 'speed', arrows=2, free=TRUE ), mxPath( from = 'textual', to = 'speed', arrows=2, free=TRUE ), #Create residual variance terms for the latent variables mxPath( from= c('visual', 'textual', 'speed'), arrows=2, #Here we are fixing the latent variances to 1 #These two lines are like st.lv = TRUE in lavaan free=c(FALSE,FALSE,FALSE), values=1 ), #Create residual variance terms mxPath( from= c('x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'x7', 'x8', 'x9'), arrows=2, ), mxData( observed=cov(hs.dat[,c(7:15)]), type="cov", numObs=301 ) ) fit.hs.open.mx <- mxRun(hs.model.open.mx) summary(fit.hs.open.mx)
Here are the results of the OpenMx model fit, which look very similar to lavaan's. This gives a long output. For ease of viewing, only the most relevant parts of the output are included in the following model (the last column that R prints giving the standard error of estimates is also not shown here):
> summary(fit.hs.open.mx) … free parameters: name matrix row col Estimate Std.Error 1 Holzinger Swineford.A[1,10] A x1 visual 0.9011177 2 Holzinger Swineford.A[2,10] A x2 visual 0.4987688 3 Holzinger Swineford.A[3,10] A x3 visual 0.6572487 4 Holzinger Swineford.A[4,11] A x4 textual 0.9913408 5 Holzinger Swineford.A[5,11] A x5 textual 1.1034381 6 Holzinger Swineford.A[6,11] A x6 textual 0.9181265 7 Holzinger Swineford.A[7,12] A x7 speed 0.6205055 8 Holzinger Swineford.A[8,12] A x8 speed 0.7321655 9 Holzinger Swineford.A[9,12] A x9 speed 0.6710954 10 Holzinger Swineford.S[1,1] S x1 x1 0.5508846 11 Holzinger Swineford.S[2,2] S x2 x2 1.1376195 12 Holzinger Swineford.S[3,3] S x3 x3 0.8471385 13 Holzinger Swineford.S[4,4] S x4 x4 0.3724102 14 Holzinger Swineford.S[5,5] S x5 x5 0.4477426 15 Holzinger Swineford.S[6,6] S x6 x6 0.3573899 16 Holzinger Swineford.S[7,7] S x7 x7 0.8020562 17 Holzinger Swineford.S[8,8] S x8 x8 0.4893230 18 Holzinger Swineford.S[9,9] S x9 x9 0.5680182 19 Holzinger Swineford.S[10,11] S visual textual 0.4585093 20 Holzinger Swineford.S[10,12] S visual speed 0.4705348 21 Holzinger Swineford.S[11,12] S textual speed 0.2829848
In summary, the results agree quite closely. For example, looking at the coefficient for the path going from the latent variable visual to the observed variable x1, lavaan gives an estimate of 0.900 while OpenMx computes a value of 0.901.
18.223.172.132