Added typings to annoMatrix dir. (#2365) (#2371)

* 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:
Mim Hastie
2021-08-18 13:18:54 -07:00
committed by GitHub
parent 08b03ace60
commit b814489328
19 changed files with 1121 additions and 948 deletions

View File

@@ -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);

View File

@@ -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",
});

View File

@@ -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",

View File

@@ -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,
});
};

View File

@@ -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");
}

View File

@@ -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);
}

View File

@@ -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()];

View File

@@ -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));
}

View File

@@ -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}`;

View File

@@ -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)
);
}

View File

@@ -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);

View File

@@ -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(""));
}

View File

@@ -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");
}

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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",
}

View File

@@ -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 */