UNPKG

bakana

Version:

Backend for kana's single-cell analyses. This supports single or multiple samples, execution in Node.js or the browser, in-memory caching of results for iterative analyses, and serialization to/from file for redistribution.

223 lines (200 loc) 10.6 kB
import * as inputs from "./steps/inputs.js"; import * as qc from "./steps/rna_quality_control.js"; import * as qcadt from "./steps/adt_quality_control.js"; import * as qccrispr from "./steps/crispr_quality_control.js"; import * as filter from "./steps/cell_filtering.js"; import * as norm from "./steps/rna_normalization.js"; import * as normadt from "./steps/adt_normalization.js"; import * as normcrispr from "./steps/crispr_normalization.js"; import * as fsel from "./steps/feature_selection.js"; import * as pca from "./steps/rna_pca.js"; import * as pcaadt from "./steps/adt_pca.js"; import * as pcacrispr from "./steps/crispr_pca.js"; import * as combine from "./steps/combine_embeddings.js"; import * as correct from "./steps/batch_correction.js"; import * as index from "./steps/neighbor_index.js"; import * as snngraph from "./steps/snn_graph_cluster.js"; import * as tsne from "./steps/tsne.js"; import * as umap from "./steps/umap.js"; import * as kmeans from "./steps/kmeans_cluster.js"; import * as choose from "./steps/choose_clustering.js"; import * as markers from "./steps/marker_detection.js"; import * as custom from "./steps/custom_selections.js"; import * as enrichment from "./steps/feature_set_enrichment.js"; import * as labelling from "./steps/cell_labelling.js"; /** * Generate an object containing all of the default analysis parameters. * * @return An object where each property corresponds to an analysis step and contains the default parameters for that step. * See the documentation for each step's `compute` method for more details: * * - {@linkcode InputsState#compute inputs} * - {@linkcode RnaQualityControlState#compute rna_quality_control} * - {@linkcode AdtQualityControlState#compute adt_quality_control} * - {@linkcode CrisprQualityControlState#compute crispr_quality_control} * - {@linkcode CellFiltering#compute cell_filtering} * - {@linkcode RnaNormalizationState#compute rna_normalization} * - {@linkcode AdtNormalizationState#compute adt_normalization} * - {@linkcode CrisprNormalizationState#compute crispr_normalization} * - {@linkcode FeatureSelectionState#compute feature_selection} * - {@linkcode RnaPcaState#compute rna_pca} * - {@linkcode AdtPcaState#compute adt_pca} * - {@linkcode CrisprPcaState#compute crispr_pca} * - {@linkcode NeighborIndexState#compute neighbor_index} * - {@linkcode TsneState#compute tsne} * - {@linkcode UmapState#compute umap} * - {@linkcode KmeansClusterState#compute kmeans_cluster} * - {@linkcode SnnGraphClusterState#compute snn_graph_cluster} * - {@linkcode ChooseClusteringState#compute choose_clustering} * - {@linkcode CellLabellingState#compute cell_labelling} * - {@linkcode FeatureSetEnrichmentState#compute feature_set_enrichment} * * See also {@linkcode configureBatchCorrection} and {@linkcode configureApproximateNeighbors} to synchronize certain parameter settings across multiple steps. */ export function analysisDefaults() { var output = {}; output[inputs.step_name] = inputs.InputsState.defaults(); output[fsel.step_name] = fsel.FeatureSelectionState.defaults(); output[qc.step_name] = qc.RnaQualityControlState.defaults(); output[qcadt.step_name] = qcadt.AdtQualityControlState.defaults(); output[qccrispr.step_name] = qccrispr.CrisprQualityControlState.defaults(); output[filter.step_name] = filter.CellFilteringState.defaults(); output[norm.step_name] = norm.RnaNormalizationState.defaults(); output[normadt.step_name] = normadt.AdtNormalizationState.defaults(); output[normcrispr.step_name] = normcrispr.CrisprNormalizationState.defaults(); output[pca.step_name] = pca.RnaPcaState.defaults(); output[pcaadt.step_name] = pcaadt.AdtPcaState.defaults(); output[pcacrispr.step_name] = pcacrispr.CrisprPcaState.defaults(); output[combine.step_name] = combine.CombineEmbeddingsState.defaults(); output[correct.step_name] = correct.BatchCorrectionState.defaults(); output[index.step_name] = index.NeighborIndexState.defaults(); output[snngraph.step_name] = snngraph.SnnGraphClusterState.defaults(); output[tsne.step_name] = tsne.TsneState.defaults(); output[umap.step_name] = umap.UmapState.defaults(); output[kmeans.step_name] = kmeans.KmeansClusterState.defaults(); output[choose.step_name] = choose.ChooseClusteringState.defaults(); output[markers.step_name] = markers.MarkerDetectionState.defaults(); output[custom.step_name] = custom.CustomSelectionsState.defaults(); output[enrichment.step_name] = enrichment.FeatureSetEnrichmentState.defaults(); output[labelling.step_name] = labelling.CellLabellingState.defaults(); return output; } const correctible_pca_steps = [pca.step_name, pcaadt.step_name, pcacrispr.step_name]; /** * Set the batch correction parameters across multiple steps. * This is a convenient helper as the correction process is split across the PCA and batch correction steps. * For MNN, we project cells onto rotation vectors computed within each batch in the various PCA steps (e.g., {@linkplain PcaState}) before performing MNN correction in {@linkplain BatchCorrectionState}; * for linear regression, we need to regress by block in the PCA without any additional correction in {@linkplain BatchCorrectionState}; * and for no correction, we need to turn off any block handling in the PCA as well as removing any additional correction in {@linkplain BatchCorrectionState}. * * @param {object} parameters Object containing parameters for all steps, e.g., from {@linkcode analysisDefaults}. * @param {string} method Correction method to perform, one of `"mnn"`, "`regress"` or `"none"`. * * @return `parameters` is modified with appropriate parameters in `batch_correction`, `pca` and `adt_pca`. */ export function configureBatchCorrection(parameters, method) { let correct_method; let pca_blocker; if (method == "mnn") { correct_method = method; pca_blocker = "project"; } else if (method == "regress") { correct_method = "none"; pca_blocker = method; } else if (method == "none") { correct_method = method; pca_blocker = method; } else { throw new Error("unknown correction method '" + method + "'"); } parameters[correct.step_name].method = correct_method; for (const x of correctible_pca_steps) { parameters[x].block_method = pca_blocker; } return parameters; } /** * Guess the `method` value from {@linkcode configureBatchCorrection} based on the parameter object. * This effectively consolidates the various correction parameters into a single setting. * * @param {object} parameters - Object containing parameters for all steps, typically after {@linkcode configureBatchCorrection}. * @param {object} [options] - Optional parameters. * @param {boolean} [options.strict] - Whether to only report the `method` when the set of parameters modified by {@linkcode configureBatchCorrection} are consistent. * * @return {?string} One of `"mnn"`, `"regress"` or `"none"`, based on the expected set of modifications from {@linkcode configureBatchCorrection}. * If `strict = false` and there is no exact match to the expected set, the most appropriate method is returned; * otherwise, if `strict = true`, `null` is returned. */ export function guessBatchCorrectionConfig(parameters, { strict = false } = {}) { let pca_blockers = new Set(correctible_pca_steps.map(x => parameters[x].block_method)); let resp; if (parameters[correct.step_name].method == "mnn") { resp = "mnn"; if (strict) { if (pca_blockers.size > 1 || !pca_blockers.has("project")) { resp = null; } } } else { if (pca_blockers.has("regress")) { if (strict && pca_blockers.size > 1) { resp = null; } else { resp = "regress"; } } else if (pca_blockers.has("none")) { if (strict && pca_blockers.size > 1) { resp = null; } else { resp = "none"; } } else { // If pca block_methods are set to 'project', // this doesn't really correspond to anything, // but is closest to 'none'. if (strict) { resp = null; } else { resp = "none"; } } } return resp; } const approximatable_steps = [correct.step_name, combine.step_name, index.step_name]; /** * Specify whether approximate neighbor searches should be performed across all affected steps. * This is a convenient helper as it is generally unnecessary to switch between exact and approximate searches in different steps. * Affected steps are {@linkplain BatchCorrectionState}, {@linkplain CombineEmbeddingsState} and {@linkplain NeighborIndexState}. * * @param {object} parameters Object containing parameters for all steps, e.g., from {@linkcode analysisDefaults}. * @param {boolean} approximate Whether to perform approximate nearest neighbor searces. * * @return `parameters` is modified with appropriate parameters in relevant steps, e.g., `batch_correction`, `combine_embeddings` and `neighbor_index`. */ export function configureApproximateNeighbors(parameters, approximate) { for (const step of approximatable_steps) { parameters[step].approximate = approximate; } return parameters; } /** * Guess the value of `approximate` from {@linkcode configureApproximateNeighbors} based on the parameter object. * This effectively consolidates the various approximation parameters into a single setting. * * @param {object} parameters - Object containing parameters for all steps, typically after {@linkcode configureApproximateNeighbors}. * @param {object} [options] - Optional parameters. * @param {boolean} [options.strict] - Whether to only report `approximate` when the set of parameters modified by {@linkcode configureApproximateNeighbors} are consistent. * * @return {?boolean} Whether or not approximate neighbor search was used. * If `strict = false` and there is a mixture of approximate and exact matches, an approximate search is reported; * otherwise, if `strict = true`, `null` is returned. */ export function guessApproximateNeighborsConfig(parameters, { strict = false } = {}) { let approximates = new Set(approximatable_steps.map(x => parameters[x].approximate)); if (strict && approximates.size > 1) { return null; } else { return approximates.has(true); } }