# Metric non-invariance, what's next?

268 views

### Brandon Bretl

Oct 9, 2019, 10:40:43 PM10/9/19
to lavaan
In checking 8 different factors for metric and scalar invariance, one of my factors (with 6 items) came back with a significant difference in the metric invariance test, suggesting it is non-invariant. Is this the end of the game for this factor? Or can I run modification indices, or what can I do to try and correct for metric invariance?

### Mauricio Garnier-Villarreal

Oct 10, 2019, 12:58:40 AM10/10/19
to lavaan

The most common next step would be to test for partial invariance. This means to equate most  of the items in the factor, but not all of them, allowing the misbehaving items to be different between groups. Modification indices and lavtestscore are good ways to identify which items cannot be equated.

In the Bayesian SEM framework you could use approximate invariance, where the misbehaving items would be allowed to be closer, but not exactly the same, by adjusting the variance in the difference between parameters. You can use blavaan for this that uses similar syntax as lavaan

### Terrence Jorgensen

Oct 10, 2019, 6:38:00 AM10/10/19
to lavaan
can I run modification indices

That's only for fixed parameters, not constrained estimates.  For the latter, use lavTestScore()

Or do what Mauricio suggested if it seems to be more than just one item with DIF.

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

### Brandon Bretl

Oct 16, 2019, 3:42:50 PM10/16/19
to lavaan
Thanks for all your help. So this is what I did:

I ran a CFA of the factor that showed metric non-invariance:

`ce <- 'ce =~  Q1_1 + Q2_1 + Q3_1 + Q4_1 + Q5_1 + Q6_1'ce1 <- cfa(ce, data = cfs,std.lv = TRUE, mimic = "mplus", estimator = "MLR")summary(ce1, fit.measures = TRUE, rsquare=TRUE, standardized = TRUE)modificationindices(ce1, standardized = TRUE,sort. = TRUE)`

Then I used your code to fit a metric invariance model and release parameters based my grouping variable "Q19":

`## fit a metric-invariance modelfitmg <- update(ce1, group = "Q19", group.equal = "loadings")## save the full parameter tablePT <- parTable(fitmg)## print the part with loadings (to see labels) and constraints (to test)PT[PT\$op %in% c("=~","=="), ]## pass the numbers (in the order they appear in the full PT) of the## 6 constraints you want to test (i.e., 1:6) one at a time to lavTestScore()lavTestScore(fitmg, release = 1, epc = TRUE)`

