Contents

Overview

The dlvm1 (Dynamic Latent Variable Model, lag-1) is a model family for panel data where multiple subjects are measured across multiple time points. It is a multi-level structural equation model that properly decomposes variance into:

This decomposition yields three network structures:

The most common use case is the panel-GVAR model via the panelgvar() wrapper, which models the within-person contemporaneous and between-person structures as Gaussian Graphical Models (GGMs). Panel data requires a minimum of 3 waves (time points).

Note: The dlvm1 family is distinct from the var1 family. While var1() / gvar() fit a single VAR model to (possibly pooled) time-series data, the dlvm1 family properly separates within-person and between-person effects in a multi-level framework.
Computational complexity: The DLVM1 model scales in complexity with the product of the number of variables and the number of time points, because all waves are modeled simultaneously in a single SEM. As a result, models with more than roughly 10 waves of data can become computationally demanding and slow to fit. For intensive time-series data (many time points per person), consider using var1 / gvar or mlVAR instead.

The Panel-GVAR Model

The general dlvm1 model combines a measurement model with lag-1 dynamics at the latent level:

$$\boldsymbol{y}_{it} = \boldsymbol{\nu} + \boldsymbol{\Lambda}\boldsymbol{\eta}_{it} + \boldsymbol{\varepsilon}_{it}$$

Where $\boldsymbol{y}_{it}$ is the observed response vector for person $i$ at time $t$, $\boldsymbol{\Lambda}$ is the factor loading matrix, $\boldsymbol{\eta}_{it}$ is the latent variable vector, and $\boldsymbol{\varepsilon}_{it}$ is the residual vector.

The latent variables are decomposed into within-person and between-person components:

$$\boldsymbol{\eta}_{it} = \boldsymbol{\eta}_{i}^{(B)} + \boldsymbol{\eta}_{it}^{(W)}$$

The within-person component follows a VAR(1) process:

$$\boldsymbol{\eta}_{it}^{(W)} = \boldsymbol{B}\,\boldsymbol{\eta}_{i,t-1}^{(W)} + \boldsymbol{\zeta}_{it}$$

Where $\boldsymbol{B}$ is the temporal network (lag-1 regression coefficients), and $\boldsymbol{\zeta}_{it}$ is the within-person innovation vector with covariance $\boldsymbol{\Sigma}_\zeta^{(W)}$. The between-person component captures stable individual differences: $\boldsymbol{\eta}_{i}^{(B)} = \boldsymbol{\zeta}_{i}^{(B)}$ with covariance $\boldsymbol{\Sigma}_\zeta^{(B)}$.

The general DLVM1 model has five key parameter matrices:

For the panel-GVAR (the most common special case), $\boldsymbol{\Lambda} = \boldsymbol{I}$ (identity matrix), so observed variables equal latent variables, and both residual covariance matrices are fixed to zero. The within-person contemporaneous and between-person covariance structures are parameterized as GGMs, yielding three network matrices: PDC, omega_zeta_within, and omega_zeta_between.

Warning: As with var1 models, do not use getmatrix(model, "beta") directly in qgraph(). The PDC matrix is the transpose of beta after standardization. Always use getmatrix(model, "PDC") for visualization.

Within-Person vs Between-Person Decomposition

The decomposition into within-person and between-person effects is the central feature of the DLVM1 framework:

This separation is important because within-person and between-person relationships can be quite different. For example, at the between-person level, people who exercise more might also report more stress (because busy people do both), while at the within-person level, exercising on a given day might reduce stress relative to that person's baseline.

Covariance Structures

The DLVM1 model has four covariance components, each of which can be parameterized in four ways:

Type Key Matrix Equation
"cov" sigma_zeta_* $\boldsymbol{\Sigma}_\zeta$ (directly estimated)
"chol" lowertri_zeta_* $\boldsymbol{\Sigma}_\zeta = \boldsymbol{L}_\zeta\boldsymbol{L}_\zeta'$
"prec" kappa_zeta_* $\boldsymbol{\Sigma}_\zeta = \boldsymbol{K}_\zeta^{-1}$
"ggm" omega_zeta_*, delta_zeta_* $\boldsymbol{\Sigma}_\zeta = \boldsymbol{\Delta}_\zeta(\boldsymbol{I} - \boldsymbol{\Omega}_\zeta)^{-1}\boldsymbol{\Delta}_\zeta$

These parameterizations can be applied to each of the four model components via separate arguments:

Argument Component panelgvar() default
within_latent Contemporaneous within-person (innovation covariance) "ggm"
within_residual Residual within-person fixed to zero
between_latent Between-person individual differences "ggm"
between_residual Residual between-person fixed to zero

