Item Factor Analysis, Measurement Invariance + 2nd order Growth Model (ECLS-K)

1 Overview

In this tutorial we walk through example of testing measurement invariance and modeling change with latent variables measured by Ordinal indicators.
The example follows Chapter 15 of Growth Modeling: Structural Equation and Multilevel Modeling Approaches (Grimm, Ram & Estabrook, 2017).

Using 3-occasion data from the ECLS-K, we set up an item factor analysis model, test for factorial invariance, and then use a second-order growth model to describe change in the factor scores across time.

1.0.0.1 Prelim - Loading libraries used in this script.

library(psych)
library(ggplot2)
library(corrplot) #plotting correlation matrices
library(lavaan)  #for fitting structural equation models
library(semPlot)  #for automatically making diagrams 

1.0.0.2 Prelim - Reading in Repeated Measures Data

For this example, we use an ECLS-K dataset that is in wideform. There are variables for parent’s and teachers’ ratings of children’s of children’s behavior in the fall and spring of kindergarten and spring of first grade on interpersonal skills, self-control, and externalizing behaviors. Ratings were made using a four-point (1-4) scale where higher values indicated more of the behavior.

These behavior ratings are thought to be indicators of a higher-order child behavior factor

We only use the teacher ratings here.

#set filepath for data file
filepath <- "https://raw.githubusercontent.com/LRI-2/Data/main/GrowthModeling/ECLS_Behavior.dat"
#read in the text data file using the url() function
dat <- read.table(file=url(filepath),na.strings = ".") 

names(dat) <- c("id", 
                "p1_sc", "p1_soc", "p1_sad", "p1_imp", 
                "p2_sc", "p2_soc", "p2_sad", "p2_imp", 
                "p4_sc", "p4_soc", "p4_sad", "p4_imp", 
                "t1_sc", "t1_intp", "t1_ext", "t1_int", 
                "t2_sc", "t2_intp", "t2_ext", "t2_int", 
                "t4_sc", "t4_intp", "t4_ext", "t4_int")

#selecting only the teacher reports
dat <- dat[ ,c("id", "t1_sc", "t1_intp", "t1_ext", "t1_int", 
                "t2_sc", "t2_intp", "t2_ext", "t2_int", 
                "t4_sc", "t4_intp", "t4_ext", "t4_int")]

1.0.0.3 Prelim - Descriptives

Lets have a quick look at the data file and the descriptives.

#look at data structure
head(dat,10)
id t1_sc t1_intp t1_ext t1_int t2_sc t2_intp t2_ext t2_int t4_sc t4_intp t4_ext t4_int
1 4 4 1 2 NA NA NA NA 4 3 1 1
2 4 4 1 1 4 4 1 1 4 4 1 1
16 3 2 2 1 4 4 1 1 3 4 1 2
19 4 4 1 1 4 4 1 1 3 3 1 3
35 4 3 1 2 3 3 1 3 3 2 1 2
36 3 3 2 1 3 3 2 2 3 3 2 2
48 4 3 1 2 4 4 1 2 4 3 1 1
50 4 2 1 2 2 3 2 3 4 3 1 2
54 3 3 2 3 3 2 2 3 3 2 3 2
58 3 NA 3 2 3 2 4 3 3 3 3 2
#descriptives (means, sds)
describe(dat[,-1]) #-1 to remove the id column
vars n mean sd median trimmed mad min max range skew kurtosis se
t1_sc 1 3198 3.215135 0.6949452 3 3.273828 1.4826 1 4 3 -0.3897593 -0.6238324 0.0122889
t1_intp 2 3162 3.024668 0.7159051 3 3.040712 0.0000 1 4 3 -0.1654381 -0.6702866 0.0127314
t1_ext 3 3262 1.586143 0.6981145 1 1.476628 0.0000 1 4 3 1.0780229 0.9799469 0.0122232
t1_int 4 3231 1.545342 0.6273211 1 1.475048 0.0000 1 4 3 0.8931608 0.6133514 0.0110362
t2_sc 5 3479 3.307560 0.7001419 3 3.389228 1.4826 1 4 3 -0.5764546 -0.5477944 0.0118702
t2_intp 6 3460 3.191330 0.7131807 3 3.244220 1.4826 1 4 3 -0.3633800 -0.7522430 0.0121244
t2_ext 7 3482 1.646467 0.7069649 2 1.536253 1.4826 1 4 3 0.9315256 0.6705202 0.0119807
t2_int 8 3466 1.587998 0.6323781 2 1.520908 1.4826 1 4 3 0.7793179 0.4288969 0.0107414
t4_sc 9 3619 3.284885 0.6969259 3 3.359337 1.4826 1 4 3 -0.5016578 -0.6670892 0.0115849
t4_intp 10 3610 3.125208 0.7246991 3 3.163435 1.4826 1 4 3 -0.2816960 -0.7838474 0.0120616
t4_ext 11 3621 1.631593 0.7122732 2 1.517777 1.4826 1 4 3 0.9590041 0.6168455 0.0118367
t4_int 12 3639 1.636988 0.6445657 2 1.567800 1.4826 1 4 3 0.7215088 0.4414765 0.0106850
#correlation matrix
round(cor(dat[,-1], use = "pairwise.complete"),2)
##         t1_sc t1_intp t1_ext t1_int t2_sc t2_intp t2_ext t2_int t4_sc t4_intp
## t1_sc    1.00    0.69  -0.59  -0.25  0.55    0.48  -0.48  -0.18  0.34    0.33
## t1_intp  0.69    1.00  -0.48  -0.31  0.47    0.53  -0.39  -0.23  0.29    0.32
## t1_ext  -0.59   -0.48   1.00   0.20 -0.51   -0.45   0.64   0.14 -0.38   -0.34
## t1_int  -0.25   -0.31   0.20   1.00 -0.18   -0.23   0.15   0.47 -0.10   -0.15
## t2_sc    0.55    0.47  -0.51  -0.18  1.00    0.70  -0.63  -0.26  0.39    0.36
## t2_intp  0.48    0.53  -0.45  -0.23  0.70    1.00  -0.54  -0.31  0.34    0.36
## t2_ext  -0.48   -0.39   0.64   0.15 -0.63   -0.54   1.00   0.24 -0.41   -0.36
## t2_int  -0.18   -0.23   0.14   0.47 -0.26   -0.31   0.24   1.00 -0.13   -0.16
## t4_sc    0.34    0.29  -0.38  -0.10  0.39    0.34  -0.41  -0.13  1.00    0.72
## t4_intp  0.33    0.32  -0.34  -0.15  0.36    0.36  -0.36  -0.16  0.72    1.00
## t4_ext  -0.37   -0.30   0.46   0.09 -0.41   -0.36   0.49   0.11 -0.62   -0.55
## t4_int  -0.15   -0.16   0.12   0.20 -0.14   -0.16   0.13   0.24 -0.28   -0.32
##         t4_ext t4_int
## t1_sc    -0.37  -0.15
## t1_intp  -0.30  -0.16
## t1_ext    0.46   0.12
## t1_int    0.09   0.20
## t2_sc    -0.41  -0.14
## t2_intp  -0.36  -0.16
## t2_ext    0.49   0.13
## t2_int    0.11   0.24
## t4_sc    -0.62  -0.28
## t4_intp  -0.55  -0.32
## t4_ext    1.00   0.29
## t4_int    0.29   1.00
#visusal correlation matrix
corrplot(cor(dat[,-1], use = "pairwise.complete"), order = "original", tl.col='black', tl.cex=.75)