Skip to content

Edge Selection

Edge selection: which edges enter the model.

The statistics themselves live in statistics.py. This module is the policy layer on top of them -- the significance thresholding (PThreshold), the two structural filters (presence, connected components), and the UnivariateEdgeSelection facade that expands a user's configuration into the parameter grid the inner CV iterates over.

EdgeStatistic

Bases: BaseEstimator

The per-edge statistic used to rank and threshold edges.

Two independent choices:

selection_statistic 'pearson' or 'spearman'. A binary 0/1 target through the Pearson path is the point-biserial correlation, so it needs no separate value.

selection_input 'raw' ignores the covariates. 'residualized' controls for them: one regression per edge, y ~ 1 + Z + edge, reporting the semipartial correlation as the effect size and the coefficient's p-value with df = N - 2 - C.

On 'residualized': the effect size is reported as the semipartial correlation -- the edge's unique contribution as a share of the total variance of y -- because that denominator stays comparable across analyses. The partial correlation is the same effect on a denominator that shrinks with how confounded y is. They are monotone transforms of each other within a fold (verified: rank correlation exactly 1.0), so the choice affects what is printed, never which edges are selected. Both come from one regression, which is where the p-value comes from too.

For Spearman the ranking happens first, including on the confounds, and the residualisation follows -- the conventional "rank, then partial" definition. The reverse order does not work: ranking is nonlinear, so ranking a residualised edge puts the confound signal back in (measured: 11.9 versus 6.5e-13 of residual confound signal).

PThreshold

Bases: BaseEdgeSelector

Select edges whose correlation with the target is significant at a p-value threshold, optionally after multiple-comparison correction.

Pass a list of thresholds (and/or corrections) to search over them with an inner cross-validation loop.

__init__(threshold=0.05, correction=None)

:param threshold: p-value threshold(s); edges with p below the threshold are selected. A single value (e.g. 0.05) or a list (e.g. [0.01, 0.05]) to tune via inner CV. :param correction: multiple-comparison correction, or None for no correction. Can be one of statsmodels' methods: bonferroni : one-step correction sidak : one-step correction holm-sidak : step down method using Sidak adjustments holm : step-down method using Bonferroni adjustments simes-hochberg : step-up method (independent) hommel : closed method based on Simes tests (non-negative) fdr_bh : Benjamini/Hochberg (non-negative) fdr_by : Benjamini/Yekutieli (negative) fdr_tsbh : two stage fdr correction (non-negative) fdr_tsbky : two stage fdr correction (non-negative)

select(r, t, df, thresholds=None)

Select edges whose (optionally corrected) p-value falls below each threshold, split into positive- and negative-correlation networks.

Selection is exact without computing a p-value per edge: for fixed df the two-sided p-value is strictly decreasing in |t|, so p < alpha is |t| > critical_t(alpha, df) -- one scalar scipy call per threshold, with the comparison on the device. Step-wise corrections (Holm, FDR, ...) need the p-values themselves and compute them exactly on the CPU.

The correction is applied per run: each column of t is one analysis (the real target, or one permutation) with N_features tests of its own. A permuted run must be thresholded exactly like the real one, or the null distribution is not a null of the same procedure.

Args: r: [N_features, rest] effect sizes; their sign picks the network. t: [N_features, rest] t statistics, rest being any batch axes already present (in practice the runs/permutations axis). df: degrees of freedom of t. thresholds: sequence of p-value thresholds. Defaults to self.threshold, which is always a list.

Returns: Boolean tensor [N_features, 2, N_params, *rest], where dim 1 is [positive, negative] and N_params == len(thresholds).

UnivariateEdgeSelection

Bases: BaseEstimator

Univariate edge selection for CPM.

Correlates each edge with the target using the chosen statistic and selects edges with one or more selection strategies (e.g. a p-value threshold). When several configurations are supplied, they form a hyperparameter grid that the inner cross-validation loop searches over.

Parameters:

Name Type Description Default
selection_statistic str

Correlation used to relate each edge to the target: 'pearson' or 'spearman'. A binary 0/1 target through the Pearson path is the point-biserial correlation, so it needs no separate value.

'spearman'
selection_input str

Whether edge selection controls for the covariates. 'raw' ignores them. 'residualized' fits one regression per edge, y ~ 1 + Z + edge, reporting the semipartial correlation and the coefficient's p-value (df = N - 2 - C) -- so a p < 0.05 threshold means a 5% per-edge false-positive rate whatever the confounding. It requires covariates.

'raw'
presence_filter Union[bool, float]

Optional pre-filter that keeps only edges which are nonzero in at least a given fraction of subjects, dropping structural/near-zero edges before selection. True uses a fraction of 0.5 (present in the majority); a float sets the fraction explicitly (e.g. 0.75). Intended for sparse structural connectomes (e.g. DTI streamline counts); leave off (False) for functional data, whose edges have a real signed distribution around a mean of ~0. Computed per fold on the training subjects from the connectome only, so it adds no target leakage. It always sees the raw connectome, including under selection_input='residualized' -- confound control now happens inside the statistic rather than by residualising X beforehand.

False
connected_components Union[bool, int]

If set, keep only selected edges that belong to a connected component with at least this many edges (per positive/negative network), dropping isolated edges — this can improve edge stability. True uses a minimum of 2 edges (drop lone single edges); an int sets the minimum explicitly. Applied per fold and per permutation after thresholding.

False
edge_selection Union[list, None, PThreshold]

One or more selection strategies. Provide a list of multiple configurations to tune them via an inner CV.

None

return_selected_edges(thresholds=None)

Edge masks [Features, 2, N_params, *runs] for the fitted statistics.

filter_connected_components(mask, min_edges)

Keep only selected edges that belong to a connected component with at least min_edges edges; drop the rest.

A graph is built independently for each (network, *batch) slice from that slice's selected edges, treating edges sharing a node as connected. Isolated single edges form a one-edge component and are removed when min_edges >= 2.

mask is [Features, 2, *batch] (dim 1 = positive/negative; batch is typically params x runs). The returned tensor has the same shape and dtype, with dropped edges set to 0.

This is CPU/networkx work and is not vectorised -- it runs once per (network, *batch) slice.

resolve_min_component_size(connected_components)

Resolve the connected_components argument to a minimum component size (in edges), or None when the filter is off (True -> 2, i.e. drop lone single edges).

resolve_presence_threshold(presence_filter)

Resolve the presence_filter argument to a nonzero-fraction threshold.

Returns None when the filter is off, otherwise the fraction of subjects that must have a nonzero value for an edge to be kept (True -> 0.5).

resolve_selection_spec(selection_statistic, selection_input)

Validate the edge-selection specification.

Returns (selection_statistic, selection_input).