A latent growth curve model describes change across repeated
measurements with latent intercept and slope factors. From
semTests’ point of view, it is a continuous SEM. The usual
robust p-values therefore work, including with full-information maximum
likelihood (FIML) when a few repeated measurements have gone
missing.
We will fit a linear trajectory, check its overall fit, and then ask whether a more flexible shape fits noticeably better.
Demo.growth is complete. To exercise FIML, missingness
in the later waves t3 and t4 depends on the
first wave t1. This gives the example a reproducible
MAR-like mechanism and says nothing about a real missingness
process.
set.seed(20260718)
driver <- as.numeric(scale(growth_data$t1))
growth_data$t3[runif(nrow(growth_data)) < plogis(qlogis(.20) + .8 * driver)] <- NA
growth_data$t4[runif(nrow(growth_data)) < plogis(qlogis(.20) + .8 * driver)] <- NA
colMeans(is.na(growth_data[paste0("t", 1:4)]))
#> t1 t2 t3 t4
#> 0.000 0.000 0.195 0.190The linear model fixes the slope loadings to
0, 1, 2, 3:
linear <- growth(
"i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
s =~ 0*t1 + 1*t2 + 2*t3 + 3*t4",
growth_data,
missing = "fiml", estimator = "MLR"
)
pvalues(linear, c("SB", "SS", "ALL", "PEBA4"))
#> sb_ml ss_ml all_ml peba4_ml
#> 0.09706262 0.09900832 0.09857646 0.09741806
#> estimator: ML (MLR) (FIML) | data: continuous | information: observed | df: 5 | FIML convention: observedFIML uses the standard likelihood-ratio statistic and the observed-information convention by default. The footer records both choices, so they do not vanish when the result is copied into another script.
A latent-basis model frees the last two slope loadings and lets the
data choose the shape of change. Linear growth fixes those loadings to
2 and 3, which makes it a special case of the
latent-basis model. The linear model is the more constrained fit, so it
goes first:
basis <- growth(
"i =~ 1*t1 + 1*t2 + 1*t3 + 1*t4
s =~ 0*t1 + 1*t2 + t3 + t4",
growth_data,
missing = "fiml", estimator = "MLR"
)
pvalues_nested(linear, basis, tests = c("SB", "SS", "ALL", "PEBA2"))
#> sb_ml ss_ml all_ml peba2_ml
#> 0.5701799 0.5691468 0.5692181 0.5699399
#> estimator: ML (MLR) (FIML) | data: continuous | information: observed | df: 2 | FIML convention: observed | nested (method 2000, A.method delta)A small p-value would be evidence that the linear shape is too rigid.
A large one means this comparison found no clear reason to let the
trajectory bend. The two freed loadings give a two-degree-of-freedom
test, so we use PEBA2. The number of blocks cannot exceed
the test degrees of freedom.
The missing-data details are collected in
vignette("fiml-missing-data", package = "semTests").