Chapter 5 Testing for measurement invariance with lavaan in R
5.4 Data cleaning
Do whatever data wrangling necessary (missing values, etc)
Remember that you need a variable that identifies the group
5.5 Use lavaan language to formalize the model
The lavaan tutorial explains well how to formalize a theoretical model. It even includes a measurement invariance testing example.
In a nutshell:
Factor loadings in R are indicated by =∼ and covariances (between factors or error terms for items) are indicated by ∼∼. The model is specified similar to writing regression equations.
5.5.1 Example: Testing the Political Trust for measurement invariance
We want to know if we can compare the political trust concept in Portugal and Spain.
5.5.3 Model Building
Let´s build our model using lavaan model syntax
baseline <- 'political_trust =~ trstprl + trstlgl + trstplt + trstprt
'
#CFA function
fit_baseline<-cfa(baseline, data = mea_inv, group="cntry")
#Summary shows the estimation results
summary(fit_baseline, fit.measures=T, standardized = T)## Length  Class   Mode 
##      1 lavaan     S4
5.5.4 Testing
Not a good model.
Commonly reported fit indices and recommended cut-offs
| Measure | Name | Cut-off | 
|---|---|---|
| CFI | Comparative Fit Index | CFI>=.90 | 
| RMSEA | Root Mean Square Root of Approximation | RMSEA<0.08 | 
| SRMR | Standardized Root Mean Square Residual | SRMR<0.08 | 
Please take this as generally indicative. Read more about testing on Further Readings
5.5.5 Changing the original model
baseline1 <- 'political_trust =~ trstprl + trstlgl + trstplt + trstprt
trstplt ~~ trstprt
'
fit_configural<-cfa(baseline1, data = mea_inv, group="cntry")
summary(fit_configural, fit.measures=T, standardized = T)## Length  Class   Mode 
##      1 lavaan     S4
Much better model. Configural Invariance set. Let’s go for metric.
5.5.6 Metric Invariance
metric <- 'political_trust =~ trstprl + trstlgl + trstplt + trstprt
trstplt ~~ trstprt
'
fit_metric<-cfa(baseline1, data = mea_inv, group="cntry", group.equal = c("loadings"))
summary(fit_metric, fit.measures=T, standardized = T)## Length  Class   Mode 
##      1 lavaan     S4
Also a good model. Let’s go ahead for scalar.
5.5.7 Scalar Invariance
scalar <- 'political_trust =~ trstprl + trstlgl + trstplt + trstprt
trstplt ~~ trstprt
'
fit_scalar<-cfa(baseline1, data = mea_inv, group="cntry", group.equal = c("loadings", "intercepts"))
summary(fit_scalar, fit.measures=T, standardized = T)## Length  Class   Mode 
##      1 lavaan     S4
Scalar Invariance holds!
5.5.8 Comparing several models using compareFit
## ################### Nested Model Comparison #########################
## Chi-Squared Difference Test
## 
##                Df   AIC   BIC   Chisq Chisq diff Df diff Pr(>Chisq)  
## fit_configural  2 25032 25174  6.9645                                
## fit_metric      5 25031 25157 11.6200     4.6555       3    0.19883  
## fit_scalar      8 25031 25141 18.0521     6.4321       3    0.09238 .
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## ####################### Model Fit Indices ###########################
##                  chisq df pvalue    cfi    tli        aic        bic rmsea  srmr
## fit_configural  6.965†  2   .031 0.998†  .991  25032.320  25174.353  .053  .006†
## fit_metric     11.620   5   .040 0.998   .995  25030.976† 25156.620  .039  .018 
## fit_scalar     18.052   8   .021 0.997  0.995† 25031.408  25140.664† .038† .020 
## 
## ################## Differences in Fit Indices #######################
##                             df    cfi   tli    aic     bic  rmsea  srmr
## fit_metric - fit_configural  3 -0.001 0.004 -1.345 -17.733 -0.014 0.012
## fit_scalar - fit_metric      3 -0.001 0.000  0.432 -15.956 -0.001 0.002
5.6 Omnibus invariance testing
## 
## Measurement invariance models:
## 
## Model 1 : fit.configural
## Model 2 : fit.loadings
## Model 3 : fit.intercepts
## Model 4 : fit.means
## 
## Chi-Squared Difference Test
## 
##                Df   AIC   BIC   Chisq Chisq diff Df diff Pr(>Chisq)  
## fit.configural  2 25032 25174  6.9645                                
## fit.loadings    5 25031 25157 11.6200     4.6555       3    0.19883  
## fit.intercepts  8 25031 25141 18.0521     6.4321       3    0.09238 .
## fit.means       9 25034 25138 22.3435     4.2914       1    0.03831 *
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## 
## Fit measures:
## 
##                  cfi rmsea cfi.delta rmsea.delta
## fit.configural 0.998 0.053        NA          NA
## fit.loadings   0.998 0.039     0.001       0.014
## fit.intercepts 0.997 0.038     0.001       0.001
## fit.means      0.996 0.041     0.001       0.003
Terry Jorgensen measurementInvariance function, within the semTools package is very handy
5.7 Advanced Tutorial: Partial Invariance (individually constraining parameters)
Figure 5.1: Source: Designed by starline / Freepik