What am I looking for in the result? How can I tell if that item is causing the metric non-invariance and what will I do with that information?
`\$testtotal score test:   test    X2 df p.value1 score 2.031  1   0.154\$uniunivariate score tests:   lhs op   rhs    X2 df p.value1 .p1. == .p21. 2.031  1   0.154\$epcexpected parameter changes (epc) and expected parameter values (epv):    lhs op  rhs block group free label plabel   est    epc   epv sepc.lv sepc.all sepc.nox1    ce =~ Q1_1     1     1    1  .p1.   .p1. 0.308 -0.032 0.275  -0.032   -0.046   -0.0462    ce =~ Q2_1     1     1    2  .p2.   .p2. 0.661  0.004 0.665   0.004    0.004    0.0043    ce =~ Q3_1     1     1    3  .p3.   .p3. 0.682  0.005 0.686   0.005    0.005    0.0054    ce =~ Q4_1     1     1    4  .p4.   .p4. 0.677  0.004 0.681   0.004    0.004    0.0045    ce =~ Q5_1     1     1    5  .p5.   .p5. 0.501  0.003 0.504   0.003    0.004    0.0046    ce =~ Q6_1     1     1    6  .p6.   .p6. 0.692  0.005 0.696   0.005    0.005    0.0057  Q1_1 ~~ Q1_1     1     1    7         .p7. 0.386  0.004 0.390   0.386    0.803    0.8038  Q2_1 ~~ Q2_1     1     1    8         .p8. 0.450 -0.001 0.449  -0.450   -0.507   -0.5079  Q3_1 ~~ Q3_1     1     1    9         .p9. 0.524 -0.002 0.522  -0.524   -0.530   -0.53010 Q4_1 ~~ Q4_1     1     1   10        .p10. 0.769 -0.001 0.768  -0.769   -0.627   -0.62711 Q5_1 ~~ Q5_1     1     1   11        .p11. 0.392 -0.001 0.392  -0.392   -0.610   -0.61012 Q6_1 ~~ Q6_1     1     1   12        .p12. 0.432 -0.002 0.430  -0.432   -0.474   -0.47413   ce ~~   ce     1     1    0        .p13. 1.000     NA    NA      NA       NA       NA14 Q1_1 ~1          1     1   13        .p14. 4.657  0.000 4.657   0.000    0.000    0.00015 Q2_1 ~1          1     1   14        .p15. 3.617  0.000 3.617   0.000    0.000    0.00016 Q3_1 ~1          1     1   15        .p16. 3.528  0.000 3.528   0.000    0.000    0.00017 Q4_1 ~1          1     1   16        .p17. 3.239  0.000 3.239   0.000    0.000    0.00018 Q5_1 ~1          1     1   17        .p18. 4.470  0.000 4.470   0.000    0.000    0.00019 Q6_1 ~1          1     1   18        .p19. 3.989  0.000 3.989   0.000    0.000    0.00020   ce ~1          1     1    0        .p20. 0.000     NA    NA      NA       NA       NA21   ce =~ Q1_1     2     2   19  .p1.  .p21. 0.308  0.034 0.342   0.037    0.056    0.05622   ce =~ Q2_1     2     2   20  .p2.  .p22. 0.661  0.004 0.665   0.004    0.004    0.00423   ce =~ Q3_1     2     2   21  .p3.  .p23. 0.682  0.005 0.686   0.005    0.005    0.00524   ce =~ Q4_1     2     2   22  .p4.  .p24. 0.677  0.004 0.681   0.004    0.004    0.00425   ce =~ Q5_1     2     2   23  .p5.  .p25. 0.501  0.003 0.504   0.003    0.003    0.00326   ce =~ Q6_1     2     2   24  .p6.  .p26. 0.692  0.005 0.696   0.005    0.005    0.00527 Q1_1 ~~ Q1_1     2     2   25        .p27. 0.340 -0.004 0.336  -0.340   -0.752   -0.75228 Q2_1 ~~ Q2_1     2     2   26        .p28. 0.575  0.002 0.577   0.575    0.526    0.52629 Q3_1 ~~ Q3_1     2     2   27        .p29. 0.466  0.002 0.468   0.466    0.458    0.45830 Q4_1 ~~ Q4_1     2     2   28        .p30. 0.936  0.002 0.937   0.936    0.632    0.63231 Q5_1 ~~ Q5_1     2     2   29        .p31. 0.545  0.001 0.545   0.545    0.646    0.64632 Q6_1 ~~ Q6_1     2     2   30        .p32. 0.443  0.002 0.446   0.443    0.439    0.43933   ce ~~   ce     2     2   31        .p33. 1.186 -0.033 1.154  -1.000   -1.000   -1.00034 Q1_1 ~1          2     2   32        .p34. 4.633  0.000 4.633   0.000    0.000    0.00035 Q2_1 ~1          2     2   33        .p35. 3.417  0.000 3.417   0.000    0.000    0.00036 Q3_1 ~1          2     2   34        .p36. 3.506  0.000 3.506   0.000    0.000    0.00037 Q4_1 ~1          2     2   35        .p37. 3.218  0.000 3.218   0.000    0.000    0.00038 Q5_1 ~1          2     2   36        .p38. 4.207  0.000 4.207   0.000    0.000    0.00039 Q6_1 ~1          2     2   37        .p39. 3.671  0.000 3.671   0.000    0.000    0.00040   ce ~1          2     2    0        .p40. 0.000     NA    NA      NA       NA       NA`

### Mauricio Garnier-Villarreal

Oct 17, 2019, 12:48:22 AM10/17/19
to lavaan

`univariate score tests:   lhs op   rhs    X2 df p.value1 .p1. == .p21. 2.031  1   0.154`

