bakana-takane
Version:
Extensions to the bakana single-cell analysis pipeline to accept takane-formatted datasets and results. This facilitates interoperability with the rest of the ArtifactDB ecosystem.
134 lines (117 loc) • 4.42 kB
JavaScript
import * as scran from "scran.js";
import * as se from "./SummarizedExperiment.js";
import * as arr from "./array.js";
import * as utils from "./utils.js";
function getMainExperimentName(obj_info) {
const sce_meta = obj_info.single_cell_experiment;
if ("main_experiment_name" in sce_meta) {
return sce_meta.main_experiment_name;
}
return null;
}
async function readAlternativeExperiments(
path,
obj_info,
listing,
navigator,
{
includeAlternativeExperimentColumnData = true,
includeAlternativeExperimentMetadata = false,
} = {})
{
if (listing.indexOf("alternative_experiments") == -1) {
return [];
}
const altnames = await navigator.fetchJson(path + "/alternative_experiments/names.json");
const tasks = [];
for (const [i, alt] of altnames.entries()) {
let alt_path = path + "/alternative_experiments/" + String(i);
tasks.push(se.readSummarizedExperiment(
alt_path,
navigator,
{
includeColumnData: includeAlternativeExperimentColumnData,
includeMetadata: includeAlternativeExperimentMetadata,
}
));
}
const alternatives = [];
const collected = await Promise.allSettled(tasks);
for (const [i, res] of collected.entries()) {
if (res.status == "fulfilled") {
alternatives.push({ name: altnames[i], experiment: res.value });
} else {
console.warn("failed to extract features for alternative experiment " + String(i) + "; " + res.reason);
}
}
return alternatives;
}
async function getReducedDimensionNames(path, obj_info, listing, navigator) {
if (listing.indexOf("reduced_dimensions") == -1) {
return [];
}
const all_rd_names = await navigator.fetchJson(path + "/reduced_dimensions/names.json");
const tasks = [];
// Only supporting dense_arrays in the reduced dimensions for now.
for (var i = 0; i < all_rd_names.length; i++) {
const red_path = path + "/reduced_dimensions/" + String(i);
tasks.push(navigator.fetchObjectMetadata(red_path));
}
const output = [];
const resolved = await Promise.all(tasks);
for (const [i, meta] of resolved.entries()) {
if (meta.type == "dense_array") {
output.push(all_rd_names[i]);
}
}
return output;
}
export async function readSingleCellExperiment(
path,
navigator,
{
includeColumnData = true,
includeMetadata = true,
includeReducedDimensionNames = true,
includeAlternativeExperiments = true,
includeAlternativeExperimentColumnData = false,
includeAlternativeExperimentMetadata = false,
} = {})
{
const output = await se.readSummarizedExperiment(path, navigator, { includeColumnData, includeMetadata });
const obj_info = await navigator.fetchObjectMetadata(path);
if ("single_cell_experiment" in obj_info) {
const listing = await navigator.listFiles(path);
const tasks = [ getMainExperimentName(obj_info) ];
if (includeAlternativeExperiments) {
tasks.push(readAlternativeExperiments(
path,
obj_info,
listing,
navigator,
{
includeAlternativeExperimentColumnData,
includeAlternativeExperimentMetadata
}
));
}
if (includeReducedDimensionNames) {
tasks.push(getReducedDimensionNames(path, obj_info, listing, navigator));
}
const resolved = await Promise.all(tasks);
output.main_experiment_name = resolved[0];
output.alternative_experiments = resolved[1];
if (includeReducedDimensionNames) {
output.reduced_dimension_names = resolved[2];
}
}
return output;
}
export async function readReducedDimensions(path, name, navigator, { maxDimensions = 10 } = {}) {
const red_names = await navigator.fetchJson(path + "/reduced_dimensions/names.json");
const chosen = red_names.indexOf(name);
if (chosen < 0) {
throw new Error("cannot find reduced dimensions entry named '" + name + "'");
}
return arr.readDenseMatrix(path + "/reduced_dimensions/" + String(chosen), navigator, { maxColumns: maxDimensions });
}