Skip to contents

Runs n_runs HALS-NMF restarts, pools their components, drops unstable ones by local density, k-means clusters the survivors into k groups and refits the partner factor against the per-cluster median. This is cNMF: the answer is the structure the restarts agree on, and the mean silhouette of those clusters (stability) tells you how much they agreed.

Prefer this over stabilised_nmf_bulk(), which just picks the lowest-loss restart and tells you nothing about whether that restart is reproducible.

Usage

consensus_nmf_bulk(
  object,
  k,
  n_runs = 30L,
  preprocessing = c("none", "sd", "sqrt_sd"),
  nmf_hals_params = params_nmf_hals(),
  nmf_consensus_params = params_nmf_consensus(),
  membership_params = params_module_membership(),
  seed = 42L,
  .verbose = TRUE
)

Arguments

object

The class, see BulkCoExp(). Ideally, you should run preprocess_bulk_coexp() before applying this function. The NMF requires non-negative input, so the pre-processing should either skip scaling or use a strictly non-negative representation.

k

Integer. Number of latent factors (modules). Must be at least 2.

n_runs

Integer. Number of random restarts. Must be at least 2.

preprocessing

String. One of c("none", "sd", "sqrt_sd"). Applied inside the Rust kernel to the input matrix before the HALS updates.

nmf_hals_params

List. Output of params_nmf_hals(). The nmf_init field is ignored, restarts always use random initialisation.

nmf_consensus_params

List. Output of params_nmf_consensus(). Controls the clustering of the pooled components.

membership_params

List. Controls how the gene loadings are turned into module membership, see params_module_membership().

seed

Integer. Base random seed. Restart i uses seed + i, and the k-means step is seeded from it too.

.verbose

Boolean or integer 0L/1L/2L. Controls verbosity.

Value

The class with final_results populated from the consensus fit, with the clustering diagnostics under diagnostics and the fit parameters under params$nmf_fit.

Details

Use nmf_k_sweep_bulk() first if you do not already know k.

The density filter is the part that bites. Components whose mean cosine distance to their neighbours exceeds density_threshold are dropped, and if that leaves fewer than k survivors the fit errors rather than returning something degenerate. With few restarts the filter is jumpy, so either raise n_runs or set density_threshold = 2 to switch it off.

References

Kotliar et al., eLife, 2019

Examples

# consensus over ten restarts
syn <- generate_gene_module_data(n_samples = 24L, n_genes = 60L)
# NMF needs a non-negative matrix
mat <- syn$data - min(syn$data)
obj <- BulkCoExp(mat, syn$meta_data)
obj <- preprocess_bulk_coexp(
  obj, hvg = NULL, scaling = FALSE, .verbose = FALSE
)
obj <- consensus_nmf_bulk(obj, k = 4L, n_runs = 10L, .verbose = FALSE)
get_nmf_stability(obj)$stability
#> [1] 0.9998821