BrainNetTest implements the non-parametric L1-distance
ANOVA test of Fraiman and Fraiman (2018) for comparing populations of
brain networks represented as graphs. The package provides:
T and its permutation test
(global_test);identify_critical_links) that
pinpoints the edges driving between-group differences; andThis vignette introduces the basic API on simple synthetic data. The companion vignette, Generating and Analyzing Brain Networks with Community Structures, shows the full pipeline on community-structured populations.
generate_random_graph() returns a symmetric binary
adjacency matrix with no self-loops:
Given a population (list of adjacency matrices), the central
graph is the entry-wise mean; compute_distance()
returns the L1 (Manhattan) distance between two graphs:
Tcompute_test_statistic() aggregates within- and
between-group Manhattan distances into the statistic T.
Larger values indicate stronger evidence that the groups differ:
control <- replicate(
15, generate_random_graph(n_nodes = 10, edge_prob = 0.20),
simplify = FALSE)
patient <- replicate(
15, generate_random_graph(n_nodes = 10, edge_prob = 0.40),
simplify = FALSE)
populations <- list(Control = control, Patient = patient)
compute_test_statistic(populations, a = 1)
#> [1] -18.54162compute_test_statistic() returns only the observed
value. global_test() assesses it: it permutes the group
labels across the pooled sample, keeping the group sizes, recomputes
T for each permutation, and reports the proportion of
permutations with a statistic smaller than the observed one as the
p-value. The normalisation constant a is fixed at 1; the
p-value does not depend on it.
gt <- global_test(populations, n_permutations = 500, seed = 42)
gt
#>
#> Global test for populations of brain networks
#>
#> Populations: 2 (Control, Patient), 15 / 15 graphs
#> Nodes: 10 (45 possible edges)
#> Statistic: T = -18.54162
#> p value: < 0.002 (500 permutations)The permutation null distribution is kept in
gt$null_distribution, so it can be inspected or plotted.
With B permutations the p-value cannot resolve below
1 / B, which is how an estimate of zero is reported.
Once a difference is established,
identify_critical_links() looks for the edges responsible.
It runs the same global test first and then iteratively removes edges,
ranked by their marginal p-values, until the populations are no longer
distinguishable:
result <- identify_critical_links(
populations,
alpha = 0.05,
method = "fisher",
n_permutations = 200,
seed = 42)
result
#>
#> Critical edges between populations of brain networks
#>
#> Populations: 2 (Control, Patient), 15 / 15 graphs
#> Candidate edges: 45
#> Global test: p < 0.005 (200 permutations, alpha = 0.05)
#> Critical edges: 7 of 45 (15.6%), ranked by Fisher's exact test
#>
#> Most significant 6 of them:
#> node1 node2 p_value
#> 1 2 4 0.01419290
#> 2 4 10 0.01419290
#> 3 2 5 0.02093953
#> 4 2 10 0.02093953
#> 5 8 10 0.02532769
#> 6 1 3 0.03518241
#> ... 1 more; see the critical_edges componentThe result is a critical_links object. Printing it, as
above, gives a compact report; summary() adds the settings
the analysis ran with and the node-level ranking:
summary(result)
#>
#> Critical edges between populations of brain networks
#>
#> Call:
#> identify_critical_links(populations = populations, alpha = 0.05,
#> method = "fisher", n_permutations = 200, seed = 42)
#>
#> Populations: 2 (Control, Patient), 15 / 15 graphs
#> Candidate edges: 45
#> Global test: p < 0.005
#> Critical edges: 7 (15.6% of candidates)
#>
#> Settings:
#> marginal test Fisher's exact test
#> multiplicity none
#> significance level 0.05
#> edges removed / step 1
#> permutations 200
#> normalisation a 1
#> seed 42
#>
#> Most significant edges:
#> node1 node2 p_value
#> 1 2 4 0.01419290
#> 2 4 10 0.01419290
#> 3 2 5 0.02093953
#> 4 2 10 0.02093953
#> 5 8 10 0.02532769
#> 6 1 3 0.03518241
#> 7 4 6 0.05017491
#>
#> Nodes ranked by critical degree:
#> node critical_degree
#> 1 2 3
#> 2 4 3
#> 3 10 3
#> 4 1 1
#> 5 3 1
#> 6 5 1
#> 7 6 1
#> 8 8 1The individual components remain available,
result$critical_edges being the ranked table of critical
edges. The get_critical_nodes() helper summarises the
result at the node level on its own:
Fraiman, D. and Fraiman, R. (2018) An ANOVA approach for statistical comparisons of brain networks. Scientific Reports, 8, 4746. https://doi.org/10.1038/s41598-018-23152-5