-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy path26-LongitudinalCFA.Rmd
400 lines (331 loc) · 14.2 KB
/
26-LongitudinalCFA.Rmd
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
# Longitudinal Factor Analysis {#ch26}
With longitudinal factor analysis, a factor model is fitted to a set of indicators of which responses are collected at different time points. It may be interesting to compare factor means and variance across time.
Fitting a longitudinal factor model is not very different from fitting a factor model with means, except that in a longitudinal factor model there are covariances between the residual factors of the same indicator at different time points. Figure 1 depicts a one-factor model of “Self-efficacy” with six indicators, measured at two different time points. Because each indicator is measured twice in the same individuals, the item specific components of each indicator covary across timepoints.
```{r label="fig26-1", echo = FALSE, out.width = "80%", fig.align = "center", fig.cap="Self-efficacy measured at two time points." }
knitr::include_graphics("images/Ch26_self-efficacy.png")
```
In order to compare latent variances or latent means over across timepoints, the measurement of the latent variables should be equal over time. To test longitudinal invariance, we can use similar steps as when testing measurement invariance across groups:
1. Configural invariance. Fit the same factor model to all time points (with residual covariances).
2. Weak factorial invariance. Fit a model with equality constraints on the factor loadings across time, and free factor variances at the later time points.
3. Strong factorial invariance. Fit a model with additional equality constraints on the intercepts across time, and free factor means at later time points.
4. Strict factorial invariance. Fit a model with additional equality constraints on the residual factor variances across time points.
The last step (strict factorial invariance) is not required for a valid comparison of common factor means, variances, or covariances across occasions, but it is required (along with equality of common-factor variances over time) to test the assumption that the observed indicators are measured with the same reliability across time points.
## Step 1: A longitudinal factor model with configural invariance
[Script 26.1](#script-26.1) fits the model from Figure 1 to data from Hornstra, van der Veen, Peetsma and Volman (2013), without equality constraints across time.
### Script 26.1 {-}
```
## observed data
obsnames <- c("t1v1","t1v2","t1v3","t1v4","t1v5","t1v6",
"t2v1","t2v2","t2v3","t2v4","t2v5","t2v6")
values <- c(
0.5237,
0.2387, 0.6653,
0.0606, 0.0576, 0.6169,
0.1260, 0.1640, 0.1800, 0.6467,
0.1489, 0.2452, 0.0621, 0.1665, 0.6151,
0.2383, 0.3781, 0.1109, 0.2248, 0.3143, 0.7241,
0.2691, 0.1920, 0.0778, 0.1296, 0.1445, 0.2599, 0.6678,
0.1686, 0.2949, 0.0952, 0.1548, 0.2139, 0.3171, 0.3080, 0.7102,
0.0881, 0.0667, 0.1673, 0.0872, 0.0719, 0.0852, 0.1681, 0.1604, 0.6583,
0.1473, 0.1594, 0.0795, 0.2021, 0.1393, 0.1769, 0.2639, 0.2910, 0.1445, 0.6218,
0.1744, 0.2025, 0.0899, 0.1621, 0.2381, 0.2736, 0.2792, 0.3640, 0.1411, 0.3163, 0.7072,
0.2192, 0.2630, 0.1325, 0.1730, 0.2348, 0.3630, 0.3613, 0.4471, 0.1884, 0.2894, 0.4354, 0.7768)
hornstracov <- getCov(x=values, names = obsnames)
hornstrameans <- c(3.3640, 3.4138, 3.9444, 3.8544, 3.8391, 3.1992, 3.4674, 3.4693, 4.0057, 3.9119, 3.8218, 3.2778)
names(hornstrameans) <- obsnames
## STEP 1: define longitudinal model without equality constraints
hornstramodel1 <- '
# COVARIANCES
# regression equations
selfeff1 =~ t1v1 + t1v2 + t1v3 + t1v4 + t1v5 + t1v6
selfeff2 =~ t2v1 + t2v2 + t2v3 + t2v4 + t2v5 + t2v6
# residual variances
t1v1 ~~ t1v1
t1v2 ~~ t1v2
t1v3 ~~ t1v3
t1v4 ~~ t1v4
t1v5 ~~ t1v5
t1v6 ~~ t1v6
t2v1 ~~ t2v1
t2v2 ~~ t2v2
t2v3 ~~ t2v3
t2v4 ~~ t2v4
t2v5 ~~ t2v5
t2v6 ~~ t2v6
# residual covariances
t1v1 ~~ t2v1
t1v2 ~~ t2v2
t1v3 ~~ t2v3
t1v4 ~~ t2v4
t1v5 ~~ t2v5
t1v6 ~~ t2v6
# common factor (co)variances
selfeff1 ~~ 1*selfeff1
selfeff2 ~~ 1*selfeff2
selfeff1 ~~ selfeff2
# MEANS
# intercepts observed indicators
t1v1 ~ 1
t1v2 ~ 1
t1v3 ~ 1
t1v4 ~ 1
t1v5 ~ 1
t1v6 ~ 1
t2v1 ~ 1
t2v2 ~ 1
t2v3 ~ 1
t2v4 ~ 1
t2v5 ~ 1
t2v6 ~ 1
# means common factors
selfeff1 ~ 0*1
selfeff2 ~ 0*1
'
## run the model
hornstramodel1Out <- lavaan(hornstramodel1, sample.cov = hornstracov,
sample.mean = hornstrameans, sample.nobs = 522,
likelihood = "normal", fixed.x = FALSE)
## output
summary(hornstramodel1Out)
```
Actually, the longitudinal factor model is an ordinary factor model (with mean structure). Differences are present in the $\mathbfΘ$ matrix, where we have now specified residual factor covariances between the residual factors of the same indicators across time.
## Step 2: A longitudinal model with weak factorial invariance.
[Script 26.2](#script-26.2) shows how to fit the model from Figure 1 to the data of Hornstra et al. with equality constraints on the factor loadings across time, and free to be estimated factor variances at the later time points.
### Script 26.2 {-}
```
## STEP 2
## constrain factor loadings to be equal across occasions
hornstramodel2 <- '
# COVARIANCES
# regression equations
selfeff1 =~ L1*t1v1 + L2*t1v2 + L3*t1v3 + L4*t1v4 +
L5*t1v5 + L6*t1v6
selfeff2 =~ L1*t2v1 + L2*t2v2 + L3*t2v3 + L4*t2v4 +
L5*t2v5 + L6*t2v6
# residual variances
t1v1 ~~ t1v1
t1v2 ~~ t1v2
t1v3 ~~ t1v3
t1v4 ~~ t1v4
t1v5 ~~ t1v5
t1v6 ~~ t1v6
t2v1 ~~ t2v1
t2v2 ~~ t2v2
t2v3 ~~ t2v3
t2v4 ~~ t2v4
t2v5 ~~ t2v5
t2v6 ~~ t2v6
# residual covariances
t1v1 ~~ t2v1
t1v2 ~~ t2v2
t1v3 ~~ t2v3
t1v4 ~~ t2v4
t1v5 ~~ t2v5
t1v6 ~~ t2v6
# common factor (co)variances
selfeff1 ~~ 1*selfeff1
selfeff2 ~~ selfeff2
selfeff1 ~~ selfeff2
# MEANS
# intercepts observed indicators
t1v1 ~ 1
t1v2 ~ 1
t1v3 ~ 1
t1v4 ~ 1
t1v5 ~ 1
t1v6 ~ 1
t2v1 ~ 1
t2v2 ~ 1
t2v3 ~ 1
t2v4 ~ 1
t2v5 ~ 1
t2v6 ~ 1
# means common factors
selfeff1 ~ 0*1
selfeff2 ~ 0*1
'
## run model
hornstramodel2Out <- lavaan(hornstramodel2, sample.cov = hornstracov,
sample.mean = hornstrameans, sample.nobs = 522,
likelihood = "normal", fixed.x = FALSE)
## output
summary(hornstramodel2Out)
## compare fit with configural model
anova(hornstramodel1Out,hornstramodel2Out)
```
We constrain the factor loadings of the same indicator to be equal across time points, by using identical labels for the factor loadings. Because with this equality constraint the variance of the common factor of the second measurement is already identified, we can freely estimate this parameter (do not forget to release the constraint!).
The fit of this model can be compared to the model fit of the model without any across occasion constraints to test whether the equality constraints on the factor loadings lead to a (significant) deterioration in model fit. When the deterioration in fit is not significant, we may conclude that the assumption of weak factorial invariance holds.
## Step 3: Longitudinal model with strong factorial invariance
[Script 26.3](#script-26.3) shows how to fit the model with additional equality constraints on the intercepts across time, and free to be estimated factor means at later time points.
### Script 26.3 {-}
```
## STEP 3
## Constrain intercepts to be equal across occasions
hornstramodel3 <- '
# COVARIANCES
# regression equations
selfeff1 =~ L1*t1v1 + L2*t1v2 + L3*t1v3 + L4*t1v4 +
L5*t1v5 + L6*t1v6
selfeff2 =~ L1*t2v1 + L2*t2v2 + L3*t2v3 + L4*t2v4 +
L5*t2v5 + L6*t2v6
# residual variances
t1v1 ~~ t1v1
t1v2 ~~ t1v2
t1v3 ~~ t1v3
t1v4 ~~ t1v4
t1v5 ~~ t1v5
t1v6 ~~ t1v6
t2v1 ~~ t2v1
t2v2 ~~ t2v2
t2v3 ~~ t2v3
t2v4 ~~ t2v4
t2v5 ~~ t2v5
t2v6 ~~ t2v6
# residual covariances
t1v1 ~~ t2v1
t1v2 ~~ t2v2
t1v3 ~~ t2v3
t1v4 ~~ t2v4
t1v5 ~~ t2v5
t1v6 ~~ t2v6
# common factor (co)variances
selfeff1 ~~ 1*selfeff1
selfeff2 ~~ selfeff2
selfeff1 ~~ selfeff2
# MEANS
# intercepts observed indicators
t1v1 ~ T1*1
t1v2 ~ T2*1
t1v3 ~ T3*1
t1v4 ~ T4*1
t1v5 ~ T5*1
t1v6 ~ T6*1
t2v1 ~ T1*1
t2v2 ~ T2*1
t2v3 ~ T3*1
t2v4 ~ T4*1
t2v5 ~ T5*1
t2v6 ~ T6*1
# means common factors
selfeff1 ~ 0*1
selfeff2 ~ 1
'
## run model
hornstramodel3Out <- lavaan(hornstramodel3, sample.cov = hornstracov,
sample.mean = hornstrameans, sample.nobs = 522,
likelihood = "normal", fixed.x = FALSE)
## output
summary(hornstramodel3Out)
## compare fit with weak factorial invariance model
anova(hornstramodel2Out,hornstramodel3Out)
```
Again, we use labels to constrain the intercepts to be equal across occasions. This allows us to freely estimate the common factor mean of self-efficacy at the second time point. To test whether the assumption of strong factorial invariance holds, we can compare the model fit of this model to the model fit of the model without equality constraints imposed on the intercepts.
## Step 4: A model with strict factorial invariance
Sometimes, additional equality constraints are imposed on the residual factor variances across time. This is also referred to as ‘strict factorial invariance’. We can use labels to impose the equality constraints, similar like we did with the intercepts. [Script 26.4](#script-26.4) shows how to fit this model.
### Script 26.4 {-}
```
## STEP 4: Additional constraints on THETA
hornstramodel4 <- '
# COVARIANCES
# regression equations
selfeff1 =~ L1*t1v1 + L2*t1v2 + L3*t1v3 + L4*t1v4 +
L5*t1v5 + L6*t1v6
selfeff2 =~ L1*t2v1 + L2*t2v2 + L3*t2v3 + L4*t2v4 +
L5*t2v5 + L6*t2v6
# residual variances
t1v1 ~~ TH1*t1v1
t1v2 ~~ TH2*t1v2
t1v3 ~~ TH3*t1v3
t1v4 ~~ TH4*t1v4
t1v5 ~~ TH5*t1v5
t1v6 ~~ TH6*t1v6
t2v1 ~~ TH1*t2v1
t2v2 ~~ TH2*t2v2
t2v3 ~~ TH3*t2v3
t2v4 ~~ TH4*t2v4
t2v5 ~~ TH5*t2v5
t2v6 ~~ TH6*t2v6
# residual covariances
t1v1 ~~ t2v1
t1v2 ~~ t2v2
t1v3 ~~ t2v3
t1v4 ~~ t2v4
t1v5 ~~ t2v5
t1v6 ~~ t2v6
# common factor (co)variances
selfeff1 ~~ 1*selfeff1
selfeff2 ~~ selfeff2
selfeff1 ~~ selfeff2
# MEANS
# intercepts observed indicators
t1v1 ~ T1*1
t1v2 ~ T2*1
t1v3 ~ T3*1
t1v4 ~ T4*1
t1v5 ~ T5*1
t1v6 ~ T6*1
t2v1 ~ T1*1
t2v2 ~ T2*1
t2v3 ~ T3*1
t2v4 ~ T4*1
t2v5 ~ T5*1
t2v6 ~ T6*1
# means common factors
selfeff1 ~ 0*1
selfeff2 ~ 1
'
## run model
hornstramodel4Out <- lavaan(hornstramodel4, sample.cov = hornstracov,
sample.mean = hornstrameans, sample.nobs = 522,
sample.cov.rescale = FALSE, fixed.x = FALSE)
## output
summary(hornstramodel4Out)
## compare fit with strong factorial invariance model
anova(hornstramodel3Out,hornstramodel4Out)
```
## Standardized estimates for a (longitudinal) factor model with means
Standardizing a longitudinal factor model works the same as standardizing a normal factor model, only now we also have a mean structure which can be standardized. To extract the estimates in matrix form from the `lavaan` output, including the estimates for the intercepts of the observed indicators and the common factor means, we use the `lavInspect()` function.
[Script 26.5](#script-26.5) has the code that uses matrix algebra to obtain the standardized parameter estimates.
### Script 26.5 {-}
```
factornames <- c("selfeff1","selfeff2")
Estimates <- lavInspect(hornstramodel3Out, "est")
LAMBDA <- Estimates$lambda[obsnames, factornames]
PHI <- Estimates$psi[factornames, factornames]
THETA <- Estimates$theta[obsnames, obsnames]
TAU <- Estimates$nu[obsnames,1,drop=FALSE]
KAPPA <- Estimates$alpha[factornames,1,drop=FALSE]
# Model implied covariance matrix (SIGMA) and mean vector (MU)
SIGMA <- LAMBDA %*% PHI %*% t(LAMBDA) + THETA
MU <- TAU + LAMBDA %*% KAPPA
## calculate standard deviations sigma and phi
SDSIGMA <- sqrt(diag(diag(SIGMA)))
SDPHI <- sqrt(diag(diag(PHI)))
## calculate standardized parameters
LAMBDAstar <- solve(SDSIGMA) %*% LAMBDA %*% SDPHI
PHIstar <- solve(SDPHI) %*% PHI %*% solve(SDPHI)
THETAstar <- solve(SDSIGMA) %*% THETA %*% solve(SDSIGMA)
STRUC <- LAMBDAstar %*% PHIstar
KAPPAstar <- KAPPA * diag(solve(SDPHI))
TAUstar <- TAU * diag(solve(SDSIGMA))
## provide labels
dimnames(LAMBDAstar) <- list(obsnames,factornames)
dimnames(PHIstar) <- list(factornames,factornames)
dimnames(THETAstar) <- list(obsnames,obsnames)
dimnames(STRUC) <- list(obsnames,factornames)
dimnames(KAPPAstar) <- list(factornames,"Common factor means")
dimnames(TAUstar) <- list(obsnames,"Intercepts")
```
The intercepts (`TAU`) and factor means (`KAPPA`) are part of respectively the ‘mu’ ($\mathbf\mu$) and ‘alpha’($\mathbf\alpha$) matrices in `lavaan.` We extract them by selecting the rows that correspond to the observed variable names for `TAU` and with the factor names for `KAPPA.` The ‘`1`’ specifies that we want to select the first column, and `drop = FALSE` is needed to ensure that the result is stored in a column vector.
```
TAU <- Estimates$nu[obsnames,1,drop=FALSE]
KAPPA <- Estimates$alpha[factornames,1,drop=FALSE]
```
The common factor means are standardized by multiplying the column vector with the factor means with a vector with the inverse of the factor standard deviations. As the standard deviations are on the diagonal of `SDPHI`, we use the `diag()` function to extract the diagonal elements. By using simple multiplication `*` (as opposed to matrix multiplication) the first elements of the two object are multiplied with each other, the second elements with each other, and so on. Intercepts are standardized in the same way as factor means, but with respect to the standard deviations of the observed variables:
```
KAPPAstar <- KAPPA * diag(solve(SDPHI))
TAUstar <- TAU * diag(solve(SDSIGMA))
```
Alternatively, you can specify “`standardized = TRUE`” in the `summary()` function.
## References {-}
Hornstra, L., van der Veen, I., Peetsma T. & Volman M. (2013). Developments in motivation and achievement during primary school: A longitudinal study on group-specific differences. *Learning and Individual Differences, 23*, 195-204.