import * as generics from "./AllGenerics.js";
import * as rse from "./RangedSummarizedExperiment.js";
import * as se from "./SummarizedExperiment.js";
import * as utils from "./utils.js";
import * as cutils from "./clone-utils.js";
import * as il from "./InternalList.js";
/**
* A SingleCellExperiment is a {@linkplain RangedSummarizedExperiment} subclass that contains additional fields for storing reduced dimensions and alternative experiments.
* It supports the same set of generics as the {@linkplain SummarizedExperiment}.
*
* Each reduced dimension instance should have number of rows equal to the number of columns of the SingleCellExperiment.
* Each instance is expected to provide methods for the following generics:
*
* - {@linkcode NUMBER_OF_ROWS}
* - {@linkcode SLICE_2D}
* - {@linkcode COMBINE_ROWS}
* - {@linkcode CLONE}
*
* Each alternative experiment should be a {@linkplain SummarizedExperiment} with number of columns equal to that of the SingleCellExperiment.
*
* @extends RangedSummarizedExperiment
*/
export class SingleCellExperiment extends rse.RangedSummarizedExperiment {
/**
* @param {Object} assays - Object where keys are the assay names and values are multi-dimensional arrays of experimental data.
* @param {Object} [options={}] - Optional parameters, including those used in the {@linkplain RangedSummarizedExperiment} constructor.
* @param {?(GRanges|GroupedGRanges)} [options.rowRanges=null] - Genomic ranges corresponding to each row, see the {@linkplain RangedSummarizedExperiment} constructor.
* @param {Object|Map} [options.reducedDimensions={}] - Object containing named reduced dimensions.
* Each value should be a 2-dimensional object with number of rows equal to the number of columns of the assays.
* @param {?Array} [options.reducedDimensionOrder=null] - Array containing the order of the reduced dimensions.
* This should have the same values as the keys of `reducedDimensions`, and defaults to those keys if `null`.
* @param {Object|Map} [options.alternativeExperiments={}] - Object containing named alternative experiments.
* Each value should be a 2-dimensional object with number of columns equal to that of the assays.
* @param {?Array} [options.alternativeExperimentOrder=null] - Array containing the order of the alternative experiments.
* This should have the same values as the keys of `alternativeExperiments`, and defaults to those keys if `null`.
*/
constructor(assays, options={}) {
if (arguments.length == 0) {
super();
return;
}
let { reducedDimensions = {}, reducedDimensionOrder = null, alternativeExperiments = {}, alternativeExperimentOrder = null, rowRanges = null } = options;
super(assays, rowRanges, options);
let ncols = this.numberOfColumns();
try {
this._reducedDimensions = new il.InternalList(reducedDimensions, reducedDimensionOrder);
} catch (e) {
throw new Error("failed to initialize reduced dimension list for this " + this.constructor.className + "; " + e.message, { cause: e });
}
for (const k of this._reducedDimensions.names()) {
let v = this._reducedDimensions.entry(k);
if (generics.NUMBER_OF_ROWS(v) !== ncols) {
throw new Error("number of rows for reduced dimension '" + k + "' is not equal to number of columns for this " + this.constructor.className);
}
}
try {
this._alternativeExperiments = new il.InternalList(alternativeExperiments, alternativeExperimentOrder);
} catch (e) {
throw new Error("failed to initialize alternative experiment list for this " + this.constructor.className + "; " + e.message, { cause: e });
}
for (const k of this._alternativeExperiments.names()) {
let v = this._alternativeExperiments.entry(k);
if (!(v instanceof se.SummarizedExperiment)) {
throw new Error("alternative experiment '" + k + "' is not a SummarizedExperiment");
}
if (v.numberOfColumns(v) !== ncols) {
throw new Error("number of columns for alternative experiment '" + k + "' is not equal to number of columns for this " + this.constructor.className);
}
}
return;
}
static className = "SingleCellExperiment";
/**************************************************************************
**************************************************************************
**************************************************************************/
/**
* @return {Array} Array of strings containing the (ordered) names of the reduced dimensions.
*/
reducedDimensionNames() {
return this._reducedDimensions.names();
}
/**
* @param {string|number} i - Reduced dimension to retrieve, either by name or index.
* @return {*} The contents of reduced dimension `i` as an multi-dimensional array-like object.
*/
reducedDimension(i) {
let output;
try {
output = this._reducedDimensions.entry(i);
} catch (e) {
throw new Error("failed to retrieve the specified reduced dimension from this " + this.constructor.className + "; " + e.message, { cause: e });
}
return output;
}
/**
* @return {Array} Array of strings containing the (ordered) names of the alternative experiments.
*/
alternativeExperimentNames() {
return this._alternativeExperiments.names();
}
/**
* @param {string|number} i - Alternative experiment to retrieve, either by name or index.
* @return {SummarizedExperiment} The specified alternative experiment `i`.
*/
alternativeExperiment(i) {
let output;
try {
output = this._alternativeExperiments.entry(i);
} catch (e) {
throw new Error("failed to retrieve the specified alternative experiment from this " + this.constructor.className + "; " + e.message, { cause: e });
}
return output;
}
/**************************************************************************
**************************************************************************
**************************************************************************/
/**
* @param {string|number} i - Identity of the reduced dimension to remove, either by name or index.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SingleCellExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SingleCellExperiment} The SingleCellExperiment after removing the specified assay.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
removeReducedDimension(i, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._reducedDimensions = target._reducedDimensions.delete(i, { inPlace });
} catch (e) {
throw new Error("failed to remove the specified reduced dimension from this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {string|number} i - Identity of the reduced dimension to remove, either by name or index.
* @return {SingleCellExperiment} A reference to this SingleCellExperiment after removing the specified assay.
*/
$removeReducedDimension(i) {
return this.removeReducedDimension(i, { inPlace: true });
}
/**
* @param {string|number} i - Identity of the reduced dimension to add, either by name or index.
* - If `i` is a number, the reduced dimension at the specified index is replaced.
* `i` should be non-negative and less than the number of reduced dimensions.
* - If `i` is a string, any reduced dimension with the same name is replaced.
* If no such reduced dimension exists, a new reduced dimension is appended to the list of reduced dimensions.
* @param {*} value - Multi-dimensional array-like object to set/add as the reduced dimension.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SingleCellExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SingleCellExperiment} The SingleCellExperiment with modified reduced dimensions.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
setReducedDimension(i, value, { inPlace = false } = {}) {
if (generics.NUMBER_OF_ROWS(value) != this.numberOfColumns()) {
throw new Error("number of rows of 'value' should be the same as the number of columns of this SingleCellExperiment");
}
let target = cutils.setterTarget(this, inPlace);
target._reducedDimensions = target._reducedDimensions.set(i, value, { inPlace });
return target;
}
/**
* @param {string|number} i - Identity of the reduced dimension to add, either by name or index.
* - If `i` is a number, the reduced dimension at the specified index is replaced.
* `i` should be non-negative and less than the number of reduced dimensions.
* - If `i` is a string, any reduced dimension with the same name is replaced.
* If no such reduced dimension exists, a new reduced dimension is appended to the list of reduced dimensions.
* @param {*} value - Multi-dimensional array-like object to set/add as the reduced dimension.
*
* @return {SingleCellExperiment} A reference to this SingleCellExperiment with modified reduced dimensions.
*/
$setReducedDimension(i, value) {
return this.setReducedDimension(i, value, { inPlace: true });
}
/**
* @param {Array} names - Array of strings containing the reduced dimension names.
* This should be of the same length as the number of reduced dimensions and contain unique values.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SummarizedExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SummarizedExperiment} The SummarizedExperiment with modified reduced dimension names.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
setReducedDimensionNames(names, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._reducedDimensions = target._reducedDimensions.setNames(names, { inPlace });
} catch (e) {
throw new Error("failed to set the reduced dimension names for this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {Array} names - Array of strings containing the reduced dimension names.
* This should be of the same length as the number of reduced dimensions and contain unique values.
* @return {SummarizedExperiment} A reference to this SummarizedExperiment with modified reduced dimension names.
*/
$setReducedDimensionNames(names) {
return this.setReducedDimensionNames(names, { inPlace: true });
}
/**
* @param {Array} i - Array of strings or indices specifying the reduced dimensions to retain in the slice.
* This should refer to unique reduced dimension names.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SummarizedExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SummarizedExperiment} The SummarizedExperiment with sliced reduced dimensions.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
sliceReducedDimensions(i, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._reducedDimensions = this._reducedDimensions.slice(i, { inPlace });
} catch (e) {
throw new Error("failed to slice the reduced dimensions for this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {Array} i - Array of strings or indices specifying the reduced dimensions to retain in the slice.
* This should refer to unique reduced dimension names.
* @return {SummarizedExperiment} A reference to this SummarizedExperiment with sliced reduced dimensions.
*/
$sliceReducedDimensions(i) {
return this.sliceReducedDimensions(i, { inPlace: true });
}
/**
* @param {string|number} i - Identity of the reduced dimension to remove, either by name or index.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SingleCellExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SingleCellExperiment} The SingleCellExperiment after removing the specified assay.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
removeAlternativeExperiment(i, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._alternativeExperiments = target._alternativeExperiments.delete(i, { inPlace });
} catch (e) {
throw new Error("failed to remove the specified alternative experiment from this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {string|number} i - Identity of the reduced dimension to remove, either by name or index.
* @return {SingleCellExperiment} A reference to this SingleCellExperiment after removing the specified assay.
*/
$removeAlternativeExperiment(i) {
return this.removeAlternativeExperiment(i, { inPlace: true });;
}
/**
* @param {string|number} i - Identity of the alternative experiment to add, either by name or index.
* - If `i` is a number, the alternative experiment at the specified index is replaced.
* `i` should be non-negative and less than the number of alternative experiments.
* - If `i` is a string, any alternative experiment with the same name is replaced.
* If no such alternative experiment exists, a new alternative experiment is appended to the list of alternative experiments.
* @param {*} value - Multi-dimensional array-like object to set/add as the alternative experiment.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SingleCellExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SingleCellExperiment} The SingleCellExperiment with modified alternative experiments.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
setAlternativeExperiment(i, value, { inPlace = false } = {}) {
if (!(value instanceof se.SummarizedExperiment) || generics.NUMBER_OF_COLUMNS(value) != this.numberOfColumns()) {
throw new Error("'value' should be a SummarizedExperiment with the same number of columns as this SingleCellExperiment");
}
let target = cutils.setterTarget(this, inPlace);
target._alternativeExperiments = target._alternativeExperiments.set(i, value, { inPlace });
return target;
}
/**
* @param {string|number} i - Identity of the alternative experiment to add, either by name or index.
* - If `i` is a number, the alternative experiment at the specified index is replaced.
* `i` should be non-negative and less than the number of alternative experiments.
* - If `i` is a string, any alternative experiment with the same name is replaced.
* If no such alternative experiment exists, a new alternative experiment is appended to the list of alternative experiments.
* @param {*} value - Multi-dimensional array-like object to set/add as the alternative experiment.
*
* @return {SingleCellExperiment} A reference to this SingleCellExperiment with modified alternative experiments.
*/
$setAlternativeExperiment(i, value) {
return this.setAlternativeExperiment(i, value, { inPlace: true });
}
/**
* @param {Array} names - Array of strings containing the alternative experiment names.
* This should be of the same length as the number of alternative experiments and contain unique values.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SummarizedExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SummarizedExperiment} The SummarizedExperiment with modified alternative experiment names.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
setAlternativeExperimentNames(names, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._alternativeExperiments = target._alternativeExperiments.setNames(names, { inPlace });
} catch (e) {
throw new Error("failed to set the alternative experiment names for this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {Array} names - Array of strings containing the alternative experiment names.
* This should be of the same length as the number of alternative experiments and contain unique values.
* @return {SummarizedExperiment} A reference to this SummarizedExperiment with modified alternative experiment names.
*/
$setAlternativeExperimentNames(names) {
return this.setAlternativeExperimentNames(names, { inPlace: true });
}
/**
* @param {Array} i - Array of strings or indices specifying the alternative experiments to retain in the slice.
* This should refer to unique alternative experiment names.
* @param {Object} [options={}] - Optional parameters.
* @param {boolean} [options.inPlace=false] - Whether to mutate this SummarizedExperiment instance in place.
* If `false`, a new instance is returned.
*
* @return {SummarizedExperiment} The SummarizedExperiment with sliced alternative experiments.
* If `inPlace = true`, this is a reference to the current instance, otherwise a new instance is created and returned.
*/
sliceAlternativeExperiments(i, { inPlace = false } = {}) {
let target = cutils.setterTarget(this, inPlace);
try {
target._alternativeExperiments = this._alternativeExperiments.slice(i, { inPlace });
} catch (e) {
throw new Error("failed to slice the alternative experiments for this " + this.constructor.className + "; " + e.message, { cause: e });
}
return target;
}
/**
* @param {Array} i - Array of strings or indices specifying the alternative experiments to retain in the slice.
* This should refer to unique alternative experiment names.
* @return {SummarizedExperiment} A reference to this SummarizedExperiment with sliced alternative experiments.
*/
$sliceAlternativeExperiments(i) {
return this.sliceAlternativeExperiments(i, { inPlace: true });
}
/**************************************************************************
**************************************************************************
**************************************************************************/
_bioconductor_SLICE_2D(output, rows, columns, { allowView = false }) {
super._bioconductor_SLICE_2D(output, rows, columns, { allowView });
if (columns !== null) {
output._reducedDimensions = this._reducedDimensions.apply(v => generics.SLICE_2D(v, columns, null, { allowView }));
output._alternativeExperiments = this._alternativeExperiments.apply(v => generics.SLICE_2D(v, null, columns, { allowView }));
} else {
output._reducedDimensions = this._reducedDimensions;
output._alternativeExperiments = this._alternativeExperiments;
}
}
_bioconductor_COMBINE_ROWS(output, objects) {
super._bioconductor_COMBINE_ROWS(output, objects);
output._reducedDimensions = this._reducedDimensions;
output._alternativeExperiments = this._alternativeExperiments;
return;
}
_bioconductor_COMBINE_COLUMNS(output, objects) {
super._bioconductor_COMBINE_COLUMNS(output, objects);
try {
output._reducedDimensions = il.InternalList.parallelCombine(objects.map(x => x._reducedDimensions), generics.COMBINE_ROWS);
} catch (e) {
throw new Error("failed to combine reduced dimensions for " + this.constructor.className + " objects; " + e.message, { cause: e });
}
try {
output._alternativeExperiments = il.InternalList.parallelCombine(objects.map(x => x._alternativeExperiments), generics.COMBINE_COLUMNS);
} catch (e) {
throw new Error("failed to combine alternative experiments for " + this.constructor.className + " objects; " + e.message, { cause: e });
}
return;
}
_bioconductor_CLONE(output, { deepCopy }) {
super._bioconductor_CLONE(output, { deepCopy });
output._reducedDimensions = cutils.cloneField(this._reducedDimensions, deepCopy);
output._alternativeExperiments = cutils.cloneField(this._alternativeExperiments, deepCopy);
return;
}
}