| Title: | Tracking Communities in Changing Temporal Networks |
|---|---|
| Description: | Tools for temporal networks that change over time, where nodes enter and leave, groups form, split, merge and dissolve, and periods differ in size. The temporal fitters track nodes and communities across periods. A community keeps one identifier while it persists, and a newly formed group receives a new one. Two-stage fitters run Louvain or Leiden community detection in each period and link communities in linked periods by Jaccard similarity, the overlap coefficient or node-strength weighted variants. The 'layer_links' argument sets which periods are directly linked, adjacent periods only by default. By default all periods are fitted jointly, so later periods can change earlier labels, and tracking = 'filtered' prevents this. Multislice modularity (Mucha et al. 2010) <doi:10.1126/science.1184819> and Hungarian matching are included as comparison methods, and a bootstrap stability score helps choose between the two-stage and multislice approaches. |
| Authors: | Jared Edgerton [aut, cre] |
| Maintainer: | Jared Edgerton <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 1.4.0 |
| Built: | 2026-10-15 01:00:40 UTC |
| Source: | https://github.com/cran/dynamicmultiplex |
Creates one frame per layer and colors nodes by community using
colorblind-friendly ColorBrewer palettes (Set2 or Dark2).
animate_multilayer_gif( layers, fit = NULL, community_memberships = NULL, output_file, directed = FALSE, fps = 2, width = 800, height = 600, layout = "layout_with_fr", palette = "Dark2" )animate_multilayer_gif( layers, fit = NULL, community_memberships = NULL, output_file, directed = FALSE, fps = 2, width = 800, height = 600, layout = "layout_with_fr", palette = "Dark2" )
layers |
List of |
fit |
Optional fit object from any |
community_memberships |
Optional list of membership vectors
(one per layer). Ignored when |
output_file |
Output GIF file path. Required; the function writes the
animation to this location and nowhere else. Use
|
directed |
Logical; if |
fps |
Frames per second. |
width |
Width of GIF in pixels. |
height |
Height of GIF in pixels. |
layout |
|
palette |
ColorBrewer qualitative palette, one of |
The path to the created GIF.
if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("gganimate", quietly = TRUE) && requireNamespace("gifski", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") gif_path <- animate_multilayer_gif( layers, fit = fit, output_file = tempfile(fileext = ".gif"), fps = 2, width = 200, height = 150 ) }if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("gganimate", quietly = TRUE) && requireNamespace("gifski", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") gif_path <- animate_multilayer_gif( layers, fit = fit, output_file = tempfile(fileext = ".gif"), fps = 2, width = 200, height = 150 ) }
Quantifies the uncertainty of multilayer community detection
by refitting communities on n_boot resampled networks, using a
parametric network bootstrap: within- and between-community edge
probabilities (and, for weighted networks, edge-weight pools) are
estimated from the observed network using the point-estimate partition,
and each replicate redraws the full edge set from those estimates. This
reproduces the variability of fresh data, including which edges exist.
Since 1.4.0 the package uses the bootstrap only for the stability score
reported by partition_stability, which compares fits
of the same network. The interval outputs of earlier versions
(co_assignment_ci(), community_est(), the calibrated accuracy
floor) were removed because their coverage was not reliable.
Agreement is measured on the cross-layer meta-communities (the tracked partition from the second-stage detection), not the independently-detected per-layer communities. Co-assignment therefore answers "do these two nodes belong to the same persistent community," and the community count is the number of meta-communities per layer.
Versions before 1.1.0 instead used a Bayesian bootstrap on edge weights (Exponential(1) multipliers on a fixed topology). That scheme was removed: because it never varies which edges exist, it understates the variability of fresh data, and in simulation studies confidence intervals built from it undercovered substantially (pairwise co-assignment intervals covered ~45-48 percent at a nominal 95 percent).
bootstrap_multilayer( layers, fit_type = c("jaccard", "overlap", "weighted_jaccard", "weighted_overlap", "identity"), algorithm = c("leiden", "louvain"), n_boot = 100, layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, seed = NULL, objective = NULL, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2, allow_unequal_nodes = FALSE )bootstrap_multilayer( layers, fit_type = c("jaccard", "overlap", "weighted_jaccard", "weighted_overlap", "identity"), algorithm = c("leiden", "louvain"), n_boot = 100, layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, seed = NULL, objective = NULL, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2, allow_unequal_nodes = FALSE )
layers |
List of adjacency matrices or igraph objects. |
fit_type |
One of |
algorithm |
Community detection algorithm: |
n_boot |
Number of bootstrap replicates. |
layer_links |
Optional data.frame defining layer connectivity. |
min_similarity |
Minimum weighted similarity for interlayer ties. |
resolution_parameter |
Resolution parameter for community detection. |
directed |
Logical; if |
seed |
Optional random seed for reproducibility. |
objective |
Objective for the first-stage (per-layer) detection,
passed to the fit function: |
tracking, repair_chains, repair_min_similarity
|
Passed to the two-stage
fit functions (see |
allow_unequal_nodes |
Logical. If |
A list of class "multilayer_bootstrap" with components:
Number of completed bootstrap replicates.
Per-layer co-assignment probability matrices on the meta-communities (probability two nodes share a persistent community).
Per-layer vectors giving the fraction of replicates in which each node was assigned to its modal community.
Per-layer vectors of bootstrap modularity values.
Per-layer numeric vector: the share of completed replicates whose meta-community count equals the observed-network count. A descriptive stability measure. The raw per-replicate community counts are intentionally not returned.
List with matrices nmi and ari
(completed replicates x layers): agreement of each replicate's
meta-partition with the point-estimate partition. Summarised by
partition_stability.
Per-layer vectors: for each node, the mean Jaccard overlap between its replicate community and its point-estimate community.
The fit result from the original data.
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) boot <- bootstrap_multilayer( layers, fit_type = "jaccard", algorithm = "louvain", n_boot = 5, seed = 123 ) names(boot)set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) boot <- bootstrap_multilayer( layers, fit_type = "jaccard", algorithm = "louvain", n_boot = 5, seed = 123 ) names(boot)
Returns the tracked (cross-layer) community assignment produced
by the second-stage detection: for each layer, a vector giving every node's
meta-community. Unlike the per-layer layer_communities (detected
independently, ignoring the coupling), the meta-communities are the partition
that reflects the interlayer ties and any custom layer_links. This is
the membership that bootstrap_multilayer validates.
extract_meta_membership(fit)extract_meta_membership(fit)
fit |
A fit object from one of the |
A list with one integer vector per layer giving each node's meta-community assignment (node order).
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)); diag(m) <- 0; m }) fit <- fit_multilayer_jaccard(layers, algorithm = "leiden") extract_meta_membership(fit)set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)); diag(m) <- 0; m }) fit <- fit_multilayer_jaccard(layers, algorithm = "leiden") extract_meta_membership(fit)
Detects communities independently in each layer and then tracks
them across time by matching community labels between consecutive layers with
the Hungarian (optimal linear-sum assignment) algorithm applied to the
community overlap (contingency) matrix. This is a two-stage
snapshot-and-match tracker: unlike the coupling-based fits
(fit_multilayer_jaccard, fit_multilayer_overlap,
fit_multilayer_identity_ties), communities are found per layer
and aligned post hoc, not jointly optimised on a coupled supra-graph.
It provides the common "independent detection + optimal label matching"
baseline (e.g. per-layer Leiden matched with the Hungarian algorithm) as a
first-class method with the same interface as the other fits.
fit_multilayer_hungarian( layers, algorithm = c("leiden", "louvain"), resolution_parameter = 1, directed = FALSE, objective = NULL, seed = 123, allow_unequal_nodes = FALSE )fit_multilayer_hungarian( layers, algorithm = c("leiden", "louvain"), resolution_parameter = 1, directed = FALSE, objective = NULL, seed = 123, allow_unequal_nodes = FALSE )
layers |
List of |
algorithm |
Community algorithm: |
resolution_parameter |
Leiden resolution parameter. |
directed |
Logical; if |
objective |
Objective for the per-layer detection: |
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
Matching is sequential: layer is aligned to layer
. Nodes are matched across layers by vertex name when names are
present (by position otherwise), so the order in which vertices are stored
does not affect the result. For each adjacent pair the community overlap matrix is completed
to a square cost matrix and solve_LSAP maximises total
overlap; unmatched current communities receive fresh labels. The result is a
consistently-labelled sequence suitable for extract_meta_membership,
bootstrap_multilayer, and the plotting helpers.
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities), detected independently.
The tracked partition: one integer vector per
layer where labels are aligned across consecutive layers by Hungarian
matching on community overlap. A community with no positive-overlap
match is given a new label (a birth); labels not carried forward are
treated as deaths. See extract_meta_membership.
NULL - no coupled supra-graph is built.
"hungarian".
Sequential adjacent-layer links (the chain the matcher walks).
fit_multilayer_jaccard,
fit_multilayer_overlap,
fit_multilayer_identity_ties for coupling-based tracking.
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)); diag(m) <- 0; m }) fit <- fit_multilayer_hungarian(layers, algorithm = "leiden") extract_meta_membership(fit)set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)); diag(m) <- 0; m }) fit <- fit_multilayer_hungarian(layers, algorithm = "leiden") extract_meta_membership(fit)
Runs Louvain or Leiden community detection for each layer and
creates interlayer ties between the same node in selected adjacent layers.
With allow_unequal_nodes = TRUE, layers may contain different node sets;
only nodes present in both layers of a linked pair receive interlayer
edges. Nodes are matched by V(g)$name when available, or by vertex
index otherwise.
For this specification the cross-layer meta_communities are obtained
by Mucha et al. (2010) multislice modularity: each layer keeps its
own configuration null model (), interlayer identity
ties (each node to its copies in the coupled layers, weight = layer-link
weight times omega) carry no null term, and the objective is
optimised with a generalized Louvain (local moving + aggregation, random
restarts). Versions before 1.2.1 ran plain single-graph modularity on the
stacked supra-graph, which is a different objective and returns each layer
as one community at omega <= 1.
fit_multilayer_identity_ties( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, resolution_parameter = 1, omega = 1, directed = FALSE, objective = NULL, seed = 123, allow_unequal_nodes = FALSE )fit_multilayer_identity_ties( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, resolution_parameter = 1, omega = 1, directed = FALSE, objective = NULL, seed = 123, allow_unequal_nodes = FALSE )
layers |
List of |
algorithm |
Community algorithm for the per-layer (first-stage)
partitions: |
layer_links |
Optional data.frame defining which layers to connect,
with columns |
resolution_parameter |
Leiden resolution parameter. Also forwarded to the multislice supra-graph detection as Mucha's modularity resolution (larger values yield more, smaller meta-communities). |
omega |
Interlayer coupling strength for the multislice supra-graph
(Mucha's omega). Multiplies the interlayer identity-edge weights on top of
any |
directed |
Logical; if |
objective |
Objective for the first-stage (per-layer) detection
stored in |
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities). Detected independently
per layer.
The cross-layer tracked partition from the
second-stage detection: one integer vector per layer giving each
node's meta-community. This is the membership that reflects the
interlayer ties and any custom layer_links, and the one
validated by bootstrap_multilayer. See
extract_meta_membership.
Interlayer similarity edges between communities (plus self-loops).
The layer connectivity used.
With directed = TRUE the multislice stage that produces
meta_communities respects edge direction (Leicht and Newman's
directed null model in each slice). A directed network in which every arc
is reciprocated gives the same meta_communities as its undirected
twin at the same omega; before version 1.3.2 such a network was fitted
as if omega were halved. The first-stage layer_communities
still collapse directed layers to weighted undirected graphs (igraph's
detectors are undirected-only).
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_identity_ties(layers, algorithm = "louvain") names(fit)set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_identity_ties(layers, algorithm = "louvain") names(fit)
Runs Louvain or Leiden community detection for each layer and creates interlayer ties between communities in selected layer pairs using weighted Jaccard similarity.
fit_multilayer_jaccard( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )fit_multilayer_jaccard( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )
layers |
List of |
algorithm |
Community algorithm: |
layer_links |
Optional data.frame defining which layers to connect,
with columns |
min_similarity |
Minimum weighted similarity required to keep an interlayer tie. |
resolution_parameter |
Leiden resolution parameter. |
directed |
Logical; if |
add_self_loops |
Logical; if |
self_loop_multiplier |
Numeric multiplier applied to self-loop weighted
ties. The default |
objective |
Objective for the first-stage (per-layer) detection:
|
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
tracking |
|
repair_chains |
Logical. If |
repair_min_similarity |
Similarity floor for chain repair, between 0 and 1. Default 0.2. |
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities). Detected independently
per layer.
The cross-layer tracked partition from the
second-stage detection: one integer vector per layer giving each
node's meta-community. This is the membership that reflects the
interlayer ties and any custom layer_links, and the one
validated by bootstrap_multilayer. See
extract_meta_membership.
Interlayer similarity edges between communities (plus self-loops).
The layer connectivity used.
Directed layers are stored as directed graphs and the interlayer self-loop
weighting is directed-aware, but community detection collapses
directed layers to weighted undirected graphs on both Louvain and Leiden
(igraph's detectors are undirected-only). For detection that respects edge
direction, use the Python package with algorithm = "leiden".
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") names(fit) fit$layer_communities[[1]]$membershipset.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") names(fit) fit$layer_communities[[1]]$membership
Runs Louvain or Leiden community detection for each layer and creates interlayer ties between communities in selected layer pairs using a weighted overlap coefficient.
fit_multilayer_overlap( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )fit_multilayer_overlap( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )
layers |
List of |
algorithm |
Community algorithm: |
layer_links |
Optional data.frame defining which layers to connect,
with columns |
min_similarity |
Minimum weighted similarity required to keep an interlayer tie. |
resolution_parameter |
Leiden resolution parameter. |
directed |
Logical; if |
add_self_loops |
Logical; if |
self_loop_multiplier |
Numeric multiplier applied to self-loop weighted
ties. The default |
objective |
Objective for the first-stage (per-layer) detection:
|
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
tracking |
|
repair_chains |
Logical. If |
repair_min_similarity |
Similarity floor for chain repair, between 0 and 1. Default 0.2. |
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities). Detected independently
per layer.
The cross-layer tracked partition from the
second-stage detection: one integer vector per layer giving each
node's meta-community. This is the membership that reflects the
interlayer ties and any custom layer_links, and the one
validated by bootstrap_multilayer. See
extract_meta_membership.
Interlayer similarity edges between communities (plus self-loops).
The layer connectivity used.
Directed layers are stored as directed graphs and the interlayer self-loop
weighting is directed-aware, but community detection collapses
directed layers to weighted undirected graphs on both Louvain and Leiden
(igraph's detectors are undirected-only). For detection that respects edge
direction, use the Python package with algorithm = "leiden".
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_overlap(layers, algorithm = "louvain") names(fit)set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_overlap(layers, algorithm = "louvain") names(fit)
Runs Louvain or Leiden community detection for each layer and creates interlayer ties between communities in selected layer pairs using node-strength weighted Jaccard similarity.
fit_multilayer_weighted_jaccard( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )fit_multilayer_weighted_jaccard( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )
layers |
List of |
algorithm |
Community algorithm: |
layer_links |
Optional data.frame defining which layers to connect,
with columns |
min_similarity |
Minimum weighted similarity required to keep an interlayer tie. |
resolution_parameter |
Leiden resolution parameter. |
directed |
Logical; if |
add_self_loops |
Logical; if |
self_loop_multiplier |
Numeric multiplier applied to self-loop weighted ties. |
objective |
Objective for the first-stage (per-layer) detection:
|
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
tracking |
|
repair_chains |
Logical. If |
repair_min_similarity |
Similarity floor for chain repair, between 0 and 1. Default 0.2. |
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities). Detected independently
per layer.
The cross-layer tracked partition from the
second-stage detection: one integer vector per layer giving each
node's meta-community. This is the membership that reflects the
interlayer ties and any custom layer_links, and the one
validated by bootstrap_multilayer. See
extract_meta_membership.
Interlayer similarity edges between communities (plus self-loops).
The layer connectivity used.
Directed layers are stored as directed graphs and the interlayer self-loop
weighting is directed-aware, but community detection collapses
directed layers to weighted undirected graphs on both Louvain and Leiden
(igraph's detectors are undirected-only). For detection that respects edge
direction, use the Python package with algorithm = "leiden".
set.seed(123) layers <- lapply(1:3, function(i) { w <- matrix(runif(64), 8, 8) * matrix(rbinom(64, 1, 0.35), 8, 8) w <- (w + t(w)) / 2 diag(w) <- 0 w }) fit <- fit_multilayer_weighted_jaccard(layers, algorithm = "louvain") names(fit)set.seed(123) layers <- lapply(1:3, function(i) { w <- matrix(runif(64), 8, 8) * matrix(rbinom(64, 1, 0.35), 8, 8) w <- (w + t(w)) / 2 diag(w) <- 0 w }) fit <- fit_multilayer_weighted_jaccard(layers, algorithm = "louvain") names(fit)
Runs Louvain or Leiden community detection for each layer and creates interlayer ties between communities in selected layer pairs using node-strength weighted overlap coefficient.
fit_multilayer_weighted_overlap( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )fit_multilayer_weighted_overlap( layers, algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, resolution_parameter = 1, directed = FALSE, add_self_loops = TRUE, self_loop_multiplier = 1, objective = NULL, seed = 123, allow_unequal_nodes = FALSE, tracking = "smoothed", repair_chains = FALSE, repair_min_similarity = 0.2 )
layers |
List of |
algorithm |
Community algorithm: |
layer_links |
Optional data.frame defining which layers to connect,
with columns |
min_similarity |
Minimum weighted similarity required to keep an interlayer tie. |
resolution_parameter |
Leiden resolution parameter. |
directed |
Logical; if |
add_self_loops |
Logical; if |
self_loop_multiplier |
Numeric multiplier applied to self-loop weighted ties. |
objective |
Objective for the first-stage (per-layer) detection:
|
seed |
Integer seed for reproducible community detection (default 123, as in
the Python package; pass NULL for unseeded detection).
When supplied, the global RNG state is saved, the RNG is seeded for the
duration of the call, and the previous state is restored on exit, so the
caller's random number stream (e.g. bootstrap resampling) is unaffected.
Defaults to |
allow_unequal_nodes |
Logical; if |
tracking |
|
repair_chains |
Logical. If |
repair_min_similarity |
Similarity floor for chain repair, between 0 and 1. Default 0.2. |
A list of class "multilayer_community_fit" with components:
Per-layer community detection (each with
membership and communities). Detected independently
per layer.
The cross-layer tracked partition from the
second-stage detection: one integer vector per layer giving each
node's meta-community. This is the membership that reflects the
interlayer ties and any custom layer_links, and the one
validated by bootstrap_multilayer. See
extract_meta_membership.
Interlayer similarity edges between communities (plus self-loops).
The layer connectivity used.
Directed layers are stored as directed graphs and the interlayer self-loop
weighting is directed-aware, but community detection collapses
directed layers to weighted undirected graphs on both Louvain and Leiden
(igraph's detectors are undirected-only). For detection that respects edge
direction, use the Python package with algorithm = "leiden".
set.seed(123) layers <- lapply(1:3, function(i) { w <- matrix(runif(64), 8, 8) * matrix(rbinom(64, 1, 0.35), 8, 8) w <- (w + t(w)) / 2 diag(w) <- 0 w }) fit <- fit_multilayer_weighted_overlap(layers, algorithm = "louvain") names(fit)set.seed(123) layers <- lapply(1:3, function(i) { w <- matrix(runif(64), 8, 8) * matrix(rbinom(64, 1, 0.35), 8, 8) w <- (w + t(w)) / 2 diag(w) <- 0 w }) fit <- fit_multilayer_weighted_overlap(layers, algorithm = "louvain") names(fit)
Membership panels (states in IGOs, legislators in caucuses) are
usually projected to a node-by-node network whose tie weight counts shared
groups. layers_from_membership() builds that projection, or one of
two size-aware alternatives, slice by slice, and returns layers that the
fit_multilayer_* functions accept.
Let be the 0/1 membership matrix of a slice and
the size of group .
"count" (number of shared groups), diagonal 0.
"size", so each
group adds total weight proportional to its size rather than its size
squared.
"kumar"Kumar et al.'s hypergraph reweighting (as in
hypernetx 2.4.3). Group weights start at 1; the slice is
projected as in "size" with weights and clustered with
algorithm; each group gets the target
and
, until
tol. The projection with the final
is returned (the reference returns only the partition). The
clustering calls the same per-layer routine as the fitters, so
algorithm and resolution_parameter must be the ones you
pass to the fitter.
Every slice is built on its own and reseeded with seed: the weights
of slice never depend on any other slice, so no later information
enters an earlier layer.
No weighting is safe in general. In small Python simulations (hypotheses, not established results): the count projection failed badly when a few very large groups were present, where size-weighted and Kumar both recovered the planted blocs; Kumar beat size-weighting only under noise groups of the same size as real ones; under small groups bridging blocs, count was best and both size-aware weightings failed; and per-layer Louvain reproduced Kumar's own partition less reliably than Leiden.
layers_from_membership( memberships, weighting, algorithm = NULL, resolution_parameter = 1, seed = 123, max_iter = 50L, tol = 0.01, min_group_size = 2L, rescale = "none" )layers_from_membership( memberships, weighting, algorithm = NULL, resolution_parameter = 1, seed = 123, max_iter = 50L, tol = 0.01, min_group_size = 2L, rescale = "none" )
memberships |
List with one entry per slice: a 0/1 matrix with node
labels as row names (columns are groups), or a data frame with exactly
the columns |
weighting |
|
algorithm |
|
resolution_parameter |
Resolution for Kumar's clustering (> 0). |
seed |
Seed for Kumar's clustering, reused for every slice (default 123). The caller's RNG state is restored on exit. |
max_iter |
Maximum number of Kumar reweighting passes (default 50). Failing to converge is an error. |
tol |
Convergence tolerance on the largest half-change in a group weight. |
min_group_size |
Groups with fewer members (default 2) are dropped, with a warning. |
rescale |
|
Projected ties are not independent across pairs (one group creates many ties), so the bootstrap stability score has not been checked for layers built this way.
A list of weighted undirected igraph graphs, one per slice, with
vertex names. Pass allow_unequal_nodes = TRUE to the fitter when
node sets differ across slices. The graph attribute dm_membership
holds weighting, group_weights (final by group),
iterations, final_diff, groups_dropped,
rescale and partition (Kumar's final partition, a named
integer vector; NULL otherwise).
m1 <- data.frame(node = c("a", "b", "c", "c", "d"), group = c("g1", "g1", "g1", "g2", "g2")) m2 <- data.frame(node = c("a", "b", "b", "d"), group = c("g1", "g1", "g3", "g3")) layers <- layers_from_membership(list(m1, m2), weighting = "size") igraph::E(layers[[1]])$weightm1 <- data.frame(node = c("a", "b", "c", "c", "d"), group = c("g1", "g1", "g1", "g2", "g2")) m2 <- data.frame(node = c("a", "b", "b", "d"), group = c("g1", "g1", "g3", "g3")) layers <- layers_from_membership(list(m1, m2), weighting = "size") igraph::E(layers[[1]])$weight
Summarises a bootstrap_multilayer result into the
stability score : the mean agreement (NMI by default, or ARI) between
each bootstrap replicate's meta-partition and the point-estimate
meta-partition, averaged over layers and replicates. is a point
estimate with Monte Carlo standard error stability_mc_se. It has no
validated mapping to accuracy: use it only to compare two fits of the same
network (for example DynMux against multislice), never as an estimate of
how accurate either fit is. Since 1.4.0 the package reports no interval or
accuracy floor.
partition_stability(boot_result, metric = c("nmi", "ari"))partition_stability(boot_result, metric = c("nmi", "ari"))
boot_result |
Output from |
metric |
|
A list of class "partition_stability":
Stability score (mean over replicates and layers).
Per-layer mean agreement.
Monte Carlo standard error of from the
replicate spread.
The agreement measure used.
One-sentence plain-language summary.
set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(400, 1, 0.3), nrow = 20) m <- pmax(m, t(m)); diag(m) <- 0; m }) boot <- bootstrap_multilayer(layers, fit_type = "jaccard", algorithm = "leiden", n_boot = 10, seed = 123) ps <- partition_stability(boot) ps$stability; ps$reportset.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(400, 1, 0.3), nrow = 20) m <- pmax(m, t(m)); diag(m) <- 0; m }) boot <- bootstrap_multilayer(layers, fit_type = "jaccard", algorithm = "leiden", n_boot = 10, seed = 123) ps <- partition_stability(boot) ps$stability; ps$report
Plot an alluvial view of community transitions over time
plot_multilayer_alluvial(fit, max_nodes = NULL, palette = "Dark2")plot_multilayer_alluvial(fit, max_nodes = NULL, palette = "Dark2")
fit |
Fit object from any |
max_nodes |
Optional cap on number of nodes to include for readability. |
palette |
ColorBrewer qualitative palette, one of |
A ggplot object.
if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("ggalluvial", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") p <- plot_multilayer_alluvial(fit) }if (requireNamespace("ggplot2", quietly = TRUE) && requireNamespace("ggalluvial", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") p <- plot_multilayer_alluvial(fit) }
Draws one panel per layer with nodes colored by community assignment, using a faceted/wrapped layout.
plot_multilayer_series( layers, fit = NULL, community_memberships = NULL, directed = FALSE, layout = "layout_with_fr", ncol = 3, palette = "Dark2" )plot_multilayer_series( layers, fit = NULL, community_memberships = NULL, directed = FALSE, layout = "layout_with_fr", ncol = 3, palette = "Dark2" )
layers |
List of |
fit |
Optional fit object from any |
community_memberships |
Optional list of membership vectors
(one per layer). Ignored when |
directed |
Logical; if |
layout |
|
ncol |
Number of wrap columns in the panel layout. |
palette |
ColorBrewer qualitative palette, one of |
A ggplot object.
if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") p <- plot_multilayer_series(layers, fit = fit) }if (requireNamespace("ggplot2", quietly = TRUE)) { set.seed(123) layers <- lapply(1:3, function(i) { m <- matrix(rbinom(64, 1, 0.35), nrow = 8) m <- pmax(m, t(m)) diag(m) <- 0 m }) fit <- fit_multilayer_jaccard(layers, algorithm = "louvain") p <- plot_multilayer_series(layers, fit = fit) }
Generates synthetic multiplex network layers using a planted partition model, then fits one of the provided interlayer tie strategies.
simulate_and_fit_multilayer( n_nodes = 100, n_layers = 4, n_communities = 4, p_in = 0.2, p_out = 0.05, fit_type = c("jaccard", "overlap", "weighted_jaccard", "weighted_overlap", "identity"), algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, seed = NULL, directed = FALSE )simulate_and_fit_multilayer( n_nodes = 100, n_layers = 4, n_communities = 4, p_in = 0.2, p_out = 0.05, fit_type = c("jaccard", "overlap", "weighted_jaccard", "weighted_overlap", "identity"), algorithm = c("leiden", "louvain"), layer_links = NULL, min_similarity = 0, seed = NULL, directed = FALSE )
n_nodes |
Number of nodes per layer. |
n_layers |
Number of temporal layers. |
n_communities |
Number of latent communities. |
p_in |
Probability of an in-community edge. |
p_out |
Probability of an out-community edge. |
fit_type |
One of |
algorithm |
Community algorithm for fitting: |
layer_links |
Optional layer connectivity specification. |
min_similarity |
Minimum similarity threshold for overlap-based methods. |
seed |
Optional random seed. |
directed |
Logical; if |
A list containing simulated layers, true memberships, and fit results.
sim <- simulate_and_fit_multilayer( n_nodes = 30, n_layers = 3, n_communities = 3, fit_type = "jaccard", algorithm = "louvain", seed = 123 ) names(sim)sim <- simulate_and_fit_multilayer( n_nodes = 30, n_layers = 3, n_communities = 3, fit_type = "jaccard", algorithm = "louvain", seed = 123 ) names(sim)