`This shows the hypothesis test if the parameters p1 and p21 are equal, a large X2 and low p-value would indiate that that parameter most likely should not be equated`

`This case only shows 1 parameter because you set release=1, if you dont it will show you the inivariate test for all the equated parameters `
`lavTestScore(fitmg, epc = TRUE)`

`This will help you find which indicator should not be held equal between groups`

`total score test:   test    X2 df p.value1 score 2.031  1   0.154`

`Shows the overall test, if all the equated parameters (shown in the univariate section) are equal`

### Brandon Bretl

Oct 17, 2019, 8:49:39 AM10/17/19
to lavaan
Thanks so much for your help with this. So this is what I got:

`   lhs op   rhs    X2 df p.value1 .p1. == .p21. 2.031  1   0.154`
`2 .p2. == .p22. 7.876  1   0.0053 .p3. == .p23. 0.030  1   0.8634 .p4. == .p24. 0.901  1   0.3425 .p5. == .p25. 3.644  1   0.0566 .p6. == .p26. 1.425  1   0.233`

If I am reading this right, .p2. and .p22. should not be equated (and possibly .p5 and .p25.). Do you have any guidance for how I un-equate these in the model then?

I feel like a real bum here because my R and stats knowledge is limited, but this is helping me learn a lot, so I really appreciate it.

Here is the rest of the printout:

`    lhs op  rhs block group free label plabel   est    epc   epv sepc.lv sepc.all sepc.nox1    ce =~ Q1_1     1     1    1  .p1.   .p1. 0.308 -0.032 0.275  -0.032   -0.046   -0.046`
`2    ce =~ Q2_1     1     1    2  .p2.   .p2. 0.661  0.061 0.722   0.061    0.065    0.0653    ce =~ Q3_1     1     1    3  .p3.   .p3. 0.682  0.004 0.686   0.004    0.004    0.0044    ce =~ Q4_1     1     1    4  .p4.   .p4. 0.677  0.027 0.704   0.027    0.025    0.0255    ce =~ Q5_1     1     1    5  .p5.   .p5. 0.501 -0.038 0.463  -0.038   -0.048   -0.0486    ce =~ Q6_1     1     1    6  .p6.   .p6. 0.692 -0.027 0.665  -0.027   -0.028   -0.028`
`7  Q1_1 ~~ Q1_1     1     1    7         .p7. 0.386  0.004 0.390   0.386    0.803    0.803`
`8  Q2_1 ~~ Q2_1     1     1    8         .p8. 0.450 -0.023 0.427  -0.450   -0.507   -0.5079  Q3_1 ~~ Q3_1     1     1    9         .p9. 0.524 -0.001 0.523  -0.524   -0.530   -0.53010 Q4_1 ~~ Q4_1     1     1   10        .p10. 0.769 -0.008 0.761  -0.769   -0.627   -0.62711 Q5_1 ~~ Q5_1     1     1   11        .p11. 0.392  0.009 0.402   0.392    0.610    0.61012 Q6_1 ~~ Q6_1     1     1   12        .p12. 0.432  0.012 0.443   0.432    0.474    0.474`
`13   ce ~~   ce     1     1    0        .p13. 1.000     NA    NA      NA       NA       NA14 Q1_1 ~1          1     1   13        .p14. 4.657  0.000 4.657   0.000    0.000    0.00015 Q2_1 ~1          1     1   14        .p15. 3.617  0.000 3.617   0.000    0.000    0.00016 Q3_1 ~1          1     1   15        .p16. 3.528  0.000 3.528   0.000    0.000    0.00017 Q4_1 ~1          1     1   16        .p17. 3.239  0.000 3.239   0.000    0.000    0.00018 Q5_1 ~1          1     1   17        .p18. 4.470  0.000 4.470   0.000    0.000    0.00019 Q6_1 ~1          1     1   18        .p19. 3.989  0.000 3.989   0.000    0.000    0.00020   ce ~1          1     1    0        .p20. 0.000     NA    NA      NA       NA       NA`
`21   ce =~ Q1_1     2     2   19  .p1.  .p21. 0.308  0.031 0.339   0.034    0.050    0.05022   ce =~ Q2_1     2     2   20  .p2.  .p22. 0.661 -0.078 0.584  -0.085   -0.081   -0.08123   ce =~ Q3_1     2     2   21  .p3.  .p23. 0.682 -0.002 0.679  -0.002   -0.002   -0.00224   ce =~ Q4_1     2     2   22  .p4.  .p24. 0.677 -0.033 0.644  -0.036   -0.029   -0.02925   ce =~ Q5_1     2     2   23  .p5.  .p25. 0.501  0.056 0.557   0.061    0.067    0.06726   ce =~ Q6_1     2     2   24  .p6.  .p26. 0.692  0.031 0.723   0.034    0.034    0.034`
`27 Q1_1 ~~ Q1_1     2     2   25        .p27. 0.340 -0.004 0.336  -0.340   -0.752   -0.752`
`28 Q2_1 ~~ Q2_1     2     2   26        .p28. 0.575  0.031 0.606   0.575    0.526    0.526`
`29 Q3_1 ~~ Q3_1     2     2   27        .p29. 0.466  0.002 0.468   0.466    0.458    0.458`
`30 Q4_1 ~~ Q4_1     2     2   28        .p30. 0.936  0.012 0.948   0.936    0.632    0.63231 Q5_1 ~~ Q5_1     2     2   29        .p31. 0.545 -0.014 0.531  -0.545   -0.646   -0.64632 Q6_1 ~~ Q6_1     2     2   30        .p32. 0.443 -0.015 0.429  -0.443   -0.439   -0.43933   ce ~~   ce     2     2   31        .p33. 1.186 -0.007 1.180  -1.000   -1.000   -1.000`

