Skip to contents

resamples 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.

Usage

bootstrap_netlet(
  netlet,
  fn,
  n_boot = 200L,
  alpha = 0.05,
  seed = NULL,
  verbose = TRUE
)

Arguments

netlet

a netify object.

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:

metric

name of the output element (or "value" for scalar fn).

point

point estimate from fn(netlet) on the original (un-resampled) netlet.

n_valid

number of non-na bootstrap draws used for that metric.

mean

bootstrap mean.

sd

bootstrap standard deviation.

lower, upper

lower / 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.

Author

cassy dorff, shahryar minhas

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
# }