Partial Metric Invariance, 4 Groups, releasing parameters

268 views
Skip to first unread message

Brandon Bretl

unread,
Feb 15, 2022, 2:54:07 PM2/15/22
to lavaan
I am looking to compare factor means after establishing partial metric invariance. 

I have gotten as far as determining what parameters to release, but I am wondering if I release the entire item or just the specific parameters suggested. I have 4 groups that I am comparing.

Parameters to release =

.p19 == .p117 (item 2 ~1, groups 1 and 2, respectively)
.p20. == .p69. (item 3 ~1, groups 1 and 2, respectively)
.p16. == .p114. (item 5 ~1, groups 1 and 3, respectively)

So what code should I use:

group.partial = c(".p19. == .p117.", ".p20. == .p69.", ".p16. == .p114."),

OR, release the entire items:

group.partial = c("item 2", "item 3", "item 5"),

Thanks for any guidance.

Kindly,.
Brandon


Code I think I should use:

factor_compare <- cfa(cfs_efa_model_003, data = cfs_3middle,
                      group.equal = c("loadings", "intercepts"),
                      group.partial = c(".p19. == .p117.", ".p20. == .p69.", ".p16. == .p114."),
                      group ="dissgroup", group.label=c(1,2,3,4), std.lv = FALSE, mimic = "mplus", estimator = "MLR")

summary(factor_compare, fit.measures = TRUE, rsquare=FALSE, standardized = TRUE)




Brandon Bretl

unread,
Feb 15, 2022, 3:09:27 PM2/15/22
to lavaan
Correction, wondering I release identified parameters or entire items as follows:

group.partial = c("item 2 ~1", "item 3 ~1", "item 5 ~1"),

Emily Gabriela Vira

unread,
Feb 16, 2022, 10:53:37 AM2/16/22
to lavaan
Hi Brandon,

I haven't come across releasing parameters in the way you were thinking of trying: "group.partial = c(".p19. == .p117.", ".p20. == .p69.", ".p16. == .p114.")", so I gave it a try and it doesn't seem to release the parameter it indicates. I recommend releasing the entire item intercept that is coming up as non-invariant. This would be the correct code "group.partial = c("item 2 ~1", "item 3 ~1", "item 5 ~1") " 

Best,
Emily

Brandon Bretl

unread,
Feb 16, 2022, 11:11:16 AM2/16/22
to lavaan
Thanks, Emily! That's what I did initially, but then I was second guessing myself because of the 4 groups. I tried it too and it seemed to do nothing, so I went back and released entire items.
Thanks again,
Brandon

Emily Gabriela Vira

unread,
Feb 16, 2022, 1:52:35 PM2/16/22
to lavaan
Completely understandable! I also paused to think about the 4 groups, but the best you can do is report where the differences lie. Perhaps two groups are more similar than the rest, but since you're statistically comparing 4 groups it would make sense to just release the problematic intercepts overall. Best of luck! 

Emily

ARTEM ZADOROZHNYY

unread,
Feb 22, 2022, 9:35:40 AM2/22/22
to lavaan
Dear Dr. Brandon, 

Hope I do not disturb you much. 
Recently I have encountered scalar invariance issue among two data sites where I have collected data. In my attempts to discover which factors "to blame" I have found the syntax I attach below for exploring the model in case of strict invariance issues. I found that while "~~" is used to work with residuals, "~1" is used to explore intercepts and "=~" to work with factor loadings. Would you be so kind to share the code you used to define "problematic" factors at the metric level so that I can adopt it for exploring intercepts in my case. 

Many thanks in advance, 
Archie 


partial_syntax <- paste(colnames(my_dataset)[1:12], #all the columns
                        "~~", #intercepts
                        colnames(my_dataset)[1:12]) #all columns again 

CFI_list  <- 1:length(partial_syntax)
names(CFI_list) <- partial_syntax

for (i in 1:length(partial_syntax)){
   
    temp <- cfa(model = IDLE_CFA,
                data = my_dataset,
                meanstructure = TRUE,
                group = "UNI_MIM",
                group.equal = c("loadings","intercepts","residuals"),
                group.partial = partial_syntax[i])
   
    CFI_list[i] <- fitmeasures(temp, "cfi")
}
CFI_list

Brandon Bretl

unread,
Mar 21, 2022, 1:14:31 PM3/21/22
to lavaan
Hi Artem, 

Here is the entire code I used. At the very bottom, I run lavTestScore(fit.scalar) and View(parTable(fit.scalar)) to determine parameters to release. 

Hope that helps,
Kindly,
Brandon



cfs_efa_model_001 <- '
factor2 =~ Q1_8 + Q2_2 + Q3_2 + Q4_2 + Q5_2
factor4 =~ Q2_4 + Q3_4 + Q4_4 + Q6_4
factor6 =~ Q1_6 + Q2_6 + Q3_6 + Q4_6
Q4_4 ~~ Q6_4
'


######
######
######
# INVARIANCE TESTING
######
######
######

# add grouping variable here
g1 <- "dissgroup"
# add factor model here
mft_fact <- cfs_efa_model_001
# add data variable here
mft_dat <- cfs_3middle


#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 invariance
syntax.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 & occasion
cat(as.character(syntax.thresh))
## print a summary of model features
summary(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")

lavInspect(fit.config, "cov.lv")

## 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 invariance
anova(fit.config, fit.thresh)

######
# METRIC INVARIANCE TESTING
######

## metric invariance
syntax.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 features
mod.metric <- as.character(syntax.metric) # save as text
cat(mod.metric)                           # print/view lavaan syntax
## fit model to data
fit.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 thresholds
anova(fit.thresh, fit.metric)

######
# SCALAR INVARIANCE TESTING
######

## scalar invariance
syntax.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"),
                               group.partial = c("Q2_4 ~1","Q3_4 ~ 1", "Q3_2 ~ 1"))
summary(syntax.scalar)                    # summarize model features
mod.scalar <- as.character(syntax.scalar) # save as text
cat(mod.scalar)                           # print/view lavaan syntax
## fit model to data
fit.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 & loadings
anova(fit.metric, fit.scalar)




######
# INVARIANCE TESTING TABLE
######

## For a single table with all results, you can pass the models to
## summarize to the compareFit() function
compareFit(fit.config, fit.thresh, fit.metric, fit.scalar)

lavInspect(fit.metric, "cov.lv")



#TEST FOR which parameters to release
lavTestScore(fit.scalar)

View(parTable(fit.scalar))

Reply all
Reply to author
Forward
0 new messages