--- title: "Latent growth models with missing data" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Latent growth models with missing data} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE ) ``` ## Scope 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. ```{r setup} library("semTests") library("lavaan") growth_data <- Demo.growth ``` ## A reproducible missing-data mechanism `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. ```{r missing} 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)])) ``` ## Linear growth under FIML The linear model fixes the slope loadings to `0, 1, 2, 3`: ```{r linear} 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")) ``` FIML 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. ## Is the growth linear? 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: ```{r basis} 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")) ``` 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")`.