The redeem package provides tools for the scalable
estimation of continuous-time network models. While its primary focus is
on models that explicitly incorporate duration (Durational Event
Models), the framework natively supports standard Relational
Event Models (REM) for point-process interaction data via the
rem() function. This vignette illustrates how to estimate
REMs, interpret the results, and evaluate model fit using simulated
data.
A standard Relational Event Model (REM) conceptualizes social interactions as instantaneous events in continuous time. It models a stream of interactions between actors without associated durations (e.g., sending an email, posting a tweet, or a discrete behavioral action).
The generating mechanism of a REM is a multidimensional point process, where each potential directed dyad \((i,j)\) has a distinct, continuous-time intensity (or rate) of interaction: \[\lambda_{ij}(\mathscr{H}_t, \beta, \alpha, \gamma) = \exp(s_{ij}(\mathscr{H}_t)^\top \beta + \alpha_i + \alpha_j + f(t, \gamma))\]
where:
The package implements several key statistics to capture network
dynamics. For full mathematical definitions and descriptions of the
available transformations (e.g., log, sig,
bin), please refer to the redeem_terms
documentation.
When specifying a REM, it is crucial to balance structural network statistics with actor-specific heterogeneity features:
Let’s simulate a basic relational event stream and fit a scalable REM.
For a standard REM, the event sequence is represented as a matrix
with three main columns: time, from, and
to. (If a type column is present, standard REM
treats all interactions as instantaneous events of type 1).
We estimate the REM using the rem() function. The model
formula is specified using the model terms documented in
redeem_terms. In this simple example, we fit an
intercept-only model. Complex combinations of structural network
statistics and explicit node parameters can be mapped analogously.
# Fit the Relational Event Model
fit_rem <- rem(
events = events,
n_nodes = n_nodes,
formula = ~1,
control = control.redeem(estimate = "Blockwise")
)
# View summaries using `summary.redeem_result`
summary(fit_rem)
#> Call:
#> rem(events = events, formula = ~1, n_nodes = n_nodes, control = control.redeem(estimate = "Blockwise"))
#>
#> Fixed Effects:
#> Estimate Std. Error t value Pr(>|t|)
#> Intercept -3.40120 0.40825 -8.3312 < 2.2e-16 ***
#> ---
#> Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
#>
#> Log-likelihood: -26.407
#>
#> Estimation time: 0.002043009 secsThe estimated coefficients \(\beta\) represent how the covariates influence the incidence of instantaneous events. A positive coefficient indicates that higher values of the corresponding statistic structurally increase the rate at which \((i, j)\) interacts.
Ensuring your estimated REM accurately reflects the observed data involves rigorous simulation and residual checking tests against continuous network evolution.
The redeem package enables generating brand-new
event networks using the estimated parameters. Utilizing the
simulate() method on the fitted object:
# Simulate networks from the fitted REM
simulated_events <- simulate(fit_rem, nsim = 100, time_horizon = 10)Comparing networks generated from this simulation against your observed data provides a holistic check. If macroscopic patterns (e.g., degree distribution, inter-arrival times, sequence clustering) match comprehensively, the model exhibits good structural and generating fit.
To statistically diagnose the fit at the dyad level, you can assess the model’s unobserved error using Cox-Snell residuals.
The concept relies on the property that if the specified intensity \(\lambda_{ij}(t)\) is the true generating model, then the integrated cumulative intensity computed up to the precise time of an observed dyadic event will behave like a standard exponential random variable \(Exp(1)\).
The redeem package provides the
get_residuals() function to automate this check. It
calculates the cumulative intensities for all dyads and returns
Kaplan-Meier estimates of the residual survival function alongside the
theoretical \(Exp(1)\) curve.
# Extract residuals for diagnostics using `get_residuals()`
# Note: Ensure return_data = TRUE was set in `control.redeem()`
resids <- get_residuals(fit_rem)
# Plot the Kaplan-Meier estimate of the residual survival vs. Theoretical Exp(1)
plot(resids$time, resids$surv, type = "l", log = "y",
xlab = "Cox-Snell Residuals", ylab = "Survival Probability",
main = "Cox-Snell Residual Diagnostic")
lines(resids$time, resids$theoretical, col = "red", lty = 2)
legend("topright", legend = c("Empirical", "Theoretical Exp(1)"),
col = c("black", "red"), lty = 1:2)If the model is accurately specified, the empirical survival curve (black line) should closely follow the theoretical exponential decay (red dashed line). Significant deviations, especially in the tails, suggest that the model fails to capture certain temporal dynamics or that there is unmodeled heterogeneity among dyads.