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.
139 lines (116 loc) • 5.16 kB
JavaScript
import * as scran from "scran.js";
import * as bioc from "bioconductor";
import * as utils from "./utils.js";
export async function readDataFrame(path, navigator) {
let colnames;
let columns = [];
let rownames = null;
let nrows;
let contents = await navigator.get(path + "/basic_columns.h5")
let out = scran.realizeFile(contents);
try {
let handle = new scran.H5File(out.path);
let ghandle = handle.open("data_frame");
nrows = ghandle.readAttribute("row-count").values[0];
colnames = ghandle.open("column_names", { load: true }).values;
if ("row_names" in ghandle.children) {
rownames = ghandle.open("row_names", { load: true }).values;
}
let chandle = ghandle.open("data");
for (var i = 0; i < colnames.length; i++) {
let iname = String(i);
if (!(iname in chandle.children)) {
// Try to load nested objects if they're DFs, but don't try too hard.
let nested_path = path + "/other_columns/" + iname;
try {
columns.push(await readDataFrame(nested_path, navigator));
} catch (e) {
console.warn("failed to extract nested DataFrame at '" + nested_path + "'; " + e.message);
columns.push(null);
}
continue;
}
let current;
let objtype = chandle.children[iname]
if (objtype == "DataSet") {
let dhandle = chandle.open(iname, { load: true });
let type = dhandle.readAttribute("type").values[0];
let placeholder = null;
if (dhandle.attributes.indexOf("missing-value-placeholder") >= 0) {
placeholder = dhandle.readAttribute("missing-value-placeholder").values[0];
}
if (type == "integer" || type == "string") {
current = dhandle.values;
if (placeholder !== null) {
current = utils.substitutePlaceholder(current, placeholder);
}
} else if (type == "number") {
current = dhandle.values;
if (!(current instanceof Float64Array) && !(current instanceof Float32Array)) {
current = new Float64Array(current);
}
if (placeholder !== null) {
current = utils.substitutePlaceholder(current, placeholder);
}
} else if (type == "boolean") {
current = new Array(dhandle.values.length);
if (placeholder !== null) {
for (const [i, x] of dhandle.values.entries()) {
if (x == placeholder) {
current[i] = null;
} else {
current[i] = (x != 0);
}
}
} else {
for (const [i, x] of dhandle.values.entries()) {
current[i] = (x != 0);
}
}
} else {
throw new Error("data frame column has unknown type '" + type + "'");
}
} else if (objtype == "Group") {
let fhandle = chandle.open(iname);
let type = fhandle.readAttribute("type").values[0];
if (type == "factor") {
let levels = fhandle.open("levels", { load: true }).values;
let chandle = fhandle.open("codes", { load: true });
let codes = chandle.values;
let placeholder = -1;
if (chandle.attributes.indexOf("missing-value-placeholder") >= 0) {
placeholder = chandle.readAttribute("missing-value-placeholder").values[0];
}
current = new Array(codes.length);
for (const [i, x] of codes.entries()) {
if (x != placeholder) {
current[i] = levels[x];
} else {
current[i] = null;
}
}
} else {
throw new Error("data frame column has unknown type '" + type + "'");
}
} else {
throw new Error("data frame column is of an unknown HDF5 object type");
}
columns.push(current);
}
} finally {
out.flush();
}
let new_columns = {};
let new_colnames = [];
for (var i = 0; i < columns.length; i++) {
if (columns[i] != null) {
new_columns[colnames[i]] = columns[i];
new_colnames.push(colnames[i]);
}
}
return new bioc.DataFrame(new_columns, {
columnOrder: new_colnames,
rowNames: rownames,
numberOfRows: nrows
});
}