Temporal dynamics: choosing a model for ILD

tidyILD authors

2026-04-17

This article is a decision guide for temporal structure: lags in the mean, residual autocorrelation, time-varying effects, and latent dynamics. It does not replace statistical theory or domain expertise; it maps scientific questions to tidyILD entry points you already have.

Three axes before you fit anything

  1. Estimand — Associational lag effect? Stability of the outcome (lagged outcome in the mean)? Causal effect under a clear identification strategy (see MSM vignettes)? Latent smooth trajectory?
  2. Spacing — Occasions are regular-ish or irregular-ish? See ild_spacing_class(), ild_spacing(), and vignette("ild-decomposition-and-spacing", package = "tidyILD").
  3. Where dynamics live — In the fixed-effects mean (e.g. y ~ x_lag1), in residual correlation (AR1/CAR1 on within-person residuals), in a smooth over calendar/study time (ild_tvem()), or in a latent state (ild_kfas(), ild_ctsem()).

Decision flow (conceptual)

The following diagram is a navigation aid (rendered on sites that support Mermaid; otherwise read the labels as a checklist). Source in Mermaid syntax:

flowchart TD
  q1[Estimand clear?]
  spacing[Check ild_spacing_class]
  meanLag[Lags in mean: ild_lag ild_panel_lag_prepare ild_crosslag]
  residAR[Residual AR: ild_lme ar1=TRUE]
  tvem[Effect varies over time: ild_tvem]
  ct[Continuous-time latent: ild_ctsem]
  kfas[Discrete-time latent level: ild_kfas]
  q1 --> spacing
  spacing --> meanLag
  spacing --> residAR
  meanLag --> residAR
  spacing --> tvem
  spacing --> ct
  spacing --> kfas

Feature map

Question tidyILD tools Backend / notes Not a substitute for
Lagged predictor → outcome ild_lag(), ild_crosslag(), ild_panel_lag_prepare() ild_lme / ild_brms Full panel VAR / DSEM (multivariate lag system)
Lagged outcome (stability) Same lag helpers; model y ~ y_lag1 + ... carefully Mixed model Dynamic structural equation modeling software if that is the estimand
Residual serial correlation ild_lme(..., ar1 = TRUE) nlme AR1 or CAR1 Does not add lagged mean structure by itself
Effect changes over study time ild_tvem(), ild_tvem_plot() mgcv GAM Random slopes over time per person (consider ild_brms recipes)
Discrete-time latent level ild_kfas() KFAS Pooled multilevel latent model across many IDs
Continuous-time latent dynamics ild_ctsem() ctsem / Stan Quick lag regression on irregular data without CT assumptions
Compare a few fitted models ild_compare_fits() AIC/BIC where defined Likelihood-ratio tests unless models are nested and comparable
Multivariate lags / feedback (joint system) Same lag helpers; ild_crosslag() is one equation at a time Export preprocessed data; see vignette("ild-specialist-backends", package = "tidyILD") dynamite, lavaan DSEM, multivariate brms / ctsem
High-dimensional time-varying predictors (p >> n) Unpenalized ild_lme / lme4 is not designed for this Same vignette: hand off to penalized longitudinal tools PGEE and related methods

Minimal examples