Wrapper Functions

Several convenience wrappers simplify common use cases of the general dlvm1() function:

Function $\boldsymbol{\Lambda}$ Residuals Default structure Use case
panelgvar() $\boldsymbol{I}$ 0 "ggm" Panel graphical VAR (most common for network analysis)
panelvar() $\boldsymbol{I}$ 0 "cov" Panel VAR with covariance structures
dlvm1() custom estimated flexible Full model with latent variables (requires lambda)
panellvgvar() custom estimated "ggm" Latent variable graphical VAR
Note: The older function ml_gvar() is deprecated. Use panelgvar() or dlvm1() instead.

Data Format

The DLVM1 family accepts two data formats, auto-detected from the type of the vars argument:

Wide Format

In wide format, each row in the data is one subject. Columns represent variable×time combinations. You specify a design matrix where rows = variables, columns = time points, and cell entries are column names in the data. The row names of the design matrix become variable labels in the model.

# Example design matrix for 3 variables across 4 time points:
Design <- matrix(c(
  "stress_T1", "stress_T2", "stress_T3", "stress_T4",
  "fitness_T1", "fitness_T2", "fitness_T3", "fitness_T4",
  "prod_T1", "prod_T2", "prod_T3", "prod_T4"
), nrow = 3, byrow = TRUE)
rownames(Design) <- c("stress", "fitness", "productivity")

# Fit panel graphical VAR:
mod <- panelgvar(data, vars = Design, estimator = "FIML")

Long Format

In long format, each row is one observation for one person at one time point. Specify vars as a character vector of variable names, along with idvar (subject ID) and optionally beepvar (time point indicator). The data is automatically reshaped to wide format internally.

# Long-format specification:
mod <- panelgvar(data,
                 vars = c("stress", "fitness", "productivity"),
                 idvar = "subject_id",
                 beepvar = "wave",
                 estimator = "FIML")

Standardization

Standardizing the data is recommended for numerical stability. Use the standardize argument:

# Standardize variables (recommended):
mod <- panelgvar(data, vars = ..., standardize = "z", ...)

# Or standardize per wave (useful for wide-format data):
mod <- panelgvar(data, vars = ..., standardize = "z_per_wave", ...)

The "z" option standardizes each variable globally, while "z_per_wave" standardizes each variable separately at each time point, which can be useful when means or variances differ across waves.

Example: Panel GVAR (Wide Format)

This example demonstrates the full panel-GVAR workflow using wide-format data. The data contains multiple subjects measured on several variables across multiple time points.

library("psychonetrics")
library("dplyr")
library("qgraph")

# Load data and design matrix:
# data: wide-format panel data (rows = subjects, columns = variable_time)
# design: matrix mapping variables (rows) to time points (columns)

# Fit saturated panel graphical VAR:
mod <- panelgvar(data, vars = design, estimator = "FIML")
mod <- mod %>% runmodel

# Inspect fit:
mod %>% fit

# Confidence interval plots:
CIplot(mod, "beta")
CIplot(mod, "omega_zeta_within")
CIplot(mod, "omega_zeta_between")

# Model search:
mod <- mod %>%
  prune(alpha = 0.01) %>%
  stepup(alpha = 0.05)

# Extract the three networks:
temporal <- getmatrix(mod, "PDC")
contemporaneous <- getmatrix(mod, "omega_zeta_within")
between <- getmatrix(mod, "omega_zeta_between")

# Visualize side by side:
Layout <- averageLayout(temporal, contemporaneous, between)
layout(t(1:3))

qgraph(temporal, layout = Layout, theme = "colorblind", cut = 0,
       directed = TRUE, diag = TRUE,
       title = "Temporal (within)", vsize = 12, mar = rep(5, 4),
       asize = 7)

qgraph(contemporaneous, layout = Layout, theme = "colorblind", cut = 0,
       title = "Contemporaneous (within)", vsize = 12, mar = rep(5, 4))

qgraph(between, layout = Layout, theme = "colorblind", cut = 0,
       title = "Between-person", vsize = 12, mar = rep(5, 4))

Example: Panel VAR on Smoking Data

This minimal example uses a covariance matrix from smoking behavior measured across three waves of the LISS panel study (2008–2010), demonstrating the wide-format specification with summary statistics.

library("psychonetrics")
library("dplyr")