### Mauricio Garnier-Villarreal

Oct 17, 2019, 2:24:01 PM10/17/19
to lavaan

`ce <- 'ce =~ c(l1,l1)*Q1_1 + ``c(l21,l22)*Q2_1 + ``c(l3,l3)*Q3_1 + ``c(l4,l4)*Q4_1 + ``c(l5,l5)*Q5_1 + ``c(l6,l6)*Q6_1`

`ce ~~ c(1,NA)*cee`
`'ce1 <- cfa(ce, data = cfs,std.lv = TRUE, mimic = "mplus", std.lv=T,`
`estimator = "MLR", group="``Q19")`

`Here I am setting the equating of parameters manually with labels. As you set the same label to a parameter for both groups lavaan will equate this parameter, ``c(l1,l1)*Q1_1 sets the label l1 for factor loading 1 for both groups. Based on the univariate results, I am un equating the factor loading ``c(l21,l22)*Q2_1 by switching it different labels for group 1 l21 and group 2 l22`

### Brandon Bretl

Oct 23, 2019, 6:08:18 PM10/23/19
to lavaan
Got it. That worked. Now that I have metric invariance or partial metric invariance for all my factors, I want to compare factor means. Is there any problem with me just entering the code from freeing these parameters into the model syntax I use for comparing factor means? This is the syntax I use:

`cfs_model_base <-   ' cpa =~ Q1_2 + Q1_8 + Q2_2 + Q3_2 + Q4_2 + Q5_2cph =~ Q2_8 + Q3_8 + Q4_8 + Q5_9 + Q6_2 + Q6_9`
`ce =~ c(l1,l1)*Q1_1 + c(l21,l22)*Q2_1 + c(l3,l3)*Q3_1 + c(l4,l4)*Q4_1 + c(l5,l5)*Q5_1 + c(l6,l6)*Q6_1ce ~~ c(1,NA)*ce`
`just =~ Q1_3 + Q2_3 + Q3_3 + Q4_3 + Q5_3 + Q6_3lib =~ Q1_4 + Q2_4 + Q3_4 + Q4_4 + Q5_4 + Q6_4auth =~ Q1_5 + Q2_5 + Q3_5 + Q4_5 + Q5_6 + Q6_5loya =~ Q1_6 + Q2_6 + Q3_6 + Q4_6 + Q5_7 + Q6_7sanc =~ Q1_7 + Q2_7 + Q3_7 + Q4_7 + Q5_8 + Q6_8'factor_compare <- cfa(cfs_model_base, data = cfs,                       group.equal = c("loadings", "intercepts"),                       group ="Q19", std.lv = TRUE, mimic = "mplus", estimator = "MLR")summary(factor_compare, fit.measures = TRUE, rsquare=TRUE, standardized = TRUE)`

