--- title: "9. Rolling networks" output: rmarkdown::html_vignette: toc: true bibliography: references.bib link-citations: true vignette: > %\VignetteIndexEntry{9. Rolling networks} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", message = FALSE, warning = FALSE, fig.width = 7, fig.height = 5.5 ) library(idiographic) data(srl) has_cograph <- requireNamespace("cograph", quietly = TRUE) ``` Rolling networks relax the one assumption every full-series model in this package shares: that a single within-person process holds across the whole observation window. A vector autoregression fitted to all of one person's occasions returns one temporal and one contemporaneous network and asserts, through its weak-stationarity assumption, that the same dynamics generated the first week of the protocol and the last. Rolling estimation withdraws that assertion. It slides a window of fixed length along the ordered series of a single person and refits the estimator inside each window, so stationarity is required only locally, within a window, and the sequence of window fits describes how that person's own dynamics change over the protocol. The estimand is correspondingly local and within-person. A temporal edge `from -> to` in a given window states that this person's deviation on `from` at occasion $t-1$ predicts their deviation on `to` at occasion $t$, for the occasions inside that window, holding the other lagged variables constant. A contemporaneous edge is a window-local partial correlation among the within-occasion innovations — the associations that lag-one prediction inside the window does not account for [@bringmann2013; @epskamp2018mlvar]. Nothing in either estimand refers to other people: every window is estimated from one individual's occasions, and differences between windows are within-person variation in estimated dynamics, not differences between persons. Two rolling estimators are provided. `fit_rolling_var()` refits the ordinary least-squares VAR in each window and is the unregularized, time-varying baseline: every window returns a dense temporal matrix and a dense contemporaneous matrix. `fit_rolling_graphical_var()` refits the penalized two-step estimator in each window — lasso-penalized lagged regressions, a graphical lasso on their residuals, the penalty selected per window by the extended Bayesian information criterion — so each window carries its own sparsity decision. Both are descriptive instruments: a sequence of window fits shows that local dynamics differ, not why they differ. Rolling estimation is also not a remedy for short series — each window contains fewer observations than the full series, so the window size sets a bias-variance tradeoff in which smaller windows track change more finely but estimate every network from less data. Because adjacent windows share most of their occasions, their estimates are strongly dependent, and window-size sensitivity should be reported whenever rolling networks are offered as evidence of changing dynamics. # Data and preprocessing The rolling estimators expect the same long format as their full-series counterparts: one row per person-occasion, an id column, and numeric time-varying indicators ordered within person. The bundled `srl` data hold self-regulated-learning indicators for 36 students measured over 156 occasions each; this vignette fits a single student, Grace, on five indicators. Passing `subject = "Grace"` selects her series inside the call, so the full `srl` data remain intact. The stationarity screen precedes the fit. ```{r vars-audit} vars <- c("efficacy", "value", "planning", "monitoring", "effort") preprocess(srl, vars = vars, id = "name", subject = "Grace") ``` Grace's 156 ordered occasions yield 155 complete current/lagged pairs, and no series trips a trend, high-autoregression, drift, unit-root, or zero-variance flag. A clean global screen does not settle the question rolling estimation asks: the whole-series diagnostics average over the protocol, and dynamics that shift midway can leave every global flag silent. The window fits below pose that local question directly. # Fitting the model `window_size` sets the number of occasions in each local fit, `step` sets how far the window advances, and `keep_fits = TRUE` stores each fitted window model so that `matrices()` and `plot()` can inspect a selected window afterwards. With Grace's 156 occasions, a 50-occasion window advancing by 20 gives six windows, starting at occasions 1, 21, 41, 61, 81, and 101 and ending at occasion 150; each window's networks are estimated from the 49 lagged pairs its 50 occasions provide. ```{r fit-rolling-var} rolling_ols <- fit_rolling_var( srl, vars = vars, id = "name", subject = "Grace", window_size = 50, step = 20, scale = TRUE, keep_fits = TRUE ) rolling_ols ``` The ordinary rolling fit returns six windows for Grace, each holding a dense least-squares temporal matrix and a dense contemporaneous matrix — the unregularized, time-varying baseline against which the sparse variant can be read. The graphical variant repeats the penalized estimation in every window. Sparse selection contributes sampling variability of its own — the selected edge set can change from window to window even when the underlying process does not — so a larger window of 70 occasions advancing by 25 is used to partially offset it, and a coarse penalty grid (`n_lambda = 8`) with `gamma = 0`, the plain-BIC end of the criterion, keeps the per-window selection inexpensive and less severe toward retained edges than the default `gamma = 0.5`. ```{r fit-rolling-gvar} rolling_gvar <- fit_rolling_graphical_var( srl, vars = vars, id = "name", subject = "Grace", window_size = 70, step = 25, n_lambda = 8, gamma = 0, keep_fits = TRUE ) rolling_gvar ``` The graphical rolling fit returns four windows, starting at occasions 1, 26, 51, and 76, each estimated from the 69 lagged pairs its 70 occasions provide and each carrying its own EBIC-selected sparsity decision. # Reading the output `as.data.frame()` flattens a rolling result to one row per window and edge, with the window's start and end rows carried alongside each weight; the first twelve rows cover the temporal edges of Grace's first window. ```{r rolling-table} head(as.data.frame(rolling_ols), 12) ``` Within the first window, monitoring at occasion $t-1$ predicts lower efficacy at $t$ (-0.272), planning predicts lower value (-0.265), and planning predicts higher effort (0.277). These local coefficients exceed anything the full-series least-squares fit reports for the same student — its largest temporal coefficient is 0.160 in absolute value — which is the rolling tradeoff stated numerically: a 50-occasion window can express transient local dynamics that the full series averages away, and it estimates them with correspondingly more noise. ```{r rolling-matrices} matrices(rolling_ols, fit = 1) ``` `matrices()` with `fit = 1` returns the first window's coefficient matrices: the lag-one temporal matrix, the residual covariance and precision of the innovations, and the partial correlations derived from them. The window-one contemporaneous layer is dominated by efficacy–monitoring (0.447) and monitoring–effort (0.440), close to Grace's full-series contemporaneous pattern: in this series the within-occasion structure is more stable than the temporal coefficients. ```{r rolling-gvar-table} edge_counts <- do.call(rbind, lapply(seq_along(rolling_gvar$fits), function(i) { data.frame(window = i, summary(rolling_gvar$fits[[i]])) })) edge_counts edges(rolling_gvar$fits[[1]]) matrices(rolling_gvar, fit = 1) ``` All four sparse windows retain no temporal edges, matching the full-series graphical fit for the same student. This is an executed selection result, not a missing or disabled network; with these data, windows, and criterion, the regularized temporal layer is empty. The first window's contemporaneous layer keeps efficacy–monitoring (0.247), monitoring–effort (0.264), and a small planning–effort edge (0.071) — the same three within-occasion partial correlations the full-series graphical fit selects, here at window-local magnitudes. # Visualizing the network Plotting a rolling fit with `fit = 1` draws the selected window's temporal and contemporaneous panels side by side, with edge width scaled to absolute weight and colour encoding sign. ```{r plot-rolling-var-all, eval=has_cograph} plot(rolling_ols, fit = 1) ``` The first-window OLS plot is dense, as every unregularized window is: each of the 25 possible temporal arrows and 10 possible contemporaneous edges carries some estimate, and the eye is drawn by width rather than by presence. ```{r plot-rolling-var-temporal, eval=has_cograph} plot(rolling_ols, fit = 1, layer = "temporal") ``` The temporal panel isolates the local lag-one structure — the monitoring-to-efficacy and planning-to-effort effects read from the table above. ```{r plot-rolling-var-contemporaneous, eval=has_cograph} plot(rolling_ols, fit = 1, layer = "contemporaneous") ``` The contemporaneous panel carries the efficacy–monitoring–effort core that also anchors Grace's full-series fits. ```{r plot-rolling-gvar, eval=has_cograph} plot(rolling_gvar, fit = 1, layer = "contemporaneous") ``` The sparse window keeps the same core and sets the remaining within-occasion associations to exact zeros. Redrawing this panel for each of the four windows, by varying `fit`, is the graphical form of the question rolling estimation answers: whether this person's local structure holds steady or moves over the protocol. # References