mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-15 12:47:56 +08:00
* Added typings to annoMatrix directory. (#2365) * Added review point updates. (#2365) * Fixed viewOf defect. Added review point updates. (#2365) * Updated test to use Field key interpolation. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Updated test to use Field key interpolation. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Updated test to use Field key interpolation. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Updated test to use Field key interpolation. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Linting. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Simplified while condition in middleware GC. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com> * Reverted embedding.ts prettier to resolve conflict. (#2365) Co-authored-by: Timmy Huang <tihuan@users.noreply.github.com>
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
||||
isubsetMask,
|
||||
} from "../../../src/annoMatrix";
|
||||
import { Dataframe } from "../../../src/util/dataframe";
|
||||
import { Field } from "../../../src/common/types/schema";
|
||||
|
||||
enableFetchMocks();
|
||||
|
||||
@@ -34,7 +35,7 @@ describe("AnnoMatrix", () => {
|
||||
expect(annoMatrix.nObs).toEqual(serverMocks.schema.schema.dataframe.nObs);
|
||||
expect(annoMatrix.nVar).toEqual(serverMocks.schema.schema.dataframe.nVar);
|
||||
expect(annoMatrix.isView).toBeFalsy();
|
||||
expect(annoMatrix.viewOf).toBeUndefined();
|
||||
expect(annoMatrix.viewOf).toBe(annoMatrix);
|
||||
expect(annoMatrix.rowIndex).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -42,7 +43,7 @@ describe("AnnoMatrix", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).once(serverMocks.annotationsObs(["name_0"]));
|
||||
|
||||
const df = await annoMatrix.fetch("obs", "name_0");
|
||||
const df = await annoMatrix.fetch(Field.obs, "name_0");
|
||||
expect(df).toBeInstanceOf(Dataframe);
|
||||
expect(df.colIndex.labels()).toEqual(["name_0"]);
|
||||
expect(df.dims).toEqual([annoMatrix.nObs, 1]);
|
||||
@@ -55,7 +56,7 @@ describe("AnnoMatrix", () => {
|
||||
.once(serverMocks.annotationsObs(["n_genes"]));
|
||||
|
||||
await expect(
|
||||
annoMatrix.fetch("obs", ["name_0", "n_genes"])
|
||||
annoMatrix.fetch(Field.obs, ["name_0", "n_genes"])
|
||||
).resolves.toBeInstanceOf(Dataframe);
|
||||
});
|
||||
|
||||
@@ -72,7 +73,7 @@ describe("AnnoMatrix", () => {
|
||||
).resolves.toBeInstanceOf(Dataframe);
|
||||
};
|
||||
|
||||
test("obs", async () => getLastTwo("obs"));
|
||||
test(Field.obs, async () => getLastTwo(Field.obs));
|
||||
test("var", async () => getLastTwo("var"));
|
||||
test("emb", async () => getLastTwo("emb"));
|
||||
});
|
||||
@@ -81,15 +82,15 @@ describe("AnnoMatrix", () => {
|
||||
// single string is a column name
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).once(serverMocks.annotationsObs(["n_genes"]));
|
||||
await expect(annoMatrix.fetch("obs", "n_genes")).resolves.toBeInstanceOf(
|
||||
Dataframe
|
||||
);
|
||||
await expect(
|
||||
annoMatrix.fetch(Field.obs, "n_genes")
|
||||
).resolves.toBeInstanceOf(Dataframe);
|
||||
|
||||
// array of column names, expecting n_genes to be cached.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).once(serverMocks.annotationsObs(["percent_mito"]));
|
||||
await expect(
|
||||
annoMatrix.fetch("obs", ["n_genes", "percent_mito"])
|
||||
annoMatrix.fetch(Field.obs, ["n_genes", "percent_mito"])
|
||||
).resolves.toBeInstanceOf(Dataframe);
|
||||
|
||||
// more complex value filter query, enumerated
|
||||
@@ -151,9 +152,9 @@ describe("AnnoMatrix", () => {
|
||||
|
||||
test("schema accessors", () => {
|
||||
expect(annoMatrix.getMatrixFields()).toEqual(
|
||||
expect.arrayContaining(["X", "obs", "emb", "var"])
|
||||
expect.arrayContaining(["X", Field.obs, "emb", "var"])
|
||||
);
|
||||
expect(annoMatrix.getMatrixColumns("obs")).toEqual(
|
||||
expect(annoMatrix.getMatrixColumns(Field.obs)).toEqual(
|
||||
expect.arrayContaining(["name_0", "n_genes", "louvain"])
|
||||
);
|
||||
expect(annoMatrix.getColumnSchema("emb", "umap")).toEqual({
|
||||
@@ -171,7 +172,7 @@ describe("AnnoMatrix", () => {
|
||||
test the mask & label access to subset via isubset and isubsetMask
|
||||
*/
|
||||
test("isubset", async () => {
|
||||
const rowList = [0, 10];
|
||||
const rowList = new Int32Array([0, 10]);
|
||||
const rowMask = new Uint8Array(annoMatrix.nObs);
|
||||
for (let i = 0; i < rowList.length; i += 1) {
|
||||
rowMask[rowList[i]] = 1;
|
||||
@@ -188,8 +189,8 @@ describe("AnnoMatrix", () => {
|
||||
(fetch as any)
|
||||
.once(serverMocks.annotationsObs(["n_genes"]))
|
||||
.once(serverMocks.annotationsObs(["n_genes"]));
|
||||
const ng1 = (await am1.fetch("obs", "n_genes")) as Dataframe;
|
||||
const ng2 = (await am2.fetch("obs", "n_genes")) as Dataframe;
|
||||
const ng1 = (await am1.fetch(Field.obs, "n_genes")) as Dataframe;
|
||||
const ng2 = (await am2.fetch(Field.obs, "n_genes")) as Dataframe;
|
||||
expect(ng1).toBeDefined();
|
||||
expect(ng2).toBeDefined();
|
||||
expect(ng1).toHaveLength(ng2.length);
|
||||
@@ -203,10 +204,10 @@ describe("AnnoMatrix", () => {
|
||||
describe("add/drop column", () => {
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'base' implicitly has an 'any' type.
|
||||
async function addDrop(base) {
|
||||
expect(base.getMatrixColumns("obs")).not.toContain("foo");
|
||||
expect(base.getMatrixColumns(Field.obs)).not.toContain("foo");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(base.fetch("obs", "foo")).rejects.toThrow(
|
||||
await expect(base.fetch(Field.obs, "foo")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
|
||||
@@ -216,9 +217,9 @@ describe("AnnoMatrix", () => {
|
||||
Float32Array,
|
||||
0
|
||||
);
|
||||
expect(base.getMatrixColumns("obs")).not.toContain("foo");
|
||||
expect(am1.getMatrixColumns("obs")).toContain("foo");
|
||||
const foo: Dataframe = await am1.fetch("obs", "foo");
|
||||
expect(base.getMatrixColumns(Field.obs)).not.toContain("foo");
|
||||
expect(am1.getMatrixColumns(Field.obs)).toContain("foo");
|
||||
const foo: Dataframe = await am1.fetch(Field.obs, "foo");
|
||||
expect(foo).toBeDefined();
|
||||
expect(foo).toBeInstanceOf(Dataframe);
|
||||
expect(foo).toHaveLength(am1.nObs);
|
||||
@@ -228,11 +229,11 @@ describe("AnnoMatrix", () => {
|
||||
|
||||
/* drop */
|
||||
const am2 = am1.dropObsColumn("foo");
|
||||
expect(base.getMatrixColumns("obs")).not.toContain("foo");
|
||||
expect(am2.getMatrixColumns("obs")).not.toContain("foo");
|
||||
expect(base.getMatrixColumns(Field.obs)).not.toContain("foo");
|
||||
expect(am2.getMatrixColumns(Field.obs)).not.toContain("foo");
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(am2.fetch("obs", "foo")).rejects.toThrow(
|
||||
await expect(am2.fetch(Field.obs, "foo")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
}
|
||||
@@ -245,10 +246,10 @@ describe("AnnoMatrix", () => {
|
||||
const am1 = clip(annoMatrix, 0.1, 0.9);
|
||||
await addDrop(am1);
|
||||
|
||||
const am2 = isubset(am1, [0, 1, 2, 20, 30, 400]);
|
||||
const am2 = isubset(am1, new Int32Array([0, 1, 2, 20, 30, 400]));
|
||||
await addDrop(am2);
|
||||
|
||||
const am3 = isubset(annoMatrix, [10, 0, 7, 3]);
|
||||
const am3 = isubset(annoMatrix, new Int32Array([10, 0, 7, 3]));
|
||||
await addDrop(am3);
|
||||
|
||||
const am4 = clip(am3, 0, 1);
|
||||
@@ -257,10 +258,10 @@ describe("AnnoMatrix", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockResponse(serverMocks.responder);
|
||||
|
||||
await am1.fetch("obs", am1.getMatrixColumns("obs"));
|
||||
await am2.fetch("obs", am2.getMatrixColumns("obs"));
|
||||
await am3.fetch("obs", am3.getMatrixColumns("obs"));
|
||||
await am4.fetch("obs", am4.getMatrixColumns("obs"));
|
||||
await am1.fetch(Field.obs, am1.getMatrixColumns(Field.obs));
|
||||
await am2.fetch(Field.obs, am2.getMatrixColumns(Field.obs));
|
||||
await am3.fetch(Field.obs, am3.getMatrixColumns(Field.obs));
|
||||
await am4.fetch(Field.obs, am4.getMatrixColumns(Field.obs));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).resetMocks();
|
||||
@@ -287,7 +288,7 @@ describe("AnnoMatrix", () => {
|
||||
"unassigned"
|
||||
);
|
||||
|
||||
const testVal = await am.fetch("obs", "test");
|
||||
const testVal = await am.fetch(Field.obs, "test");
|
||||
expect(testVal.col("test").asArray()).toEqual(
|
||||
new Array(am.nObs).fill("unassigned")
|
||||
);
|
||||
@@ -295,7 +296,7 @@ describe("AnnoMatrix", () => {
|
||||
/* set values in column */
|
||||
const whichRows = [1, 2, 10];
|
||||
const am1 = await am.setObsColumnValues("test", whichRows, "yo");
|
||||
const testVal1 = await am1.fetch("obs", "test");
|
||||
const testVal1 = await am1.fetch(Field.obs, "test");
|
||||
const expt = new Array(am1.nObs).fill("unassigned");
|
||||
for (let i = 0; i < whichRows.length; i += 1) {
|
||||
const offset = am1.rowIndex.getOffset(whichRows[i]);
|
||||
@@ -303,8 +304,8 @@ describe("AnnoMatrix", () => {
|
||||
}
|
||||
expect(testVal1).not.toBe(testVal);
|
||||
expect(testVal1.col("test").asArray()).toEqual(expt);
|
||||
expect(am1.getColumnSchema("obs", "test").type).toBe("categorical");
|
||||
expect(am1.getColumnSchema("obs", "test").categories).toEqual(
|
||||
expect(am1.getColumnSchema(Field.obs, "test").type).toBe("categorical");
|
||||
expect(am1.getColumnSchema(Field.obs, "test").categories).toEqual(
|
||||
expect.arrayContaining(["unassigned", "red", "green", "yo"])
|
||||
);
|
||||
|
||||
@@ -312,7 +313,7 @@ describe("AnnoMatrix", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
am = am1.dropObsColumn("test");
|
||||
await expect(am.fetch("obs", "test")).rejects.toThrow(
|
||||
await expect(am.fetch(Field.obs, "test")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
}
|
||||
@@ -325,18 +326,18 @@ describe("AnnoMatrix", () => {
|
||||
const am1 = clip(annoMatrix, 0.1, 0.9);
|
||||
await addSetDrop(am1);
|
||||
|
||||
const am2 = isubset(am1, [0, 1, 2, 10, 20, 30, 400]);
|
||||
const am2 = isubset(am1, new Int32Array([0, 1, 2, 10, 20, 30, 400]));
|
||||
await addSetDrop(am2);
|
||||
|
||||
const am3 = isubset(annoMatrix, [10, 1, 0, 30, 2]);
|
||||
const am3 = isubset(annoMatrix, new Int32Array([10, 1, 0, 30, 2]));
|
||||
await addSetDrop(am3);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockResponse(serverMocks.responder);
|
||||
|
||||
await am1.fetch("obs", am1.getMatrixColumns("obs"));
|
||||
await am2.fetch("obs", am2.getMatrixColumns("obs"));
|
||||
await am3.fetch("obs", am3.getMatrixColumns("obs"));
|
||||
await am1.fetch(Field.obs, am1.getMatrixColumns(Field.obs));
|
||||
await am2.fetch(Field.obs, am2.getMatrixColumns(Field.obs));
|
||||
await am3.fetch(Field.obs, am3.getMatrixColumns(Field.obs));
|
||||
|
||||
await addSetDrop(am1);
|
||||
await addSetDrop(am2);
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
} from "../../../src/annoMatrix";
|
||||
import { Dataframe } from "../../../src/util/dataframe";
|
||||
import { rangeFill } from "../../../src/util/range";
|
||||
import { Field, Schema } from "../../../src/common/types/schema";
|
||||
|
||||
enableFetchMocks();
|
||||
|
||||
@@ -29,7 +30,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// reset all fetch mocking state
|
||||
annoMatrix = new AnnoMatrixLoader(
|
||||
serverMocks.baseDataURL,
|
||||
serverMocks.schema.schema
|
||||
serverMocks.schema.schema as Schema
|
||||
);
|
||||
crossfilter = new AnnoMatrixObsCrossfilter(annoMatrix);
|
||||
});
|
||||
@@ -76,7 +77,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(fetch as any).once(
|
||||
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
|
||||
);
|
||||
let newCrossfilter = await crossfilter.select("obs", "louvain", {
|
||||
let newCrossfilter = await crossfilter.select(Field.obs, "louvain", {
|
||||
mode: "none",
|
||||
});
|
||||
|
||||
@@ -87,7 +88,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
expect((fetch as any).mock.calls).toHaveLength(1);
|
||||
|
||||
newCrossfilter = await crossfilter.select("obs", "louvain", {
|
||||
newCrossfilter = await crossfilter.select(Field.obs, "louvain", {
|
||||
mode: "all",
|
||||
});
|
||||
expect(newCrossfilter.countSelected()).toEqual(annoMatrix.nObs);
|
||||
@@ -100,7 +101,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(fetch as any).once(
|
||||
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
|
||||
);
|
||||
xfltr = await crossfilter.select("obs", "louvain", {
|
||||
xfltr = await crossfilter.select(Field.obs, "louvain", {
|
||||
mode: "exact",
|
||||
values: ["NK cells", "B cells"],
|
||||
});
|
||||
@@ -133,7 +134,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
)
|
||||
);
|
||||
|
||||
const df: Dataframe = await annoMatrix.fetch("obs", "louvain");
|
||||
const df: Dataframe = await annoMatrix.fetch(Field.obs, "louvain");
|
||||
const values = df.col("louvain").asArray();
|
||||
const selected = xfltr.allSelectedMask();
|
||||
values.every(
|
||||
@@ -146,7 +147,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(fetch as any).once(
|
||||
serverMocks.dataframeResponse(["n_genes"], [new Int32Array(obsNGenes)])
|
||||
);
|
||||
xfltr = await xfltr.select("obs", "n_genes", {
|
||||
xfltr = await xfltr.select(Field.obs, "n_genes", {
|
||||
mode: "range",
|
||||
lo: 0,
|
||||
hi: 500,
|
||||
@@ -258,7 +259,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(fetch as any).once(
|
||||
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
|
||||
);
|
||||
xfltr = await xfltr.select("obs", "louvain", {
|
||||
xfltr = await xfltr.select(Field.obs, "louvain", {
|
||||
mode: "exact",
|
||||
values: ["NK cells", "B cells"],
|
||||
});
|
||||
@@ -267,7 +268,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
expect(xfltr.countSelected()).toEqual(240);
|
||||
|
||||
const df: Dataframe = (await annoMatrixSubset.fetch(
|
||||
"obs",
|
||||
Field.obs,
|
||||
"louvain"
|
||||
)) as Dataframe;
|
||||
expect(df).toBeDefined();
|
||||
@@ -291,7 +292,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(crossfilter.select("obs", "foo")).rejects.toThrow(
|
||||
await expect(crossfilter.select(Field.obs, "foo")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
});
|
||||
@@ -304,7 +305,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
async function helperAddTestCol(cf: any, colName: any, colSchema = null) {
|
||||
expect(
|
||||
cf.annoMatrix.getMatrixColumns("obs").includes(colName)
|
||||
cf.annoMatrix.getMatrixColumns(Field.obs).includes(colName)
|
||||
).toBeFalsy();
|
||||
|
||||
if (colSchema === null) {
|
||||
@@ -326,7 +327,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(v: any) => v.name === colName
|
||||
)
|
||||
).toHaveLength(1);
|
||||
const df = await xfltr.annoMatrix.fetch("obs", colName);
|
||||
const df = await xfltr.annoMatrix.fetch(Field.obs, colName);
|
||||
expect(df.hasCol(colName)).toBeTruthy();
|
||||
return xfltr;
|
||||
}
|
||||
@@ -334,7 +335,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
test("addObsColumn", async () => {
|
||||
expect(crossfilter.countSelected()).toBe(annoMatrix.nObs);
|
||||
expect(
|
||||
crossfilter.annoMatrix.getMatrixColumns("obs").includes("foo")
|
||||
crossfilter.annoMatrix.getMatrixColumns(Field.obs).includes("foo")
|
||||
).toBeFalsy();
|
||||
const xfltr = crossfilter.addObsColumn(
|
||||
{ name: "foo", type: "categorical", categories: ["A"] },
|
||||
@@ -345,7 +346,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// check schema updates correctly.
|
||||
expect(xfltr.countSelected()).toBe(annoMatrix.nObs);
|
||||
expect(
|
||||
xfltr.annoMatrix.getMatrixColumns("obs").includes("foo")
|
||||
xfltr.annoMatrix.getMatrixColumns(Field.obs).includes("foo")
|
||||
).toBeTruthy();
|
||||
expect(xfltr.annoMatrix.schema.annotations.obsByName.foo).toMatchObject({
|
||||
name: "foo",
|
||||
@@ -359,7 +360,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
).toHaveLength(1);
|
||||
|
||||
// check data update.
|
||||
const df: Dataframe = await xfltr.annoMatrix.fetch("obs", "foo");
|
||||
const df: Dataframe = await xfltr.annoMatrix.fetch(Field.obs, "foo");
|
||||
expect(
|
||||
df
|
||||
.col("foo")
|
||||
@@ -408,13 +409,13 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
expect(xfltr.annoMatrix.schema.annotations.obsByName.foo).toBeUndefined();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow(
|
||||
await expect(xfltr.annoMatrix.fetch(Field.obs, "foo")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
|
||||
// now same, but ensure we have built an index before doing the drop
|
||||
xfltr = await helperAddTestCol(crossfilter, "bar");
|
||||
xfltr = await xfltr.select("obs", "bar", {
|
||||
xfltr = await xfltr.select(Field.obs, "bar", {
|
||||
mode: "exact",
|
||||
values: "whatever",
|
||||
});
|
||||
@@ -422,9 +423,9 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
await expect(
|
||||
xfltr.select(Field.obs, "bar", { mode: "all" })
|
||||
).rejects.toThrow("unknown column name");
|
||||
});
|
||||
|
||||
test("renameObsColumn", async () => {
|
||||
@@ -441,23 +442,25 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// add a column, then rename it.
|
||||
xfltr = await helperAddTestCol(crossfilter, "foo");
|
||||
xfltr = xfltr.renameObsColumn("foo", "bar");
|
||||
expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toBeUndefined();
|
||||
expect(xfltr.annoMatrix.getColumnSchema("obs", "bar")).toMatchObject({
|
||||
expect(
|
||||
xfltr.annoMatrix.getColumnSchema(Field.obs, "foo")
|
||||
).toBeUndefined();
|
||||
expect(xfltr.annoMatrix.getColumnSchema(Field.obs, "bar")).toMatchObject({
|
||||
name: "bar",
|
||||
type: "categorical",
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(xfltr.annoMatrix.fetch("obs", "foo")).rejects.toThrow(
|
||||
await expect(xfltr.annoMatrix.fetch(Field.obs, "foo")).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
const df = await xfltr.annoMatrix.fetch("obs", "bar");
|
||||
const df = await xfltr.annoMatrix.fetch(Field.obs, "bar");
|
||||
expect(df.hasCol("bar")).toBeTruthy();
|
||||
|
||||
// now same, but ensure we have built an index before doing the rename
|
||||
xfltr = await helperAddTestCol(crossfilter, "bar");
|
||||
xfltr = await xfltr.select("obs", "bar", {
|
||||
xfltr = await xfltr.select(Field.obs, "bar", {
|
||||
mode: "exact",
|
||||
values: "whatever",
|
||||
});
|
||||
@@ -465,11 +468,11 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(fetch as any).mockRejectOnce(new Error("unknown column name"));
|
||||
await expect(xfltr.select("obs", "bar", { mode: "all" })).rejects.toThrow(
|
||||
"unknown column name"
|
||||
);
|
||||
await expect(
|
||||
xfltr.select("obs", "xyz", { mode: "none" })
|
||||
xfltr.select(Field.obs, "bar", { mode: "all" })
|
||||
).rejects.toThrow("unknown column name");
|
||||
await expect(
|
||||
xfltr.select(Field.obs, "xyz", { mode: "none" })
|
||||
).resolves.toBeInstanceOf(AnnoMatrixObsCrossfilter);
|
||||
});
|
||||
|
||||
@@ -493,7 +496,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
categories: ["unassigned"],
|
||||
});
|
||||
xfltr = xfltr.addObsAnnoCategory("foo", "a-new-label");
|
||||
expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
expect(xfltr.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining(["a-new-label", "unassigned"]),
|
||||
@@ -511,12 +514,12 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
type: "categorical",
|
||||
categories: ["unassigned"],
|
||||
});
|
||||
xfltr = await xfltr.select("obs", "bar", {
|
||||
xfltr = await xfltr.select(Field.obs, "bar", {
|
||||
mode: "exact",
|
||||
values: "something",
|
||||
});
|
||||
xfltr = xfltr.addObsAnnoCategory("bar", "a-new-label");
|
||||
expect(xfltr.annoMatrix.getColumnSchema("obs", "bar")).toMatchObject({
|
||||
expect(xfltr.annoMatrix.getColumnSchema(Field.obs, "bar")).toMatchObject({
|
||||
name: "bar",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining(["a-new-label", "unassigned"]),
|
||||
@@ -540,14 +543,14 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
type: "categorical",
|
||||
categories: ["unassigned", "red", "green", "blue"],
|
||||
});
|
||||
xfltr = await xfltr.select("obs", "foo", { mode: "all" });
|
||||
xfltr = await xfltr.select(Field.obs, "foo", { mode: "all" });
|
||||
expect(
|
||||
(await xfltr.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.every((v: any) => v === "unassigned")
|
||||
).toBeTruthy();
|
||||
expect(xfltr.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
expect(xfltr.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
@@ -561,21 +564,23 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
// remove an unused category
|
||||
const xfltr1 = await xfltr.removeObsAnnoCategory("foo", "red", "mumble");
|
||||
expect(
|
||||
(await xfltr1.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr1.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.every((v: any) => v === "unassigned")
|
||||
).toBeTruthy();
|
||||
expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"green",
|
||||
"blue",
|
||||
"mumble",
|
||||
]),
|
||||
});
|
||||
expect(xfltr1.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject(
|
||||
{
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"green",
|
||||
"blue",
|
||||
"mumble",
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
// remove a used category
|
||||
const xfltr2 = await xfltr.removeObsAnnoCategory(
|
||||
@@ -584,16 +589,18 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
"red"
|
||||
);
|
||||
expect(
|
||||
(await xfltr2.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr2.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.every((v: any) => v === "red")
|
||||
).toBeTruthy();
|
||||
expect(xfltr2.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining(["green", "blue", "red"]),
|
||||
});
|
||||
expect(xfltr2.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject(
|
||||
{
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining(["green", "blue", "red"]),
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test("setObsColumnValues", async () => {
|
||||
@@ -611,7 +618,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
type: "categorical",
|
||||
categories: ["unassigned", "red", "green", "blue"],
|
||||
});
|
||||
xfltr = await xfltr.select("obs", "foo", { mode: "all" });
|
||||
xfltr = await xfltr.select(Field.obs, "foo", { mode: "all" });
|
||||
|
||||
// catch unknown row label
|
||||
await expect(() =>
|
||||
@@ -620,14 +627,14 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
|
||||
// set a few rows
|
||||
expect(
|
||||
(await xfltr.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.every((v: any) => v === "unassigned")
|
||||
).toBeTruthy();
|
||||
const xfltr1 = await xfltr.setObsColumnValues("foo", [0, 10], "purple");
|
||||
expect(
|
||||
(await xfltr1.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr1.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray()
|
||||
.every(
|
||||
@@ -636,20 +643,22 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
v === "unassigned" || (v === "purple" && (i === 0 || i === 10))
|
||||
)
|
||||
).toBeTruthy();
|
||||
expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"purple",
|
||||
]),
|
||||
});
|
||||
expect(xfltr1.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject(
|
||||
{
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"purple",
|
||||
]),
|
||||
}
|
||||
);
|
||||
|
||||
expect(xfltr1.countSelected()).toEqual(xfltr1.annoMatrix.nObs);
|
||||
const xfltr2 = await xfltr1.select("obs", "foo", {
|
||||
const xfltr2 = await xfltr1.select(Field.obs, "foo", {
|
||||
mode: "exact",
|
||||
values: ["purple"],
|
||||
});
|
||||
@@ -672,7 +681,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
type: "categorical",
|
||||
categories: ["unassigned", "red", "green", "blue"],
|
||||
});
|
||||
xfltr = await xfltr.select("obs", "foo", {
|
||||
xfltr = await xfltr.select(Field.obs, "foo", {
|
||||
mode: "exact",
|
||||
values: "red",
|
||||
});
|
||||
@@ -683,12 +692,12 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
).rejects.toThrow("unknown category");
|
||||
|
||||
let xfltr1 = await xfltr.setObsColumnValues("foo", [0, 10], "purple");
|
||||
xfltr1 = await xfltr1.select("obs", "foo", {
|
||||
xfltr1 = await xfltr1.select(Field.obs, "foo", {
|
||||
mode: "exact",
|
||||
values: "purple",
|
||||
});
|
||||
expect(
|
||||
(await xfltr1.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr1.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.filter((v: any) => v === "purple")
|
||||
@@ -696,29 +705,31 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
|
||||
xfltr1 = await xfltr1.resetObsColumnValues("foo", "purple", "magenta");
|
||||
expect(
|
||||
(await xfltr1.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr1.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.filter((v: any) => v === "magenta")
|
||||
).toHaveLength(2);
|
||||
expect(
|
||||
(await xfltr1.annoMatrix.fetch("obs", "foo"))
|
||||
(await xfltr1.annoMatrix.fetch(Field.obs, "foo"))
|
||||
.col("foo")
|
||||
.asArray() // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.filter((v: any) => v === "purple")
|
||||
).toHaveLength(0);
|
||||
expect(xfltr1.annoMatrix.getColumnSchema("obs", "foo")).toMatchObject({
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"purple",
|
||||
"magenta",
|
||||
]),
|
||||
});
|
||||
expect(xfltr1.annoMatrix.getColumnSchema(Field.obs, "foo")).toMatchObject(
|
||||
{
|
||||
name: "foo",
|
||||
type: "categorical",
|
||||
categories: expect.arrayContaining([
|
||||
"unassigned",
|
||||
"red",
|
||||
"green",
|
||||
"blue",
|
||||
"purple",
|
||||
"magenta",
|
||||
]),
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -729,7 +740,7 @@ describe("AnnoMatrixCrossfilter", () => {
|
||||
(fetch as any).once(
|
||||
serverMocks.dataframeResponse(["louvain"], [obsLouvain])
|
||||
);
|
||||
const xfltr = await crossfilter.select("obs", "louvain", {
|
||||
const xfltr = await crossfilter.select(Field.obs, "louvain", {
|
||||
mode: "exact",
|
||||
values: "B cells",
|
||||
});
|
||||
|
||||
@@ -4,31 +4,34 @@ import {
|
||||
_whereCacheCreate,
|
||||
_whereCacheMerge,
|
||||
} from "../../../src/annoMatrix/whereCache";
|
||||
import { Field, Schema } from "../../../src/common/types/schema";
|
||||
import { Query } from "../../../src/annoMatrix/query";
|
||||
|
||||
const schema = {};
|
||||
const schema = {} as Schema;
|
||||
|
||||
describe("whereCache", () => {
|
||||
test("whereCacheGet - where query, missing cache values", () => {
|
||||
expect(
|
||||
_whereCacheGet({}, schema, "X", {
|
||||
_whereCacheGet({}, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
field: Field.var,
|
||||
column: "foo",
|
||||
value: "bar",
|
||||
},
|
||||
})
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet({}, schema, "X", {
|
||||
_whereCacheGet({}, schema, Field.X, {
|
||||
summarize: {
|
||||
field: "var",
|
||||
method: "mean",
|
||||
field: Field.var,
|
||||
column: "foo",
|
||||
values: ["bar"],
|
||||
},
|
||||
})
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet({ where: { X: {} } }, schema, "X", {
|
||||
_whereCacheGet({ where: { X: {} } }, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "foo",
|
||||
@@ -37,7 +40,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet({ where: { X: { var: new Map() } } }, schema, "X", {
|
||||
_whereCacheGet({ where: { X: { var: new Map() } } }, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "foo",
|
||||
@@ -49,7 +52,7 @@ describe("whereCache", () => {
|
||||
_whereCacheGet(
|
||||
{ where: { X: { var: new Map([["foo", new Map()]]) } } },
|
||||
schema,
|
||||
"X",
|
||||
Field.X,
|
||||
{
|
||||
where: {
|
||||
field: "var",
|
||||
@@ -63,7 +66,7 @@ describe("whereCache", () => {
|
||||
|
||||
test("whereCacheGet - summarize query, missing cache values", () => {
|
||||
expect(
|
||||
_whereCacheGet({}, schema, "X", {
|
||||
_whereCacheGet({}, schema, Field.X, {
|
||||
summarize: {
|
||||
method: "mean",
|
||||
field: "var",
|
||||
@@ -76,7 +79,7 @@ describe("whereCache", () => {
|
||||
_whereCacheGet(
|
||||
{ summarize: { X: { mean: { var: new Map() } } } },
|
||||
schema,
|
||||
"X",
|
||||
Field.X,
|
||||
{
|
||||
summarize: {
|
||||
method: "mean",
|
||||
@@ -122,7 +125,7 @@ describe("whereCache", () => {
|
||||
};
|
||||
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "foo",
|
||||
@@ -131,7 +134,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([0]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
summarize: {
|
||||
method: "mean",
|
||||
field: "var",
|
||||
@@ -141,7 +144,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([0]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "foo",
|
||||
@@ -150,7 +153,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([1, 2]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
summarize: {
|
||||
method: "mean",
|
||||
field: "var",
|
||||
@@ -159,9 +162,12 @@ describe("whereCache", () => {
|
||||
},
|
||||
})
|
||||
).toEqual([1, 2]);
|
||||
expect(_whereCacheGet(whereCache, schema, "Y", {})).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
// Force invalid field value Y
|
||||
_whereCacheGet(whereCache, schema, "Y" as Field, {} as Query)
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
where: {
|
||||
field: "whoknows",
|
||||
column: "whatever",
|
||||
@@ -170,7 +176,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "whatever",
|
||||
@@ -179,7 +185,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual([undefined]);
|
||||
expect(
|
||||
_whereCacheGet(whereCache, schema, "X", {
|
||||
_whereCacheGet(whereCache, schema, Field.X, {
|
||||
where: {
|
||||
field: "var",
|
||||
column: "foo",
|
||||
@@ -198,7 +204,7 @@ describe("whereCache", () => {
|
||||
},
|
||||
};
|
||||
const wc = _whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{
|
||||
where: {
|
||||
field: "queryField",
|
||||
@@ -212,7 +218,7 @@ describe("whereCache", () => {
|
||||
expect(wc).toEqual(
|
||||
expect.objectContaining({
|
||||
where: {
|
||||
field: {
|
||||
[Field.obs]: {
|
||||
queryField: expect.any(Map),
|
||||
},
|
||||
},
|
||||
@@ -220,18 +226,19 @@ describe("whereCache", () => {
|
||||
);
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
expect((wc.where as any).field.queryField.has("queryColumn")).toEqual(true);
|
||||
expect((wc.where as any)[Field.obs].queryField.has("queryColumn")).toEqual(true);
|
||||
expect(
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(wc.where as any).field.queryField.get("queryColumn")
|
||||
(wc.where as any)[Field.obs].queryField.get("queryColumn")
|
||||
).toBeInstanceOf(Map);
|
||||
expect(
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(wc.where as any).field.queryField.get("queryColumn").has("queryValue")
|
||||
(wc.where as any)[Field.obs].queryField.get("queryColumn").has("queryValue")
|
||||
).toEqual(true);
|
||||
expect(_whereCacheGet(wc, schema, "field", query)).toEqual([0, 1, 2]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert wc to be non-null
|
||||
expect(_whereCacheGet(wc!, schema, Field.obs, query)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
test("whereCacheCreate, summarize query", () => {
|
||||
@@ -243,12 +250,14 @@ describe("whereCache", () => {
|
||||
values: ["queryValue"],
|
||||
},
|
||||
};
|
||||
const wc = _whereCacheCreate("field", query, [0, 1, 2]);
|
||||
expect(_whereCacheGet(wc, schema, "field", query)).toEqual([0, 1, 2]);
|
||||
const wc = _whereCacheCreate(Field.obs, query, [0, 1, 2]);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert wc to be non-null
|
||||
expect(_whereCacheGet(wc!, schema, Field.obs, query)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
test("whereCacheCreate, unknown query type", () => {
|
||||
expect(_whereCacheCreate("field", { foobar: true }, [1])).toEqual({});
|
||||
// @ts-expect-error --- force invalid query {foobar: true}
|
||||
expect(_whereCacheCreate(Field.obs, { foobar: true }, [1])).toEqual({});
|
||||
});
|
||||
|
||||
test("whereCacheMerge, where queries", () => {
|
||||
@@ -256,19 +265,20 @@ describe("whereCache", () => {
|
||||
|
||||
// remember, will mutate dst
|
||||
const src = _whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{ where: { field: "queryField", column: "queryColumn", value: "foo" } },
|
||||
["foo"]
|
||||
);
|
||||
|
||||
const dst1 = _whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{ where: { field: "queryField", column: "queryColumn", value: "bar" } },
|
||||
["dst1"]
|
||||
);
|
||||
wc = _whereCacheMerge(dst1, src);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert dst1 and src to be non-null
|
||||
wc = _whereCacheMerge(dst1!, src!);
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -277,7 +287,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual(["foo"]);
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -287,13 +297,14 @@ describe("whereCache", () => {
|
||||
).toEqual(["dst1"]);
|
||||
|
||||
const dst2 = _whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{ where: { field: "queryField", column: "queryColumn", value: "bar" } },
|
||||
["dst2"]
|
||||
);
|
||||
wc = _whereCacheMerge(dst2, dst1, src);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert dst2m, dst1 and src to be non-null
|
||||
wc = _whereCacheMerge(dst2!, dst1!, src!);
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -302,7 +313,7 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual(["foo"]);
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -311,17 +322,20 @@ describe("whereCache", () => {
|
||||
})
|
||||
).toEqual(["dst1"]);
|
||||
|
||||
wc = _whereCacheMerge({}, src);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert src to be non-null
|
||||
wc = _whereCacheMerge({}, src!);
|
||||
expect(wc).toEqual(src);
|
||||
|
||||
wc = _whereCacheMerge({ where: { field: { queryField: new Map() } } }, src);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert src to be non-null
|
||||
wc = _whereCacheMerge({ where: { obs: { queryField: new Map() } } }, src!);
|
||||
expect(wc).toEqual(src);
|
||||
});
|
||||
|
||||
test("whereCacheMerge, mixed queries", () => {
|
||||
const wc = _whereCacheMerge(
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert wc to be non-null
|
||||
_whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{
|
||||
where: {
|
||||
field: "queryField",
|
||||
@@ -330,9 +344,10 @@ describe("whereCache", () => {
|
||||
},
|
||||
},
|
||||
["a"]
|
||||
),
|
||||
)!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion --- assert wc to be non-null
|
||||
_whereCacheCreate(
|
||||
"field",
|
||||
Field.obs,
|
||||
{
|
||||
summarize: {
|
||||
method: "mean",
|
||||
@@ -342,11 +357,11 @@ describe("whereCache", () => {
|
||||
},
|
||||
},
|
||||
["b"]
|
||||
)
|
||||
)!
|
||||
);
|
||||
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -356,7 +371,7 @@ describe("whereCache", () => {
|
||||
).toEqual(["a"]);
|
||||
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
summarize: {
|
||||
method: "mean",
|
||||
field: "queryField",
|
||||
@@ -367,7 +382,7 @@ describe("whereCache", () => {
|
||||
).toEqual(["b"]);
|
||||
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
where: {
|
||||
field: "queryField",
|
||||
column: "queryColumn",
|
||||
@@ -377,7 +392,7 @@ describe("whereCache", () => {
|
||||
).toEqual([undefined]);
|
||||
|
||||
expect(
|
||||
_whereCacheGet(wc, schema, "field", {
|
||||
_whereCacheGet(wc, schema, Field.obs, {
|
||||
summarize: {
|
||||
method: "no-such-method",
|
||||
field: "queryField",
|
||||
|
||||
@@ -7,6 +7,7 @@ import { ThunkAction } from "redux-thunk";
|
||||
import { AnnoMatrixObsCrossfilter } from "../annoMatrix";
|
||||
import type { AppDispatch, RootState } from "../reducers";
|
||||
import { _setEmbeddingSubset } from "../util/stateManager/viewStackHelpers";
|
||||
import { Field } from "../common/types/schema";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export async function _switchEmbedding(
|
||||
@@ -26,7 +27,7 @@ export async function _switchEmbedding(
|
||||
const obsCrossfilter = await new AnnoMatrixObsCrossfilter(
|
||||
annoMatrix,
|
||||
prevCrossfilter.obsCrossfilter
|
||||
).select("emb", newEmbeddingName, {
|
||||
).select(Field.emb, newEmbeddingName, {
|
||||
mode: "all",
|
||||
});
|
||||
return [annoMatrix, obsCrossfilter];
|
||||
@@ -41,17 +42,19 @@ export const layoutChoiceAction: ActionCreator<
|
||||
On layout choice, make sure we have selected all on the previous layout, AND the new
|
||||
layout.
|
||||
*/
|
||||
const { annoMatrix: prevAnnoMatrix, obsCrossfilter: prevCrossfilter } =
|
||||
getState();
|
||||
const [annoMatrix, obsCrossfilter] = await _switchEmbedding(
|
||||
prevAnnoMatrix,
|
||||
prevCrossfilter,
|
||||
newLayoutChoice
|
||||
);
|
||||
dispatch({
|
||||
type: "set layout choice",
|
||||
layoutChoice: newLayoutChoice,
|
||||
obsCrossfilter,
|
||||
annoMatrix,
|
||||
});
|
||||
};
|
||||
const {
|
||||
annoMatrix: prevAnnoMatrix,
|
||||
obsCrossfilter: prevCrossfilter,
|
||||
} = getState();
|
||||
const [annoMatrix, obsCrossfilter] = await _switchEmbedding(
|
||||
prevAnnoMatrix,
|
||||
prevCrossfilter,
|
||||
newLayoutChoice
|
||||
);
|
||||
dispatch({
|
||||
type: "set layout choice",
|
||||
layoutChoice: newLayoutChoice,
|
||||
obsCrossfilter,
|
||||
annoMatrix,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2,6 +2,9 @@ import {
|
||||
Dataframe,
|
||||
IdentityInt32Index,
|
||||
dataframeMemo,
|
||||
LabelType,
|
||||
DataframeValue,
|
||||
DataframeValueArray,
|
||||
} from "../util/dataframe";
|
||||
import {
|
||||
_getColumnDimensionNames,
|
||||
@@ -10,45 +13,70 @@ import {
|
||||
_getWritableColumns,
|
||||
} from "./schema";
|
||||
import { indexEntireSchema } from "../util/stateManager/schemaHelpers";
|
||||
import { _whereCacheGet, _whereCacheMerge } from "./whereCache";
|
||||
import {
|
||||
_whereCacheGet,
|
||||
_whereCacheMerge,
|
||||
WhereCache,
|
||||
WhereCacheColumnLabels,
|
||||
} from "./whereCache";
|
||||
import _shallowClone from "./clone";
|
||||
import { _queryValidate, _queryCacheKey } from "./query";
|
||||
import { _queryValidate, _queryCacheKey, Query } from "./query";
|
||||
import { GCHints } from "../common/types/entities";
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
Category,
|
||||
Field,
|
||||
EmbeddingSchema,
|
||||
Schema,
|
||||
ArraySchema,
|
||||
RawSchema,
|
||||
} from "../common/types/schema";
|
||||
import { LabelArray } from "../util/dataframe/types";
|
||||
import { LabelIndexBase } from "../util/dataframe/labelIndex";
|
||||
|
||||
const _dataframeCache = dataframeMemo(128);
|
||||
|
||||
export default class AnnoMatrix {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public isView: any;
|
||||
interface Cache {
|
||||
[Field.obs]: Dataframe;
|
||||
[Field.var]: Dataframe;
|
||||
[Field.emb]: Dataframe;
|
||||
[Field.X]: Dataframe;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public nObs: any;
|
||||
interface PendingLoad {
|
||||
[Field.obs]: { [key: string]: Promise<void> };
|
||||
[Field.var]: { [key: string]: Promise<void> };
|
||||
[Field.emb]: { [key: string]: Promise<void> };
|
||||
[Field.X]: { [key: string]: Promise<void> };
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public nVar: any;
|
||||
export interface UserFlags {
|
||||
isUserSubsetView?: boolean;
|
||||
isEmbSubsetView?: boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public rowIndex: any;
|
||||
export default abstract class AnnoMatrix {
|
||||
public isView: boolean;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public schema: any;
|
||||
public nObs: number;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public userFlags: any;
|
||||
public nVar: number;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
public viewOf: any;
|
||||
public rowIndex: LabelIndexBase;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
protected _cache: any;
|
||||
public schema: Schema;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
private _pendingLoad: any;
|
||||
public userFlags: UserFlags;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
private _whereCache: any;
|
||||
public viewOf: AnnoMatrix;
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
private _gcInfo: any;
|
||||
public _cache: Cache;
|
||||
|
||||
private _pendingLoad: PendingLoad;
|
||||
|
||||
private _whereCache: WhereCache;
|
||||
|
||||
private _gcInfo: Map<string, number>;
|
||||
|
||||
/*
|
||||
Abstract base class for all AnnoMatrix objects. This class provides a proxy
|
||||
@@ -80,16 +108,19 @@ export default class AnnoMatrix {
|
||||
subset(annoMatrix, rowLabels) -> annoMatrix
|
||||
etc.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
static fields() {
|
||||
static fields(): Field[] {
|
||||
/*
|
||||
return the fields present in the AnnoMatrix instance.
|
||||
*/
|
||||
return ["obs", "var", "emb", "X"];
|
||||
return [Field.obs, Field.var, Field.emb, Field.X];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(schema: any, nObs: any, nVar: any, rowIndex = null) {
|
||||
constructor(
|
||||
schema: RawSchema,
|
||||
nObs: number,
|
||||
nVar: number,
|
||||
rowIndex: LabelIndexBase | null = null
|
||||
) {
|
||||
/*
|
||||
Private constructor - this is an abstract base class. Do not use.
|
||||
*/
|
||||
@@ -105,7 +136,7 @@ export default class AnnoMatrix {
|
||||
* rowIndex - a rowIndex shared by all data on this view (ie, the list of cells).
|
||||
The row index labels are as defined by the base dataset from the server.
|
||||
* isView - true if this is a view, false if not.
|
||||
* viewOf - pointer to parent annomatrix if a view, undefined/null if not a view.
|
||||
* viewOf - pointer to parent annomatrix if a view, self if not a view.
|
||||
* userFlags - container for any additional state a user of this API wants to hang
|
||||
off of an annoMatrix, and have propagated by the (shallow) cloning protocol.
|
||||
*/
|
||||
@@ -114,7 +145,7 @@ export default class AnnoMatrix {
|
||||
this.nVar = nVar;
|
||||
this.rowIndex = rowIndex || new IdentityInt32Index(nObs);
|
||||
this.isView = false;
|
||||
this.viewOf = undefined;
|
||||
this.viewOf = this;
|
||||
this.userFlags = {};
|
||||
|
||||
/*
|
||||
@@ -137,16 +168,14 @@ export default class AnnoMatrix {
|
||||
emb: {},
|
||||
X: {},
|
||||
};
|
||||
this._whereCache = {};
|
||||
this._whereCache = {} as WhereCache;
|
||||
this._gcInfo = new Map();
|
||||
}
|
||||
|
||||
/**
|
||||
** Schema helper/accessors
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
getMatrixColumns(field) {
|
||||
getMatrixColumns(field: Field): string[] {
|
||||
/*
|
||||
Return array of column names in the field. ONLY supported on the
|
||||
obs, var and emb fields. X currently unimplemented and will throw.
|
||||
@@ -158,8 +187,8 @@ export default class AnnoMatrix {
|
||||
return _schemaColumns(this.schema, field);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/explicit-module-boundary-types -- need to be able to call this on instances
|
||||
getMatrixFields() {
|
||||
// eslint-disable-next-line class-methods-use-this -- need to be able to call this on instances
|
||||
getMatrixFields(): Field[] {
|
||||
/*
|
||||
Return array of fields in this annoMatrix. Currently hard-wired to
|
||||
return: ["X", "obs", "var", "emb"].
|
||||
@@ -169,9 +198,7 @@ export default class AnnoMatrix {
|
||||
return AnnoMatrix.fields();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
getColumnSchema(field, col) {
|
||||
getColumnSchema(field: Field, col: LabelType): ArraySchema {
|
||||
/*
|
||||
Return the schema for the field & column ,eg,
|
||||
|
||||
@@ -183,9 +210,7 @@ export default class AnnoMatrix {
|
||||
return _getColumnSchema(this.schema, field, col);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
getColumnDimensions(field, col) {
|
||||
getColumnDimensions(field: Field, col: LabelType): LabelArray | undefined {
|
||||
/*
|
||||
Return the dimensions on this field / column. For most fields, which are 1D,
|
||||
this just return the column name. Multi-dimensional columns, such as embeddings,
|
||||
@@ -203,23 +228,19 @@ export default class AnnoMatrix {
|
||||
/**
|
||||
** General utility methods
|
||||
**/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
base() {
|
||||
base(): AnnoMatrix {
|
||||
/*
|
||||
return the base of view, or `this` if not a view.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/no-this-alias --- FIXME: disabled temporarily on migrate to TS.
|
||||
let annoMatrix = this;
|
||||
while (annoMatrix.isView) annoMatrix = annoMatrix.viewOf;
|
||||
let annoMatrix = this._getViewOf();
|
||||
while (annoMatrix.isView) annoMatrix = annoMatrix._getViewOf();
|
||||
return annoMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
** Load / read interfaces
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
fetch(field, q): Dataframe {
|
||||
fetch(field: Field, q: Query | Query[]): Promise<Dataframe> {
|
||||
/*
|
||||
Return the given query on a single matrix field as a single dataframe.
|
||||
Currently supports ONLY full column query.
|
||||
@@ -276,9 +297,7 @@ export default class AnnoMatrix {
|
||||
return this._fetch(field, q);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
prefetch(field, q): void {
|
||||
prefetch(field: Field, q: Query): void {
|
||||
/*
|
||||
Start a data fetch & cache fill. Identical to fetch() except it does
|
||||
not return a value.
|
||||
@@ -307,163 +326,147 @@ export default class AnnoMatrix {
|
||||
** The actual implementation is in the sub-classes, which MUST override these.
|
||||
**/
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
addObsAnnoCategory(col, category) {
|
||||
/*
|
||||
Add a new category value (aka "label") to a writable obs column, and return the new AnnoMatrix.
|
||||
Typical use is to add a new user-created label to a user-created obs categorical
|
||||
annotation.
|
||||
/*
|
||||
Add a new category value (aka "label") to a writable obs column, and return the new AnnoMatrix.
|
||||
Typical use is to add a new user-created label to a user-created obs categorical
|
||||
annotation.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
Example:
|
||||
|
||||
addObsAnnoCategory("my cell type", "left toenail") -> AnnoMatrix
|
||||
addObsAnnoCategory("my cell type", "left toenail") -> AnnoMatrix
|
||||
|
||||
*/
|
||||
abstract addObsAnnoCategory(col: LabelType, category: string): AnnoMatrix;
|
||||
|
||||
/*
|
||||
Remove a category value from an obs column, reassign any obs having that value
|
||||
to the 'unassignedCategory' value, and return a promise for a new AnnoMatrix.
|
||||
Typical use is to remove a user-created label from a user-created obs categorical
|
||||
annotation.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
An `unassignedCategory` value must be provided, for assignment to any obs/cells
|
||||
that had the now-delete category label as their value.
|
||||
|
||||
Example:
|
||||
await removeObsAnnoCategory("my-tissue-type", "right earlobe", "unassigned") -> AnnoMatrix
|
||||
|
||||
NOTE: method is async as it may need to fetch data to provide the reassignment.
|
||||
*/
|
||||
abstract removeObsAnnoCategory(
|
||||
col: LabelType,
|
||||
category: Category,
|
||||
unassignedCategory: string
|
||||
): Promise<AnnoMatrix>;
|
||||
|
||||
/*
|
||||
Drop an entire writable column, eg a user-created obs annotation. Typical use
|
||||
is to provide the "Delete Category" implementation. Returns the new AnnoMatrix.
|
||||
Will throw if not a writable annotation.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
|
||||
dropObsColumn("old annotations") -> AnnoMatrix
|
||||
*/
|
||||
abstract dropObsColumn(col: LabelType): AnnoMatrix;
|
||||
|
||||
/*
|
||||
Add a new writable OBS annotation column, with the caller-specified schema, initial value
|
||||
type and value.
|
||||
|
||||
Value may be any one of:
|
||||
* an array of values
|
||||
* a primitive type, including null or undefined.
|
||||
If an array, length must be the same as 'this.nObs', and constructor must equal 'Ctor'.
|
||||
If a primitive, 'Ctor' will be used to create the initial value, which will be filled
|
||||
with 'value'.
|
||||
|
||||
Throws if the name specified in 'colSchema' duplicates an existing obs column.
|
||||
|
||||
Returns a new AnnoMatrix.
|
||||
|
||||
Examples:
|
||||
|
||||
addObsColumn(
|
||||
{ name: "foo", type: "categorical", categories: "unassigned" },
|
||||
Array,
|
||||
"unassigned"
|
||||
) -> AnnoMatrix
|
||||
|
||||
*/
|
||||
abstract addObsColumn<T extends DataframeValueArray>(
|
||||
colSchema: AnnotationColumnSchema,
|
||||
Ctor: new (n: number) => T,
|
||||
value: T
|
||||
): AnnoMatrix;
|
||||
|
||||
/*
|
||||
Rename the obs column 'oldCol' to have name 'newCol' and returns new AnnoMatrix.
|
||||
|
||||
Will throw column does not exist or is not writable, or if 'newCol' is not unique.
|
||||
|
||||
Example:
|
||||
|
||||
renameObsColumn('cell type', 'old cell type') -> AnnoMatrix.
|
||||
|
||||
*/
|
||||
abstract renameObsColumn(oldCol: LabelType, newCol: LabelType): AnnoMatrix;
|
||||
|
||||
/*
|
||||
Set all obs with label in array 'obsLabels' to have 'value'. Typical use would be
|
||||
to set a group of cells to have a label on a user-created categorical annotation
|
||||
(eg set all selected cells to have a label).
|
||||
|
||||
NOTE: async method, as it may need to fetch.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
await setObsColmnValues("flavor", [383, 400], "tasty") -> AnnoMtarix
|
||||
*/
|
||||
abstract setObsColumnValues(
|
||||
col: LabelType,
|
||||
obsLabels: Int32Array,
|
||||
value: DataframeValue
|
||||
): Promise<AnnoMatrix>;
|
||||
|
||||
/*
|
||||
Set by value - all elements in the column with value 'oldValue' are set to 'newValue'.
|
||||
Async method - returns a promise for a new AnnoMatrix.
|
||||
|
||||
Typical use would be to set all labels of one value to another.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
await resetObsColumnValues("my notes", "good", "not-good") -> AnnoMatrix
|
||||
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
abstract resetObsColumnValues<T extends DataframeValue>(
|
||||
col: LabelType,
|
||||
oldValue: T,
|
||||
newValue: T
|
||||
): Promise<AnnoMatrix>;
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
async removeObsAnnoCategory(col, category, unassignedCategory) {
|
||||
/*
|
||||
Remove a category value from an obs column, reassign any obs having that value
|
||||
to the 'unassignedCategory' value, and return a promise for a new AnnoMatrix.
|
||||
Typical use is to remove a user-created label from a user-created obs categorical
|
||||
annotation.
|
||||
/*
|
||||
Add a new obs embedding to the AnnoMatrix, with provided schema.
|
||||
Returns a new annomatrix.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
Typical use will be to add a re-embedding that the server has calculated.
|
||||
|
||||
An `unassignedCategory` value must be provided, for assignment to any obs/cells
|
||||
that had the now-delete category label as their value.
|
||||
Will throw if the column schema is invalid (eg, duplicate name).
|
||||
*/
|
||||
abstract addEmbedding(colSchema: EmbeddingSchema): AnnoMatrix;
|
||||
|
||||
Example:
|
||||
await removeObsAnnoCategory("my-tissue-type", "right earlobe", "unassigned") -> AnnoMatrix
|
||||
|
||||
NOTE: method is async as it may need to fetch data to provide the reassignment.
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
dropObsColumn(col) {
|
||||
/*
|
||||
Drop an entire writable column, eg a user-created obs annotation. Typical use
|
||||
is to provide the "Delete Category" implementation. Returns the new AnnoMatrix.
|
||||
Will throw if not a writable annotation.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
|
||||
dropObsColumn("old annotations") -> AnnoMatrix
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'colSchema' is declared but its value is never rea... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
addObsColumn(colSchema, Ctor, value) {
|
||||
/*
|
||||
Add a new writable OBS annotation column, with the caller-specified schema, initial value
|
||||
type and value.
|
||||
|
||||
Value may be any one of:
|
||||
* an array of values
|
||||
* a primitive type, including null or undefined.
|
||||
If an array, length must be the same as 'this.nObs', and constructor must equal 'Ctor'.
|
||||
If a primitive, 'Ctor' will be used to create the initial value, which will be filled
|
||||
with 'value'.
|
||||
|
||||
Throws if the name specified in 'colSchema' duplicates an existing obs column.
|
||||
|
||||
Returns a new AnnoMatrix.
|
||||
|
||||
Examples:
|
||||
|
||||
addObsColumn(
|
||||
{ name: "foo", type: "categorical", categories: "unassigned" },
|
||||
Array,
|
||||
"unassigned"
|
||||
) -> AnnoMatrix
|
||||
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'oldCol' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
renameObsColumn(oldCol, newCol) {
|
||||
/*
|
||||
Rename the obs column 'oldCol' to have name 'newCol' and returns new AnnoMatrix.
|
||||
|
||||
Will throw column does not exist or is not writable, or if 'newCol' is not unique.
|
||||
|
||||
Example:
|
||||
|
||||
renameObsColumn('cell type', 'old cell type') -> AnnoMatrix.
|
||||
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
async setObsColumnValues(col, obsLabels, value) {
|
||||
/*
|
||||
Set all obs with label in array 'obsLabels' to have 'value'. Typical use would be
|
||||
to set a group of cells to have a label on a user-created categorical anntoation
|
||||
(eg set all selected cells to have a label).
|
||||
|
||||
NOTE: async method, as it may need to fetch.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
await setObsColmnValues("flavor", [383, 400], "tasty") -> AnnoMtarix
|
||||
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'col' is declared but its value is never read.
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
async resetObsColumnValues(col, oldValue, newValue) {
|
||||
/*
|
||||
Set by value - all elements in the column with value 'oldValue' are set to 'newValue'.
|
||||
Async method - returns a promise for a new AnnoMatrix.
|
||||
|
||||
Typical use would be to set all labels of one value to another.
|
||||
|
||||
Will throw column does not exist or is not writable.
|
||||
|
||||
Example:
|
||||
await resetObsColumnValues("my notes", "good", "not-good") -> AnnoMatrix
|
||||
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(6133) FIXME: 'colSchema' is declared but its value is never rea... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/no-unused-vars, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
addEmbedding(colSchema) {
|
||||
/*
|
||||
Add a new obs embedding to the AnnoMatrix, with provided schema.
|
||||
Returns a new annomatrix.
|
||||
|
||||
Typical use will be to add a re-embedding that the server has calculated.
|
||||
|
||||
Will throw if the column schema is invalid (eg, duplicate name).
|
||||
*/
|
||||
_subclassResponsibility();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
getCacheKeys(field, query) {
|
||||
getCacheKeys(
|
||||
field: Field,
|
||||
query: Query
|
||||
): WhereCacheColumnLabels | [undefined] {
|
||||
/*
|
||||
Return cache keys for columns associated with this query. May return
|
||||
[unknown] if no keys are known (ie, nothing is or was cached).
|
||||
@@ -474,26 +477,20 @@ Return cache keys for columns associated with this query. May return
|
||||
/**
|
||||
** Private interfaces below.
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_resolveCachedQueries(field, queries) {
|
||||
return (
|
||||
queries
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'query' implicitly has an 'any' type.
|
||||
.map((query) =>
|
||||
_whereCacheGet(this._whereCache, this.schema, field, query).filter(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'cacheKey' implicitly has an 'any' type.
|
||||
(cacheKey) =>
|
||||
cacheKey !== undefined && this._cache[field].hasCol(cacheKey)
|
||||
)
|
||||
_resolveCachedQueries(field: Field, queries: Query[]): LabelArray {
|
||||
return queries
|
||||
.map((query: Query) =>
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// `filter`: This expression is not callable.
|
||||
_whereCacheGet(this._whereCache, this.schema, field, query).filter(
|
||||
(cacheKey?: LabelType) =>
|
||||
cacheKey !== undefined && this._cache[field].hasCol(cacheKey)
|
||||
)
|
||||
.flat()
|
||||
);
|
||||
)
|
||||
.flat();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async _fetch(field, q): Dataframe {
|
||||
async _fetch(field: Field, q: Query | Query[]): Promise<Dataframe> {
|
||||
if (!AnnoMatrix.fields().includes(field)) return Dataframe.empty();
|
||||
const queries = Array.isArray(q) ? q : [q];
|
||||
queries.forEach(_queryValidate);
|
||||
@@ -505,8 +502,7 @@ Return cache keys for columns associated with this query. May return
|
||||
/* find any query not already cached */
|
||||
const uncachedQueries = queries.filter((query) =>
|
||||
_whereCacheGet(this._whereCache, this.schema, field, query).some(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'cacheKey' implicitly has an 'any' type.
|
||||
(cacheKey) =>
|
||||
(cacheKey?: LabelType) =>
|
||||
cacheKey === undefined || !this._cache[field].hasCol(cacheKey)
|
||||
)
|
||||
);
|
||||
@@ -515,17 +511,19 @@ Return cache keys for columns associated with this query. May return
|
||||
if (uncachedQueries.length > 0) {
|
||||
await Promise.all(
|
||||
uncachedQueries.map((query) =>
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter '_field' implicitly has an 'any' type.
|
||||
this._getPendingLoad(field, query, async (_field, _query) => {
|
||||
/* fetch, then index. _doLoad is subclass interface */
|
||||
// @ts-expect-error ts-migrate(2488) FIXME: Type 'void' must have a '[Symbol.iterator]()' meth... Remove this comment to see the full error message
|
||||
const [whereCacheUpdate, df] = await this._doLoad(_field, _query);
|
||||
this._cache[_field] = this._cache[_field].withColsFrom(df);
|
||||
this._whereCache = _whereCacheMerge(
|
||||
this._whereCache,
|
||||
whereCacheUpdate
|
||||
);
|
||||
})
|
||||
this._getPendingLoad(
|
||||
field,
|
||||
query,
|
||||
async (_field: Field, _query: Query): Promise<void> => {
|
||||
/* fetch, then index. _doLoad is subclass interface */
|
||||
const [whereCacheUpdate, df] = await this._doLoad(_field, _query);
|
||||
this._cache[_field] = this._cache[_field].withColsFrom(df);
|
||||
this._whereCache = _whereCacheMerge(
|
||||
this._whereCache,
|
||||
whereCacheUpdate
|
||||
);
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -539,9 +537,11 @@ Return cache keys for columns associated with this query. May return
|
||||
return response;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async _getPendingLoad(field, query, fetchFn) {
|
||||
async _getPendingLoad(
|
||||
field: Field,
|
||||
query: Query,
|
||||
fetchFn: (_field: Field, _query: Query) => Promise<void>
|
||||
): Promise<void> {
|
||||
/*
|
||||
Given a query on a field, ensure that we only have a single outstanding
|
||||
fetch at any given time. If multiple requests occur while a fetch is
|
||||
@@ -562,9 +562,22 @@ Return cache keys for columns associated with this query. May return
|
||||
return this._pendingLoad[field][key];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this, @typescript-eslint/explicit-module-boundary-types -- make sure subclass implements
|
||||
async _doLoad() {
|
||||
_subclassResponsibility();
|
||||
abstract _doLoad(
|
||||
field: Field,
|
||||
query: Query
|
||||
): Promise<[WhereCache | null, Dataframe]>;
|
||||
|
||||
/**
|
||||
* Determines viewOf for this annoMatrix.
|
||||
*
|
||||
* @internal
|
||||
* @returns - parent annoMatrix if this annoMatrix is a view, otherwise this annoMatrix if it's not a view.
|
||||
*/
|
||||
_getViewOf(): AnnoMatrix {
|
||||
if (this.isView) {
|
||||
return this.viewOf;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -596,23 +609,21 @@ Return cache keys for columns associated with this query. May return
|
||||
To be effective, the GC callback needs to be invoked from the undo/redo code,
|
||||
as much of the cache is pinned by that data structure.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_gcField(field, isHot, pinnedColumns) {
|
||||
_gcField(field: Field, isHot: boolean, pinnedColumns: LabelArray): void {
|
||||
const maxColumns = isHot ? 256 : 10;
|
||||
const cache = this._cache[field];
|
||||
if (cache.colIndex.size() < maxColumns) return; // trivial rejection
|
||||
|
||||
const candidates = cache.colIndex
|
||||
.labels()
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
.filter((col) => !pinnedColumns.includes(col));
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// `col`: Argument of type 'LabelType' is not assignable to parameter of type 'number'. Type 'string' is not assignable to type 'number'.
|
||||
.filter((col: LabelType) => !pinnedColumns.includes(col));
|
||||
|
||||
const excessCount = candidates.length + pinnedColumns.length - maxColumns;
|
||||
if (excessCount > 0) {
|
||||
const { _gcInfo } = this;
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'a' implicitly has an 'any' type.
|
||||
candidates.sort((a, b) => {
|
||||
candidates.sort((a: LabelType, b: LabelType) => {
|
||||
let atime = _gcInfo.get(_columnCacheKey(field, a));
|
||||
if (atime === undefined) atime = 0;
|
||||
|
||||
@@ -629,49 +640,49 @@ Return cache keys for columns associated with this query. May return
|
||||
// ", "
|
||||
// )}]`
|
||||
// );
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// `reduce`: This expression is not callable.
|
||||
this._cache[field] = toDrop.reduce(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'df' implicitly has an 'any' type.
|
||||
(df, col) => df.dropCol(col),
|
||||
(df: Dataframe, col: LabelType) => df.dropCol(col),
|
||||
this._cache[field]
|
||||
);
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
toDrop.forEach((col) => _gcInfo.delete(_columnCacheKey(field, col)));
|
||||
toDrop.forEach((col: LabelType) =>
|
||||
_gcInfo.delete(_columnCacheKey(field, col))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_gcFetchCleanup(field, pinnedColumns) {
|
||||
_gcFetchCleanup(field: Field, pinnedColumns: LabelArray): void {
|
||||
/*
|
||||
Called during data load/fetch. By definition, this is 'hot', so we
|
||||
only want to gc X.
|
||||
*/
|
||||
if (field === "X") {
|
||||
if (field === Field.X) {
|
||||
this._gcField(
|
||||
field,
|
||||
true,
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// Property 'concat' does not exist on type 'LabelArray'.
|
||||
pinnedColumns.concat(_getWritableColumns(this.schema, field))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'hints' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_gc(hints) {
|
||||
_gc(hints: GCHints): void {
|
||||
/*
|
||||
Called from middleware, or elsewhere. isHot is true if we are in the active store,
|
||||
or false if we are in some other context (eg, history state).
|
||||
*/
|
||||
const { isHot } = hints;
|
||||
const candidateFields = isHot ? ["X"] : ["X", "emb", "var", "obs"];
|
||||
const candidateFields = isHot
|
||||
? [Field.X]
|
||||
: [Field.X, Field.emb, Field.var, Field.obs];
|
||||
candidateFields.forEach((field) =>
|
||||
this._gcField(field, isHot, _getWritableColumns(this.schema, field))
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_gcUpdateStats(field, dataframe) {
|
||||
_gcUpdateStats(field: Field, dataframe: Dataframe): void {
|
||||
/*
|
||||
called each time a query is performed, allowing the gc to update any bookkeeping
|
||||
information. Currently, this is just a simple last-fetched timestamp, stored
|
||||
@@ -680,8 +691,7 @@ Return cache keys for columns associated with this query. May return
|
||||
const cols = dataframe.colIndex.labels();
|
||||
const { _gcInfo } = this;
|
||||
const now = Date.now();
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'c' implicitly has an 'any' type.
|
||||
cols.forEach((c) => {
|
||||
cols.forEach((c: LabelType) => {
|
||||
_gcInfo.set(_columnCacheKey(field, c), now);
|
||||
});
|
||||
}
|
||||
@@ -698,9 +708,7 @@ Return cache keys for columns associated with this query. May return
|
||||
|
||||
Do not override _clone();
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'clone' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_cloneDeeper(clone) {
|
||||
_cloneDeeper(clone: AnnoMatrix): AnnoMatrix {
|
||||
clone._cache = _shallowClone(this._cache);
|
||||
clone._gcInfo = new Map();
|
||||
clone._pendingLoad = {
|
||||
@@ -712,8 +720,7 @@ Return cache keys for columns associated with this query. May return
|
||||
return clone;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_clone() {
|
||||
_clone(): AnnoMatrix {
|
||||
const clone = _shallowClone(this);
|
||||
this._cloneDeeper(clone);
|
||||
Object.seal(clone);
|
||||
@@ -724,12 +731,6 @@ Return cache keys for columns associated with this query. May return
|
||||
/*
|
||||
private utility functions below
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
function _columnCacheKey(field, column) {
|
||||
function _columnCacheKey(field: Field, column: LabelType): string {
|
||||
return `${field}/${column}`;
|
||||
}
|
||||
|
||||
function _subclassResponsibility() {
|
||||
/* protect against bugs in subclass */
|
||||
throw new Error("subclass failed to implement required method");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
Shallow clone an object, correctly handling prototype
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export default function _shallowClone(orig: any) {
|
||||
|
||||
export default function _shallowClone<T>(orig: T): T {
|
||||
return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig);
|
||||
}
|
||||
|
||||
@@ -9,38 +9,58 @@ AnnoMatrix stay in sync:
|
||||
*/
|
||||
import Crossfilter from "../util/typedCrossfilter";
|
||||
import { _getColumnSchema } from "./schema";
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
Field,
|
||||
EmbeddingSchema,
|
||||
} from "../common/types/schema";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import {
|
||||
Dataframe,
|
||||
DataframeValue,
|
||||
DataframeValueArray,
|
||||
LabelType,
|
||||
} from "../util/dataframe";
|
||||
import { Query } from "./query";
|
||||
import { TypedArray } from "../common/types/arraytypes";
|
||||
import { LabelArray } from "../util/dataframe/types";
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
function _dimensionNameFromDf(field, df) {
|
||||
type ObsDimensionParams =
|
||||
| [string, DataframeValueArray, DataframeValueArray]
|
||||
| [string, DataframeValueArray]
|
||||
| [string, DataframeValueArray, Int32ArrayConstructor]
|
||||
| [string, DataframeValueArray, Float32ArrayConstructor];
|
||||
|
||||
function _dimensionNameFromDf(field: Field, df: Dataframe): string {
|
||||
const colNames = df.colIndex.labels();
|
||||
return _dimensionName(field, colNames);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
function _dimensionName(field, colNames) {
|
||||
function _dimensionName(
|
||||
field: Field,
|
||||
colNames: LabelType | LabelArray
|
||||
): string {
|
||||
if (!Array.isArray(colNames)) return `${field}/${colNames}`;
|
||||
return `${field}/${colNames.join(":")}`;
|
||||
}
|
||||
|
||||
export default class AnnoMatrixObsCrossfilter {
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(annoMatrix, _obsCrossfilter = null) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).annoMatrix = annoMatrix;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter =
|
||||
annoMatrix: AnnoMatrix;
|
||||
|
||||
obsCrossfilter: Crossfilter;
|
||||
|
||||
constructor(
|
||||
annoMatrix: AnnoMatrix,
|
||||
_obsCrossfilter: Crossfilter | null = null
|
||||
) {
|
||||
this.annoMatrix = annoMatrix;
|
||||
this.obsCrossfilter =
|
||||
_obsCrossfilter || new Crossfilter(annoMatrix._cache.obs);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter = (this as any).obsCrossfilter.setData(
|
||||
annoMatrix._cache.obs
|
||||
);
|
||||
this.obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
size() {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).obsCrossfilter.size();
|
||||
size(): number {
|
||||
return this.obsCrossfilter.size();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,44 +70,33 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
|
||||
See API documentation in annoMatrix.js.
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
addObsColumn(colSchema, Ctor, value) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = (this as any).annoMatrix.addObsColumn(
|
||||
colSchema,
|
||||
Ctor,
|
||||
value
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const obsCrossfilter = (this as any).obsCrossfilter.setData(
|
||||
annoMatrix._cache.obs
|
||||
);
|
||||
addObsColumn<T extends DataframeValueArray>(
|
||||
colSchema: AnnotationColumnSchema,
|
||||
Ctor: new (n: number) => T,
|
||||
value: T
|
||||
): AnnoMatrixObsCrossfilter {
|
||||
const annoMatrix = this.annoMatrix.addObsColumn(colSchema, Ctor, value);
|
||||
const obsCrossfilter = this.obsCrossfilter.setData(annoMatrix._cache.obs);
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
dropObsColumn(col) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = (this as any).annoMatrix.dropObsColumn(col);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
dropObsColumn(col: LabelType): AnnoMatrixObsCrossfilter {
|
||||
const annoMatrix = this.annoMatrix.dropObsColumn(col);
|
||||
let { obsCrossfilter } = this;
|
||||
const dimName = _dimensionName("obs", col);
|
||||
const dimName = _dimensionName(Field.obs, col);
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
}
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'oldCol' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
renameObsColumn(oldCol, newCol) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = (this as any).annoMatrix.renameObsColumn(oldCol, newCol);
|
||||
const oldDimName = _dimensionName("obs", oldCol);
|
||||
const newDimName = _dimensionName("obs", newCol);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
renameObsColumn(
|
||||
oldCol: LabelType,
|
||||
newCol: LabelType
|
||||
): AnnoMatrixObsCrossfilter {
|
||||
const annoMatrix = this.annoMatrix.renameObsColumn(oldCol, newCol);
|
||||
const oldDimName = _dimensionName(Field.obs, oldCol);
|
||||
const newDimName = _dimensionName(Field.obs, newCol);
|
||||
let { obsCrossfilter } = this;
|
||||
if (obsCrossfilter.hasDimension(oldDimName)) {
|
||||
obsCrossfilter = obsCrossfilter.renameDimension(oldDimName, newDimName);
|
||||
@@ -95,16 +104,12 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
addObsAnnoCategory(col, category) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = (this as any).annoMatrix.addObsAnnoCategory(
|
||||
col,
|
||||
category
|
||||
);
|
||||
const dimName = _dimensionName("obs", col);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
addObsAnnoCategory(
|
||||
col: LabelType,
|
||||
category: string
|
||||
): AnnoMatrixObsCrossfilter {
|
||||
const annoMatrix = this.annoMatrix.addObsAnnoCategory(col, category);
|
||||
const dimName = _dimensionName(Field.obs, col);
|
||||
let { obsCrossfilter } = this;
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
@@ -112,17 +117,17 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async removeObsAnnoCategory(col, category, unassignedCategory) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = await (this as any).annoMatrix.removeObsAnnoCategory(
|
||||
async removeObsAnnoCategory(
|
||||
col: LabelType,
|
||||
category: string,
|
||||
unassignedCategory: string
|
||||
): Promise<AnnoMatrixObsCrossfilter> {
|
||||
const annoMatrix = await this.annoMatrix.removeObsAnnoCategory(
|
||||
col,
|
||||
category,
|
||||
unassignedCategory
|
||||
);
|
||||
const dimName = _dimensionName("obs", col);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
const dimName = _dimensionName(Field.obs, col);
|
||||
let { obsCrossfilter } = this;
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
@@ -130,17 +135,17 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async setObsColumnValues(col, rowLabels, value) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = await (this as any).annoMatrix.setObsColumnValues(
|
||||
async setObsColumnValues(
|
||||
col: LabelType,
|
||||
rowLabels: Int32Array,
|
||||
value: DataframeValue
|
||||
): Promise<AnnoMatrixObsCrossfilter> {
|
||||
const annoMatrix = await this.annoMatrix.setObsColumnValues(
|
||||
col,
|
||||
rowLabels,
|
||||
value
|
||||
);
|
||||
const dimName = _dimensionName("obs", col);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
const dimName = _dimensionName(Field.obs, col);
|
||||
let { obsCrossfilter } = this;
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
@@ -148,17 +153,17 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async resetObsColumnValues(col, oldValue, newValue) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = await (this as any).annoMatrix.resetObsColumnValues(
|
||||
async resetObsColumnValues<T extends DataframeValue>(
|
||||
col: LabelType,
|
||||
oldValue: T,
|
||||
newValue: T
|
||||
): Promise<AnnoMatrixObsCrossfilter> {
|
||||
const annoMatrix = await this.annoMatrix.resetObsColumnValues(
|
||||
col,
|
||||
oldValue,
|
||||
newValue
|
||||
);
|
||||
const dimName = _dimensionName("obs", col);
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
const dimName = _dimensionName(Field.obs, col);
|
||||
let { obsCrossfilter } = this;
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
@@ -166,35 +171,25 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
addEmbedding(colSchema) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrix = (this as any).annoMatrix.addEmbedding(colSchema);
|
||||
return new AnnoMatrixObsCrossfilter(
|
||||
annoMatrix,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter
|
||||
);
|
||||
addEmbedding(colSchema: EmbeddingSchema): AnnoMatrixObsCrossfilter {
|
||||
const annoMatrix = this.annoMatrix.addEmbedding(colSchema);
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, this.obsCrossfilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop the crossfilter dimension. Do not change the annoMatrix. Useful when we
|
||||
* want to stop trackin the selection state, but aren't sure we want to blow the
|
||||
* want to stop tracking the selection state, but aren't sure we want to blow the
|
||||
* annomatrix cache.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
dropDimension(field, query) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
|
||||
dropDimension(field: Field, query: Query): AnnoMatrixObsCrossfilter {
|
||||
const { annoMatrix } = this;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
let { obsCrossfilter } = this;
|
||||
const keys = annoMatrix
|
||||
.getCacheKeys(field, query)
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'k' implicitly has an 'any' type.
|
||||
.filter((k) => k !== undefined);
|
||||
const dimName = _dimensionName(field, keys);
|
||||
// @ts-expect-error ts-migrate --- suppressing TS defect (https://github.com/microsoft/TypeScript/issues/44373).
|
||||
// Compiler is complaining that expression is not callable on array union types. Remove suppression once fixed.
|
||||
.filter((k?: string | number) => k !== undefined);
|
||||
const dimName = _dimensionName(field, keys as string[]);
|
||||
if (obsCrossfilter.hasDimension(dimName)) {
|
||||
obsCrossfilter = obsCrossfilter.delDimension(dimName);
|
||||
}
|
||||
@@ -206,12 +201,13 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
are just wrappers to lazy create indices.
|
||||
**/
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async select(field, query, spec) {
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
|
||||
async select(
|
||||
field: Field,
|
||||
query: Query,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- TODO revisit: waiting for typings from util/typedCrossfilter
|
||||
spec: any
|
||||
): Promise<AnnoMatrixObsCrossfilter> {
|
||||
const { annoMatrix } = this;
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'obsCrossfilter' does not exist on type '... Remove this comment to see the full error message
|
||||
let { obsCrossfilter } = this;
|
||||
|
||||
if (!annoMatrix?._cache?.[field]) {
|
||||
@@ -223,7 +219,9 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
|
||||
// grab the data, so we can grab the index.
|
||||
const df = await annoMatrix.fetch(field, query);
|
||||
|
||||
if (!df) {
|
||||
throw new Error("Dataframe cannot be `undefined`");
|
||||
}
|
||||
const dimName = _dimensionNameFromDf(field, df);
|
||||
if (!obsCrossfilter.hasDimension(dimName)) {
|
||||
// lazy index generation - add dimension when first used
|
||||
@@ -240,86 +238,66 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
selectAll() {
|
||||
selectAll(): AnnoMatrixObsCrossfilter {
|
||||
/*
|
||||
Select all on any dimension in this field.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Anno... Remove this comment to see the full error message
|
||||
const { annoMatrix } = this;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const currentDims = (this as any).obsCrossfilter.dimensionNames();
|
||||
const currentDims = this.obsCrossfilter.dimensionNames();
|
||||
const obsCrossfilter = currentDims.reduce(
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'xfltr' implicitly has an 'any' type.
|
||||
(xfltr, dim) => xfltr.select(dim, { mode: "all" }),
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter
|
||||
); // eslint-disable-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
this.obsCrossfilter
|
||||
);
|
||||
return new AnnoMatrixObsCrossfilter(annoMatrix, obsCrossfilter);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
countSelected() {
|
||||
countSelected(): number {
|
||||
/* if no data yet indexed in the crossfilter, just say everything is selected */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
if ((this as any).obsCrossfilter.size() === 0)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).annoMatrix.nObs;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).obsCrossfilter.countSelected();
|
||||
if (this.obsCrossfilter.size() === 0) return this.annoMatrix.nObs;
|
||||
return this.obsCrossfilter.countSelected();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
allSelectedMask() {
|
||||
allSelectedMask(): Uint8Array {
|
||||
/* if no data yet indexed in the crossfilter, just say everything is selected */
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.size() === 0 ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.dimensionNames().length === 0
|
||||
this.obsCrossfilter.size() === 0 ||
|
||||
this.obsCrossfilter.dimensionNames().length === 0
|
||||
) {
|
||||
/* fake the mask */
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return new Uint8Array((this as any).annoMatrix.nObs).fill(1);
|
||||
return new Uint8Array(this.annoMatrix.nObs).fill(1);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).obsCrossfilter.allSelectedMask();
|
||||
return this.obsCrossfilter.allSelectedMask();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
allSelectedLabels() {
|
||||
allSelectedLabels(): LabelArray {
|
||||
/* if no data yet indexed in the crossfilter, just say everything is selected */
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.size() === 0 ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.dimensionNames().length === 0
|
||||
this.obsCrossfilter.size() === 0 ||
|
||||
this.obsCrossfilter.dimensionNames().length === 0
|
||||
) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).annoMatrix.rowIndex.labels();
|
||||
return this.annoMatrix.rowIndex.labels();
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const mask = (this as any).obsCrossfilter.allSelectedMask();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const index = (this as any).annoMatrix.rowIndex.isubsetMask(mask);
|
||||
const mask = this.obsCrossfilter.allSelectedMask();
|
||||
const index = this.annoMatrix.rowIndex.isubsetMask(mask);
|
||||
return index.labels();
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'array' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
fillByIsSelected(array, selectedValue, deselectedValue) {
|
||||
fillByIsSelected<A extends TypedArray>(
|
||||
array: A,
|
||||
selectedValue: A[0],
|
||||
deselectedValue: A[0]
|
||||
): A {
|
||||
/* if no data yet indexed in the crossfilter, just say everything is selected */
|
||||
if (
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.size() === 0 ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).obsCrossfilter.dimensionNames().length === 0
|
||||
this.obsCrossfilter.size() === 0 ||
|
||||
this.obsCrossfilter.dimensionNames().length === 0
|
||||
) {
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// Type 'Int8Array | Uint8Array | Int16Array | Uint16Array | Int32Array | Uint32Array | Float32Array | Float64Array' is not assignable to type 'A'...
|
||||
return array.fill(selectedValue);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).obsCrossfilter.fillByIsSelected(
|
||||
return this.obsCrossfilter.fillByIsSelected(
|
||||
array,
|
||||
selectedValue,
|
||||
deselectedValue
|
||||
@@ -330,37 +308,35 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
** Private below
|
||||
**/
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_addObsCrossfilterDimension(annoMatrix, obsCrossfilter, field, df) {
|
||||
_addObsCrossfilterDimension(
|
||||
annoMatrix: AnnoMatrix,
|
||||
obsCrossfilter: Crossfilter,
|
||||
field: Field,
|
||||
df: Dataframe
|
||||
): Crossfilter {
|
||||
if (field === "var") return obsCrossfilter;
|
||||
const dimName = _dimensionNameFromDf(field, df);
|
||||
const dimParams = this._getObsDimensionParams(field, df);
|
||||
obsCrossfilter = obsCrossfilter.setData(annoMatrix._cache.obs);
|
||||
// @ts-expect-error ts-migrate(2488) FIXME: Type 'any[] | undefined' must have a '[Symbol.iter... Remove this comment to see the full error message
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `...dimParams`: A spread argument must either have a tuple type or be passed to a rest parameter.
|
||||
obsCrossfilter = obsCrossfilter.addDimension(dimName, ...dimParams);
|
||||
return obsCrossfilter;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_getColumnBaseType(field, col) {
|
||||
_getColumnBaseType(field: Field, col: LabelType): string {
|
||||
/* Look up the primitive type for this field/col */
|
||||
const colSchema = _getColumnSchema(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).annoMatrix.schema,
|
||||
field,
|
||||
col
|
||||
);
|
||||
const colSchema = _getColumnSchema(this.annoMatrix.schema, field, col);
|
||||
return colSchema.type;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
_getObsDimensionParams(field, df) {
|
||||
_getObsDimensionParams(
|
||||
field: Field,
|
||||
df: Dataframe
|
||||
): ObsDimensionParams | undefined {
|
||||
/* return the crossfilter dimensiontype type and params for this field/dataframe */
|
||||
|
||||
if (field === "emb") {
|
||||
if (field === Field.emb) {
|
||||
/* assumed to be 2D */
|
||||
return ["spatial", df.icol(0).asArray(), df.icol(1).asArray()];
|
||||
}
|
||||
@@ -368,6 +344,8 @@ export default class AnnoMatrixObsCrossfilter {
|
||||
/* assumed to be 1D */
|
||||
const col = df.icol(0);
|
||||
const colName = df.colIndex.getLabel(0);
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// `colName` Argument of type 'LabelType | undefined' is not assignable to parameter of type 'LabelType'. Type 'undefined' is not assignable to type 'LabelType'.
|
||||
const type = this._getColumnBaseType(field, colName);
|
||||
if (type === "string" || type === "categorical" || type === "boolean") {
|
||||
return ["enum", col.asArray()];
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
export { doBinaryRequest, doFetch } from "../util/actionHelpers";
|
||||
|
||||
/* double URI encode - needed for query-param filters */
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _dubEncURIComp(s: any) {
|
||||
export function _dubEncURIComp(s: string | number | boolean): string {
|
||||
return encodeURIComponent(encodeURIComponent(s));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,33 +2,46 @@ import { doBinaryRequest, doFetch } from "./fetchHelpers";
|
||||
import { matrixFBSToDataframe } from "../util/stateManager/matrix";
|
||||
import { _getColumnSchema } from "./schema";
|
||||
import {
|
||||
addObsAnnoColumn,
|
||||
removeObsAnnoColumn,
|
||||
addObsAnnoCategory,
|
||||
removeObsAnnoCategory,
|
||||
addObsAnnoColumn,
|
||||
addObsLayout,
|
||||
removeObsAnnoCategory,
|
||||
removeObsAnnoColumn,
|
||||
} from "../util/stateManager/schemaHelpers";
|
||||
import { isAnyArray } from "../common/types/arraytypes";
|
||||
import { _whereCacheCreate } from "./whereCache";
|
||||
import { _whereCacheCreate, WhereCache } from "./whereCache";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import PromiseLimit from "../util/promiseLimit";
|
||||
import {
|
||||
_expectSimpleQuery,
|
||||
_expectComplexQuery,
|
||||
_urlEncodeLabelQuery,
|
||||
_urlEncodeComplexQuery,
|
||||
_expectSimpleQuery,
|
||||
_hashStringValues,
|
||||
_urlEncodeComplexQuery,
|
||||
_urlEncodeLabelQuery,
|
||||
ComplexQuery,
|
||||
Query,
|
||||
} from "./query";
|
||||
import {
|
||||
normalizeResponse,
|
||||
normalizeWritableCategoricalSchema,
|
||||
} from "./normalize";
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
Field,
|
||||
EmbeddingSchema,
|
||||
RawSchema,
|
||||
} from "../common/types/schema";
|
||||
import {
|
||||
Dataframe,
|
||||
DataframeValue,
|
||||
DataframeValueArray,
|
||||
LabelType,
|
||||
} from "../util/dataframe";
|
||||
|
||||
const promiseThrottle = new PromiseLimit(5);
|
||||
|
||||
export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
baseURL: any;
|
||||
baseURL: string;
|
||||
|
||||
/*
|
||||
AnnoMatrix implementation which proxies to HTTP server using the CXG REST API.
|
||||
@@ -40,8 +53,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
new AnnoMatrixLoader(serverBaseURL, schema) -> instance
|
||||
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(baseURL: any, schema: any) {
|
||||
constructor(baseURL: string, schema: RawSchema) {
|
||||
const { nObs, nVar } = schema.dataframe;
|
||||
super(schema, nObs, nVar);
|
||||
|
||||
@@ -56,33 +68,36 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
/**
|
||||
** Public. API described in base class.
|
||||
**/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
addObsAnnoCategory(col: any, category: any) {
|
||||
addObsAnnoCategory(col: LabelType, category: string): AnnoMatrix {
|
||||
/*
|
||||
Add a new category (aka label) to the schema for an obs column.
|
||||
*/
|
||||
const colSchema = _getColumnSchema(this.schema, "obs", col);
|
||||
_writableCategoryTypeCheck(colSchema); // throws on error
|
||||
const colSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
col
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCategoryTypeCheck(colSchema); // throws on error
|
||||
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.schema = addObsAnnoCategory(this.schema, col, category);
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async removeObsAnnoCategory(
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
col: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
category: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
unassignedCategory: any
|
||||
) {
|
||||
col: LabelType,
|
||||
category: string,
|
||||
unassignedCategory: string
|
||||
): Promise<AnnoMatrix> {
|
||||
/*
|
||||
Remove a single "category" (aka "label") from the data & schema of an obs column.
|
||||
*/
|
||||
const colSchema = _getColumnSchema(this.schema, "obs", col);
|
||||
_writableCategoryTypeCheck(colSchema); // throws on error
|
||||
const colSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
col
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCategoryTypeCheck(colSchema); // throws on error
|
||||
|
||||
const newAnnoMatrix = await this.resetObsColumnValues(
|
||||
col,
|
||||
@@ -97,24 +112,28 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
dropObsColumn(col: any) {
|
||||
dropObsColumn(col: LabelType): AnnoMatrix {
|
||||
/*
|
||||
drop column from field
|
||||
*/
|
||||
const colSchema = _getColumnSchema(this.schema, "obs", col);
|
||||
_writableCheck(colSchema); // throws on error
|
||||
const colSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
col
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCheck(colSchema); // throws on error
|
||||
|
||||
const newAnnoMatrix = this._clone();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
newAnnoMatrix._cache.obs = (this as any)._cache.obs.dropCol(col);
|
||||
newAnnoMatrix._cache.obs = this._cache.obs.dropCol(col);
|
||||
newAnnoMatrix.schema = removeObsAnnoColumn(this.schema, col);
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
addObsColumn(colSchema, Ctor, value) {
|
||||
addObsColumn<T extends DataframeValueArray>(
|
||||
colSchema: AnnotationColumnSchema,
|
||||
Ctor: new (n: number) => T,
|
||||
value: T
|
||||
): AnnoMatrix {
|
||||
/*
|
||||
add a column to field, initializing with value. Value may
|
||||
be one of:
|
||||
@@ -125,9 +144,8 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
colSchema.writable = true;
|
||||
const colName = colSchema.name;
|
||||
if (
|
||||
_getColumnSchema(this.schema, "obs", colName) ||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any)._cache.obs.hasCol(colName)
|
||||
_getColumnSchema(this.schema, Field.obs, colName) ||
|
||||
this._cache.obs.hasCol(colName)
|
||||
) {
|
||||
throw new Error("column already exists");
|
||||
}
|
||||
@@ -143,8 +161,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
} else {
|
||||
data = new Ctor(this.nObs).fill(value);
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
newAnnoMatrix._cache.obs = (this as any)._cache.obs.withCol(colName, data);
|
||||
newAnnoMatrix._cache.obs = this._cache.obs.withCol(colName, data);
|
||||
normalizeWritableCategoricalSchema(
|
||||
colSchema,
|
||||
newAnnoMatrix._cache.obs.col(colName)
|
||||
@@ -153,47 +170,55 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'oldCol' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
renameObsColumn(oldCol, newCol) {
|
||||
renameObsColumn(oldCol: LabelType, newCol: LabelType): AnnoMatrix {
|
||||
/*
|
||||
Rename the obs oldColName to newColName. oldCol must be writable.
|
||||
*/
|
||||
const oldColSchema = _getColumnSchema(this.schema, "obs", oldCol);
|
||||
_writableCheck(oldColSchema);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const value = (this as any)._cache.obs.hasCol(oldCol)
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any)._cache.obs.col(oldCol).asArray()
|
||||
const oldColSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
oldCol
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCheck(oldColSchema);
|
||||
const value = this._cache.obs.hasCol(oldCol)
|
||||
? this._cache.obs.col(oldCol).asArray()
|
||||
: undefined;
|
||||
return this.dropObsColumn(oldCol).addObsColumn(
|
||||
{
|
||||
...oldColSchema,
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `name`: Type 'LabelType' is not assignable to type 'string'. Type 'number' is not assignable to type 'string'.
|
||||
name: newCol,
|
||||
},
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `value`: Object is possibly 'undefined'.
|
||||
value.constructor,
|
||||
value
|
||||
);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async setObsColumnValues(col, rowLabels, value) {
|
||||
async setObsColumnValues(
|
||||
col: LabelType,
|
||||
rowLabels: Int32Array,
|
||||
value: DataframeValue
|
||||
): Promise<AnnoMatrix> {
|
||||
/*
|
||||
Set all rows identified by rowLabels to value.
|
||||
*/
|
||||
const colSchema = _getColumnSchema(this.schema, "obs", col);
|
||||
_writableCategoryTypeCheck(colSchema); // throws on error
|
||||
const colSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
col
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCategoryTypeCheck(colSchema); // throws on error
|
||||
|
||||
// ensure that we have the data in cache before we manipulate it
|
||||
await this.fetch("obs", col);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
if (!(this as any)._cache.obs.hasCol(col))
|
||||
await this.fetch(Field.obs, col);
|
||||
if (!this._cache.obs.hasCol(col))
|
||||
throw new Error("Internal error - user annotation data missing");
|
||||
|
||||
const rowIndices = this.rowIndex.getOffsets(rowLabels);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const data = (this as any)._cache.obs.col(col).asArray().slice();
|
||||
const data = this._cache.obs.col(col).asArray().slice();
|
||||
for (let i = 0, len = rowIndices.length; i < len; i += 1) {
|
||||
const idx = rowIndices[i];
|
||||
if (idx === -1) throw new Error("Unknown row label");
|
||||
@@ -201,11 +226,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
}
|
||||
|
||||
const newAnnoMatrix = this._clone();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
newAnnoMatrix._cache.obs = (this as any)._cache.obs.replaceColData(
|
||||
col,
|
||||
data
|
||||
);
|
||||
newAnnoMatrix._cache.obs = this._cache.obs.replaceColData(col, data);
|
||||
const { categories } = colSchema;
|
||||
if (!categories?.includes(value)) {
|
||||
newAnnoMatrix.schema = addObsAnnoCategory(this.schema, col, value);
|
||||
@@ -213,37 +234,39 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'col' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async resetObsColumnValues(col, oldValue, newValue) {
|
||||
async resetObsColumnValues<T extends DataframeValue>(
|
||||
col: LabelType,
|
||||
oldValue: T,
|
||||
newValue: T
|
||||
): Promise<AnnoMatrix> {
|
||||
/*
|
||||
Set all rows with value 'oldValue' to 'newValue'.
|
||||
*/
|
||||
const colSchema = _getColumnSchema(this.schema, "obs", col);
|
||||
_writableCategoryTypeCheck(colSchema); // throws on error
|
||||
const colSchema = _getColumnSchema(
|
||||
this.schema,
|
||||
Field.obs,
|
||||
col
|
||||
) as AnnotationColumnSchema;
|
||||
_writableObsCategoryTypeCheck(colSchema); // throws on error
|
||||
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `colSchema.categories`: Object is possibly 'undefined'.
|
||||
if (!colSchema.categories.includes(oldValue)) {
|
||||
throw new Error("unknown category");
|
||||
}
|
||||
|
||||
// ensure that we have the data in cache before we manipulate it
|
||||
await this.fetch("obs", col);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
if (!(this as any)._cache.obs.hasCol(col))
|
||||
await this.fetch(Field.obs, col);
|
||||
if (!this._cache.obs.hasCol(col))
|
||||
throw new Error("Internal error - user annotation data missing");
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
const data = (this as any)._cache.obs.col(col).asArray().slice();
|
||||
const data = this._cache.obs.col(col).asArray().slice();
|
||||
for (let i = 0, l = data.length; i < l; i += 1) {
|
||||
if (data[i] === oldValue) data[i] = newValue;
|
||||
}
|
||||
|
||||
const newAnnoMatrix = this._clone();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
newAnnoMatrix._cache.obs = (this as any)._cache.obs.replaceColData(
|
||||
col,
|
||||
data
|
||||
);
|
||||
newAnnoMatrix._cache.obs = this._cache.obs.replaceColData(col, data);
|
||||
const { categories } = colSchema;
|
||||
if (!categories?.includes(newValue)) {
|
||||
newAnnoMatrix.schema = addObsAnnoCategory(this.schema, col, newValue);
|
||||
@@ -251,14 +274,12 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
addEmbedding(colSchema) {
|
||||
addEmbedding(colSchema: EmbeddingSchema): AnnoMatrix {
|
||||
/*
|
||||
add new layout to the obs embeddings
|
||||
*/
|
||||
const { name: colName } = colSchema;
|
||||
if (_getColumnSchema(this.schema, "emb", colName)) {
|
||||
if (_getColumnSchema(this.schema, Field.emb, colName)) {
|
||||
throw new Error("column already exists");
|
||||
}
|
||||
|
||||
@@ -270,9 +291,10 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
/**
|
||||
** Private below
|
||||
**/
|
||||
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixLoader' is n... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async _doLoad(field, query) {
|
||||
async _doLoad(
|
||||
field: Field,
|
||||
query: Query
|
||||
): Promise<[WhereCache | null, Dataframe]> {
|
||||
/*
|
||||
_doLoad - evaluates the query against the field. Returns:
|
||||
* whereCache update: column query map mapping the query to the column labels
|
||||
@@ -300,7 +322,8 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
throw new Error("Unknown field name");
|
||||
}
|
||||
const buffer = await promiseThrottle.priorityAdd(priority, doRequest);
|
||||
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'unknown' is not assignable to parameter of type 'ArrayBuffer | ArrayBuffer[]'.... Remove this comment to see the full error message
|
||||
// @ts-expect-error --- TODO revisit:
|
||||
// `buffer`: Argument of type 'unknown' is not assignable to parameter of type 'ArrayBuffer | ArrayBuffer[]'. Type 'unknown' is not assignable to type 'ArrayBuffer[]'.
|
||||
let result = matrixFBSToDataframe(buffer);
|
||||
if (!result || result.isEmpty()) throw Error("Unknown field/col");
|
||||
|
||||
@@ -320,23 +343,26 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
|
||||
Utility functions below
|
||||
*/
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
function _writableCheck(colSchema) {
|
||||
if (!colSchema?.writable) {
|
||||
function _writableObsCheck(obsColSchema: AnnotationColumnSchema): void {
|
||||
if (!obsColSchema?.writable) {
|
||||
throw new Error("Unknown or readonly obs column");
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colSchema' implicitly has an 'any' type... Remove this comment to see the full error message
|
||||
function _writableCategoryTypeCheck(colSchema) {
|
||||
_writableCheck(colSchema);
|
||||
if (colSchema.type !== "categorical") {
|
||||
function _writableObsCategoryTypeCheck(
|
||||
obsColSchema: AnnotationColumnSchema
|
||||
): void {
|
||||
_writableObsCheck(obsColSchema);
|
||||
if (obsColSchema.type !== "categorical") {
|
||||
throw new Error("column must be categorical");
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
|
||||
function _embLoader(baseURL, _field, query) {
|
||||
function _embLoader(
|
||||
baseURL: string,
|
||||
_field: Field,
|
||||
query: Query
|
||||
): () => Promise<ArrayBuffer> {
|
||||
_expectSimpleQuery(query);
|
||||
|
||||
const urlBase = `${baseURL}layout/obs`;
|
||||
@@ -345,8 +371,11 @@ function _embLoader(baseURL, _field, query) {
|
||||
return () => doBinaryRequest(url);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
|
||||
function _obsOrVarLoader(baseURL, field, query) {
|
||||
function _obsOrVarLoader(
|
||||
baseURL: string,
|
||||
field: Field,
|
||||
query: Query
|
||||
): () => Promise<ArrayBuffer> {
|
||||
_expectSimpleQuery(query);
|
||||
|
||||
const urlBase = `${baseURL}annotations/${field}`;
|
||||
@@ -355,20 +384,26 @@ function _obsOrVarLoader(baseURL, field, query) {
|
||||
return () => doBinaryRequest(url);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'baseURL' implicitly has an 'any' type.
|
||||
function _XLoader(baseURL, field, query) {
|
||||
function _XLoader(
|
||||
baseURL: string,
|
||||
_field: Field,
|
||||
query: Query
|
||||
): () => Promise<ArrayBuffer> {
|
||||
_expectComplexQuery(query);
|
||||
|
||||
if (query.where) {
|
||||
// Casting here as query is validated to be complex in _expectComplexQuery above.
|
||||
const complexQuery = query as ComplexQuery;
|
||||
|
||||
if ("where" in complexQuery) {
|
||||
const urlBase = `${baseURL}data/var`;
|
||||
const urlQuery = _urlEncodeComplexQuery(query);
|
||||
const urlQuery = _urlEncodeComplexQuery(complexQuery);
|
||||
const url = `${urlBase}?${urlQuery}`;
|
||||
return () => doBinaryRequest(url);
|
||||
}
|
||||
|
||||
if (query.summarize) {
|
||||
if ("summarize" in complexQuery) {
|
||||
const urlBase = `${baseURL}summarize/var`;
|
||||
const urlQuery = _urlEncodeComplexQuery(query);
|
||||
const urlQuery = _urlEncodeComplexQuery(complexQuery);
|
||||
|
||||
if (urlBase.length + urlQuery.length < 2000) {
|
||||
const url = `${urlBase}?${urlQuery}`;
|
||||
|
||||
@@ -11,17 +11,24 @@ Undoable metareducer and the AnnoMatrix private API. It would be helpful
|
||||
to make the Undoable interface better factored.
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
const annoMatrixGC = (store: any) => (next: any) => (action: any) => {
|
||||
if (_itIsTimeForGC()) {
|
||||
_doGC(store);
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
import { Action, Dispatch, MiddlewareAPI } from "redux";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import { GCHints } from "../common/types/entities";
|
||||
|
||||
const annoMatrixGC =
|
||||
(store: MiddlewareAPI) =>
|
||||
// GC middleware doesn't add any extra types to dispatch; it just executes GC and continues.
|
||||
(next: Dispatch) =>
|
||||
(action: Action): Action => {
|
||||
if (_itIsTimeForGC()) {
|
||||
_doGC(store);
|
||||
}
|
||||
return next(action);
|
||||
};
|
||||
|
||||
let lastGCTime = 0;
|
||||
const InterGCDelayMS = 30 * 1000; // 30 seconds
|
||||
function _itIsTimeForGC() {
|
||||
function _itIsTimeForGC(): boolean {
|
||||
/*
|
||||
we don't want to run GC on every dispatch, so throttle it a bit.
|
||||
|
||||
@@ -35,20 +42,22 @@ function _itIsTimeForGC() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
function _doGC(store: any) {
|
||||
function _doGC(store: MiddlewareAPI): void {
|
||||
const state = store.getState();
|
||||
|
||||
// these should probably be a function imported from undoable.js, etc, as
|
||||
// they have overly intimiate knowledge of our reducers.
|
||||
// they have overly intimate knowledge of our reducers.
|
||||
const undoablePast = state["@@undoable/past"];
|
||||
const undoableFuture = state["@@undoable/future"];
|
||||
const undoableStack = undoablePast
|
||||
.concat(undoableFuture)
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- TODO revisit: waiting for typings from /reducers
|
||||
.flatMap((snapshot: any) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
snapshot.filter((v: any) => v[0] === "annoMatrix").map((v: any) => v[1])
|
||||
snapshot
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- TODO revisit: waiting for typings from /reducers
|
||||
.filter((v: any) => v[0] === "annoMatrix")
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- TODO revisit: waiting for typings from /reducers
|
||||
.map((v: any) => v[1])
|
||||
);
|
||||
const currentAnnoMatrix = state.annoMatrix;
|
||||
|
||||
@@ -56,18 +65,16 @@ function _doGC(store: any) {
|
||||
We want to identify those matrixes currently "hot", ie, linked from the current annoMatrix,
|
||||
as our current gc algo is more aggressive with those not hot.
|
||||
*/
|
||||
const allAnnoMatrices = new Map(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
undoableStack.map((m: any) => [m, { isHot: false }])
|
||||
const allAnnoMatrices = new Map<AnnoMatrix, GCHints>(
|
||||
undoableStack.map((m: AnnoMatrix) => [m, { isHot: false }])
|
||||
);
|
||||
let am = currentAnnoMatrix;
|
||||
while (am) {
|
||||
while (am?.isView) {
|
||||
allAnnoMatrices.set(am, { isHot: true });
|
||||
am = am.viewOf;
|
||||
}
|
||||
allAnnoMatrices.forEach((hints, annoMatrix) =>
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(annoMatrix as any)._gc(hints)
|
||||
allAnnoMatrices.forEach((hints, annoMatrix: AnnoMatrix) =>
|
||||
annoMatrix._gc(hints)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { Schema } from "../common/types/schema";
|
||||
import { _getColumnSchema, _isIndex } from "./schema";
|
||||
import catLabelSort from "../util/catLabelSort";
|
||||
import {
|
||||
@@ -7,9 +6,15 @@ import {
|
||||
globalConfig,
|
||||
} from "../globals";
|
||||
import { Dataframe, LabelType, DataframeColumn } from "../util/dataframe";
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
ArraySchema,
|
||||
Field,
|
||||
Schema,
|
||||
} from "../common/types/schema";
|
||||
|
||||
export function normalizeResponse(
|
||||
field: string,
|
||||
field: Field,
|
||||
schema: Schema,
|
||||
response: Dataframe
|
||||
): Dataframe {
|
||||
@@ -37,11 +42,15 @@ export function normalizeResponse(
|
||||
*/
|
||||
|
||||
// currently no data or schema normalization necessary for X or emb
|
||||
if (field !== "obs" && field !== "var") return response;
|
||||
if (field !== Field.obs && field !== Field.var) return response;
|
||||
|
||||
const colLabels = response.colIndex.labels();
|
||||
for (const colLabel of colLabels) {
|
||||
const colSchema = _getColumnSchema(schema, field, colLabel);
|
||||
const colSchema = _getColumnSchema(
|
||||
schema,
|
||||
field,
|
||||
colLabel
|
||||
) as AnnotationColumnSchema;
|
||||
const isIndex = _isIndex(schema, field, colLabel);
|
||||
const { type, writable } = colSchema;
|
||||
|
||||
@@ -73,11 +82,10 @@ function castColumnToBoolean(df: Dataframe, label: LabelType): Dataframe {
|
||||
return df;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function normalizeWritableCategoricalSchema(
|
||||
colSchema: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colSchema: AnnotationColumnSchema, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
col: DataframeColumn
|
||||
) {
|
||||
): ArraySchema {
|
||||
/*
|
||||
Ensure all enum writable / categorical schema have a categories array, that
|
||||
the categories array contains all unique values in the data array, AND that
|
||||
@@ -96,7 +104,7 @@ export function normalizeWritableCategoricalSchema(
|
||||
export function normalizeCategorical(
|
||||
df: Dataframe,
|
||||
colLabel: LabelType,
|
||||
colSchema: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
colSchema: AnnotationColumnSchema
|
||||
): Dataframe {
|
||||
/*
|
||||
If writable, ensure schema matches data and we have an unassigned label
|
||||
@@ -106,7 +114,6 @@ export function normalizeCategorical(
|
||||
*/
|
||||
const { writable } = colSchema;
|
||||
const col = df.col(colLabel);
|
||||
|
||||
if (writable) {
|
||||
// writable (aka user) annotations
|
||||
normalizeWritableCategoricalSchema(colSchema, col);
|
||||
|
||||
@@ -1,22 +1,52 @@
|
||||
import sha1 from "sha1";
|
||||
import { _dubEncURIComp } from "./fetchHelpers";
|
||||
import { Field } from "../common/types/schema";
|
||||
import { LabelType } from "../util/dataframe";
|
||||
|
||||
/**
|
||||
* Query utilities, mostly for debugging support and validation.
|
||||
*/
|
||||
|
||||
export type ComplexQuery = SummarizeQuery | WhereQuery;
|
||||
|
||||
export type Query = LabelType | ComplexQuery;
|
||||
|
||||
interface SummarizeQuery {
|
||||
summarize: SummarizeQueryTerm;
|
||||
}
|
||||
|
||||
interface SummarizeQueryTerm {
|
||||
column: string;
|
||||
field: string;
|
||||
method: string;
|
||||
values: string[];
|
||||
}
|
||||
|
||||
interface WhereQuery {
|
||||
where: WhereQueryTerm;
|
||||
}
|
||||
|
||||
interface WhereQueryTerm {
|
||||
column: string;
|
||||
field: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
export function _expectSimpleQuery(query: Query): void {
|
||||
if (typeof query === "object") throw new Error("expected simple query");
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalize & error check the query.
|
||||
* @param {object | string} query - the query
|
||||
* @returns {object | string} - the normalized query
|
||||
* @param {Query} query - the query
|
||||
* @returns {Query} - the normalized query
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _queryValidate(query: any) {
|
||||
export function _queryValidate(query: Query): Query {
|
||||
if (typeof query !== "object") return query;
|
||||
|
||||
if (query.where && query.summarize)
|
||||
if ("where" in query && "summarize" in query)
|
||||
throw new Error("query may not specify both where and summarize");
|
||||
if (query.where) {
|
||||
if ("where" in query) {
|
||||
const {
|
||||
field: queryField,
|
||||
column: queryColumn,
|
||||
@@ -26,7 +56,7 @@ export function _queryValidate(query: any) {
|
||||
throw new Error("Incomplete where query");
|
||||
return query;
|
||||
}
|
||||
if (query.summarize) {
|
||||
if ("summarize" in query) {
|
||||
const {
|
||||
field: queryField,
|
||||
column: queryColumn,
|
||||
@@ -41,13 +71,7 @@ export function _queryValidate(query: any) {
|
||||
throw new Error("query must specify one of where or summarize");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _expectSimpleQuery(query: any) {
|
||||
if (typeof query === "object") throw new Error("expected simple query");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _expectComplexQuery(query: any) {
|
||||
export function _expectComplexQuery(query: Query): void {
|
||||
if (typeof query !== "object") throw new Error("expected complex query");
|
||||
}
|
||||
|
||||
@@ -56,13 +80,12 @@ export function _expectComplexQuery(query: any) {
|
||||
*
|
||||
* @param {string} field
|
||||
* @param {string|object} query
|
||||
* @returns the key
|
||||
* @returns {string} the key
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _queryCacheKey(field: any, query: any) {
|
||||
export function _queryCacheKey(field: Field, query: Query): string {
|
||||
if (typeof query === "object") {
|
||||
// complex query
|
||||
if (query.where) {
|
||||
if ("where" in query) {
|
||||
const {
|
||||
field: queryField,
|
||||
column: queryColumn,
|
||||
@@ -70,7 +93,7 @@ export function _queryCacheKey(field: any, query: any) {
|
||||
} = query.where;
|
||||
return `${field}/${queryField}/${queryColumn}/${queryValue}`;
|
||||
}
|
||||
if (query.summarize) {
|
||||
if ("summarize" in query) {
|
||||
const {
|
||||
method,
|
||||
field: queryField,
|
||||
@@ -88,38 +111,34 @@ export function _queryCacheKey(field: any, query: any) {
|
||||
return `${field}/${query}`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
function _urlEncodeWhereQuery(q: any) {
|
||||
function _urlEncodeWhereQuery(q: WhereQueryTerm): string {
|
||||
const { field: queryField, column: queryColumn, value: queryValue } = q;
|
||||
return `${_dubEncURIComp(queryField)}:${_dubEncURIComp(
|
||||
queryColumn
|
||||
)}=${_dubEncURIComp(queryValue)}`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
function _urlEncodeSummarizeQuery(q: any) {
|
||||
function _urlEncodeSummarizeQuery(q: SummarizeQueryTerm): string {
|
||||
const { method, field, column, values } = q;
|
||||
const filter = values // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
.map((value: any) => _urlEncodeWhereQuery({ field, column, value }))
|
||||
const filter = values
|
||||
.map((value: string) => _urlEncodeWhereQuery({ field, column, value }))
|
||||
.join("&");
|
||||
return `method=${method}&${filter}`;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _urlEncodeComplexQuery(q: any) {
|
||||
export function _urlEncodeComplexQuery(q: ComplexQuery): string {
|
||||
if (typeof q === "object") {
|
||||
if (q.where) {
|
||||
if ("where" in q) {
|
||||
return _urlEncodeWhereQuery(q.where);
|
||||
}
|
||||
if (q.summarize) {
|
||||
if ("summarize" in q) {
|
||||
return _urlEncodeSummarizeQuery(q.summarize);
|
||||
}
|
||||
}
|
||||
throw new Error("Unrecognized complex query type");
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _urlEncodeLabelQuery(colKey: any, q: any) {
|
||||
export function _urlEncodeLabelQuery(colKey: string, q: Query): string {
|
||||
if (!colKey) throw new Error("Unsupported query by name");
|
||||
if (typeof q !== "string") throw new Error("Query must be a simple label.");
|
||||
return `${colKey}=${encodeURIComponent(q)}`;
|
||||
@@ -128,8 +147,6 @@ export function _urlEncodeLabelQuery(colKey: any, q: any) {
|
||||
/**
|
||||
* Generate the column key the server will send us for this query.
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _hashStringValues(arrayOfString: any) {
|
||||
const hash = sha1(arrayOfString.join(""));
|
||||
return hash;
|
||||
export function _hashStringValues(arrayOfString: string[]): string {
|
||||
return sha1(arrayOfString.join(""));
|
||||
}
|
||||
|
||||
@@ -1,37 +1,54 @@
|
||||
/*
|
||||
Private helper functions related to schema
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _getColumnSchema(schema: any, field: any, col: any) {
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
ArraySchema,
|
||||
Field,
|
||||
Schema,
|
||||
} from "../common/types/schema";
|
||||
import { LabelArray, LabelType } from "../util/dataframe/types";
|
||||
|
||||
export function _getColumnSchema(
|
||||
schema: Schema,
|
||||
field: Field,
|
||||
col: LabelType
|
||||
): ArraySchema {
|
||||
/* look up the column definition */
|
||||
switch (field) {
|
||||
case "obs":
|
||||
case Field.obs:
|
||||
if (typeof col === "object")
|
||||
throw new Error("unable to get column schema by query");
|
||||
return schema.annotations.obsByName[col];
|
||||
case "var":
|
||||
case Field.var:
|
||||
if (typeof col === "object")
|
||||
throw new Error("unable to get column schema by query");
|
||||
return schema.annotations.varByName[col];
|
||||
case "emb":
|
||||
case Field.emb:
|
||||
if (typeof col === "object")
|
||||
throw new Error("unable to get column schema by query");
|
||||
return schema.layout.obsByName[col];
|
||||
case "X":
|
||||
case Field.X:
|
||||
return schema.dataframe;
|
||||
default:
|
||||
throw new Error(`unknown field name: ${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function _isIndex(schema: any, field: any, col: any): boolean {
|
||||
export function _isIndex(
|
||||
schema: Schema,
|
||||
field: Field.obs | Field.var,
|
||||
col: LabelType
|
||||
): boolean {
|
||||
const index = schema.annotations?.[field].index;
|
||||
return index && index === col;
|
||||
return !!(index && index === col);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _getColumnDimensionNames(schema: any, field: any, col: any) {
|
||||
export function _getColumnDimensionNames(
|
||||
schema: Schema,
|
||||
field: Field,
|
||||
col: LabelType
|
||||
): LabelArray | undefined {
|
||||
/*
|
||||
field/col may be an alias for multiple columns. Currently used to map ND
|
||||
values to 1D dataframe columns for embeddings/layout. Signified by the presence
|
||||
@@ -41,40 +58,33 @@ export function _getColumnDimensionNames(schema: any, field: any, col: any) {
|
||||
if (!colSchema) {
|
||||
return undefined;
|
||||
}
|
||||
return colSchema.dims || [col];
|
||||
if ("dims" in colSchema) {
|
||||
return colSchema.dims;
|
||||
}
|
||||
return [col];
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _schemaColumns(schema, field) {
|
||||
export function _schemaColumns(schema: Schema, field: Field): string[] {
|
||||
switch (field) {
|
||||
case "obs":
|
||||
case Field.obs:
|
||||
return Object.keys(schema.annotations.obsByName);
|
||||
case "var":
|
||||
case Field.var:
|
||||
return Object.keys(schema.annotations.varByName);
|
||||
case "emb":
|
||||
case Field.emb:
|
||||
return Object.keys(schema.layout.obsByName);
|
||||
default:
|
||||
throw new Error(`unknown field name: ${field}`);
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _getWritableColumns(schema, field) {
|
||||
if (field !== "obs") return [];
|
||||
return (
|
||||
schema.annotations.obs.columns
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'v' implicitly has an 'any' type.
|
||||
.filter((v) => v.writable)
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'v' implicitly has an 'any' type.
|
||||
.map((v) => v.name)
|
||||
);
|
||||
export function _getWritableColumns(schema: Schema, field: Field): string[] {
|
||||
if (field !== Field.obs) return [];
|
||||
return schema.annotations.obs.columns
|
||||
.filter((v: AnnotationColumnSchema) => v.writable)
|
||||
.map((v: AnnotationColumnSchema) => v.name);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'schema' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _isContinuousType(schema) {
|
||||
export function _isContinuousType(schema: ArraySchema): boolean {
|
||||
const { type } = schema;
|
||||
return !(type === "string" || type === "boolean" || type === "categorical");
|
||||
}
|
||||
|
||||
@@ -4,9 +4,18 @@ instances of AnnoMatrix, implementing common UI functions.
|
||||
*/
|
||||
|
||||
import { AnnoMatrixRowSubsetView, AnnoMatrixClipView } from "./views";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import {
|
||||
DenseInt32Index,
|
||||
IdentityInt32Index,
|
||||
KeyIndex,
|
||||
} from "../util/dataframe";
|
||||
import { OffsetArray } from "../util/dataframe/types";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
export function isubsetMask(annoMatrix: any, obsMask: any) {
|
||||
export function isubsetMask(
|
||||
annoMatrix: AnnoMatrix,
|
||||
obsMask: Uint8Array
|
||||
): AnnoMatrixRowSubsetView {
|
||||
/*
|
||||
Subset annomatrix to contain the rows which have truish value in the mask.
|
||||
Maks length must equal annoMatrix.nObs (row count).
|
||||
@@ -14,9 +23,10 @@ export function isubsetMask(annoMatrix: any, obsMask: any) {
|
||||
return isubset(annoMatrix, _maskToList(obsMask));
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function isubset(annoMatrix, obsOffsets) {
|
||||
export function isubset(
|
||||
annoMatrix: AnnoMatrix,
|
||||
obsOffsets: OffsetArray
|
||||
): AnnoMatrixRowSubsetView {
|
||||
/*
|
||||
Subset annomatrix to contain the positions contained in the obsOffsets array
|
||||
|
||||
@@ -28,9 +38,10 @@ export function isubset(annoMatrix, obsOffsets) {
|
||||
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function subset(annoMatrix, obsLabels) {
|
||||
export function subset(
|
||||
annoMatrix: AnnoMatrix,
|
||||
obsLabels: Int32Array
|
||||
): AnnoMatrixRowSubsetView {
|
||||
/*
|
||||
subset based on labels
|
||||
*/
|
||||
@@ -38,18 +49,21 @@ export function subset(annoMatrix, obsLabels) {
|
||||
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function subsetByIndex(annoMatrix, obsIndex) {
|
||||
export function subsetByIndex(
|
||||
annoMatrix: AnnoMatrix,
|
||||
obsIndex: DenseInt32Index | IdentityInt32Index | KeyIndex
|
||||
): AnnoMatrixRowSubsetView {
|
||||
/*
|
||||
subset based upon the new obs index.
|
||||
*/
|
||||
return new AnnoMatrixRowSubsetView(annoMatrix, obsIndex);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function clip(annoMatrix, qmin, qmax) {
|
||||
export function clip(
|
||||
annoMatrix: AnnoMatrix,
|
||||
qmin: number,
|
||||
qmax: number
|
||||
): AnnoMatrix {
|
||||
/*
|
||||
Create a view that clips all continuous data to the [min, max] range.
|
||||
The matrix shape does not change, but the continuous values outside the
|
||||
@@ -62,12 +76,8 @@ export function clip(annoMatrix, qmin, qmax) {
|
||||
Private utility functions below
|
||||
*/
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'mask' implicitly has an 'any' type.
|
||||
function _maskToList(mask) {
|
||||
function _maskToList(mask: Uint8Array): OffsetArray {
|
||||
/* convert masks to lists - method wastes space, but is fast */
|
||||
if (!mask) {
|
||||
return null;
|
||||
}
|
||||
const list = new Int32Array(mask.length);
|
||||
let elems = 0;
|
||||
for (let i = 0, l = mask.length; i < l; i += 1) {
|
||||
|
||||
@@ -5,21 +5,40 @@ Views on the annomatrix. all API here is defined in viewCreators.js and annoMat
|
||||
*/
|
||||
import clip from "../util/clip";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import { _whereCacheCreate } from "./whereCache";
|
||||
import { _whereCacheCreate, WhereCache } from "./whereCache";
|
||||
import { _isContinuousType, _getColumnSchema } from "./schema";
|
||||
import {
|
||||
Dataframe,
|
||||
DataframeValue,
|
||||
DataframeValueArray,
|
||||
LabelType,
|
||||
} from "../util/dataframe";
|
||||
import { Query } from "./query";
|
||||
import {
|
||||
AnnotationColumnSchema,
|
||||
ArraySchema,
|
||||
Field,
|
||||
EmbeddingSchema,
|
||||
} from "../common/types/schema";
|
||||
import { LabelIndexBase } from "../util/dataframe/labelIndex";
|
||||
|
||||
class AnnoMatrixView extends AnnoMatrix {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(viewOf: any, rowIndex = null) {
|
||||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
|
||||
type MapFn = (
|
||||
field: Field,
|
||||
colLabel: LabelType,
|
||||
colSchema: ArraySchema,
|
||||
colData: DataframeValueArray,
|
||||
df: Dataframe
|
||||
) => DataframeValueArray;
|
||||
|
||||
abstract class AnnoMatrixView extends AnnoMatrix {
|
||||
constructor(viewOf: AnnoMatrix, rowIndex: LabelIndexBase | null = null) {
|
||||
const nObs = rowIndex ? rowIndex.size() : viewOf.nObs;
|
||||
super(viewOf.schema, nObs, viewOf.nVar, rowIndex || viewOf.rowIndex);
|
||||
this.viewOf = viewOf;
|
||||
this.isView = true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
addObsAnnoCategory(col: any, category: any) {
|
||||
addObsAnnoCategory(col: LabelType, category: string): AnnoMatrix {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = this.viewOf.addObsAnnoCategory(col, category);
|
||||
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
|
||||
@@ -27,13 +46,10 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
}
|
||||
|
||||
async removeObsAnnoCategory(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
col: any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
category: any,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
unassignedCategory: any
|
||||
) {
|
||||
col: LabelType,
|
||||
category: string,
|
||||
unassignedCategory: string
|
||||
): Promise<AnnoMatrix> {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = await this.viewOf.removeObsAnnoCategory(
|
||||
col,
|
||||
@@ -44,8 +60,7 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
dropObsColumn(col: any) {
|
||||
dropObsColumn(col: LabelType): AnnoMatrix {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = this.viewOf.dropObsColumn(col);
|
||||
newAnnoMatrix._cache.obs = this._cache.obs.dropCol(col);
|
||||
@@ -53,24 +68,29 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
addObsColumn(colSchema: any, Ctor: any, value: any) {
|
||||
addObsColumn<T extends DataframeValueArray>(
|
||||
colSchema: AnnotationColumnSchema,
|
||||
Ctor: new (n: number) => T,
|
||||
value: T
|
||||
): AnnoMatrix {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = this.viewOf.addObsColumn(colSchema, Ctor, value);
|
||||
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
renameObsColumn(oldCol: any, newCol: any) {
|
||||
renameObsColumn(oldCol: LabelType, newCol: LabelType): AnnoMatrix {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = this.viewOf.renameObsColumn(oldCol, newCol);
|
||||
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
async setObsColumnValues(col: any, rowLabels: any, value: any) {
|
||||
async setObsColumnValues(
|
||||
col: LabelType,
|
||||
rowLabels: Int32Array,
|
||||
value: DataframeValue
|
||||
): Promise<AnnoMatrix> {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = await this.viewOf.setObsColumnValues(
|
||||
col,
|
||||
@@ -82,8 +102,11 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
async resetObsColumnValues(col: any, oldValue: any, newValue: any) {
|
||||
async resetObsColumnValues<T extends DataframeValue>(
|
||||
col: LabelType,
|
||||
oldValue: T,
|
||||
newValue: T
|
||||
): Promise<AnnoMatrix> {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = await this.viewOf.resetObsColumnValues(
|
||||
col,
|
||||
@@ -95,8 +118,7 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
return newAnnoMatrix;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
addEmbedding(colSchema: any) {
|
||||
addEmbedding(colSchema: EmbeddingSchema): AnnoMatrix {
|
||||
const newAnnoMatrix = this._clone();
|
||||
newAnnoMatrix.viewOf = this.viewOf.addEmbedding(colSchema);
|
||||
newAnnoMatrix.schema = newAnnoMatrix.viewOf.schema;
|
||||
@@ -105,26 +127,32 @@ class AnnoMatrixView extends AnnoMatrix {
|
||||
}
|
||||
|
||||
class AnnoMatrixMapView extends AnnoMatrixView {
|
||||
mapFn: MapFn;
|
||||
|
||||
/*
|
||||
A view which knows how to transform its data.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
|
||||
constructor(viewOf, mapFn) {
|
||||
A view which knows how to transform its data.
|
||||
*/
|
||||
constructor(viewOf: AnnoMatrix, mapFn: MapFn) {
|
||||
super(viewOf);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).mapFn = mapFn;
|
||||
this.mapFn = mapFn;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixMapView' is ... Remove this comment to see the full error message
|
||||
async _doLoad(field, query) {
|
||||
async _doLoad(
|
||||
field: Field,
|
||||
query: Query
|
||||
): Promise<[WhereCache | null, Dataframe]> {
|
||||
const df = await this.viewOf._fetch(field, query);
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'colData' implicitly has an 'any' type.
|
||||
const dfMapped = df.mapColumns((colData, colIdx) => {
|
||||
const colLabel = df.colIndex.getLabel(colIdx);
|
||||
const colSchema = _getColumnSchema(this.schema, field, colLabel);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
return (this as any).mapFn(field, colLabel, colSchema, colData, df);
|
||||
});
|
||||
const dfMapped = df.mapColumns(
|
||||
(colData: DataframeValueArray, colIdx: number) => {
|
||||
const colLabel = df.colIndex.getLabel(colIdx);
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `colLabel`: Argument of type 'LabelType | undefined' is not assignable to parameter of type 'LabelType'.
|
||||
const colSchema = _getColumnSchema(this.schema, field, colLabel);
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `colLabel`: Argument of type 'LabelType | undefined' is not assignable to parameter of type 'LabelType'.
|
||||
return this.mapFn(field, colLabel, colSchema, colData, df);
|
||||
}
|
||||
);
|
||||
const whereCacheUpdate = _whereCacheCreate(
|
||||
field,
|
||||
query,
|
||||
@@ -135,42 +163,47 @@ class AnnoMatrixMapView extends AnnoMatrixView {
|
||||
}
|
||||
|
||||
export class AnnoMatrixClipView extends AnnoMatrixMapView {
|
||||
clipRange: [number, number];
|
||||
|
||||
isClipped: boolean;
|
||||
|
||||
/*
|
||||
A view which is a clipped transformation of its parent
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(viewOf, qmin, qmax) {
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
super(viewOf, (field, colLabel, colSchema, colData, df) =>
|
||||
_clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax)
|
||||
A view which is a clipped transformation of its parent
|
||||
*/
|
||||
constructor(viewOf: AnnoMatrix, qmin: number, qmax: number) {
|
||||
super(
|
||||
viewOf,
|
||||
(
|
||||
field: Field,
|
||||
colLabel: LabelType,
|
||||
colSchema: ArraySchema,
|
||||
colData: DataframeValueArray,
|
||||
df: Dataframe
|
||||
) => _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax)
|
||||
);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).isClipped = true;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
(this as any).clipRange = [qmin, qmax];
|
||||
this.isClipped = true;
|
||||
this.clipRange = [qmin, qmax];
|
||||
Object.seal(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnnoMatrixRowSubsetView extends AnnoMatrixView {
|
||||
/*
|
||||
A view which is a subset of total rows.
|
||||
*/
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'viewOf' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
constructor(viewOf, rowIndex) {
|
||||
A view which is a subset of total rows.
|
||||
*/
|
||||
constructor(viewOf: AnnoMatrix, rowIndex: LabelIndexBase) {
|
||||
super(viewOf, rowIndex);
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(2416) FIXME: Property '_doLoad' in type 'AnnoMatrixRowSubsetVie... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
async _doLoad(field, query) {
|
||||
async _doLoad(
|
||||
field: Field,
|
||||
query: Query
|
||||
): Promise<[WhereCache | null, Dataframe]> {
|
||||
const df = await this.viewOf._fetch(field, query);
|
||||
|
||||
// don't try to row-subset the var dimension.
|
||||
if (field === "var") {
|
||||
if (field === Field.var) {
|
||||
return [null, df];
|
||||
}
|
||||
|
||||
@@ -188,10 +221,17 @@ export class AnnoMatrixRowSubsetView extends AnnoMatrixView {
|
||||
Utility functions below
|
||||
*/
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
function _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) {
|
||||
function _clipAnnoMatrix(
|
||||
field: Field,
|
||||
colLabel: LabelType,
|
||||
colSchema: ArraySchema,
|
||||
colData: DataframeValueArray,
|
||||
df: Dataframe,
|
||||
qmin: number,
|
||||
qmax: number
|
||||
): DataframeValueArray {
|
||||
/* only clip obs and var scalar columns */
|
||||
if (field !== "obs" && field !== "X") return colData;
|
||||
if (field !== Field.obs && field !== Field.X) return colData;
|
||||
if (!_isContinuousType(colSchema)) return colData;
|
||||
if (qmin < 0) qmin = 0;
|
||||
if (qmax > 1) qmax = 1;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Private support functions.
|
||||
|
||||
This implements a query resolver cache, mapping a query onto the column labels
|
||||
resolved by that query. These labels are then used to manage the acutal data cache,
|
||||
resolved by that query. These labels are then used to manage the actual data cache,
|
||||
which stores data by the resolved label.
|
||||
|
||||
There are three query forms:
|
||||
@@ -49,19 +49,33 @@ creates a cache entry of:
|
||||
}
|
||||
*/
|
||||
import { _getColumnDimensionNames } from "./schema";
|
||||
import { _hashStringValues } from "./query";
|
||||
import { _hashStringValues, Query } from "./query";
|
||||
import { Field, Schema } from "../common/types/schema";
|
||||
import { LabelArray } from "../util/dataframe/types";
|
||||
|
||||
export interface WhereCache {
|
||||
summarize?: {
|
||||
[key: string]: {
|
||||
[key: string]: WhereCacheTerms;
|
||||
};
|
||||
};
|
||||
where?: {
|
||||
[key: string]: WhereCacheTerms;
|
||||
};
|
||||
}
|
||||
|
||||
export type WhereCacheColumnLabels = LabelArray;
|
||||
|
||||
interface WhereCacheTerms {
|
||||
[key: string]: Map<string, Map<string, WhereCacheColumnLabels>>;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _whereCacheGet(
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
whereCache: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
schema: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
field: any,
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
|
||||
query: any
|
||||
) {
|
||||
whereCache: WhereCache,
|
||||
schema: Schema,
|
||||
field: Field,
|
||||
query: Query
|
||||
): WhereCacheColumnLabels | [undefined] {
|
||||
/*
|
||||
query will either be an where query (object) or a column name (string).
|
||||
|
||||
@@ -69,7 +83,7 @@ export function _whereCacheGet(
|
||||
*/
|
||||
|
||||
if (typeof query === "object") {
|
||||
if (query.where) {
|
||||
if ("where" in query) {
|
||||
const {
|
||||
field: queryField,
|
||||
column: queryColumn,
|
||||
@@ -78,7 +92,7 @@ export function _whereCacheGet(
|
||||
const columnMap = whereCache?.where?.[field]?.[queryField];
|
||||
return columnMap?.get(queryColumn)?.get(queryValue) ?? [undefined];
|
||||
}
|
||||
if (query.summarize) {
|
||||
if ("summarize" in query) {
|
||||
const {
|
||||
method,
|
||||
field: queryField,
|
||||
@@ -95,15 +109,17 @@ export function _whereCacheGet(
|
||||
return _getColumnDimensionNames(schema, field, query) ?? [undefined];
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'field' implicitly has an 'any' type.
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _whereCacheCreate(field, query, columnLabels) {
|
||||
export function _whereCacheCreate(
|
||||
field: Field,
|
||||
query: Query,
|
||||
columnLabels: LabelArray
|
||||
): WhereCache | null {
|
||||
/*
|
||||
Create a new whereCache
|
||||
*/
|
||||
if (typeof query !== "object") return null;
|
||||
|
||||
if (query.where) {
|
||||
if ("where" in query) {
|
||||
const {
|
||||
field: queryField,
|
||||
column: queryColumn,
|
||||
@@ -119,7 +135,7 @@ export function _whereCacheCreate(field, query, columnLabels) {
|
||||
},
|
||||
};
|
||||
}
|
||||
if (query.summarize) {
|
||||
if ("summarize" in query) {
|
||||
const {
|
||||
method,
|
||||
field: queryField,
|
||||
@@ -143,23 +159,25 @@ export function _whereCacheCreate(field, query, columnLabels) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'dst' implicitly has an 'any' type.
|
||||
function __mergeQueries(dst, src) {
|
||||
function __mergeQueries(dst: WhereCacheTerms, src: WhereCacheTerms) {
|
||||
for (const [queryField, columnMap] of Object.entries(src)) {
|
||||
dst[queryField] = dst[queryField] || new Map();
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
|
||||
for (const [queryColumn, valueMap] of columnMap as any) {
|
||||
for (const [queryColumn, valueMap] of columnMap) {
|
||||
if (!dst[queryField].has(queryColumn))
|
||||
dst[queryField].set(queryColumn, new Map());
|
||||
for (const [queryValue, columnLabels] of valueMap) {
|
||||
// @ts-expect-error ts-migrate --- TODO revisit:
|
||||
// `dst[queryField].get(queryColumn)` Object is possibly 'undefined'.
|
||||
dst[queryField].get(queryColumn).set(queryValue, columnLabels);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'dst' implicitly has an 'any' type.
|
||||
function __whereCacheMerge(dst, src) {
|
||||
function __whereCacheMerge(
|
||||
dst: WhereCache,
|
||||
src: WhereCache | null
|
||||
): WhereCache {
|
||||
/*
|
||||
merge src into dst (modifies dst)
|
||||
*/
|
||||
@@ -176,7 +194,6 @@ function __whereCacheMerge(dst, src) {
|
||||
dst.summarize = dst.summarize || {};
|
||||
for (const [field, method] of Object.entries(src.summarize)) {
|
||||
dst.summarize[field] = dst.summarize[field] || {};
|
||||
// @ts-expect-error ts-migrate(2769) FIXME: No overload matches this call.
|
||||
for (const [methodName, query] of Object.entries(method)) {
|
||||
dst.summarize[field][methodName] =
|
||||
dst.summarize[field][methodName] || {};
|
||||
@@ -187,8 +204,6 @@ function __whereCacheMerge(dst, src) {
|
||||
return dst;
|
||||
}
|
||||
|
||||
// @ts-expect-error ts-migrate(7019) FIXME: Rest parameter 'caches' implicitly has an 'any[]' ... Remove this comment to see the full error message
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
|
||||
export function _whereCacheMerge(...caches) {
|
||||
return caches.reduce(__whereCacheMerge, {});
|
||||
export function _whereCacheMerge(...caches: (WhereCache | null)[]): WhereCache {
|
||||
return caches.reduce(__whereCacheMerge, {} as WhereCache);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
// If a globally shared type or interface doesn't have a clear owner, put it here
|
||||
|
||||
export {};
|
||||
/**
|
||||
* Flags informing garbage collection-related logic.
|
||||
*/
|
||||
export interface GCHints {
|
||||
isHot: boolean;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
type Category = number | string | boolean;
|
||||
export type Category = number | string | boolean;
|
||||
|
||||
export interface AnnotationColumnSchema {
|
||||
categories?: Category[];
|
||||
@@ -7,7 +7,7 @@ export interface AnnotationColumnSchema {
|
||||
writable: boolean;
|
||||
}
|
||||
|
||||
interface XMatrixSchema {
|
||||
export interface XMatrixSchema {
|
||||
nObs: number;
|
||||
nVar: number;
|
||||
// TODO(thuang): Not sure what other types are available
|
||||
@@ -56,3 +56,21 @@ export interface Schema extends RawSchema {
|
||||
annotations: AnnotationsSchema;
|
||||
layout: LayoutSchema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sub-schema objects describing the schema for a primitive Array or Matrix in one of the fields.
|
||||
*/
|
||||
export type ArraySchema =
|
||||
| AnnotationColumnSchema
|
||||
| EmbeddingSchema
|
||||
| XMatrixSchema;
|
||||
|
||||
/**
|
||||
* Set of data / metadata objects that must be specified in a CXG.
|
||||
*/
|
||||
export enum Field {
|
||||
"obs" = "obs",
|
||||
"var" = "var",
|
||||
"emb" = "emb",
|
||||
"X" = "X",
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
EmbeddingSchema,
|
||||
AnnotationColumnSchema,
|
||||
} from "../../common/types/schema";
|
||||
import { LabelType } from "../dataframe/types";
|
||||
|
||||
/*
|
||||
System wide schema assumptions:
|
||||
@@ -75,7 +76,7 @@ function _reindexObsLayout(schema: Schema) {
|
||||
return schema;
|
||||
}
|
||||
|
||||
export function removeObsAnnoColumn(schema: Schema, name: string): Schema {
|
||||
export function removeObsAnnoColumn(schema: Schema, name: LabelType): Schema {
|
||||
const newSchema = _copyObsAnno(schema);
|
||||
newSchema.annotations.obs.columns = schema.annotations.obs.columns.filter(
|
||||
(v) => v.name !== name
|
||||
@@ -97,7 +98,7 @@ export function addObsAnnoColumn(
|
||||
|
||||
export function removeObsAnnoCategory(
|
||||
schema: Schema,
|
||||
name: string,
|
||||
name: LabelType,
|
||||
category: string
|
||||
): Schema {
|
||||
/* remove a category from a categorical annotation */
|
||||
|
||||
Reference in New Issue
Block a user