library(lavaan)
library(semTools)
Prerequisites
To access the datasets, help pages, and functions that we will use in this code snippet, load the following packages:
And read in the data:
<- read.csv(here("data", "2021-11-01_social-exchanges.csv")) social_exchanges
The data contains simulated values for several indicators of positive and negative social exchanges, measured on two occasions (w1
and w2
). There are three continuous indicators that measure perceived companionship (vst1
, vst2
, vst3
), and three binary indicators that measure unwanted advice (unw1
, unw2
, unw3
). The data and some of the examples come from Longitudinal Structural Equation Modeling: A Comprehensive Introduction by Jason Newsom.
Configural Invariance
Using the lavaan package.
<- ("
configural_model_lav # Measurement model
w1comp =~ w1vst1 + w1vst2 + w1vst3
w2comp =~ w2vst1 + w2vst2 + w2vst3
# Variances and covariances
w2comp ~~ w1comp
w1comp ~~ w1comp
w2comp ~~ w2comp
w1vst1 ~~ w1vst1
w1vst2 ~~ w1vst2
w1vst3 ~~ w1vst3
w2vst1 ~~ w2vst1
w2vst2 ~~ w2vst2
w2vst3 ~~ w2vst3
w1vst1 ~~ w2vst1
w1vst2 ~~ w2vst2
w1vst3 ~~ w2vst3
")
<- sem(configural_model_lav, data = social_exchanges) configural_model_lav_fit
Using the semTools package.
# First, define the configural model, using the repeated measures factors and
# indicators.
<- ("
configural_model_smt # Measurement model
w1comp =~ w1vst1 + w1vst2 + w1vst3
w2comp =~ w2vst1 + w2vst2 + w2vst3
")
# Second, create a named list indicating which factors are actually the same
# latent variable measured repeatedly.
<- list(
longitudinal_factor_names comp = c("w1comp", "w2comp")
)
# Third, generate the lavaan model syntax using semTools.
<- measEq.syntax(
configural_model_smt configural.model = configural_model_smt,
longFacNames = longitudinal_factor_names,
ID.fac = "std.lv",
ID.cat = "Wu.Estabrook.2016",
data = social_exchanges
)<- as.character(configural_model_smt)
configural_model_smt
# Finally, fit the model using lavaan.
<- sem(configural_model_smt, data = social_exchanges) configural_model_smt_fit
Compare lavaan and semTools fit measures.
Configural invariance is met if the model fits well, indicators load on the same factors, and loadings are all of acceptable magnitude. An alternative way of testing longitudinal configural invariance is to fit separate confirmatory factor models at each time point; configural invariance is met if the previously stated criteria hold and the measure has the same factor structure at each time point.
fitMeasures(configural_model_lav_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 9.911 5.000 0.078 0.997 0.041
fitMeasures(configural_model_smt_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 9.911 5.000 0.078 0.997 0.041
Weak Invariance
Using the lavaan package.
<- ("
weak_model_lav # Measurement model
w1comp =~ w1vst1 + a*w1vst2 + b*w1vst3 # Factor loading equality constraint
w2comp =~ w2vst1 + a*w2vst2 + b*w2vst3 # Factor loading equality constraint
# Variances and covariances
w2comp ~~ w1comp
w1comp ~~ w1comp
w2comp ~~ w2comp
w1vst1 ~~ w1vst1
w1vst2 ~~ w1vst2
w1vst3 ~~ w1vst3
w2vst1 ~~ w2vst1
w2vst2 ~~ w2vst2
w2vst3 ~~ w2vst3
w1vst1 ~~ w2vst1
w1vst2 ~~ w2vst2
w1vst3 ~~ w2vst3
")
<- sem(weak_model_lav, social_exchanges) weak_model_lav_fit
Using the semTools package.
<- measEq.syntax(
weak_model_smt configural.model = configural_model_smt,
longFacNames = longitudinal_factor_names,
ID.fac = "std.lv",
ID.cat = "Wu.Estabrook.2016",
long.equal = c("loadings"),
data = social_exchanges
)<- as.character(weak_model_smt)
weak_model_smt
<- sem(weak_model_smt, data = social_exchanges) weak_model_smt_fit
Compare lavaan and semTools fit measures.
fitMeasures(weak_model_lav_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 12.077 7.000 0.098 0.997 0.036
fitMeasures(weak_model_smt_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 12.077 7.000 0.098 0.997 0.036
Test weak invariance.
lavTestLRT(configural_model_lav_fit, weak_model_lav_fit)
Strong Invariance
Using the lavaan package.
Equality tests of factor variances should only be conducted when all factor loadings also are constrained to be equal over time. When all non-referent loadings are set equal in the constrained model, the chi-square is the same regardless of the referent.
<- ("
strong_model_lav # Measurement model
w1comp =~ w1vst1 + a*w1vst2 + b*w1vst3 # Factor loading equality constraint
w2comp =~ w2vst1 + a*w2vst2 + b*w2vst3 # Factor loading equality constraint
# Variances and covariances
w2comp ~~ w1comp
w2comp ~~ v*w2comp # Factor variance equality constraint
w1comp ~~ v*w1comp # Factor variance equality constraint
w1vst1 ~~ w2vst1
w1vst2 ~~ w2vst2
w1vst3 ~~ w2vst3
")
<- sem(strong_model_lav, social_exchanges) strong_model_lav_fit
Using the semTools package.
# Example 2.2
<- measEq.syntax(
strong_model_smt configural.model = configural_model_smt,
longFacNames = longitudinal_factor_names,
ID.fac = "std.lv",
ID.cat = "Wu.Estabrook.2016",
long.equal = c("loadings", "lv.variances"),
data = social_exchanges
)<- as.character(strong_model_smt)
strong_model_smt
<- sem(strong_model_smt, social_exchanges) strong_model_smt_fit
Compare lavaan and semTools fit measures.
fitMeasures(strong_model_lav_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 37.553 8.000 0.000 0.983 0.080
fitMeasures(strong_model_smt_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 37.553 8.000 0.000 0.983 0.080
Test strong invariance.
lavTestLRT(configural_model_lav_fit, weak_model_lav_fit, strong_model_lav_fit)
Strict Invariance
Using the lavaan package.
<- ("
strict_model_lav # Measurement model
w1comp =~ w1vst1 + a*w1vst2 + b*w1vst3 # Factor loading equality constraint
w2comp =~ w2vst1 + a*w2vst2 + b*w2vst3 # Factor loading equality constraint
# Variances & covariances
w2comp ~~ w1comp
w1comp ~~ c*w1comp # Factor variance equality constraint
w2comp ~~ c*w2comp # Factor variance equality constraint
w1vst1 ~~ w2vst1
w1vst2 ~~ w2vst2
w1vst3 ~~ w2vst3
w1vst1 ~~ d*w1vst1 # Measurement residual equality constraint
w1vst2 ~~ e*w1vst2 # Measurement residual equality constraint
w1vst3 ~~ f*w1vst3 # Measurement residual equality constraint
w2vst1 ~~ d*w2vst1 # Measurement residual equality constraint
w2vst2 ~~ e*w2vst2 # Measurement residual equality constraint
w2vst3 ~~ f*w2vst3 # Measurement residual equality constraint
")
<- sem(strict_model_lav, social_exchanges) strict_model_lav_fit
Using the semTools package.
<- measEq.syntax(
strict_model_smt configural.model = configural_model_smt,
longFacNames = longitudinal_factor_names,
ID.fac = "std.lv",
ID.cat = "Wu.Estabrook.2016",
long.equal = c("loadings", "lv.variances", "residuals"),
data = social_exchanges
)<- as.character(strict_model_smt)
strict_model_smt
<- sem(strict_model_smt, social_exchanges) strict_model_smt_fit
Compare lavaan and semTools fit measures.
fitMeasures(strict_model_lav_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 78.779 11.000 0.000 0.961 0.104
fitMeasures(strict_model_smt_fit, c("chisq", "df", "pvalue", "cfi", "rmsea"))
#> chisq df pvalue cfi rmsea
#> 78.779 11.000 0.000 0.961 0.104
Test strict invariance.
lavTestLRT(
configural_model_lav_fit,
weak_model_lav_fit,
strong_model_lav_fit,
strict_model_lav_fit )
Michael McCarthy
Thanks for reading! I’m Michael, the voice behind Tidy Tales. I am an award winning data scientist and R programmer with the skills and experience to help you solve the problems you care about. You can learn more about me, my consulting services, and my other projects on my personal website.
Session Info
─ Session info ───────────────────────────────────────────────────────────────
setting value
version R version 4.1.1 (2021-08-10)
os macOS Mojave 10.14.6
system x86_64, darwin17.0
ui X11
language (EN)
collate en_CA.UTF-8
ctype en_CA.UTF-8
tz America/Vancouver
date 2022-11-23
pandoc 2.14.0.3 @ /Applications/RStudio.app/Contents/MacOS/pandoc/ (via rmarkdown)
quarto 1.2.269 @ /usr/local/bin/quarto
─ Packages ───────────────────────────────────────────────────────────────────
package * version date (UTC) lib source
here * 1.0.1 2020-12-13 [1] CRAN (R 4.1.0)
lavaan * 0.6-11 2022-03-31 [1] CRAN (R 4.1.2)
semTools * 0.5-5 2021-07-07 [1] CRAN (R 4.1.0)
sessioninfo * 1.2.2 2021-12-06 [1] CRAN (R 4.1.0)
[1] /Users/Michael/Library/R/4.1/library
[2] /Library/Frameworks/R.framework/Versions/4.1/Resources/library
──────────────────────────────────────────────────────────────────────────────
Data
Download the data used in this post.
Reuse
Citation
@online{mccarthy2021,
author = {Michael McCarthy},
title = {Longitudinal {Measurement} {Invariance}},
date = {2021-11-01},
url = {https://tidytales.ca/snippets/2021-11-01_longitudinal-measurement-invariance},
langid = {en}
}
Comments