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. when an actor is drawn more than once, the pairs formed
by its copies are not observed dyads, so they are set to NA in the
resampled netlet; statistics that drop missing dyads (for example, a
density computed with na.rm = TRUE) then ignore them. for
longitudinal netlets, actors are drawn by name from the pooled roster
of all periods and the same draws are used in every period (preserves
within-actor dependence); an actor absent from a period is also absent
from that period's resample. 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
# }