MGCFA: Equal df and model fit indices when fixing thresholds, loadings and residuals vs threshold and loadings for dichotomous data in lavaan 0.6.21/0.7.2 but worse model fit and higher df for restricted model in 0.6.19

25 views
Skip to first unread message

Christian Becker

unread,
Jul 23, 2026, 7:51:34 AM (9 days ago) Jul 23
to lavaan
Dear Prof. Rosseel, dear lavaan-team, dear group members,

In July 2025 I run an MGCFA analysis using lavaan 0.6.19 to establish measurement invariance of three cognitive ability tests across a timespan of 7 years.
I did my analysis individually for each test. 
According to my - without question only partial - understanding of Wu and Estabrook (2016), for dichotomous data, fixing thresholds, loadings and residuals should result in a stricter model than fixing only thresholds and loadings, so I used the following syntax to estimate a baseline model and a restricted model:


baseline <- (model_Test1, data, 
ordered=TRUE, 
meanstructure = TRUE,
group = "Year",
group.equal = c("thresholds, loadings"),
parameterization = "theta",
estimator = "wlsmv")

restricted <- (model_Test1, data, 
ordered=TRUE, 
meanstructure = TRUE,
group = "Year",
group.equal = c("thresholds, loadings, residuals"),
parameterization = "theta",
estimator = "wlsmv").

with model_Test1 being something like "Test1=~ Test1_Item1+Test1_Item2+...+Test1_Item25" (thus covering only a single measure Test1 and its associated items),
and data being a data.frame containing the item scores from all tests across all years as dichotomous factors with levels "R" and "F" (for right and false, respectively) and  "Year" as a numerical grouping variable containing the year of testing for each person. N > 100.000, therefore I would not expect any problems based on lack of data.

This worked as intended (slightly worse model fit for and more decrees of freedom for the more restricted model). However, rerunning my analysis in march 2026, suddenly both models returned the same degrees of freedom and fit indices. After spending days trying to find a potential error i could have introduced in the mean time, I realized that I was running the analysis on a different machine with the newer lavaan 0.6.21. And really, reverting to lavaan 0.6.19 again produced the expected results, with a slightly worse model fit and more degrees of freedom for the restricted model.

Hence, it seems to me, that the source of these different outcomes must be in a change introduced in lavaan 0.6.20 or lavaan 0.6.21.
Reading the documentation for 0.6.20 I found one change directly affecting MGFCA:
"multiple group + categorical data: if group.equal = “thresholds”, lavaan now frees the intercepts in all but the first group (following the Wu and Estabrook procedure)".  
However, to me this seems to indicate that my results prior to 0.6.20 are not following Wu and Estabrook.

Just now, I ran the analysis using the new lavaan 0.7.2 version and once again got the equal degrees of freedom and fit indices between both models.

This pattern of results is independent of the specific ability measure I analyze. 

I am unsure how to interpret these results. According to the documentation, I would expect the results for the post 0.6.20 versions to be correct, however the results are not really in line with I would expect based on Wu & Estabrook... 

Is my syntax wrong, my understanding of Wu & Estabrook even more off than I assume, or is there another explanation?

Sadly, I can't make the data available.
 
Best regards,
Christian Becker

Yves Rosseel

unread,
Jul 23, 2026, 8:01:20 AM (9 days ago) Jul 23
to lav...@googlegroups.com
Hello Christian,

To the best of my knowledge, the 'current' (since 0.6-20) approach is
correct, and follows the Wu and Estabrook procedure.

Before that, setting group.equal = c("thresholds, loadings") did just
that: it fixed the thresholds and loadings to be equal across groups,
but nothing more. (lavaan never claimed to follow Wu and Estabrook
before 0.6-20).

But since 0.6-20, we free the intercepts (in all but the first group) if
group.equal= contains "thresholds". That is (I believe) what Wu and
Estabrook wrote we should do.

To get an independent verification, have a look at the measEq.syntax()
function in the semTools package.

Yves.

Reply all
Reply to author
Forward
0 new messages