I am trying to compare structural equation models using lavaan in R. I have 4 latent variables, three of which are being estimated by 8 observed variables and one of which is being estimated by 2 observed variables.
When I run the measurement model, the test user model has 293 degrees of freedom with 58 model parameters. When I run the structural model, with three additional regression paths estimated, I receive the same model statistics with the same number of degrees of freedom (293) and the same number of model parameters (58).
Because the models are identical, and I try to compare them with anova, there are no degrees of freedom difference, and no chi-square difference, because it is the same model output. So, I receive the following error
Warning message: In lavTestLRT(object = object, ..., model.names = NAMES) : lavaan WARNING: some models have the same degrees of freedom
semPaths is showing the regression coefficients estimated, and the output for parameter estimates are showing the regression coefficients for the structural model, but the fit indices (AIC, BIC, etc.), chi-square, and degrees of freedom are identical.
I thought I had simply put the wrong model in the summary function, but no, that was not it.
I am trying not to be a dolt, but I cannot figure out why lavaan is giving me exactly the same df and chi-square when I am estimating three additional paths/parameters. Any insight is welcomed. Again, I apologize of I am missing the obvious.
Here is the code:
# Pre-Post Measurement Model 1 - (TM)
MeasTM1 <- '
posttransp8 =~ post_Understand_Successful_Work +
post_Purpose_Assignment +
post_Assignment_Objectives_Course +
post_Instructor_Identified_Goal +
post_Steps_Required +
post_Assignment_Instructions +
post_Detailed_Directions +
post_Knew_How_Evaluated
preskills8 =~ pre_Express_Ideas_Write +
pre_Express_Ideas_Speak +
pre_Collaborate_Academic +
pre_Analyz + pre_Synthesize +
pre_Apply_New_Contexts +
pre_Consider_Ethics +
pre_Capable_Self_Learn
postskills8 =~ post_Express_Ideas_Write +
post_Express_Ideas_Speak +
post_Collaborate_Academic +
post_Analyz + post_Synthesize +
post_Apply_New_Contexts +
post_Consider_Ethics +
post_Capable_Self_Learn
postbelong2 =~ post_Belong_School_Commty + post_Helped_Belong_School_Commty
'
fitMeasTM1 <- sem(MeasTM1, data=TILTSEM)
summary(fitMeasTM1, standardized=TRUE, fit.measures=TRUE)
semPaths(fitMeasTM1, whatLabels = "std", layout = "tree")
# Pre-Post Factor Model 1 - (TM)
#Testing regression on Pre-Post Skills
FactTM1 <- '
#latent factors
posttransp8 =~ post_Understand_Successful_Work +
post_Purpose_Assignment +
post_Assignment_Objectives_Course +
post_Instructor_Identified_Goal +
post_Steps_Required +
post_Assignment_Instructions +
post_Detailed_Directions +
post_Knew_How_Evaluated
preskills8 =~ pre_Express_Ideas_Write +
pre_Express_Ideas_Speak +
pre_Collaborate_Academic +
pre_Analyz + pre_Synthesize +
pre_Apply_New_Contexts +
pre_Consider_Ethics +
pre_Capable_Self_Learn
postskills8 =~ post_Express_Ideas_Write +
post_Express_Ideas_Speak +
post_Collaborate_Academic +
post_Analyz + post_Synthesize +
post_Apply_New_Contexts +
post_Consider_Ethics +
post_Capable_Self_Learn
postbelong2 =~ post_Belong_School_Commty + post_Helped_Belong_School_Commty
#regressions
postskills8 ~ preskills8 + postbelong2 + posttransp8
'
fitFactTM1 <- sem(FactTM1, data=TILTSEM)
summary(fitFactTM1, standardized=TRUE, fit.measures=TRUE)
semPaths(fitFactTM1, whatLabels = "std", layout = "tree")
anova(fitMeasTM1,fitFactTM1)
Here is the model output for the two models (to show that they are identical):
=========================Pre-Post Measurement Model 1 - (TM)============================= Estimator ML Optimization method NLMINB Number of model parameters 58
Used Total
Number of observations 521 591
Model Test User Model:
Test statistic 1139.937 Degrees of freedom 293 P-value (Chi-square) 0.000
Model Test Baseline Model:
Test statistic 4720.060 Degrees of freedom 325 P-value 0.000
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.807 Tucker-Lewis Index (TLI) 0.786
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -13335.136 Loglikelihood unrestricted model (H1) -12765.167
Akaike (AIC) 26786.271 Bayesian (BIC) 27033.105 Sample-size adjusted Bayesian (BIC) 26849.000
Root Mean Square Error of Approximation:
RMSEA 0.074 90 Percent confidence interval - lower 0.070 90 Percent confidence interval - upper 0.079 P-value RMSEA <= 0.05 0.000
Standardized Root Mean Square Residual:
SRMR 0.068
=========================Pre-Post Factor Model 1 - (TM)======================
Estimator ML Optimization method NLMINB Number of model parameters 58
Used Total
Number of observations 521 591
Model Test User Model:
Test statistic 1139.937 Degrees of freedom 293 P-value (Chi-square) 0.000
Model Test Baseline Model:
Test statistic 4720.060 Degrees of freedom 325 P-value 0.000
User Model versus Baseline Model:
Comparative Fit Index (CFI) 0.807 Tucker-Lewis Index (TLI) 0.786
Loglikelihood and Information Criteria:
Loglikelihood user model (H0) -13335.136 Loglikelihood unrestricted model (H1) -12765.167
Akaike (AIC) 26786.271 Bayesian (BIC) 27033.105 Sample-size adjusted Bayesian (BIC) 26849.000
Root Mean Square Error of Approximation:
RMSEA 0.074 90 Percent confidence interval - lower 0.070 90 Percent confidence interval - upper 0.079 P-value RMSEA <= 0.05 0.000
Standardized Root Mean Square Residual:
SRMR 0.068
You aren't using up more degrees of freedom.
One thing that makes lavaan::sem()
dangerous to use over lavaan::lavaan()
is that its defaults are hard to remember and/or notice. If you look at ?lavaan::sem
, you will see those defaults:
The sem function is a wrapper for the more general lavaan function, but setting the following default options: int.ov.free = TRUE, int.lv.free = FALSE, auto.fix.first = TRUE (unless std.lv = TRUE), auto.fix.single = TRUE, auto.var = TRUE, auto.cov.lv.x = TRUE, auto.efa = TRUE, auto.th = TRUE, auto.delta = TRUE, and auto.cov.y = TRUE
You can find out what this means via ?lavOptions
:
auto.cov.lv.x: If TRUE, the covariances of exogenous latent variables are included in the model and set free.
By default, all exogenous latent variables (i.e., all of your latent factors) are correlated in your model already.
I'm also not sure how you are identifying the 2-item factor, so I'm surprised this does not throw a warnings, unless you're ignoring it.