And I can compare the difference in intercepts then as the differences in means?:

`Intercepts:                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all    cpa              -0.519    0.080   -6.497    0.000   -0.405   -0.405    cph              -0.431    0.073   -5.916    0.000   -0.362   -0.362    ce               -0.464    0.074   -6.293    0.000   -0.374   -0.374    just             -0.085    0.065   -1.322    0.186   -0.082   -0.082    lib              -0.401    0.067   -5.967    0.000   -0.396   -0.396    auth             -0.231    0.062   -3.718    0.000   -0.224   -0.224    loya             -0.225    0.062   -3.598    0.000   -0.219   -0.219    sanc             -0.203    0.071   -2.878    0.004   -0.188   -0.188`

Thus there is a significant difference between these two groups (gender) in all factors except "just" (justice)?

Continued thanks,
Brandon

### Mauricio Garnier-Villarreal

Oct 26, 2019, 5:33:41 PM10/26/19
to lavaan
yes, after measurement invariance, you can compare latent parameters, like means, variances, and covariances

### Terrence Jorgensen

Oct 28, 2019, 8:29:02 AM10/28/19
to lavaan
Now that I have metric invariance or partial metric invariance for all my factors, I want to compare factor means. Is there any problem with me just entering the code from freeing these parameters into the model syntax I use for comparing factor means?

It is highly dubious to constrain an item's intercepts to equality if its loadings are not constrained.  That is analogous to estimating an interaction term (here, ce by Q19) without including a main effect (of Q19), if you had observed ce to include as a predictor in a regression model for item Q2_1.

Your syntax indicates you freed the loadings of Q2_1, so free their intercepts too.  Either in the syntax (Q2_1 ~ c(int2.g1, int2.g2)*1 ) or using group.partial = "Q2_1~1"

### Brandon Bretl

Oct 28, 2019, 11:13:55 AM10/28/19
to lavaan
Thanks so much. So I freed the intercepts for Q2_1 by adding that code to the syntax. I reran the analysis and this is what I got for intercepts:

`Intercepts:                   Estimate  Std.Err  z-value  P(>|z|)   Std.lv  Std.all`
`    cpa              -0.263    0.089   -2.972    0.003   -0.254   -0.254    cph              -0.187    0.083   -2.254    0.024   -0.188   -0.188    ce               -0.262    0.089   -2.935    0.003   -0.231   -0.231    just              0.091    0.078    1.169    0.243    0.102    0.102    lib              -0.306    0.086   -3.542    0.000   -0.311   -0.311    auth             -0.053    0.077   -0.690    0.490   -0.055   -0.055    loya             -0.135    0.081   -1.675    0.094   -0.141   -0.141    sanc              0.026    0.093    0.284    0.777    0.027    0.027`

The way I interpret this is that for factors cpa, cph, ce, and lib, there is a significant difference in factor means between the two groups.

This is essentially saying that boys have less of the factor responsible for ratings of moral intuitions violations related to physical harm to animals (cpa), physical harm to humans (cph), emotional harm to humans (ce), and liberty/autonomy (lib). There is no significant difference in factor means between girls and boys in justice, respect for authority, in-group loyalty, or sanctity/purity.

Brandon

Code I ran:

`cfs_metric <-   ' `
`cpa =~ Q1_2 + Q1_8 + Q2_2 + Q3_2 + Q4_2 + Q5_2cph =~ Q2_8 + Q3_8 + Q4_8 + Q5_9 + Q6_2 + Q6_9ce =~ c(l1,l1)*Q1_1 + c(l21,l22)*Q2_1 + c(l3,l3)*Q3_1 + c(l4,l4)*Q4_1 + c(l5,l5)*Q5_1 + c(l6,l6)*Q6_1ce ~~ c(1,NA)*cejust =~ Q1_3 + Q2_3 + Q3_3 + Q4_3 + Q5_3 + Q6_3lib =~ Q1_4 +  Q2_4 + Q3_4 + Q4_4 + Q5_4 + Q6_4auth =~ Q1_5 + Q2_5 + Q3_5 + Q4_5 + Q5_6 + Q6_5loya =~ Q1_6 + Q2_6 + Q3_6 + Q4_6 + Q5_7 + Q6_7sanc =~ Q1_7 + Q2_7 + Q3_7 + Q4_7 + Q5_8 + Q6_8`
`Q2_8 ~~ Q3_8Q1_1 ~~ Q5_1Q4_3 ~~ Q6_3Q4_4 ~~ Q6_4Q1_5 ~~ Q2_5Q5_8 ~~ Q6_8auth =~ Q5_7cph =~ Q6_7Q4_2 ~~ Q5_9Q2_1 ~ c(int2.g1, int2.g2)*1'factor_compare <- cfa(cfs_metric, data = cfs,                       group.equal = c("loadings", "intercepts"),                       group ="Q19", std.lv = TRUE, mimic = "mplus", estimator = "MLR")`
`summary(factor_compare, fit.measures = TRUE, rsquare=TRUE, standardized = TRUE)`

### Mauricio Garnier-Villarreal

Oct 31, 2019, 12:06:31 AM10/31/19
to lavaan
Assuming you are using an alpha = 0.05, yes that would be the interpretation. For further detail interpretation you can look at std.all, which will be the cohen d effect size , and you can get a more detail interpretation of the magnituded based on this

### Brandon Bretl

Jan 22, 2020, 9:52:53 PM1/22/20
to lavaan
Hi Mauricio, I came back to reanalyze some more data, and this time I can't get the chi-square to change in my fit.metric by changing parameters like we did in the other model.

Here I am trying to test metric invariance for 4 groups at once.

Based on results form lavTestScore, I am trying to free the loading of the Q5_2 indicator in group 3. To do that I used this syntax:

`cpa <- 'cpa =~ c(l1,l1,l1,l1)*Q1_2 + c(l2,l2,l2,l2)*Q1_8 + c(l31,l31,l31,l31)*Q2_2 + c(l4,l4,l4,l4)*Q3_2 + c(l51,l51,l51,l51)*Q4_2 + c(l61,l61,l62,l61)*Q5_2 cpa ~~ c(1,1,NA,1)*cpa'`

No matter what parameters I try and free, e.g., L61, L62), I keep getting the exact same chi-square change indicating metric non-invariance (25.905, p=.039).

Here is the code I'm using to rerun the analysis after trying to free the parameters as above:

`syntax.config <- measEq.syntax(configural.model = mft_fact, data=mft_dat, parameterization="theta",                                ID.fac="auto.fix.first", ID.cat = "Wu.Estabrook.2016",                                group = g1, group.label=c(1,2,3,4))cat(as.character(syntax.config))summary(syntax.config)## threshold invariancesyntax.thresh <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",                               ID.fac = "auto.fix.first", ID.cat = "Wu.Estabrook.2016",                               group = g1, group.label=c(1,2,3,4), group.equal = "thresholds")## notice that constraining 4 thresholds allows intercepts and residual## variances to be freely estimated in all but the first group & occasioncat(as.character(syntax.thresh))## print a summary of model featuressummary(syntax.thresh)## Fit a model to the data either in a subsequent step:mod.config <- as.character(syntax.config)fit.config <- cfa(mod.config, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## or in a single step:fit.thresh <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                            parameterization = "theta",                            ID.fac = "auto.fix.first", ID.cat = "Wu.Estabrook.2016",                            group = g1, group.label=c(1,2,3,4), group.equal = "thresholds", return.fit = TRUE)## compare their fit to test threshold invarianceanova(fit.config, fit.thresh)####### METRIC INVARIANCE TESTING######## metric invariancesyntax.metric <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",                               ID.fac = "auto.fix.first", ID.cat = "Wu.Estabrook.2016",                               group = g1, group.label=c(1,2,3,4),                               group.equal = c("thresholds","loadings"))summary(syntax.metric)                    # summarize model featuresmod.metric <- as.character(syntax.metric) # save as textcat(mod.metric)                           # print/view lavaan syntax## fit model to datafit.metric <- cfa(mod.metric, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## test equivalence of loadings, given equivalence of thresholdsanova(fit.thresh, fit.metric)####### SCALAR INVARIANCE TESTING######## scalar invariancesyntax.scalar <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",                               ID.fac = "auto.fix.first", ID.cat = "Wu.Estabrook.2016",                               group = g1, group.label=c(1,2,3,4),                               group.equal = c("thresholds","loadings",                                               "intercepts"))summary(syntax.scalar)                    # summarize model featuresmod.scalar <- as.character(syntax.scalar) # save as textcat(mod.scalar)                           # print/view lavaan syntax## fit model to datafit.scalar <- cfa(mod.scalar, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## test equivalence of intercepts, given equal thresholds & loadingsanova(fit.metric, fit.scalar)####### INVARIANCE TESTING TABLE######## For a single table with all results, you can pass the models to## summarize to the compareFit() functioncompareFit(fit.config, fit.thresh, fit.metric, fit.scalar)lavInspect(fit.metric, "cov.lv")`

