Bootstrap any user-supplied function of a netify object
Source:R/bootstrap_netlet.R
bootstrap_netlet.Rdresamples actors with replacement (snijders & borgatti 1999 vertex
bootstrap) per panel, rebuilds the netlet on each draw, applies
the user-supplied fn, and returns the per-draw values plus
percentile confidence intervals.
Arguments
- netlet
a
netifyobject.- fn
function. takes a netlet, returns a single numeric value or a named numeric vector. called once per bootstrap draw.
- n_boot
integer. number of bootstrap replicates (default
200).- alpha
numeric in (0, 1). two-sided percentile-ci alpha (default
0.05-> 95% intervals).- seed
optional integer. if supplied, sets a local rng seed and restores the user's global stream afterward. if
NULL, bootstrap draws use and advance the current rng stream normally.- verbose
logical. if
TRUE(default), print a progress ticker every 50 draws.
Value
a data.frame with one row per element of fn(netlet)
and columns:
metricname of the output element (or
"value"for scalar fn).pointpoint estimate from
fn(netlet)on the original (un-resampled) netlet.n_validnumber of non-
nabootstrap draws used for that metric.meanbootstrap mean.
sdbootstrap standard deviation.
lower,upperlower / upper percentile ci bounds.
the full per-draw matrix is stashed as
attr(out, "bootstrap_draws") for callers who want the empirical
distribution (e.g., for kernel-density plots).
Details
this is the general bootstrap interface for any scalar or named numeric
vector summary of a netify. it is useful for homophily, mixing-matrix,
centrality, and downstream fit summaries (e.g., the coefficient of a
to_igraph -> igraph::cluster_* -> modularity pipeline).
resampling is vertex-level, not edge-level: actors are
sampled with replacement and the netlet's adjacency is sliced
accordingly. for longitudinal netlets, the same resampled index
set is applied to every period (preserves within-actor
dependence). multilayer netlets are unsupported – bootstrap each
layer independently via subset_netify(layers = ...) first.
the resampled netlets contain the resampled adjacency only. statistics
that require attached nodal or dyadic attributes should recreate those
attributes inside fn, or use a NULL-model workflow such as
compare_to_null(..., model = "dyad_permutation") when the goal is an
attribute-aware randomization.
parallel execution
bootstrap_netlet runs serially. when n_boot > 50 and the netlet
is large enough that each draw is non-trivial (rule of thumb:
n > ~1000 with the default centrality-heavy fn), parallelism
helps. there is no built-in parallel = argument; instead drive
the loop yourself with future.apply::future_lapply(), which
respects whatever future::plan() the caller set:
library(future); library(future.apply)
plan(multisession) # or multicore on linux/macos
draws <- future_lapply(1:n_boot, function(b) {
idx <- sample(actors, length(actors), replace = TRUE)
fn(subset_netify(net, actors = idx))
}, future.seed = TRUE)then summarize draws exactly as bootstrap_netlet does
internally. wrapping the inner body of bootstrap_netlet in a
future-aware loop is a small refactor and is the recommended path
for ~15k-node weekly snapshots where the serial pass would tie up
a single core for hours.
Examples
# \donttest{
data(icews)
net <- netify(icews[icews$year == 2010, ],
actor1 = "i", actor2 = "j", symmetric = FALSE, weight = "verbCoop")
# bootstrap a custom scalar: mean(closeness)
my_stat <- function(net) {
sa <- summary_actor(net, stats = "all")
c(mean_closeness = mean(sa$closeness_all, na.rm = TRUE))
}
boot_out <- bootstrap_netlet(net, fn = my_stat, n_boot = 100, seed = 1)
#> ℹ bootstrap draw 50/100
#> ℹ bootstrap draw 100/100
boot_out
#> metric point n_valid mean sd lower upper
#> 1 mean_closeness 55.643 100 42.11779 9.549061 25.09793 61.50988
# }