A standard relational event model assumes a single, fixed set of
coefficients over the entire observation period. That assumption is
often convenient rather than realistic: the strength of inertia,
reciprocity, or any other effect can strengthen, weaken, or reverse as a
network matures. remwindow() tests this by re-fitting the
same model specification repeatedly over subregions (“windows”) of the
event sequence, reusing a single remify() object and a
single remstats() computation throughout — no re-processing
of the event history or re-computation of statistics is needed per
window.
This vignette covers moving-window estimation for both tie-oriented and actor-oriented models: how windows are chosen, how to read the stability diagnostics, and how recall-based diagnostics are computed continuously across windows via interpolation.
Moving-window estimation is useful when:
As each window is an independent fit on a subset of events, the moving-window appraoch cannot be viewed as a substitute for a fully specified time-varying-coefficients model.
We use randomREH3 (provided with remverse):
999 directed events among 5 actors, together with the actor attributes
in info3. The sequence was generated in three successive
regimes with different parameter values, so it carries genuine
temporal drift — a natural showcase for moving-window estimation.
Statistics use exponential memory decay (half-life 2000).
data(randomREH3)
data(info3)
str(randomREH3)
#> 'data.frame': 999 obs. of 6 variables:
#> $ time : num 18 57 64 127 228 274 302 330 358 425 ...
#> $ actor1 : num 5 3 1 5 1 2 1 1 1 2 ...
#> $ actor2 : num 1 2 5 1 4 4 5 5 5 4 ...
#> $ end : num 24 70 85 159 239 320 314 339 362 521 ...
#> $ setting : chr "social" "social" "social" "social" ...
#> $ duration: num 6 13 21 32 11 46 12 9 4 96 ...With roughly a thousand events and only a handful of estimated
parameters, the default window sizing rule (20 events per parameter,
floored at 50 events per window) supports the default
n.windows = 5. Manual choices of the window size and number
of windows can be changed via the function’s arguments.
reh <- remify(
edgelist = randomREH3,
model = "tie",
directed = TRUE
)
stats_tie <- remstats(
reh = reh,
tie_effects = ~ inertia(scaling = "std") + reciprocity(scaling = "std") +
send("age", attr_actors = info3, scaling = "std"),
memory = "decay", memory_value = 2000
)mw_tie_auto <- remwindow(reh = reh, stats = stats_tie)
mw_tie_auto
#> Moving-window relational event model (tie)
#> windows = 5 (mode: auto )
#>
#> Coefficients:
#> W1 W2 W3 W4 W5
#> time_range 57-7131 7178-15441 15562-25568 25665-37469 37710-50676
#> n_events 198 198 199 198 199
#> converged TRUE TRUE TRUE TRUE TRUE
#> baseline -6.822 -7.062 -7.212 -7.418 -7.628
#> inertia 0.388 0.544 0.536 0.482 0.455
#> reciprocity 0.313 0.345 0.285 0.334 0.431
#> send_age -0.002 -0.005 -0.049 0.091 0.082With four estimated parameters (baseline,
inertia, reciprocity, send_age)
and roughly 200 events per window, the default target of five windows is
met directly.
summary(mw_tie_auto)
#> Moving-window relational event model (tie)
#> windows = 5
#>
#> Fit:
#> window time_range n_events converged loglik AIC BIC
#> 1 57-7131 198 TRUE -1450.533 2909.065 2922.218
#> 2 7178-15441 198 TRUE -1468.033 2944.066 2957.219
#> 3 15562-25568 199 TRUE -1530.498 3068.997 3082.170
#> 4 25665-37469 198 TRUE -1536.591 3081.183 3094.336
#> 5 37710-50676 199 TRUE -1540.689 3089.378 3102.551
#>
#> Stability (across converged, non-separated windows):
#> effect n mean sd min max mean_se sd_over_se excluded
#> baseline 5 -7.229 0.312 -7.628 -6.822 0.092 3.394 -
#> inertia 5 0.481 0.064 0.388 0.544 0.080 0.797 -
#> reciprocity 5 0.342 0.055 0.285 0.431 0.080 0.686 -
#> send_age 5 0.023 0.061 -0.049 0.091 0.078 0.775 -The Fit block reports one column per window: its
time range, event count, convergence status, and log-likelihood/AIC/BIC.
The Stability block collapses across windows per
effect: mean, standard deviation, and range of the estimate, together
with sd_over_se — the ratio of across-window spread to the
average within-window standard error. Values well above 1 suggest
genuine drift; values near or below 1 are consistent with a single
stable coefficient.
The ribbon is a 95% Wald interval around each window’s point estimate; the x-axis is the window midpoint in event-time.
Windows can also be specified directly via window.width
and step.size.window, which controls overlap explicitly
rather than relying on the non-overlapping automatic default.
mw_tie <- remwindow(
reh = reh,
stats = stats_tie,
window.width = 200,
step.size.window = 50
)
mw_tie
#> Moving-window relational event model (tie)
#> windows = 20 (mode: manual )
#>
#> Coefficients:
#> W1 W2 W3 W4 W5 W6
#> time_range 57-7232 2018-9436 3735-11352 5240-13436 7262-15708 9439-18242
#> n_events 200 200 200 200 200 200
#> converged TRUE TRUE TRUE TRUE TRUE TRUE
#> baseline -6.828 -6.879 -6.902 -7.007 -7.067 -7.116
#> inertia 0.391 0.525 0.474 0.543 0.523 0.465
#> reciprocity 0.315 0.22 0.298 0.276 0.358 0.421
#> send_age -0.008 0.002 0.091 0.044 0.019 -0.034
#> W7 W8 W9 W10 W11
#> time_range 11397-20472 13450-23288 15850-26015 18282-28770 20505-31575
#> n_events 200 200 200 200 200
#> converged TRUE TRUE TRUE TRUE TRUE
#> baseline -7.141 -7.223 -7.221 -7.247 -7.317
#> inertia 0.537 0.554 0.538 0.549 0.526
#> reciprocity 0.324 0.311 0.275 0.288 0.318
#> send_age -0.068 -0.039 -0.036 -0.017 -0.005
#> W12 W13 W14 W15 W16
#> time_range 23328-34285 26161-38157 28778-42174 31691-45324 34320-48242
#> n_events 200 200 200 200 200
#> converged TRUE TRUE TRUE TRUE TRUE
#> baseline -7.298 -7.446 -7.591 -7.615 -7.694
#> inertia 0.469 0.479 0.434 0.459 0.474
#> reciprocity 0.332 0.351 0.411 0.365 0.42
#> send_age 0.017 0.063 0.086 0.038 0.052
#> W17 W18 W19 W20
#> time_range 38166-50676 42218-50676 45346-50676 48436-50676
#> n_events 192 142 92 42
#> converged TRUE TRUE TRUE TRUE
#> baseline -7.596 -7.488 -7.474 -7.351
#> inertia 0.472 0.473 0.367 -0.172
#> reciprocity 0.403 0.387 0.522 0.894
#> send_age 0.099 0.077 0.143 0.178Because step.size.window (50) is smaller than
window.width (200), consecutive windows overlap by 150
events. The final window absorbs any remainder, so no events are
silently dropped. Because windows overlap, a single event can fall in
more than one window’s range — this is why the diagnostics in Section 5
are computed from a continuously interpolated coefficient rather than
assigning each event to one “owning” window.
summary(mw_tie)
#> Moving-window relational event model (tie)
#> windows = 20
#>
#> Fit:
#> window time_range n_events converged loglik AIC BIC
#> 1 57-7232 200 TRUE -1465.504 2939.007 2952.201
#> 2 2018-9436 200 TRUE -1467.755 2943.510 2956.704
#> 3 3735-11352 200 TRUE -1476.198 2960.396 2973.589
#> 4 5240-13436 200 TRUE -1487.847 2983.694 2996.887
#> 5 7262-15708 200 TRUE -1485.673 2979.346 2992.539
#> 6 9439-18242 200 TRUE -1492.908 2993.816 3007.010
#> 7 11397-20472 200 TRUE -1505.300 3018.600 3031.794
#> 8 13450-23288 200 TRUE -1518.054 3044.109 3057.302
#> 9 15850-26015 200 TRUE -1541.232 3090.464 3103.657
#> 10 18282-28770 200 TRUE -1550.364 3108.728 3121.921
#> 11 20505-31575 200 TRUE -1550.158 3108.316 3121.509
#> 12 23328-34285 200 TRUE -1546.219 3100.437 3113.630
#> 13 26161-38157 200 TRUE -1549.222 3106.443 3119.637
#> 14 28778-42174 200 TRUE -1550.387 3108.773 3121.966
#> 15 31691-45324 200 TRUE -1553.706 3115.413 3128.606
#> 16 34320-48242 200 TRUE -1549.608 3107.216 3120.410
#> 17 38166-50676 192 TRUE -1487.010 2982.019 2995.049
#> 18 42218-50676 142 TRUE -1094.357 2196.715 2208.538
#> 19 45346-50676 92 TRUE -709.959 1427.917 1438.005
#> 20 48436-50676 42 TRUE -330.323 668.646 675.597
#>
#> Stability (across converged, non-separated windows):
#> effect n mean sd min max mean_se sd_over_se excluded
#> baseline 20 -7.275 0.259 -7.694 -6.828 0.101 2.575 -
#> inertia 20 0.454 0.156 -0.172 0.554 0.099 1.582 -
#> reciprocity 20 0.374 0.140 0.220 0.894 0.099 1.413 -
#> send_age 20 0.035 0.064 -0.068 0.178 0.085 0.756 -Two columns matter most when a coefficient looks unstable:
n — the number of windows that
contributed to the mean/sd/min/max for that effect (can be less than the
total window count).excluded — which windows were left
out: a window is excluded from an effect’s stability summary when that
effect’s standard error there exceeds k times the median SE
across windows (default k = 10). This flags quasi-complete
separation — a coefficient running large because a pattern was rare or
absent in that specific window.A coefficient with a small sd_over_se and no exclusions
behaves like a single stable parameter. A coefficient with excluded
windows should be interpreted with those windows’ event counts and
convergence status in mind.
reh_ao <- remify(
edgelist = randomREH3,
model = "actor",
directed = TRUE
)
stats_ao <- remstats(
reh = reh_ao,
sender_effects = ~ outdegreeSender(scaling = "std"),
receiver_effects = ~ inertia(scaling = "std") + reciprocity(scaling = "std"),
memory = "decay", memory_value = 2000
)mw_actor <- remwindow(reh = reh_ao, stats = stats_ao)
mw_actor
#> Moving-window relational event model (actor)
#> windows = 5 (mode: auto )
#>
#> Sender (rate) model:
#> W1 W2 W3 W4 W5
#> time_range 57-7131 7178-15441 15562-25568 25665-37469 37710-50676
#> n_events 198 198 199 198 199
#> converged TRUE TRUE TRUE TRUE TRUE
#> baseline -5.187 -5.35 -5.531 -5.704 -5.831
#> outdegreeSender -0.049 0.088 0.062 0.094 0.298
#>
#> Receiver (choice) model:
#> W1 W2 W3 W4 W5
#> time_range 57-7131 7178-15441 15562-25568 25665-37469 37710-50676
#> n_events 198 198 199 198 199
#> converged TRUE TRUE TRUE TRUE TRUE
#> inertia 0.196 0.031 -0.129 0.158 0.094
#> reciprocity 0.646 -0.029 0.169 0.028 -0.078The printed table shows the sender (rate) and receiver (choice) submodels separately, each with its own coefficient rows, since they are two independently optimized fits per window.
summary(mw_actor)
#> Moving-window relational event model (actor)
#> windows = 5
#>
#> Fit:
#> window time_range n_events converged loglik AIC BIC
#> 1 57-7131 198 TRUE -1449.482 2906.963 2920.116
#> 2 7178-15441 198 TRUE -1530.431 3068.862 3082.015
#> 3 15562-25568 199 TRUE -1590.065 3188.131 3201.304
#> 4 25665-37469 198 TRUE -1606.715 3221.430 3234.583
#> 5 37710-50676 199 TRUE -1637.005 3282.009 3295.183
#>
#> Stability - sender (rate) model:
#> effect n mean sd min max mean_se sd_over_se excluded
#> baseline 5 -5.520 0.260 -5.831 -5.187 0.071 3.642 -
#> outdegreeSender 5 0.099 0.126 -0.049 0.298 0.079 1.597 -
#>
#> Stability - receiver (choice) model:
#> effect n mean sd min max mean_se sd_over_se excluded
#> inertia 5 0.070 0.128 -0.129 0.196 0.200 0.638 -
#> reciprocity 5 0.147 0.294 -0.078 0.646 0.199 1.475 -The sender-model panels are shown first, followed by the receiver-model panels.
An actor model need not specify a sender (rate) submodel — a
receiver-choice-only specification is valid, and
remwindow() handles the missing submodel throughout
(fitting, stability tables, plots, diagnostics all skip the absent piece
cleanly).
stats_choice_only <- remstats(
reh = reh_ao,
receiver_effects = ~ inertia(scaling = "std") + reciprocity(scaling = "std"),
memory = "decay", memory_value = 2000
)
mw_actor_choice <- remwindow(reh = reh_ao, stats = stats_choice_only)
mw_actor_choice
#> Moving-window relational event model (actor)
#> windows = 5 (mode: auto )
#>
#> Receiver (choice) model:
#> W1 W2 W3 W4 W5
#> time_range 57-7131 7178-15441 15562-25568 25665-37469 37710-50676
#> n_events 198 198 199 198 199
#> converged TRUE TRUE TRUE TRUE TRUE
#> inertia 0.196 0.031 -0.129 0.158 0.094
#> reciprocity 0.646 -0.029 0.169 0.028 -0.078Only the receiver (choice) model appears — there is no sender block, and no error is raised for its absence.
Because windows can overlap (Section 3.2), assigning each event to a
single window for recall-based diagnostics is not well defined at the
boundary. Instead, diagnostics() on a
remstimate_window object builds, for every event, a
coefficient vector obtained by linear interpolation between window
midpoints in event-time:
\[\hat{\boldsymbol\beta}(t) = \hat{\boldsymbol\beta}(t_w) + \frac{t - t_w}{t_{w+1} - t_w}\Big(\hat{\boldsymbol\beta}(t_{w+1}) - \hat{\boldsymbol\beta}(t_w)\Big), \qquad t_w \le t < t_{w+1}\]
where \(t_w\) is the midpoint time of window \(w\). Before \(t_1\) or after the last midpoint, the nearest window’s estimate is used unchanged (no extrapolation). Windows flagged as separated for a given effect (Section 3.3) are skipped as interpolation anchors for that effect only.
Recall is then computed once, continuously, over every event using this time-varying coefficient.
diag_tie <- diagnostics(mw_tie, reh, stats_tie)
diag_tie
#> Moving-window diagnostics (tie, interpolated coefficients)
#> windows = 20
#>
#> Recall:
#> mean rank = 0.749 | median rank = 0.842 | prob ratio = 1.87 | top 5% = 17.3%
plot(diag_tie)
#> Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
#> collapsing to unique 'x' valuesEach point is one observed event’s relative rank among its competing dyads at that moment, scored against the interpolated coefficient. The dashed horizontal line is the overall median rank; the solid line is a robust (median-smoothed) trend. Dotted vertical lines mark the original window boundaries.
diag_actor <- diagnostics(mw_actor, reh_ao, stats_ao)
plot(diag_actor)
#> Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
#> collapsing to unique 'x' values#> Warning in regularize.values(x, y, ties, missing(ties), na.rm = na.rm):
#> collapsing to unique 'x' values
Sender and receiver recall scatterplots are shown in turn, following the same layout as the tie-model version.
Each window is an independent fit, so windows can be estimated in
parallel via parallel::mclapply (Unix/macOS; falls back to
sequential with a message on Windows):
When parallelizing across windows, it is usually best to leave the
within-fit ncores at 1; remwindow() warns when
the product of ncores.window and a within-fit
ncores exceeds the detected cores.
durem) are not yet
supported. remwindow() accepts
tomstats/aomstats objects for tie- and
actor-oriented models only; a duration-model remstats
object raises an informative error. Windowing an active-state,
interval-based process raises separate design questions (an interval can
straddle a window boundary in a way a point event cannot) and is on the
roadmap.tomstats_sampled) are not yet supported.k = 10) is a
heuristic, not a formal test.| Choice | Argument | Notes |
|---|---|---|
| Window count (auto) | remwindow(n.windows = ...) |
Default 5; silently reduced if the event floor cannot be met |
| Window size (manual) | remwindow(window.width = ...) |
Disables auto mode |
| Window step | remwindow(step.size.window = ...) |
Defaults to window.width (non-overlapping); smaller
values overlap |
| Minimum events/window | remwindow(min.events = ...) |
Floor on auto-computed width |
| Separation threshold | summary(..., k = ...),
coef(..., k = ...) |
SE-ratio multiplier for flagging a diverged window, per effect |
| Estimation approach | remwindow(approach = ...) |
"frequentist" (default) or "Bayesian",
forwarded to remstimate() |
| Parallel windows | remwindow(parallel = TRUE, ncores.window = ...) |
Unix/macOS only |
remwindow() and its methods (print,
summary, plot, coef,
diagnostics) sit alongside the standard single-fit
pipeline: the same reh and stats objects are
reused unchanged, and only the estimation step is repeated across
subregions of the event sequence.
Meijerink-Bosman, M., Leenders, R., & Mulder, J. (2022). Dynamic relational event modeling: Testing, exploring, and applying. PLoS One, 17(8). https://doi.org/10.1371/journal.pone.0272309
Mulder, J., & Leenders, R. T. A. (2019). Modeling the evolution of interaction behavior in social networks: A dynamic relational event approach for real-time analysis. Chaos, Solitons & Fractals, 119, 73-85. https://doi.org/10.1016/j.chaos.2018.11.027