Group Invariance testing for ordinal Data: Partial strong invariance

78 views
Skip to first unread message

Isa

unread,
Jun 26, 2018, 11:40:28 AM6/26/18
to lavaan

Dear All,

I would like to test group invariance for an individual factor with 4 manifest indicator variables. The manifest indicator variables are ordered and they all have 3 thresholds.

The code for this factor looks the following:

                cfa1 <- "F =~ m1 + m2 + m3+ m4"

                cfa_fit <- cfa(cfa1,  estimator = "WLSMV", data = data)

 

 My strategy of testing group invariance is the following (the group is “sex”):

  1. configural model : no parameter constrains across group

  2. weak model: loadings are fixed to equal across groups

  3. strong model: loadings and thresholds are fixed across groups

 

The first and second test worked fine, I calculated them with the following code:

                config_sex <- cfa(cfa1, data=data, group="sex", parameterization = "theta")

                weak_sex <- cfa(cfa1, data=data, group="sex", group.equal=c("loadings"), parameterization = "theta")

                lavTestLRT(config_sex, weak_sex, method="satorra.2000")
              Df AIC BIC  Chisq Chisq diff Df diff Pr(>Chisq)

config_partet  2         0.6354                             

weak_partet    5         2.2179     2.2264       3     0.5268

 

But there seems to be a non-invariance for the third test:

                strong_sex  <- cfa(cfa1, data=data, group="sex", group.equal=c("loadings","thresholds"), parameterization = "theta")

              Df AIC BIC   Chisq Chisq diff Df diff Pr(>Chisq)   
weak_partet    5          2.2179                                 
strong_partet 11         13.1645     18.468       6   0.005163 **

 

To examine which item(s) is causing the problem I wanted to replicate the fixed thresholds in a second cfa model and start to free thresholds of single items. Therefore, I defined the following model which should fix at the beginning all item thresholds to be the same across groups and so be equal to the command group.equal=c("thresholds"):

                cfa2 <- "F =~ m1 + m2 + m3+ m4

                m1 | a*t1 + b*t2 + c*t3

                m2 | d *t1 + e*t2 + f*t3

                m3 | g*t1 + h*t2 + i*t3

                m4 | j*t1 + k*t2 + n*t3"

                strong_partial_sex <-  cfa(cfa2, data=data, group="sex", group.equal=c("loadings"), parameterization = "theta")

 

But when I compare the summaries of strong_sex and strong_partial_sex they are not the same (the thresholds seem to be fixed across groups for both models but the estimates are not the same).

Do you know why these two models are not the same?

Thanks very much for help and best wishes!
Jsabel

Terrence Jorgensen

unread,
Jun 29, 2018, 11:22:58 AM6/29/18
to lavaan

Do you know why these two models are not the same?

You only used 1 label for each parameter.  In multigroup models, you need to specify a vector of labels.

m1 | c(a, a)*t1 + c(b, b)*t2 + c(c, c)*t3

Terrence D. Jorgensen
Postdoctoral Researcher, Methods and Statistics
Research Institute for Child Development and Education, the University of Amsterdam



Isa

unread,
Jul 24, 2018, 6:04:59 AM7/24/18
to lavaan

Dear Terence,

thank you very much for your recommendation.

 

I tested now again the two models, but their degrees of freedom, estimates and fits are still not the same.

 

These are the two models I tested:

 cfa1 <- "F =~ m1 + m2 + m3+ m4"

strong_sex1  <- cfa(cfa1, data=data, group="sex", group.equal=c("loadings","thresholds"), parameterization = "theta")

 

cfa2< - "F =~ m1 + m2 + m3+ m4

            m1 | c(th1,th1)*t1

            m1 | c(th2,th2)*t2

            m1 | c(th3,th3)*t3

 

            m2 | c(th4,th4)*t1

            m2 | c(th5,th5)*t2

            m2 | c(th6,th6)*t3

 

            m3 | c(th7,th7)*t1

            m3 | c(th8,th8)*t2

            m3 | c(th9,th9)*t3

 

            m4 | c(th10,th10)*t1

            m4 | c(th11,th11)*t2

            m4 | c(th12,th12)*t3"

 strong_sex2 <-  cfa(cfa2, data=data, group="sex", group.equal=c("loadings"), parameterization = "theta")

 

To my understanding, the degrees of freedom, the estimates and fit indices of these two models should be the same.

So my question: what exactly is the command group.equal(“thresholds”) doing?

(I would like free thresholds of item pairs to see which items are problematic in terms of group invariance. Since it is not possible to free thresholds via the group.partial command, I came up with this above second model, where I would then start to free thresholds. But, if this second model is not the same as the first one, I run into a problem…)

Thanks again for your help, I hope you have a bit more insight into this problematic.

Best regards, jsabel


Terrence Jorgensen

unread,
Jul 24, 2018, 9:25:26 AM7/24/18
to lavaan

To my understanding, the degrees of freedom, the estimates and fit indices of these two models should be the same.

So my question: what exactly is the command group.equal(“thresholds”) doing?

That is what I would expect too, but perhaps group.equal = c(“thresholds”) triggers the latent means to be freed in all but the first group, which I think is what happens when intercepts of continuous indicators are constrained to equality.  You can compare the fixed/free parameters in the two models by inspecting their lavInspect() output, or their parTable() output.

Since it is not possible to free thresholds via the group.partial command

That's not true, the group.partial= argument accepts any parameter that can be specified in lavaan syntax.  For example, to make an exception for the 3 thresholds of the first item:

group.partial = c("m1 | t1", "m1 | t2", "m1 | t3")

Hopefully that simplifies it for you.  

Isa

unread,
Jul 24, 2018, 12:11:22 PM7/24/18
to lavaan

Thank you so much for your detailed response, very interesting. I will definitely check out these lavInspect() and parTable() functions!


And its great to know that the group.parial= argument accepts the thresholds. I knew I`ve tried it once and it didnt work, but I guess it was my mistake :)


Thanks again and best regards

jsabel


Isa

unread,
Jul 24, 2018, 1:15:40 PM7/24/18
to lavaan
Just another small thing:

 Am I correct in the assumption that the command for freeing residual variances (which were fixed for testing strict invariance) would be for example for the variable m1:

group.partial = c("m1 ~~ m1") ?


Best regards
jsabel

Isa

unread,
Jul 26, 2018, 8:13:01 AM7/26/18
to lavaan
I think it works now all fine, thank you very much again for the hint with the command for freeing thresholds! :)

Best regards,
jsabel

Terrence Jorgensen

unread,
Jul 29, 2018, 6:40:18 PM7/29/18
to lavaan
Am I correct in the assumption that the command for freeing residual variances (which were fixed for testing strict invariance) would be for example for the variable m1:

group.partial = c("m1 ~~ m1") ?

Yes.
Reply all
Reply to author
Forward
0 new messages