structboost.StabilitySelectionResult

class structboost.StabilitySelectionResult(frequency, stable_support, threshold, avg_selected, expected_false_positives, n_subsamples, subsample_frac, mode='subsample', n_iterations=0, dim_match_quality=nan, coefficient_cond_mean=None, coefficient_sd=None, sign_consistency=None)[source]

Bases: object

Result of structboost.stability_selection().

Parameters:
frequency

Per-gene, per-dimension selection frequency, shape (n_genes, latent_dim). Entry (g, j) is the fraction of subsamples in which gene g received a nonzero coefficient for latent dimension j.

Type:

NDArray[np.float64]

stable_support

Boolean mask frequency >= threshold, shape (n_genes, latent_dim).

Type:

BoolArray

threshold

The selection-frequency threshold pi used for stable_support.

Type:

float

avg_selected

Mean number of genes selected per subsample, per dimension, shape (latent_dim,). This is the q in the error bound below.

Type:

NDArray[np.float64]

expected_false_positives

Meinshausen-Bühlmann upper bound on the expected number of falsely stable genes per dimension, E[V] <= q^2 / ((2*threshold - 1) * p), shape (latent_dim,). Here q is the average number of competitively selected genes and p the number of candidate genes; forced (mandatory) genes are excluded from both, since they are always selected by construction and are not candidates. NaN when threshold <= 0.5 (the bound is undefined there) or when every gene is mandatory. The bound holds under the paper’s exchangeability and “no worse than random guessing” assumptions, which real data may violate; treat it as a guideline, not a guarantee.

Type:

NDArray[np.float64]

n_subsamples

Number of subsamples drawn. 0 for mode="iteration", which draws no subsamples.

Type:

int

subsample_frac

Fraction of cells in each subsample. NaN for mode="iteration".

Type:

float

mode

Which resampling scheme produced the frequencies.

"subsample"

Meinshausen-Bühlmann cell subsampling with the model frozen. Targets variance under cell resampling. frequency is per latent dimension and expected_false_positives carries the paper’s error bound.

"iteration"

Selection frequency across additional training iterations of a single fit. Targets variance from the optimizer’s position on its loss plateau, which is a different — and on measured data, larger — source of instability. frequency is per latent dimension, with each iteration’s dimensions matched to the fitted model’s before counting — see dim_match_quality. expected_false_positives is NaN: training iterations are neither independent nor exchangeable, so the Meinshausen-Bühlmann bound does not apply and no error control is claimed.

Type:

str

n_iterations

Number of training iterations averaged over. 0 for mode="subsample".

Type:

int

coefficient_cond_mean

Shape (n_genes, latent_dim), or None when coefficients were not collected. Mean coefficient over the runs in which each entry was selected — reliability-blind but scale-preserving, since its expectation equals the per-run coefficient. Feeds stable_encoder().

Type:

NDArray[np.float64] | None

coefficient_sd

Standard deviation of the same conditional distribution. This is a spread, not a standard error: subsample runs share half their cells by construction and iteration runs are autocorrelated, so the effective sample size is far below n_runs and sd / sqrt(n_runs) would be badly overconfident. Reporting it as a precision would require a block bootstrap.

Type:

NDArray[np.float64] | None

sign_consistency

Fraction of selecting runs in which each coefficient took its modal sign. A gene selected every time with a sign that flips is not stable, which a selection frequency alone cannot reveal. Measured at ~0.999 on real data, so in practice a guard rather than a headline.

Type:

NDArray[np.float64] | None

dim_match_quality

mode="iteration" only: mean absolute cosine similarity between each iteration’s latent dimensions and the fitted model’s, after optimal matching. Per-dimension frequencies are only meaningful when dimensions keep their identity across iterations, and this quantifies that rather than assuming it. Near 1 means the anchoring held; low values mean the per-dimension split should not be trusted and frequency.max(axis=1) (the flat union) is the safer readout. NaN for mode="subsample".

Type:

float

Methods

__init__(frequency, stable_support, ...[, ...])

stable_encoder()

Aggregate the per-run coefficients into a more reproducible encoder.

Attributes

coefficient_cond_mean

coefficient_mean

Mean coefficient over all runs, counting unselected runs as zero.

coefficient_sd

dim_match_quality

mode

n_iterations

n_runs

Number of resampling runs, whichever mode produced this result.

sign_consistency

frequency

stable_support

threshold

avg_selected

expected_false_positives

n_subsamples

subsample_frac

property n_runs: int

Number of resampling runs, whichever mode produced this result.

property coefficient_mean: ndarray[tuple[Any, ...], dtype[float64]] | None

Mean coefficient over all runs, counting unselected runs as zero.

Equals coefficient_cond_mean * frequency, so a gene selected in 40% of runs is shrunk to 40% of its typical weight — shrinkage proportional to reliability. Statistically the more principled estimator, but it changes the latent scale the decoder was trained against, which is why stable_encoder() does not offer it.

stable_encoder()[source]

Aggregate the per-run coefficients into a more reproducible encoder.

Among stably selected entries the run-to-run coefficient spread is large (CV ~0.34 median on measured data), so the support of a fit is more reproducible than its weights. This returns the conditional mean coefficient restricted to the stable support: the frequency threshold decides which genes, the conditional mean decides how much.

Measured on simulated data against exact ground truth, with the decoder left untouched, it improves on the fitted encoder on every axis that matters for selection:

encoder

precision

FDR

genes

fitted

0.62

0.38

156

this

0.74

0.26

116

and raises reconstruction from 55% to 59% of the linear ceiling. The same ordering holds under batch conditioning (precision 0.754 vs 0.691, FDR 0.246 vs 0.309). Because the latent scale is preserved, the result can be installed with BAE.apply_encoder() without refitting the decoder.

To trade precision for recall, lower threshold when calling BAE.stability_selection() rather than reaching for a different estimator — the threshold is that dial. An unmasked conditional mean is simply this with threshold at zero, and it was measured to be a worse selector at every setting tried (precision 0.47 unconditioned, 0.56 conditioned) as well as numerically fragile on batch-dominated fits, where it produced a reconstruction worse than predicting zero.

Returns:

ndarray of shape (n_genes, latent_dim) – Same orientation as frequency and adata.varm.

Raises:

ValueError – If coefficients were not collected for this result.

Return type:

ndarray[tuple[Any, …], dtype[float64]]