5 Model Inspection
5.1 lavaan
Object
Currently, semnova
relies on the lavaan
package which uses maximum likelihood for estimation. In later releases more estimators will be available. A Bayesian estimation method is on top of the to-do list.
lavaan
provides a lot of functions and methods to investigate the estimated model. These functions can also be used with semnova
. The underlying lavaan
object can be accessed via the @sem_obj
slot of the semnova
object.
## lavaan 0.6-6 ended normally after 50 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of free parameters 38
## Number of equality constraints 14
##
## Number of observations 26
##
## Model Test User Model:
##
## Test statistic 29.975
## Degrees of freedom 20
## P-value (Chi-square) 0.070
For a comprehensive summary of the model, use the summary
function on the lavaan
object.
## lavaan 0.6-6 ended normally after 50 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of free parameters 38
## Number of equality constraints 14
##
## Number of observations 26
##
## Model Test User Model:
##
## Test statistic 29.975
## Degrees of freedom 20
## P-value (Chi-square) 0.070
##
## Parameter Estimates:
##
## Standard errors Standard
## Information Expected
## Information saturated (h1) model Structured
##
## Latent Variables:
## Estimate Std.Err z-value P(>|z|)
## NN =~
## S1NN (.l1) 1.038 0.044 23.684 0.000
## S2NN (.l2) 0.962 0.044 21.936 0.000
## PN =~
## S1PN (.l1) 1.038 0.044 23.684 0.000
## S2PN (.l2) 0.962 0.044 21.936 0.000
## NP =~
## S1NP (.l1) 1.038 0.044 23.684 0.000
## S2NP (.l2) 0.962 0.044 21.936 0.000
## PP =~
## S1PP (.l1) 1.038 0.044 23.684 0.000
## S2PP (.l2) 0.962 0.044 21.936 0.000
## .pi1 =~
## NN 0.500
## PN 0.500
## NP 0.500
## PP 0.500
## .pi2 =~
## NN -0.500
## PN 0.500
## NP -0.500
## PP 0.500
## .pi3 =~
## NN -0.500
## PN -0.500
## NP 0.500
## PP 0.500
## .pi4 =~
## NN 0.500
## PN -0.500
## NP -0.500
## PP 0.500
##
## Covariances:
## Estimate Std.Err z-value P(>|z|)
## .NN ~~
## .PN 0.000
## .NP 0.000
## .PN ~~
## .NP 0.000
## .NN ~~
## .PP 0.000
## .PN ~~
## .PP 0.000
## .NP ~~
## .PP 0.000
## .pi1 ~~
## .pi2 (.__2) -0.109 0.095 -1.149 0.251
## .pi3 (.__3_1) -0.390 0.160 -2.429 0.015
## .pi2 ~~
## .pi3 (.__3_2) 0.212 0.089 2.394 0.017
## .pi1 ~~
## .pi4 (.__4_1) -0.113 0.090 -1.253 0.210
## .pi2 ~~
## .pi4 (.__4_2) 0.036 0.050 0.728 0.466
## .pi3 ~~
## .pi4 (.__4_3) 0.088 0.076 1.166 0.244
##
## Intercepts:
## Estimate Std.Err z-value P(>|z|)
## .S1NN (.i1) 0.034 0.026 1.308 0.191
## .S2NN (.i2) -0.034 0.026 -1.308 0.191
## .S1PN (.i1) 0.034 0.026 1.308 0.191
## .S2PN (.i2) -0.034 0.026 -1.308 0.191
## .S1NP (.i1) 0.034 0.026 1.308 0.191
## .S2NP (.i2) -0.034 0.026 -1.308 0.191
## .S1PP (.i1) 0.034 0.026 1.308 0.191
## .S2PP (.i2) -0.034 0.026 -1.308 0.191
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.m1) 0.168 0.181 0.924 0.356
## .pi2 (.m2) 0.042 0.098 0.427 0.670
## .pi3 (.m3) 0.728 0.153 4.756 0.000
## .pi4 (.m4) 0.024 0.094 0.258 0.797
##
## Variances:
## Estimate Std.Err z-value P(>|z|)
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.__1) 0.796 0.235 3.383 0.001
## .pi2 (.__2) 0.198 0.071 2.786 0.005
## .pi3 (.__3) 0.542 0.165 3.282 0.001
## .pi4 (.__4) 0.178 0.065 2.723 0.006
## .S1NN 0.168 0.090 1.863 0.062
## .S2NN 0.147 0.078 1.885 0.059
## .S1PN 0.049 0.055 0.895 0.371
## .S2PN 0.167 0.065 2.571 0.010
## .S1NP 0.208 0.073 2.833 0.005
## .S2NP 0.055 0.042 1.324 0.186
## .S1PP 0.342 0.120 2.835 0.005
## .S2PP 0.041 0.065 0.638 0.523
##
## Constraints:
## |Slack|
## .l1+.l2 - (2) 0.000
## .i1+.i2 - 0 0.000
5.2 Model String
In some cases, the model specified by semnova
does not completely fit the needs of the user. It is possible to plot the model string that is passed to lavaan and change it according to one’s needs.
##
## # loadings
## NN =~ .l1*S1NN + NA*S1NN + .l2*S2NN
## PN =~ .l1*S1PN + NA*S1PN + .l2*S2PN
## NP =~ .l1*S1NP + NA*S1NP + .l2*S2NP
## PP =~ .l1*S1PP + NA*S1PP + .l2*S2PP
## # intercepts
## S1NN ~ .i1*1
## S2NN ~ .i2*1
## S1PN ~ .i1*1
## S2PN ~ .i2*1
## S1NP ~ .i1*1
## S2NP ~ .i2*1
## S1PP ~ .i1*1
## S2PP ~ .i2*1
## NN ~ 0*1
## PN ~ 0*1
## NP ~ 0*1
## PP ~ 0*1
## # variances
## NN ~~ 0*NN
## PN ~~ 0*NN + 0*PN
## NP ~~ 0*NN + 0*PN + 0*NP
## PP ~~ 0*NN + 0*PN + 0*NP + 0*PP
## .pi1 ~~ .vcov_pi_1_1*.pi1
## .pi2 ~~ .vcov_pi_2_1*.pi1 + .vcov_pi_2_2*.pi2
## .pi3 ~~ .vcov_pi_3_1*.pi1 + .vcov_pi_3_2*.pi2 + .vcov_pi_3_3*.pi3
## .pi4 ~~ .vcov_pi_4_1*.pi1 + .vcov_pi_4_2*.pi2 + .vcov_pi_4_3*.pi3 + .vcov_pi_4_4*.pi4
## # struc_coeff
## .pi1 =~ 0.5*NN + 0.5*PN + 0.5*NP + 0.5*PP
## .pi2 =~ -0.5*NN + 0.5*PN + -0.5*NP + 0.5*PP
## .pi3 =~ -0.5*NN + -0.5*PN + 0.5*NP + 0.5*PP
## .pi4 =~ 0.5*NN + -0.5*PN + -0.5*NP + 0.5*PP
## # regressions
## .pi1 ~ .m1*1
## .pi2 ~ .m2*1
## .pi3 ~ .m3*1
## .pi4 ~ .m4*1
## # constraints
## .l1 + .l2 == 2
## .i1 + .i2 == 0
Using the sem()
function from the lavaan
package, we see that the summary gives just the same output. Feel free to modify the model.
## lavaan 0.6-6 ended normally after 50 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of free parameters 38
## Number of equality constraints 14
##
## Number of observations 26
##
## Model Test User Model:
##
## Test statistic 29.975
## Degrees of freedom 20
## P-value (Chi-square) 0.070
##
## Parameter Estimates:
##
## Standard errors Standard
## Information Expected
## Information saturated (h1) model Structured
##
## Latent Variables:
## Estimate Std.Err z-value P(>|z|)
## NN =~
## S1NN (.l1) 1.038 0.044 23.684 0.000
## S2NN (.l2) 0.962 0.044 21.936 0.000
## PN =~
## S1PN (.l1) 1.038 0.044 23.684 0.000
## S2PN (.l2) 0.962 0.044 21.936 0.000
## NP =~
## S1NP (.l1) 1.038 0.044 23.684 0.000
## S2NP (.l2) 0.962 0.044 21.936 0.000
## PP =~
## S1PP (.l1) 1.038 0.044 23.684 0.000
## S2PP (.l2) 0.962 0.044 21.936 0.000
## .pi1 =~
## NN 0.500
## PN 0.500
## NP 0.500
## PP 0.500
## .pi2 =~
## NN -0.500
## PN 0.500
## NP -0.500
## PP 0.500
## .pi3 =~
## NN -0.500
## PN -0.500
## NP 0.500
## PP 0.500
## .pi4 =~
## NN 0.500
## PN -0.500
## NP -0.500
## PP 0.500
##
## Covariances:
## Estimate Std.Err z-value P(>|z|)
## .NN ~~
## .PN 0.000
## .NP 0.000
## .PN ~~
## .NP 0.000
## .NN ~~
## .PP 0.000
## .PN ~~
## .PP 0.000
## .NP ~~
## .PP 0.000
## .pi1 ~~
## .pi2 (.__2) -0.109 0.095 -1.149 0.251
## .pi3 (.__3_1) -0.390 0.160 -2.429 0.015
## .pi2 ~~
## .pi3 (.__3_2) 0.212 0.089 2.394 0.017
## .pi1 ~~
## .pi4 (.__4_1) -0.113 0.090 -1.253 0.210
## .pi2 ~~
## .pi4 (.__4_2) 0.036 0.050 0.728 0.466
## .pi3 ~~
## .pi4 (.__4_3) 0.088 0.076 1.166 0.244
##
## Intercepts:
## Estimate Std.Err z-value P(>|z|)
## .S1NN (.i1) 0.034 0.026 1.308 0.191
## .S2NN (.i2) -0.034 0.026 -1.308 0.191
## .S1PN (.i1) 0.034 0.026 1.308 0.191
## .S2PN (.i2) -0.034 0.026 -1.308 0.191
## .S1NP (.i1) 0.034 0.026 1.308 0.191
## .S2NP (.i2) -0.034 0.026 -1.308 0.191
## .S1PP (.i1) 0.034 0.026 1.308 0.191
## .S2PP (.i2) -0.034 0.026 -1.308 0.191
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.m1) 0.168 0.181 0.924 0.356
## .pi2 (.m2) 0.042 0.098 0.427 0.670
## .pi3 (.m3) 0.728 0.153 4.756 0.000
## .pi4 (.m4) 0.024 0.094 0.258 0.797
##
## Variances:
## Estimate Std.Err z-value P(>|z|)
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.__1) 0.796 0.235 3.383 0.001
## .pi2 (.__2) 0.198 0.071 2.786 0.005
## .pi3 (.__3) 0.542 0.165 3.282 0.001
## .pi4 (.__4) 0.178 0.065 2.723 0.006
## .S1NN 0.168 0.090 1.863 0.062
## .S2NN 0.147 0.078 1.885 0.059
## .S1PN 0.049 0.055 0.895 0.371
## .S2PN 0.167 0.065 2.571 0.010
## .S1NP 0.208 0.073 2.833 0.005
## .S2NP 0.055 0.042 1.324 0.186
## .S1PP 0.342 0.120 2.835 0.005
## .S2PP 0.041 0.065 0.638 0.523
##
## Constraints:
## |Slack|
## .l1+.l2 - (2) 0.000
## .i1+.i2 - 0 0.000
It is also possible to append lavaan syntax to the model string using the semnova()
function. This can be achieved via the append
argument. The following chunk of code estimates the same model as in the previous section by constraints the residual variances of the indicators to be equal across the experimental conditions. This is just an example how this works, this constraint is not necessarily meaningful.
model_constraints <- "
# constraints
S1NN ~~ var1*S1NN
S2NN ~~ var2*S2NN
S1PN ~~ var1*S1PN
S2PN ~~ var2*S2PN
S1NP ~~ var1*S1NP
S2NP ~~ var2*S2NP
S1PP ~~ var1*S1PP
S2PP ~~ var2*S2PP
"
fit_constrained <- semnova(
formula = cbind(NN, PN, NP, PP) ~ 1,
data = d_wide,
idata = idata,
idesign = idesign,
mmodel = mmodel,
append = model_constraints
)
summary(fit_constrained@sem_obj)
## lavaan 0.6-6 ended normally after 35 iterations
##
## Estimator ML
## Optimization method NLMINB
## Number of free parameters 38
## Number of equality constraints 20
##
## Number of observations 26
##
## Model Test User Model:
##
## Test statistic 37.349
## Degrees of freedom 26
## P-value (Chi-square) 0.070
##
## Parameter Estimates:
##
## Standard errors Standard
## Information Expected
## Information saturated (h1) model Structured
##
## Latent Variables:
## Estimate Std.Err z-value P(>|z|)
## NN =~
## S1NN (.l1) 1.012 0.046 21.988 0.000
## S2NN (.l2) 0.988 0.046 21.457 0.000
## PN =~
## S1PN (.l1) 1.012 0.046 21.988 0.000
## S2PN (.l2) 0.988 0.046 21.457 0.000
## NP =~
## S1NP (.l1) 1.012 0.046 21.988 0.000
## S2NP (.l2) 0.988 0.046 21.457 0.000
## PP =~
## S1PP (.l1) 1.012 0.046 21.988 0.000
## S2PP (.l2) 0.988 0.046 21.457 0.000
## .pi1 =~
## NN 0.500
## PN 0.500
## NP 0.500
## PP 0.500
## .pi2 =~
## NN -0.500
## PN 0.500
## NP -0.500
## PP 0.500
## .pi3 =~
## NN -0.500
## PN -0.500
## NP 0.500
## PP 0.500
## .pi4 =~
## NN 0.500
## PN -0.500
## NP -0.500
## PP 0.500
##
## Covariances:
## Estimate Std.Err z-value P(>|z|)
## .NN ~~
## .PN 0.000
## .NP 0.000
## .PN ~~
## .NP 0.000
## .NN ~~
## .PP 0.000
## .PN ~~
## .PP 0.000
## .NP ~~
## .PP 0.000
## .pi1 ~~
## .pi2 (.__2) -0.101 0.095 -1.067 0.286
## .pi3 (.__3_1) -0.372 0.156 -2.385 0.017
## .pi2 ~~
## .pi3 (.__3_2) 0.196 0.089 2.212 0.027
## .pi1 ~~
## .pi4 (.__4_1) -0.073 0.080 -0.904 0.366
## .pi2 ~~
## .pi4 (.__4_2) 0.018 0.046 0.390 0.696
## .pi3 ~~
## .pi4 (.__4_3) 0.060 0.069 0.871 0.384
##
## Intercepts:
## Estimate Std.Err z-value P(>|z|)
## .S1NN (.i1) 0.038 0.027 1.404 0.160
## .S2NN (.i2) -0.038 0.027 -1.404 0.160
## .S1PN (.i1) 0.038 0.027 1.404 0.160
## .S2PN (.i2) -0.038 0.027 -1.404 0.160
## .S1NP (.i1) 0.038 0.027 1.404 0.160
## .S2NP (.i2) -0.038 0.027 -1.404 0.160
## .S1PP (.i1) 0.038 0.027 1.404 0.160
## .S2PP (.i2) -0.038 0.027 -1.404 0.160
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.m1) 0.147 0.179 0.820 0.412
## .pi2 (.m2) 0.061 0.103 0.590 0.555
## .pi3 (.m3) 0.724 0.152 4.747 0.000
## .pi4 (.m4) -0.006 0.087 -0.072 0.943
##
## Variances:
## Estimate Std.Err z-value P(>|z|)
## .NN 0.000
## .PN 0.000
## .NP 0.000
## .PP 0.000
## .pi1 (.__1) 0.757 0.228 3.319 0.001
## .pi2 (.__2) 0.214 0.077 2.771 0.006
## .pi3 (.__3) 0.539 0.167 3.223 0.001
## .pi4 (.__4) 0.138 0.057 2.433 0.015
## .S1NN (var1) 0.214 0.046 4.693 0.000
## .S2NN (var2) 0.081 0.035 2.331 0.020
## .S1PN (var1) 0.214 0.046 4.693 0.000
## .S2PN (var2) 0.081 0.035 2.331 0.020
## .S1NP (var1) 0.214 0.046 4.693 0.000
## .S2NP (var2) 0.081 0.035 2.331 0.020
## .S1PP (var1) 0.214 0.046 4.693 0.000
## .S2PP (var2) 0.081 0.035 2.331 0.020
##
## Constraints:
## |Slack|
## .l1+.l2 - (2) 0.000
## .i1+.i2 - 0 0.000
5.3 Fit Measures
Fit measures can be inspected via the fitmeasures()
function. Let’s have a look at the fit measures for the original unconstrained model:
## npar fmin chisq df
## 24.000 0.576 29.975 20.000
## pvalue baseline.chisq baseline.df baseline.pvalue
## 0.070 142.345 28.000 0.000
## cfi tli nnfi rfi
## 0.913 0.878 0.878 0.705
## nfi pnfi ifi rni
## 0.789 0.564 0.918 0.913
## logl unrestricted.logl aic bic
## -171.986 -156.998 391.972 422.166
## ntotal bic2 rmsea rmsea.ci.lower
## 26.000 347.672 0.139 0.000
## rmsea.ci.upper rmsea.pvalue rmr rmr_nomean
## 0.235 0.106 0.052 0.050
## srmr srmr_bentler srmr_bentler_nomean crmr
## 0.086 0.086 0.090 0.089
## crmr_nomean srmr_mplus srmr_mplus_nomean cn_05
## 0.093 0.084 0.086 28.245
## cn_01 gfi agfi pgfi
## 33.584 0.867 0.707 0.394
## mfi ecvi
## 0.825 2.999
The \(p\)-value for the \(\chi^2\)-statistic is almost below the common 5% level. The RMSEA is also quite high. However, the 5% confidence interval of the RMSEA includes the common threshold of 0.06. The fit can therefore be considered satisfactory. The small sample size might also be a reason why the fit is not “perfect”.