### Mauricio Garnier-Villarreal

Jan 22, 2020, 10:40:03 PM1/22/20
to lavaan

Brandon

A couple of differences from your previous code. In this case you change the identification method, from fixed variance to marker variable. Would recommend to switch to std.lv intstead of auto.fix.first

`syntax.config <- measEq.syntax(configural.model = mft_fact, data=mft_dat, parameterization="theta",`
`                                ID.fac="std.lv", ID.cat = "Wu.Estabrook.2016",                                group = g1, group.label=c(1,2,3,4))`

Second, when you add the factor loadings constraints except one (partial invariance), you still need to free the latent variance. This would be your weak invariance model, with equal factor loadings (except one), factor variance fix for group 1, and estimated latent variance for groups 2,3,4

`cpa <- '`
`cpa =~ c(l1,l1,l1,l1)*Q1_2 + c(l2,l2,l2,l2)*Q1_8 + c(l31,l31,l31,l31)*Q2_2 + c(l4,l4,l4,l4)*Q3_2 + c(l51,l51,l51,l51)*Q4_2 + c(l61,l61,l62,l61)*Q5_2 `
`cpa ~~ c(1,NA,NA,NA)*cpa'`

### Brandon Bretl

Jan 24, 2020, 12:52:13 PM1/24/20
to lavaan
Thanks Mauricio, but I still get the same results:

My initial model...
`cpa <- '`
`cpa =~ Q1_2 + Q1_8 + Q2_2 + Q3_2 + Q4_2 + Q5_2 `
`'`

Gives these results...

`Chi-Squared Difference Test           Df   AIC   BIC   Chisq Chisq diff Df diff Pr(>Chisq)    fit.config 36 15477 15827  46.739                                  fit.thresh 36 15477 15827  46.739      0.000       0               fit.metric 51 15473 15750  72.645     25.905      15  0.0390291 *  fit.scalar 66 15486 15690 115.471     42.826      15  0.0001675 ***`

And my updated model...

`cpa <- 'cpa =~ c(l1,l1,l1,l1)*Q1_2 + c(l2,l2,l2,l2)*Q1_8 + c(l31,l31,l31,l31)*Q2_2 + `
`c(l4,l4,l4,l4)*Q3_2 + c(l51,l51,l52,l51)*Q4_2 + c(l61,l61,l63,l61)*Q5_2 cpa ~~ c(1,NA,NA,NA)*cpa'`

Gives the exact same results...
`Chi-Squared Difference Test           Df   AIC   BIC   Chisq Chisq diff Df diff Pr(>Chisq)    fit.config 36 15477 15827  46.739                                  fit.thresh 36 15477 15827  46.739      0.000       0               fit.metric 51 15473 15750  72.645     25.905      15  0.0390291 *  fit.scalar 66 15486 15690 115.471     42.826      15  0.0001675 ***`