library(tidyILD)
set.seed(1)
d <- ild_simulate(n_id = 12, n_obs_per = 10, seed = 1)
x <- ild_prepare(d, id = "id", time = "time")
out <- ild_crosslag(x, y, y, lag = 1L, ar1 = FALSE, warn_no_ar1 = FALSE)
#> boundary (singular) fit: see help('isSingular')
out$lag_term[, c("term", "estimate", "std_error")]
#> # A tibble: 1 × 3
#>   term   estimate std_error
#>   <chr>     <dbl>     <dbl>
#> 1 y_lag1    0.778    0.0607
x2 <- ild_center(x, y)
fit_ar <- tryCatch(
  ild_lme(y ~ y_bp + y_wp, data = x2, ar1 = TRUE, warn_no_ar1 = FALSE, warn_uncentered = FALSE),
  error = function(e) NULL
)
if (!is.null(fit_ar)) {
  print(fit_ar)
} else {
  "nlme fit skipped on this platform"
}
#> [1] "nlme fit skipped on this platform"
x3 <- ild_simulate(n_id = 10, n_obs_per = 15, seed = 2)
x3$x <- rnorm(nrow(x3))
x3 <- ild_prepare(x3, id = "id", time = "time")
tv <- ild_tvem(x3, "y", "x", k = 5, re_id = TRUE)
summary(tv)
#> 
#> Family: gaussian 
#> Link function: identity 
#> 
#> Formula:
#> y ~ s(.ild_time_num, k = 5) + s(.ild_time_num, by = x, k = 5) + 
#>     s(.ild_id, bs = "re")
#> 
#> Parametric coefficients:
#>             Estimate Std. Error t value Pr(>|t|)   
#> (Intercept)   0.9312     0.3205   2.905  0.00429 **
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> Approximate significance of smooth terms:
#>                      edf Ref.df     F p-value    
#> s(.ild_time_num)   2.514  3.025 23.05  <2e-16 ***
#> s(.ild_time_num):x 2.000  2.000  0.94   0.393    
#> s(.ild_id)         8.820  9.000 49.59  <2e-16 ***
#> ---
#> Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#> 
#> R-sq.(adj) =  0.779   Deviance explained = 79.9%
#> GCV = 0.33544  Scale est. = 0.30339   n = 150

Further reading

#> R version 4.5.3 (2026-03-11)
#> Platform: aarch64-apple-darwin20
#> Running under: macOS Sequoia 15.6
#> 
#> Matrix products: default
#> BLAS:   /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRblas.0.dylib 
#> LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.12.1
#> 
#> locale:
#> [1] C/en_US/en_US/C/en_US/en_US
#> 
#> time zone: America/New_York
#> tzcode source: internal
#> 
#> attached base packages:
#> [1] stats     graphics  grDevices utils     datasets  methods   base     
#> 
#> other attached packages:
#> [1] ggplot2_4.0.0 dplyr_1.1.4   tidyILD_0.4.0
#> 
#> loaded via a namespace (and not attached):
#>  [1] sandwich_3.1-1     sass_0.4.10        utf8_1.2.6         generics_0.1.4    
#>  [5] anytime_0.3.12     lattice_0.22-9     lme4_1.1-37        digest_0.6.37     
#>  [9] magrittr_2.0.4     evaluate_1.0.5     grid_4.5.3         timechange_0.3.0  
#> [13] RColorBrewer_1.1-3 fastmap_1.2.0      jsonlite_2.0.0     Matrix_1.7-4      
#> [17] mgcv_1.9-4         scales_1.4.0       jquerylib_0.1.4    reformulas_0.4.1  
#> [21] Rdpack_2.6.4       cli_3.6.5          rlang_1.1.6        rbibutils_2.3     
#> [25] KFAS_1.6.0         splines_4.5.3      withr_3.0.2        cachem_1.1.0      
#> [29] yaml_2.3.10        tools_4.5.3        nloptr_2.2.1       minqa_1.2.8       
#> [33] tsibble_1.2.0      boot_1.3-32        vctrs_0.6.5        R6_2.6.1          
#> [37] clubSandwich_0.6.1 zoo_1.8-14         lifecycle_1.0.4    lubridate_1.9.4   
#> [41] MASS_7.3-65        pkgconfig_2.0.3    pillar_1.11.1      bslib_0.9.0       
#> [45] gtable_0.3.6       glue_1.8.0         Rcpp_1.1.0         xfun_0.53         
#> [49] tibble_3.3.0       tidyselect_1.2.1   knitr_1.50         farver_2.1.2      
#> [53] htmltools_0.5.8.1  nlme_3.1-168       rmarkdown_2.29     labeling_0.4.3    
#> [57] compiler_4.5.3     S7_0.2.0