UNPKG

scran.js

Version:

Single cell RNA-seq analysis in Javascript

183 lines (163 loc) 7.82 kB
import * as gc from "./gc.js"; import * as utils from "./utils.js"; import { ScranMatrix } from "./ScranMatrix.js"; /** * Wrapper for the cell aggregation results, produced by {@linkcode aggregateAcrossCells}. * @hideconstructor */ export class AggregateAcrossCellsResults { #id; #results; constructor(id, raw) { this.#id = id; this.#results = raw; return; } /** * @return {number} Number of groups. */ numberOfGroups() { return this.#results.num_groups(); } /** * @return {number} Number of genes. */ numberOfGenes() { return this.#results.num_genes(); } /** * @param {number} group - Index of the group. * This should be non-negative and less than {@linkcode AggregateAcrossCellsResults#numberOfGroups numberOfGroups}. * @param {object} [options={}] - Optional parameters. * @param {(string|boolean)} [options.copy=true] - Copying mode to use, see {@linkcode possibleCopy} for details. * * @return {Float64Array|Float64WasmArray} Array of length equal to the number of genes, containing the per-gene sum of values across across all cells in the specified `group`. * If `average = true` in {@linkcode aggregateAcrossCells}, each element is the mean value instead. */ groupSums(group, options = {}) { const { copy = true, ...others } = options; utils.checkOtherOptions(others); return utils.possibleCopy(this.#results.group_sums(group), copy); } /** * @param {object} [options={}] - Optional parameters. * @param {boolean} [options.asTypedArray=true] - Whether to return a Float64Array. * If `false`, a Float64WasmArray is returned instead. * @param {?Float64WasmArray} [options.buffer=null] - Buffer in which to store the output. * If not `null`, this should have the same length as the product of {@linkcode AggregateAcrossCellsResults#numberOfGenes numberOfGenes} * and {@linkcode AggregateAcrossCellsResults#numberOfGroups numberOfGroups}. * * @return {Float64Array|Float64WasmArray} Array of length equal to the product of the number of genes and groups. * This can be treated as a column-major matrix where the rows are the genes and the columns are the groups, * and each element is the sum of values for the corresponding gene in the corresponding group. * If `average = true` in {@linkcode aggregateAcrossCells}, each element is the mean value instead. * If `buffer` is supplied, the function returns `buffer` if `asTypedArray = false`, or a view on `buffer` if `asTypedArray = true`. */ allSums(options = {}) { let { asTypedArray = true, buffer = null, ...others } = options; utils.checkOtherOptions(others); let tmp = null; try { if (buffer == null) { tmp = utils.createFloat64WasmArray(this.numberOfGenes() * this.numberOfGroups()); buffer = tmp; } this.#results.all_sums(buffer.offset); } catch (e) { utils.free(tmp); throw e; } return utils.toTypedArray(buffer, tmp == null, asTypedArray); } /** * @param {number} group - Index of the group. * This should be non-negative and less than {@linkcode AggregateAcrossCellsResults#numberOfGroups numberOfGroups}. * @param {object} [options={}] - Optional parameters. * @param {(string|boolean)} [options.copy=true] - Copying mode to use, see {@linkcode possibleCopy} for details. * * @return {Float64Array|Float64WasmArray} Array of length equal to the number of genes, containing the number of cells with detected expression for each gene in the specified `group`. * If `average = true` in {@linkcode aggregateAcrossCells}, each element is the proportion of detected cells instead. */ groupDetected(group, options = {}) { const { copy = true, ...others } = options; utils.checkOtherOptions(others); return utils.possibleCopy(this.#results.group_detected(group), copy); } /** * @param {object} [options={}] - Optional parameters. * @param {boolean} [options.asTypedArray=true] - Whether to return a Float64Array. * If `false`, a Float64WasmArray is returned instead. * If not `null`, this should have the same length as the product of {@linkcode AggregateAcrossCellsResults#numberOfGenes numberOfGenes} * and {@linkcode AggregateAcrossCellsResults#numberOfGroups numberOfGroups}. * * @return {Float64Array|Float64WasmArray} Array of length equal to the product of the number of genes and groups. * This can be treated as a column-major matrix where the rows are the genes and the columns are the groups, * and each element contains the number of detected cells for the corresponding gene in the corresponding group. * If `average = true` in {@linkcode aggregateAcrossCells}, each element is the proportion of detected cells instead. * If `buffer` is supplied, the function returns `buffer` if `asTypedArray = false`, or a view on `buffer` if `asTypedArray = true`. */ allDetected(options = {}) { let { asTypedArray = true, buffer = null, ...others } = options; utils.checkOtherOptions(others); let tmp = null; try { if (buffer == null) { tmp = utils.createFloat64WasmArray(this.numberOfGenes() * this.numberOfGroups()); buffer = tmp; } this.#results.all_detected(buffer.offset); } catch (e) { utils.free(tmp); throw e; } return utils.toTypedArray(buffer, tmp == null, asTypedArray); } /** * @return Frees the memory allocated on the Wasm heap for this object. * This invalidates this object and all references to it. */ free() { if (this.#results !== null) { gc.release(this.#id); this.#results = null; } return; } } /** * Aggregate per-cell expression profiles for each group of cells. * This is typically used to summarize data into per-cluster expression profiles for easier inspection. * * @param {ScranMatrix} x - Some expression matrix, typically containing normalized log-expression values. * @param {Int32Array|Int32WasmArray} groups - Array containing group IDs for each cell. * This should have length equal to the number of cells and contain all values from 0 to `n - 1` at least once, where `n` is the number of groups. * @param {object} [options={}] - Optional parameters. * @param {boolean} [options.average=null] - Whether to compute the average within each group for each statistic. * @param {?number} [options.numberOfThreads=null] - Number of threads to use. * If `null`, defaults to {@linkcode maximumThreads}. * * @return {AggregateAcrossCellsResults} Object containing the aggregation results. */ export function aggregateAcrossCells(x, groups, options = {}) { const { average = false, numberOfThreads = null, ...others } = options; utils.checkOtherOptions(others); var group_data; var output; let nthreads = utils.chooseNumberOfThreads(numberOfThreads); try { group_data = utils.wasmifyArray(groups, "Int32WasmArray"); if (group_data.length != x.numberOfColumns()) { throw new Error("length of 'groups' should be equal to number of columns in 'x'"); } output = gc.call( module => module.aggregate_across_cells(x.matrix, group_data.offset, average, nthreads), AggregateAcrossCellsResults ); } catch (e) { utils.free(output); throw e; } finally { utils.free(group_data); } return output; }