When I run it through the same metric and scalar invariance tests:

`# add grouping variable hereg1 <- "dissgroup"# add factor variable heremft_fact <- cpa# add data variable heremft_dat <- cfs_middle#Invariance testing`
`syntax.config <- measEq.syntax(configural.model = mft_fact, data=mft_dat, parameterization="theta",                                ID.fac="std.lv", ID.cat = "Wu.Estabrook.2016",                                group = g1, group.label=c(1,2,3,4))`
`cat(as.character(syntax.config))summary(syntax.config)## threshold invariancesyntax.thresh <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",`
`                               ID.fac = "std.lv", ID.cat = "Wu.Estabrook.2016",`
`                               group = g1, group.label=c(1,2,3,4), group.equal = "thresholds")## notice that constraining 4 thresholds allows intercepts and residual## variances to be freely estimated in all but the first group & occasioncat(as.character(syntax.thresh))## print a summary of model featuressummary(syntax.thresh)## Fit a model to the data either in a subsequent step:mod.config <- as.character(syntax.config)fit.config <- cfa(mod.config, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## or in a single step:fit.thresh <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                            parameterization = "theta",`
`                            ID.fac = "std.lv", ID.cat = "Wu.Estabrook.2016",`
`                            group = g1, group.label=c(1,2,3,4), group.equal = "thresholds", return.fit = TRUE)## compare their fit to test threshold invarianceanova(fit.config, fit.thresh)####### METRIC INVARIANCE TESTING######## metric invariancesyntax.metric <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",`
`                               ID.fac = "std.lv", ID.cat = "Wu.Estabrook.2016",`
`                               group = g1, group.label=c(1,2,3,4),                               group.equal = c("thresholds","loadings"))summary(syntax.metric)                    # summarize model featuresmod.metric <- as.character(syntax.metric) # save as textcat(mod.metric)                           # print/view lavaan syntax## fit model to datafit.metric <- cfa(mod.metric, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## test equivalence of loadings, given equivalence of thresholdsanova(fit.thresh, fit.metric)####### SCALAR INVARIANCE TESTING######## scalar invariancesyntax.scalar <- measEq.syntax(configural.model = mft_fact, data = mft_dat,                               parameterization = "theta",`
`                               ID.fac = "std.lv", ID.cat = "Wu.Estabrook.2016",`
`                               group = g1, group.label=c(1,2,3,4),                               group.equal = c("thresholds","loadings",                                               "intercepts"))summary(syntax.scalar)                    # summarize model featuresmod.scalar <- as.character(syntax.scalar) # save as textcat(mod.scalar)                           # print/view lavaan syntax## fit model to datafit.scalar <- cfa(mod.scalar, data = mft_dat, group = g1, group.label=c(1,2,3,4),                  parameterization = "theta")## test equivalence of intercepts, given equal thresholds & loadingsanova(fit.metric, fit.scalar)####### INVARIANCE TESTING TABLE######## For a single table with all results, you can pass the models to## summarize to the compareFit() functioncompareFit(fit.config, fit.thresh, fit.metric, fit.scalar)`

### Terrence Jorgensen

Jan 25, 2020, 4:08:03 AM1/25/20
to lavaan
I am trying to free the loading of the Q5_2 indicator in group 3. To do that I used this syntax:

Nowhere in your syntax do you use the model "cpa <- ..." that you specified.  If you are relying on measEq.syntax() to write your syntax for you, it will always assume you are only providing a configural model (which is the name of the argument), so your labels will not be preserved.  You can only use the group.partial= argument to free a parameter in all groups/occasions.  If you want to free a parameter in only one group (or occasion), you can use the update() method to replace the specification of a particular parameter with your own custom syntax for that parameter (i.e., not the whole model, just 'cpa =~ c(l61,l61,l62,l61)*Q5_2').  See an example of this functionality at the bottom of this thread:  https://github.com/simsem/semTools/issues/60

### Brandon Bretl

`g1 <- "dissgroup"mft_fact <- cpa_modelmft_dat <- cfs_middle#In`