# Covariance matrix from LISS panel (smoking across 3 waves):
smoke <- structure(c(47.2361758611759, 43.5366809116809, 41.0057465682466,
                     43.5366809116809, 57.9789886039886, 47.6992521367521,
                     41.0057465682466, 47.6992521367521, 53.0669434731935),
                   .Dim = c(3L, 3L),
                   .Dimnames = list(
                     c("smoke2008", "smoke2009", "smoke2010"),
                     c("smoke2008", "smoke2009", "smoke2010")))

# Design matrix (1 variable measured at 3 time points):
design <- matrix(rownames(smoke), 1, 3)

# Fit panel VAR model:
mod <- panelvar(vars = design, covs = smoke, nobs = 352)
mod <- mod %>% runmodel

# Evaluate fit:
mod %>% fit
mod %>% parameters

RI-CLPM: Random-Intercept Cross-Lagged Panel Models 0.16

The random-intercept cross-lagged panel model (RI-CLPM; Hamaker, Kuiper & Grasman, 2015) is a widely used panel model that, like the panel-GVAR, separates stable between-person differences (random intercepts) from within-person dynamics. Compared to the panel-VAR, the classic RI-CLPM treats wave 1 as exogenous (a free starting point rather than part of a stationary process) and leaves parameters free to vary per wave.

ri_clpm() fits this model using the same data input as the rest of the dlvm1 family: wide-format data with a design matrix, or long-format data via idvar (auto-detected). A standardize argument ("z", "z_per_wave", "quantile") is available, and an independence baseline is built so CFI/TLI are available.

library("psychonetrics")
library("dplyr")

# Design matrix: rows = variables, columns = waves
# (entries are column names of the wide data)
design <- matrix(colnames(Data), nVar, nWave)

# Fit the RI-CLPM:
mod <- ri_clpm(Data, vars = design) %>% runmodel
mod %>% fit

# Temporal (cross-lagged) effects, wave 2 regressed on wave 1:
getmatrix(mod, "beta")[4:6, 1:3]

# Random-intercept covariance block (the last nVar latents):
getmatrix(mod, "sigma_zeta")[13:15, 13:15]

Stationarity search with ri_clpm_search()

Because the unconstrained RI-CLPM is generously parameterized, a natural question is which parameters can be constrained equal over waves (stationarity). ri_clpm_search() tests this sequentially: it adds stationarity of intercepts, temporal effects, contemporaneous relations, and innovation variances one at a time, and finally the panel-VAR model (wave 1 endogenous), retaining each constraint only if it does not worsen fit according to criterion (BIC, AIC, or a chi-square difference test):

search <- ri_clpm_search(mod, criterion = "BIC")
search
Output (4-wave simulated stationary data; see the full example)
RI-CLPM stationarity search Criterion: BIC model constraint DF AIC BIC Chisq p_diff decision base (none) 21 18346.63 18637.43 24.78 start intercepts intercepts 30 18332.83 18585.71 28.99 0.897 accept temporal temporal 48 18315.12 18492.14 47.28 0.437 accept contemporaneous contemporaneous 54 18307.42 18459.15 51.58 0.636 accept innovation innovation 60 18300.87 18427.31 57.03 0.487 accept panelVAR wave1_endogenous 66 18305.19 18406.34 73.34 0.012 accept Selected model: panelVAR

The selected fitted model is available as search@selected. A fully reproducible simulated example is on the examples page.

Performance tip: RI-CLPM models are fit as structural equation models over all waves simultaneously, so fitting slows down considerably with five or more waves. With many waves, consider the more parsimonious panelgvar().

Relationship to Other Approaches

vs var1 / gvar (Single-Subject Models)

The var1 family fits a single VAR model to time-series data from one subject (or pooled across subjects). When using var1() with idvar, subject data is simply concatenated — no between-person effects are estimated. The dlvm1 family properly separates within-person and between-person variance in a multi-level framework.

vs mlVAR

The mlVAR package estimates multi-level VAR models with random effects, yielding person-specific networks. The panel-GVAR instead estimates fixed-effect networks with random intercepts (means). Key differences:

vs RI-CLPM (Random-Intercept Cross-Lagged Panel Model)

The panel-GVAR is closely related to the random-intercept cross-lagged panel model (RI-CLPM), which is directly available in psychonetrics via ri_clpm(). Key differences: the panel-GVAR treats the first wave as endogenous (part of the stationary process, not as an exogenous starting point), constrains parameters to be equal over time (stationarity), and models the within-person contemporaneous and between-person structures as GGM networks (which is generally more parsimonious). ri_clpm_search() bridges the two by testing these constraints one at a time.

Summary

The dlvm1 family provides multi-level dynamic models for panel data in psychonetrics. Key takeaways:

Troubleshooting

Further Reading