TS typing for Dataframe (#2382)

* initial TS typing

* first cut at Dataframe TS typing

* more Dataframe typing

* comments

* more Dataframe cleanup

* PR review fixes and improvements
This commit is contained in:
Bruce Martin
2021-08-17 10:43:40 -07:00
committed by GitHub
parent 3fdf5cac9d
commit 45cecad76a
39 changed files with 1298 additions and 1124 deletions
@@ -188,8 +188,10 @@ describe("AnnoMatrix", () => {
(fetch as any)
.once(serverMocks.annotationsObs(["n_genes"]))
.once(serverMocks.annotationsObs(["n_genes"]));
const ng1 = await am1.fetch("obs", "n_genes");
const ng2 = await am2.fetch("obs", "n_genes");
const ng1 = (await am1.fetch("obs", "n_genes")) as Dataframe;
const ng2 = (await am2.fetch("obs", "n_genes")) as Dataframe;
expect(ng1).toBeDefined();
expect(ng2).toBeDefined();
expect(ng1).toHaveLength(ng2.length);
expect(ng1.colIndex.labels()).toEqual(ng2.colIndex.labels());
expect(ng1.col("n_genes").asArray()).toEqual(
@@ -216,7 +218,7 @@ describe("AnnoMatrix", () => {
);
expect(base.getMatrixColumns("obs")).not.toContain("foo");
expect(am1.getMatrixColumns("obs")).toContain("foo");
const foo = await am1.fetch("obs", "foo");
const foo: Dataframe = await am1.fetch("obs", "foo");
expect(foo).toBeDefined();
expect(foo).toBeInstanceOf(Dataframe);
expect(foo).toHaveLength(am1.nObs);
@@ -12,6 +12,7 @@ import {
AnnoMatrixObsCrossfilter,
isubsetMask,
} from "../../../src/annoMatrix";
import { Dataframe } from "../../../src/util/dataframe";
import { rangeFill } from "../../../src/util/range";
enableFetchMocks();
@@ -132,7 +133,7 @@ describe("AnnoMatrixCrossfilter", () => {
)
);
const df = await annoMatrix.fetch("obs", "louvain");
const df: Dataframe = await annoMatrix.fetch("obs", "louvain");
const values = df.col("louvain").asArray();
const selected = xfltr.allSelectedMask();
values.every(
@@ -265,7 +266,11 @@ describe("AnnoMatrixCrossfilter", () => {
expect(xfltr).toBeDefined();
expect(xfltr.countSelected()).toEqual(240);
const df = await annoMatrixSubset.fetch("obs", "louvain");
const df: Dataframe = (await annoMatrixSubset.fetch(
"obs",
"louvain"
)) as Dataframe;
expect(df).toBeDefined();
const values = df.col("louvain").asArray();
const selected = xfltr.allSelectedMask();
values.every(
@@ -354,7 +359,7 @@ describe("AnnoMatrixCrossfilter", () => {
).toHaveLength(1);
// check data update.
const df = await xfltr.annoMatrix.fetch("obs", "foo");
const df: Dataframe = await xfltr.annoMatrix.fetch("obs", "foo");
expect(
df
.col("foo")
@@ -649,7 +654,7 @@ describe("AnnoMatrixCrossfilter", () => {
values: ["purple"],
});
expect(xfltr2.countSelected()).toEqual(2);
expect(xfltr2.allSelectedLabels()).toEqual(Int32Array.from([0, 10]));
expect(xfltr2.allSelectedLabels()).toEqual(Array.from([0, 10]));
});
test("resetObsColumnValues", async () => {
@@ -43,7 +43,6 @@ function getEncodedDataframe(colNames: any, length: any, colSchemas: any) {
const colIndex = new KeyIndex(colNames);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const columns = colSchemas.map((s: any) => makeMockColumn(s, length));
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
const df = new Dataframe([length, colNames.length], columns, null, colIndex);
const body = encodeMatrixFBS(df);
return body;
@@ -56,7 +55,6 @@ export function dataframeResponse(colNames: any, columns: any) {
[columns[0].length, colNames.length],
columns,
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
colIndex
);
const body = encodeMatrixFBS(df);
+8 -8
View File
@@ -1,19 +1,19 @@
import cloneDeep from "lodash.clonedeep";
import { NumberArray } from "../../src/common/types/arraytypes";
import calcCentroid from "../../src/util/centroid";
import quantile from "../../src/util/quantile";
import { matrixFBSToDataframe } from "../../src/util/stateManager/matrix";
import * as REST from "./stateManager/sampleResponses";
import { indexEntireSchema } from "../../src/util/stateManager/schemaHelpers";
import { normalizeWritableCategoricalSchema } from "../../src/annoMatrix/normalize";
import { Dataframe } from "../../src/util/dataframe";
describe("centroid", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let schema: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let obsAnnotations: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let obsLayout: any;
let obsAnnotations: Dataframe;
let obsLayout: Dataframe;
beforeAll(() => {
schema = indexEntireSchema(cloneDeep(REST.schema.schema));
@@ -43,8 +43,8 @@ describe("centroid", () => {
// This expected result assumes that all cells belong in all categorical values inside of sample response
const expectedResult = [
quantile([0.5], obsLayout.col("umap_0").asArray())[0],
quantile([0.5], obsLayout.col("umap_1").asArray())[0],
quantile([0.5], obsLayout.col("umap_0").asArray() as NumberArray)[0],
quantile([0.5], obsLayout.col("umap_1").asArray() as NumberArray)[0],
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
@@ -68,8 +68,8 @@ describe("centroid", () => {
// This expected result assumes that all cells belong in all categorical values inside of sample response
const expectedResult = [
quantile([0.5], obsLayout.col("umap_0").asArray())[0],
quantile([0.5], obsLayout.col("umap_1").asArray())[0],
quantile([0.5], obsLayout.col("umap_0").asArray() as NumberArray)[0],
quantile([0.5], obsLayout.col("umap_1").asArray() as NumberArray)[0],
];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
+168 -167
View File
@@ -6,7 +6,8 @@ describe("dataframe constructor", () => {
expect(df).toBeDefined();
expect(df.dims).toEqual([0, 0]);
expect(df).toHaveLength(0);
expect(df.icol(0)).not.toBeDefined();
expect(df.ihasCol(0)).toBeFalsy();
expect(() => df.icol(0)).toThrow(RangeError);
});
test("create with default indices", () => {
@@ -23,13 +24,19 @@ describe("dataframe constructor", () => {
expect(df.at(2, 1)).toEqual(1);
expect(df.iat(0, 0)).toEqual(0);
expect(df.iat(2, 1)).toEqual(1);
expect(Array.from(df.rowIndex.labels())).toEqual(
df.rowIndex.getLabels(df.rowIndex.getOffsets(df.rowIndex.labels()))
);
expect(Array.from(df.colIndex.labels())).toEqual(
df.colIndex.getLabels(df.colIndex.getOffsets(df.colIndex.labels()))
);
});
test("create with labelled indices", () => {
const df = new Dataframe.Dataframe(
[3, 2],
[new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 1, 0]),
new Dataframe.KeyIndex(["A", "B"])
);
@@ -56,7 +63,6 @@ describe("simple data access", () => {
new Float64Array([0.0, Number.NaN, Number.POSITIVE_INFINITY, 3.14159]),
["red", "blue", "green", "nan"],
],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([3, 2, 1, 0]),
new Dataframe.KeyIndex(["numbers", "colors"])
);
@@ -123,7 +129,9 @@ describe("simple data access", () => {
expect(df.has(3, "foo")).toBeFalsy();
expect(df.has(-1, "numbers")).toBeFalsy();
expect(df.has(-1, -1)).toBeFalsy();
expect(df.has(null, null)).toBeFalsy();
expect(
df.has(null as unknown as number, null as unknown as number)
).toBeFalsy();
expect(df.has(0, "foo")).toBeFalsy();
expect(df.has(99, "numbers")).toBeFalsy();
expect(df.has(99, "foo")).toBeFalsy();
@@ -141,12 +149,10 @@ describe("dataframe subsetting", () => {
["red", "green", "blue"],
],
null, // identity index
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
test("all rows, one column", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfA = sourceDf.subset(null, ["colors"]);
expect(dfA).toBeDefined();
expect(dfA.dims).toEqual([3, 1]);
@@ -162,7 +168,6 @@ describe("dataframe subsetting", () => {
});
test("all rows, two columns", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfB = sourceDf.subset(null, ["float32", "colors"]);
expect(dfB).toBeDefined();
expect(dfB.dims).toEqual([3, 2]);
@@ -232,7 +237,6 @@ describe("dataframe subsetting", () => {
});
test("two rows, two colums", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfF = sourceDf.subset([0, 2], ["int32", "float32"]);
expect(dfF).toBeDefined();
expect(dfF.dims).toEqual([2, 2]);
@@ -242,7 +246,6 @@ describe("dataframe subsetting", () => {
expect(dfF.colIndex.labels()).toEqual(["int32", "float32"]);
// reverse the row and column order
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
const dfFr = sourceDf.subset([2, 0], ["float32", "int32"]);
expect(dfFr).toBeDefined();
expect(dfFr.dims).toEqual([2, 2]);
@@ -255,26 +258,23 @@ describe("dataframe subsetting", () => {
test("withRowIndex", () => {
const df = sourceDf.subset(
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
["int32", "float32"],
new Dataframe.DenseInt32Index([3, 2, 1])
new Dataframe.DenseInt32Index([2, 1])
);
expect(df.dims).toEqual([2, 2]);
expect(df.colIndex).toBeInstanceOf(Dataframe.KeyIndex);
expect(df.rowIndex).toBeInstanceOf(Dataframe.DenseInt32Index);
expect(df.at(3, "int32")).toEqual(df.iat(0, 0));
expect(df.at(2, "int32")).toEqual(df.iat(0, 0));
});
test("withRowIndex error checks", () => {
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.IdentityInt32Index(1))
).toThrow(RangeError);
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.DenseInt32Index([0, 1]))
).toThrow(RangeError);
expect(() =>
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'string[]' is not assignable to p... Remove this comment to see the full error message
sourceDf.subset(null, ["red"], new Dataframe.KeyIndex([0, 1, 2, 3]))
).toThrow(RangeError);
});
@@ -289,14 +289,12 @@ describe("dataframe subsetting", () => {
new Float32Array([4.4, 5.5, 6.6]),
["red", "green", "blue"],
],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 4, 6]),
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
const dfA = sourceDf.isubsetMask(
new Uint8Array([0, 1, 1]),
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Uint8Array' is not assignable to... Remove this comment to see the full error message
new Uint8Array([1, 0, 0, 1])
);
expect(dfA.dims).toEqual([2, 2]);
@@ -316,7 +314,6 @@ describe("dataframe subsetting", () => {
["red", "green", "blue"],
],
null, // identity index
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["int32", "string", "float32", "colors"])
);
@@ -330,7 +327,6 @@ describe("dataframe subsetting", () => {
});
test("all rows, two cols", () => {
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message
const dfA = sourceDf.isubset(null, [1, 2]);
expect(dfA.dims).toEqual([3, 2]);
expect(dfA.icol(0).asArray()).toEqual(["A", "B", "C"]);
@@ -376,7 +372,6 @@ describe("dataframe factories", () => {
const dfA = new Dataframe.Dataframe(
[3, 2],
[new Int32Array([0, 1, 2]), new Int32Array([3, 4, 5])],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([2, 1, 0]),
new Dataframe.KeyIndex(["A", "B"])
);
@@ -401,7 +396,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools"])
);
const dfA = df.withCol("numbers", [1, 0]);
@@ -425,7 +419,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([74, 75])
);
const dfA = df.withCol(72, [1, 0]);
@@ -451,7 +444,6 @@ describe("dataframe factories", () => {
[true, false],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([74, 75])
);
const dfA = df.withCol(999, [1, 0]);
@@ -560,7 +552,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -569,14 +560,11 @@ describe("dataframe factories", () => {
[3, 1],
[["red", "blue", "green"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colorsA"])
);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
expect(() => dfA.withColsFrom(dfB)).toThrow(RangeError);
/* duplicate labels should throw an error */
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
expect(() => dfA.withColsFrom(dfA)).toThrow(Error);
});
@@ -587,18 +575,15 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
[2, 1],
[[true, false]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["bools"])
);
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfLikeA = dfEmpty.withColsFrom(dfA);
expect(dfLikeA).toBeDefined();
expect(dfLikeA.dims).toEqual(dfA.dims);
@@ -607,7 +592,6 @@ describe("dataframe factories", () => {
expect(dfLikeA.rowIndex.labels()).toEqual(dfA.rowIndex.labels());
expect(dfLikeA.icol(0).asArray()).toEqual(dfA.icol(0).asArray());
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfAlsoLikeA = dfA.withColsFrom(dfEmpty);
expect(dfAlsoLikeA).toBeDefined();
expect(dfAlsoLikeA.dims).toEqual(dfA.dims);
@@ -616,7 +600,6 @@ describe("dataframe factories", () => {
expect(dfAlsoLikeA.rowIndex.labels()).toEqual(dfA.rowIndex.labels());
expect(dfAlsoLikeA.icol(0).asArray()).toEqual(dfA.icol(0).asArray());
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
const dfC = dfA.withColsFrom(dfB);
expect(dfC).toBeDefined();
expect(dfC.dims).toEqual([2, 2]);
@@ -633,7 +616,6 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
@@ -644,7 +626,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -677,7 +658,6 @@ describe("dataframe factories", () => {
[2, 1],
[["red", "blue"]],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors"])
);
const dfB = new Dataframe.Dataframe(
@@ -688,7 +668,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
@@ -712,7 +691,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["colors", "bools", "numbers"])
);
const dfA = df.dropCol("colors");
@@ -784,7 +762,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'DenseInt32Index' is not assignab... Remove this comment to see the full error message
new Dataframe.DenseInt32Index([102, 101, 100])
);
const dfA = df.dropCol(101);
@@ -811,8 +788,7 @@ describe("dataframe factories", () => {
new Float64Array(3).fill(1.1),
]
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const dfB = dfA.mapColumns((col: any, idx: any) => {
const dfB = dfA.mapColumns((col, idx) => {
expect(dfA.icol(idx).asArray()).toBe(col);
return col;
});
@@ -855,7 +831,6 @@ describe("dataframe factories", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
const dfB = dfA.renameCol("B", "C");
@@ -868,8 +843,7 @@ describe("dataframe factories", () => {
});
describe("dataframe col", () => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let df: any = null;
let df: Dataframe.Dataframe;
beforeEach(() => {
df = new Dataframe.Dataframe(
[2, 2],
@@ -878,7 +852,6 @@ describe("dataframe col", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
});
@@ -887,8 +860,8 @@ describe("dataframe col", () => {
expect(df).toBeDefined();
expect(df.col("A")).toBe(df.icol(0));
expect(df.col("B")).toBe(df.icol(1));
expect(df.col("undefined")).toBeUndefined();
expect(df.icol("undefined")).toBeUndefined();
expect(() => df.col("undefined")).toThrow(RangeError);
expect(() => df.icol("undefined" as unknown as number)).toThrow(RangeError);
const colA = df.col("A");
expect(colA).toBeInstanceOf(Function);
@@ -942,13 +915,13 @@ describe("dataframe col", () => {
expect(df.col("A").indexOf(true)).toEqual(0);
expect(df.col("A").indexOf(false)).toEqual(1);
expect(df.col("A").indexOf(99)).toBeUndefined();
expect(df.col("A").indexOf(undefined)).toBeUndefined();
expect(df.col("A").indexOf(undefined as unknown as number)).toBeUndefined();
expect(df.col("A").indexOf(1)).toBeUndefined();
expect(df.col("B").indexOf(1)).toEqual(0);
expect(df.col("B").indexOf(0)).toEqual(1);
expect(df.col("B").indexOf(99)).toBeUndefined();
expect(df.col("B").indexOf(undefined)).toBeUndefined();
expect(df.col("B").indexOf(undefined as unknown as number)).toBeUndefined();
expect(df.col("B").indexOf(true)).toBeUndefined();
});
});
@@ -991,7 +964,7 @@ describe("label indexing", () => {
test("offsets", () => {
expect(idx.getOffset(1)).toEqual(1);
expect(idx.getOffsets([1, 3])).toEqual([1, 3]);
expect(idx.getOffsets([1, 3])).toEqual(new Int32Array([1, 3]));
});
test("subset", () => {
@@ -1069,7 +1042,7 @@ describe("label indexing", () => {
false,
])
.labels()
).toEqual(new Int32Array([]));
).toEqual([]);
expect(
idx
.isubsetMask([
@@ -1163,16 +1136,14 @@ describe("label indexing", () => {
expect(idx.labels()).toEqual(new Int32Array([99, 1002, 48, 0, 22]));
expect(idx.size()).toEqual(5);
expect(idx.getLabel(0)).toEqual(99);
expect(idx.getLabels(new Int32Array([2, 4]))).toEqual(
new Int32Array([48, 22])
);
expect(idx.getLabels(new Int32Array([2, 4]))).toEqual([48, 22]);
expect(idx.getLabels([2, 4])).toEqual([48, 22]);
});
test("offsets", () => {
expect(idx.getOffset(1002)).toEqual(1);
expect(idx.getOffset(0)).toEqual(3);
expect(idx.getOffsets([0, 48])).toEqual([3, 2]);
expect(idx.getOffsets([0, 48])).toEqual(new Int32Array([3, 2]));
});
test("subset", () => {
@@ -1199,7 +1170,7 @@ describe("label indexing", () => {
);
expect(
idx.isubsetMask([false, false, false, false, false]).labels()
).toEqual(new Int32Array([]));
).toEqual([]);
expect(idx.isubsetMask([true, true, false, true, true]).labels()).toEqual(
new Int32Array([99, 1002, 0, 22])
);
@@ -1301,59 +1272,67 @@ describe("corner cases", () => {
const idx = new Dataframe.IdentityInt32Index(10);
expect(idx.getOffset(0)).toBe(0);
expect(idx.getOffset(9)).toBe(9);
expect(idx.getOffset(10)).toBeUndefined();
expect(idx.getOffset(-1)).toBeUndefined();
expect(idx.getOffset("sort")).toBeUndefined();
expect(idx.getOffset("length")).toBeUndefined();
expect(idx.getOffset(true)).toBeUndefined();
expect(idx.getOffset(0.001)).toBeUndefined();
expect(idx.getOffset({})).toBeUndefined();
expect(idx.getOffset([])).toBeUndefined();
expect(idx.getOffset(new Float32Array())).toBeUndefined();
expect(idx.getOffset("__proto__")).toBeUndefined();
expect(idx.getOffset(10)).toBe(-1);
expect(idx.getOffset(-1)).toBe(-1);
expect(idx.getOffset("sort")).toBe(-1);
expect(idx.getOffset("length")).toBe(-1);
expect(idx.getOffset(true as unknown as string)).toBe(-1);
expect(idx.getOffset(0.001)).toBe(-1);
expect(idx.getOffset({} as unknown as string)).toBe(-1);
expect(idx.getOffset([] as unknown as string)).toBe(-1);
expect(idx.getOffset(new Float32Array() as unknown as string)).toBe(-1);
expect(idx.getOffset("__proto__")).toBe(-1);
expect(idx.getLabel(0)).toBe(0);
expect(idx.getLabel(9)).toBe(9);
expect(idx.getLabel(10)).toBeUndefined();
expect(idx.getLabel(-1)).toBeUndefined();
expect(idx.getLabel("sort")).toBeUndefined();
expect(idx.getLabel("length")).toBeUndefined();
expect(idx.getLabel(true)).toBeUndefined();
expect(idx.getLabel("sort" as unknown as number)).toBeUndefined();
expect(idx.getLabel("length" as unknown as number)).toBeUndefined();
expect(idx.getLabel(true as unknown as number)).toBeUndefined();
expect(idx.getLabel(0.001)).toBeUndefined();
expect(idx.getLabel({})).toBeUndefined();
expect(idx.getLabel([])).toBeUndefined();
expect(idx.getLabel(new Float32Array())).toBeUndefined();
expect(idx.getLabel("__proto__")).toBeUndefined();
expect(idx.getLabel({} as unknown as number)).toBeUndefined();
expect(idx.getLabel([] as unknown as number)).toBeUndefined();
expect(
idx.getLabel(new Float32Array() as unknown as number)
).toBeUndefined();
expect(idx.getLabel("__proto__" as unknown as number)).toBeUndefined();
});
test("dense integer index rejects non-integer labels", () => {
const idx = new Dataframe.DenseInt32Index([-10, 0, 3, 9, 10]);
expect(idx.getOffset(-10)).toBe(0);
expect(idx.getOffset(0)).toBe(1);
expect(idx.getOffset(3)).toBe(2);
expect(idx.getOffset(9)).toBe(3);
expect(idx.getOffset(1)).toBeUndefined();
expect(idx.getOffset(11)).toBeUndefined();
expect(idx.getOffset(-1)).toBeUndefined();
expect(idx.getOffset("sort")).toBeUndefined();
expect(idx.getOffset("length")).toBeUndefined();
expect(idx.getOffset(true)).toBeUndefined();
expect(idx.getOffset(0.001)).toBeUndefined();
expect(idx.getOffset({})).toBeUndefined();
expect(idx.getOffset([])).toBeUndefined();
expect(idx.getOffset(new Float32Array())).toBeUndefined();
expect(idx.getOffset("__proto__")).toBeUndefined();
expect(idx.getOffset(10)).toBe(4);
expect(idx.getOffset(1)).toBe(-1);
expect(idx.getOffset(11)).toBe(-1);
expect(idx.getOffset(-1)).toBe(-1);
expect(idx.getOffset("sort")).toBe(-1);
expect(idx.getOffset("length")).toBe(-1);
expect(idx.getOffset(true as unknown as string)).toBe(-1);
expect(idx.getOffset(0.001)).toBe(-1);
expect(idx.getOffset({} as unknown as string)).toBe(-1);
expect(idx.getOffset([] as unknown as string)).toBe(-1);
expect(idx.getOffset(new Float32Array() as unknown as string)).toBe(-1);
expect(idx.getOffset("__proto__")).toBe(-1);
expect(idx.getLabel(0)).toBe(-10);
expect(idx.getLabel(4)).toBe(10);
expect(idx.getLabel(10)).toBeUndefined();
expect(idx.getLabel(-1)).toBeUndefined();
expect(idx.getLabel("sort")).toBeUndefined();
expect(idx.getLabel("length")).toBeUndefined();
expect(idx.getLabel(true)).toBeUndefined();
expect(idx.getLabel("sort" as unknown as number)).toBeUndefined();
expect(idx.getLabel("length" as unknown as number)).toBeUndefined();
expect(idx.getLabel(true as unknown as number)).toBeUndefined();
expect(idx.getLabel(0.001)).toBeUndefined();
expect(idx.getLabel({})).toBeUndefined();
expect(idx.getLabel([])).toBeUndefined();
expect(idx.getLabel(new Float32Array())).toBeUndefined();
expect(idx.getLabel("__proto__")).toBeUndefined();
expect(idx.getLabel({} as unknown as number)).toBeUndefined();
expect(idx.getLabel([] as unknown as number)).toBeUndefined();
expect(
idx.getLabel(new Float32Array() as unknown as number)
).toBeUndefined();
expect(idx.getLabel("__proto__" as unknown as number)).toBeUndefined();
});
test("Empty dataframe rejects bogus labels", () => {
@@ -1361,39 +1340,55 @@ describe("corner cases", () => {
expect(df.hasCol("sort")).toBeFalsy();
expect(df.hasCol(0)).toBeFalsy();
expect(df.hasCol(true)).toBeFalsy();
expect(df.hasCol(false)).toBeFalsy();
expect(df.hasCol([])).toBeFalsy();
expect(df.hasCol({})).toBeFalsy();
expect(df.hasCol(null)).toBeFalsy();
expect(df.hasCol(undefined)).toBeFalsy();
expect(df.hasCol(true as unknown as number)).toBeFalsy();
expect(df.hasCol(false as unknown as number)).toBeFalsy();
expect(df.hasCol([] as unknown as number)).toBeFalsy();
expect(df.hasCol({} as unknown as number)).toBeFalsy();
expect(df.hasCol(null as unknown as number)).toBeFalsy();
expect(df.hasCol(undefined as unknown as number)).toBeFalsy();
expect(df.col("sort")).toBeUndefined();
expect(df.col(0)).toBeUndefined();
expect(df.col(true)).toBeUndefined();
expect(df.col(false)).toBeUndefined();
expect(df.col([])).toBeUndefined();
expect(df.col({})).toBeUndefined();
expect(df.col(null)).toBeUndefined();
expect(df.col(undefined)).toBeUndefined();
expect(() => df.col("sort")).toThrow(RangeError);
expect(() => df.col(0)).toThrow(RangeError);
expect(() => df.col(true as unknown as string)).toThrow(RangeError);
expect(() => df.col(false as unknown as string)).toThrow(RangeError);
expect(() => df.col([] as unknown as string)).toThrow(RangeError);
expect(() => df.col({} as unknown as string)).toThrow(RangeError);
expect(() => df.col(null as unknown as string)).toThrow(RangeError);
expect(() => df.col(undefined as unknown as string)).toThrow(RangeError);
expect(df.icol("sort")).toBeUndefined();
expect(df.icol(0)).toBeUndefined();
expect(df.icol(true)).toBeUndefined();
expect(df.icol(false)).toBeUndefined();
expect(df.icol([])).toBeUndefined();
expect(df.icol({})).toBeUndefined();
expect(df.icol(null)).toBeUndefined();
expect(df.icol(undefined)).toBeUndefined();
expect(() => df.icol("sort" as unknown as number)).toThrow(RangeError);
expect(() => df.icol(0)).toThrow(RangeError);
expect(() => df.icol(true as unknown as number)).toThrow(RangeError);
expect(() => df.icol(false as unknown as number)).toThrow(RangeError);
expect(() => df.icol([] as unknown as number)).toThrow(RangeError);
expect(() => df.icol({} as unknown as number)).toThrow(RangeError);
expect(() => df.icol(null as unknown as number)).toThrow(RangeError);
expect(() => df.icol(undefined as unknown as number)).toThrow(RangeError);
expect(df.ihas("sort", "length")).toBeFalsy();
expect(df.ihas("0", "0")).toBeFalsy();
expect(df.ihas("", "")).toBeFalsy();
expect(df.ihas(null, null)).toBeFalsy();
expect(df.ihas(undefined, undefined)).toBeFalsy();
expect(df.ihas(true, true)).toBeFalsy();
expect(df.ihas([], [])).toBeFalsy();
expect(df.ihas({}, {})).toBeFalsy();
expect(
df.ihas("sort" as unknown as number, "length" as unknown as number)
).toBeFalsy();
expect(
df.ihas("0" as unknown as number, "0" as unknown as number)
).toBeFalsy();
expect(
df.ihas("" as unknown as number, "" as unknown as number)
).toBeFalsy();
expect(
df.ihas(null as unknown as number, null as unknown as number)
).toBeFalsy();
expect(
df.ihas(undefined as unknown as number, undefined as unknown as number)
).toBeFalsy();
expect(
df.ihas(true as unknown as number, true as unknown as number)
).toBeFalsy();
expect(
df.ihas([] as unknown as number, [] as unknown as number)
).toBeFalsy();
expect(
df.ihas({} as unknown as number, {} as unknown as number)
).toBeFalsy();
});
test("Dataframe rejects bogus labels", () => {
@@ -1404,58 +1399,64 @@ describe("corner cases", () => {
[1, 0],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["A", "B"])
);
expect(df.hasCol("sort")).toBeFalsy();
expect(df.hasCol("__proto__")).toBeFalsy();
expect(df.hasCol(0)).toBeFalsy();
expect(df.hasCol(true)).toBeFalsy();
expect(df.hasCol(false)).toBeFalsy();
expect(df.hasCol([])).toBeFalsy();
expect(df.hasCol({})).toBeFalsy();
expect(df.hasCol(null)).toBeFalsy();
expect(df.hasCol(undefined)).toBeFalsy();
expect(df.hasCol(true as unknown as string)).toBeFalsy();
expect(df.hasCol(false as unknown as string)).toBeFalsy();
expect(df.hasCol([] as unknown as string)).toBeFalsy();
expect(df.hasCol({} as unknown as string)).toBeFalsy();
expect(df.hasCol(null as unknown as string)).toBeFalsy();
expect(df.hasCol(undefined as unknown as string)).toBeFalsy();
expect(df.col("sort")).toBeUndefined();
expect(df.col("__proto__")).toBeUndefined();
expect(df.col(0)).toBeUndefined();
expect(df.col(true)).toBeUndefined();
expect(df.col(false)).toBeUndefined();
expect(df.col([])).toBeUndefined();
expect(df.col({})).toBeUndefined();
expect(df.col(null)).toBeUndefined();
expect(df.col(undefined)).toBeUndefined();
expect(() => df.col("sort")).toThrow(RangeError);
expect(() => df.col("__proto__")).toThrow(RangeError);
expect(() => df.col(0)).toThrow(RangeError);
expect(() => df.col(true as unknown as string)).toThrow(RangeError);
expect(() => df.col(false as unknown as string)).toThrow(RangeError);
expect(() => df.col([] as unknown as string)).toThrow(RangeError);
expect(() => df.col({} as unknown as string)).toThrow(RangeError);
expect(() => df.col(null as unknown as string)).toThrow(RangeError);
expect(() => df.col(undefined as unknown as string)).toThrow(RangeError);
expect(df.icol("sort")).toBeUndefined();
expect(df.icol("__proto__")).toBeUndefined();
expect(df.icol(-1)).toBeUndefined();
expect(df.icol(true)).toBeUndefined();
expect(df.icol(false)).toBeUndefined();
expect(df.icol([])).toBeUndefined();
expect(df.icol({})).toBeUndefined();
expect(df.icol(null)).toBeUndefined();
expect(df.icol(undefined)).toBeUndefined();
expect(() => df.icol("sort" as unknown as number)).toThrow(RangeError);
expect(() => df.icol("__proto__" as unknown as number)).toThrow(RangeError);
expect(() => df.icol(-1)).toThrow(RangeError);
expect(() => df.icol(true as unknown as number)).toThrow(RangeError);
expect(() => df.icol(false as unknown as number)).toThrow(RangeError);
expect(() => df.icol([] as unknown as number)).toThrow(RangeError);
expect(() => df.icol({} as unknown as number)).toThrow(RangeError);
expect(() => df.icol(null as unknown as number)).toThrow(RangeError);
expect(() => df.icol(undefined as unknown as number)).toThrow(RangeError);
expect(df.ihas("sort", "length")).toBeFalsy();
expect(df.ihas("__proto__", "__proto__")).toBeFalsy();
expect(
df.ihas("sort" as unknown as number, "length" as unknown as number)
).toBeFalsy();
expect(
df.ihas(
"__proto__" as unknown as number,
"__proto__" as unknown as number
)
).toBeFalsy();
expect(df.ihas(-1, 0)).toBeFalsy();
expect(df.ihas("0", 0)).toBeFalsy();
expect(df.ihas("", 0)).toBeFalsy();
expect(df.ihas(null, 0)).toBeFalsy();
expect(df.ihas(undefined, 0)).toBeFalsy();
expect(df.ihas([], 0)).toBeFalsy();
expect(df.ihas({}, 0)).toBeFalsy();
expect(df.ihas("0" as unknown as number, 0)).toBeFalsy();
expect(df.ihas("" as unknown as number, 0)).toBeFalsy();
expect(df.ihas(null as unknown as number, 0)).toBeFalsy();
expect(df.ihas(undefined as unknown as number, 0)).toBeFalsy();
expect(df.ihas([] as unknown as number, 0)).toBeFalsy();
expect(df.ihas({} as unknown as number, 0)).toBeFalsy();
expect(df.ihas(0, -1)).toBeFalsy();
expect(df.ihas(0, "0")).toBeFalsy();
expect(df.ihas(0, "")).toBeFalsy();
expect(df.ihas(0, null)).toBeFalsy();
expect(df.ihas(0, undefined)).toBeFalsy();
expect(df.ihas(0, [])).toBeFalsy();
expect(df.ihas(0, {})).toBeFalsy();
expect(df.ihas(0, "0" as unknown as number)).toBeFalsy();
expect(df.ihas(0, "" as unknown as number)).toBeFalsy();
expect(df.ihas(0, null as unknown as number)).toBeFalsy();
expect(df.ihas(0, undefined as unknown as number)).toBeFalsy();
expect(df.ihas(0, [] as unknown as number)).toBeFalsy();
expect(df.ihas(0, {} as unknown as number)).toBeFalsy();
expect(df.has("sort", "length")).toBeFalsy();
expect(df.has("length", "sort")).toBeFalsy();
@@ -1464,17 +1465,17 @@ describe("corner cases", () => {
expect(df.has(-1, "A")).toBeFalsy();
expect(df.has("0", "A")).toBeFalsy();
expect(df.has("", "A")).toBeFalsy();
expect(df.has(null, "A")).toBeFalsy();
expect(df.has(undefined, "A")).toBeFalsy();
expect(df.has([], "A")).toBeFalsy();
expect(df.has({}, "A")).toBeFalsy();
expect(df.has(null as unknown as string, "A")).toBeFalsy();
expect(df.has(undefined as unknown as string, "A")).toBeFalsy();
expect(df.has([] as unknown as string, "A")).toBeFalsy();
expect(df.has({} as unknown as string, "A")).toBeFalsy();
expect(df.has(0, -1)).toBeFalsy();
expect(df.has(0, "0")).toBeFalsy();
expect(df.has(0, "")).toBeFalsy();
expect(df.has(0, null)).toBeFalsy();
expect(df.has(0, undefined)).toBeFalsy();
expect(df.has(0, [])).toBeFalsy();
expect(df.has(0, {})).toBeFalsy();
expect(df.has(0, null as unknown as string)).toBeFalsy();
expect(df.has(0, undefined as unknown as string)).toBeFalsy();
expect(df.has(0, [] as unknown as string)).toBeFalsy();
expect(df.has(0, {} as unknown as string)).toBeFalsy();
});
});
@@ -6,11 +6,10 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
const h1 = df.col("cat").histogram(df.col("name"));
const h1 = df.col("cat").histogramCategoricalBy(df.col("name"));
expect(h1).toMatchObject(
new Map([
["n1", new Map([["c1", 1]])],
@@ -19,7 +18,9 @@ describe("Dataframe column histogram", () => {
])
);
// memoized?
expect(df.col("cat").histogram(df.col("name"))).toMatchObject(h1);
expect(df.col("cat").histogramCategoricalBy(df.col("name"))).toMatchObject(
h1
);
});
test("continuous by categorical", () => {
@@ -27,11 +28,10 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
const h1 = df.col("value").histogram(3, [0, 2], df.col("name"));
const h1 = df.col("value").histogramContinuousBy(3, [0, 2], df.col("name"));
expect(h1).toMatchObject(
new Map([
["n1", [1, 0, 0]],
@@ -40,9 +40,9 @@ describe("Dataframe column histogram", () => {
])
);
// memoized?
expect(df.col("value").histogram(3, [0, 2], df.col("name"))).toMatchObject(
h1
);
expect(
df.col("value").histogramContinuousBy(3, [0, 2], df.col("name"))
).toMatchObject(h1);
});
test("categorical", () => {
@@ -50,11 +50,10 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
const h1 = df.col("cat").histogram();
const h1 = df.col("cat").histogramCategorical();
expect(h1).toMatchObject(
new Map([
["c1", 1],
@@ -63,7 +62,7 @@ describe("Dataframe column histogram", () => {
])
);
// memoized?
expect(df.col("value").histogram(3, [0, 2])).toMatchObject(h1);
expect(df.col("cat").histogramCategorical()).toMatchObject(h1);
});
test("continuous", () => {
@@ -71,14 +70,13 @@ describe("Dataframe column histogram", () => {
[3, 3],
[["n1", "n2", "n3"], ["c1", "c2", "c3"], new Int32Array([0, 1, 2])],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["name", "cat", "value"])
);
const h1 = df.col("value").histogram(3, [0, 2]);
const h1 = df.col("value").histogramContinuous(3, [0, 2]);
expect(h1).toMatchObject([1, 1, 1]);
// memoized?
expect(df.col("value").histogram(3, [0, 2])).toMatchObject(h1);
expect(df.col("value").histogramContinuous(3, [0, 2])).toMatchObject(h1);
});
test("continuous thesholds correct", () => {
@@ -88,20 +86,11 @@ describe("Dataframe column histogram", () => {
[new Int32Array(vals), new Float32Array(vals)]
);
expect(df.col(0).histogram(5, [0, 100])).toEqual([5, 1, 0, 0, 2]);
expect(df.col(1).histogram(5, [0, 100])).toEqual([5, 1, 0, 0, 2]);
expect(df.col(0).histogram(2, [0, 10])).toEqual([2, 2]);
expect(df.col(0).histogram(10, [0, 100])).toEqual([
3,
2,
1,
0,
0,
0,
0,
0,
0,
2,
expect(df.col(0).histogramContinuous(5, [0, 100])).toEqual([5, 1, 0, 0, 2]);
expect(df.col(1).histogramContinuous(5, [0, 100])).toEqual([5, 1, 0, 0, 2]);
expect(df.col(0).histogramContinuous(2, [0, 10])).toEqual([2, 2]);
expect(df.col(0).histogramContinuous(10, [0, 100])).toEqual([
3, 2, 1, 0, 0, 0, 0, 0, 0, 2,
]);
});
});
@@ -8,7 +8,7 @@ function float32Conversion(f: any) {
describe("Dataframe column summary", () => {
test("empty column test", () => {
const df = Dataframe.Dataframe.create([0, 1], [[]]);
const summary = df.icol(0).summarize();
const summary = df.icol(0).summarizeCategorical();
expect(summary).toEqual(
expect.objectContaining({
categorical: true,
@@ -31,7 +31,6 @@ describe("Dataframe column summary", () => {
[1],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -42,7 +41,7 @@ describe("Dataframe column summary", () => {
])
);
expect(df.icol(0).summarize()).toEqual(
expect(df.icol(0).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: ["n1"],
@@ -50,7 +49,7 @@ describe("Dataframe column summary", () => {
numCategories: 1,
})
);
expect(df.icol(1).summarize()).toEqual(
expect(df.icol(1).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: ["hi"],
@@ -58,7 +57,7 @@ describe("Dataframe column summary", () => {
numCategories: 1,
})
);
expect(df.icol(2).summarize()).toEqual(
expect(df.icol(2).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: [true],
@@ -66,7 +65,7 @@ describe("Dataframe column summary", () => {
numCategories: 1,
})
);
expect(df.icol(3).summarize()).toEqual(
expect(df.icol(3).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: float32Conversion(39.3),
@@ -76,7 +75,7 @@ describe("Dataframe column summary", () => {
pinf: 0,
})
);
expect(df.icol(4).summarize()).toEqual(
expect(df.icol(4).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: 99,
@@ -86,7 +85,7 @@ describe("Dataframe column summary", () => {
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
expect(df.icol(5).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: [1],
@@ -108,7 +107,6 @@ describe("Dataframe column summary", () => {
[1, false, "0"],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -119,7 +117,7 @@ describe("Dataframe column summary", () => {
])
);
expect(df.icol(0).summarize()).toEqual(
expect(df.icol(0).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["n0", "n1", "n2"]),
@@ -131,7 +129,7 @@ describe("Dataframe column summary", () => {
numCategories: 3,
})
);
expect(df.icol(1).summarize()).toEqual(
expect(df.icol(1).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["hi", "bye"]),
@@ -142,7 +140,7 @@ describe("Dataframe column summary", () => {
numCategories: 2,
})
);
expect(df.icol(2).summarize()).toEqual(
expect(df.icol(2).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([true, false]),
@@ -153,7 +151,7 @@ describe("Dataframe column summary", () => {
numCategories: 2,
})
);
expect(df.icol(3).summarize()).toEqual(
expect(df.icol(3).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: 0,
@@ -163,7 +161,7 @@ describe("Dataframe column summary", () => {
pinf: 0,
})
);
expect(df.icol(4).summarize()).toEqual(
expect(df.icol(4).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: 99,
@@ -173,7 +171,7 @@ describe("Dataframe column summary", () => {
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
expect(df.icol(5).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
@@ -205,7 +203,6 @@ describe("Dataframe column summary", () => {
[1, false, "0", "0"],
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex([
"name",
"nameString",
@@ -216,7 +213,7 @@ describe("Dataframe column summary", () => {
])
);
expect(df.icol(0).summarize()).toEqual(
expect(df.icol(0).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["n0", "n1", "n2"]),
@@ -228,7 +225,7 @@ describe("Dataframe column summary", () => {
numCategories: 3,
})
);
expect(df.icol(1).summarize()).toEqual(
expect(df.icol(1).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining(["hi", "bye"]),
@@ -239,7 +236,7 @@ describe("Dataframe column summary", () => {
numCategories: 2,
})
);
expect(df.icol(2).summarize()).toEqual(
expect(df.icol(2).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([true, false]),
@@ -250,7 +247,7 @@ describe("Dataframe column summary", () => {
numCategories: 2,
})
);
expect(df.icol(3).summarize()).toEqual(
expect(df.icol(3).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: float32Conversion(39.3),
@@ -260,7 +257,7 @@ describe("Dataframe column summary", () => {
pinf: 1,
})
);
expect(df.icol(4).summarize()).toEqual(
expect(df.icol(4).summarizeContinuous()).toEqual(
expect.objectContaining({
categorical: false,
min: 99,
@@ -270,7 +267,7 @@ describe("Dataframe column summary", () => {
pinf: 0,
})
);
expect(df.icol(5).summarize()).toEqual(
expect(df.icol(5).summarizeCategorical()).toEqual(
expect.objectContaining({
categorical: true,
categories: expect.arrayContaining([1, false, "0"]),
@@ -81,7 +81,6 @@ describe("categorical color helpers", () => {
),
],
null,
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
new Dataframe.KeyIndex(["continuousColumn", "categoricalColumn"])
);
@@ -24,7 +24,6 @@ describe("encode/decode", () => {
expect(dfA.columns).toEqual(columns);
const colIndex = new KeyIndex(["a", "b", "c", "d"]);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
const dfWithColIdx = new Dataframe([3, 4], columns, null, colIndex);
const dfB = decodeMatrixFBS(encodeMatrixFBS(dfWithColIdx));
expect([dfB.nRows, dfB.nCols]).toEqual(dfWithColIdx.dims);
+76 -76
View File
@@ -21,90 +21,90 @@ The behavior manifest in these action creators:
Note that crossfilter indices are lazy created, as needed.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetDelete = (genesetName: any) => (
import { Dataframe } from "../util/dataframe";
export const genesetDelete =
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const { genesets } = state;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const geneSymbols = Array.from(gs.genes.keys());
const obsCrossfilter = dropGeneset(dispatch, state, genesetName, geneSymbols);
if (genesetName === state.colors.colorAccessor) {
(genesetName: any) => (dispatch: any, getState: any) => {
const state = getState();
const { genesets } = state;
const gs = genesets?.genesets?.get(genesetName) ?? {};
const geneSymbols = Array.from(gs.genes.keys());
const obsCrossfilter = dropGeneset(
dispatch,
state,
genesetName,
geneSymbols
);
if (genesetName === state.colors.colorAccessor) {
dispatch({
type: "reset colorscale",
});
}
dispatch({
type: "reset colorscale",
type: "geneset: delete",
genesetName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
}
dispatch({
type: "geneset: delete",
genesetName,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetAddGenes = (genesetName: any, genes: any) => async (
export const genesetAddGenes =
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const { obsCrossfilter: prevObsCrossfilter, annoMatrix } = state;
const { schema } = annoMatrix;
const varIndex = schema.annotations.var.index;
const df = await annoMatrix.fetch("var", varIndex);
const geneNames = df.col(varIndex).asArray();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
genes = genes.reduce((acc: any, gene: any) => {
if (geneNames.indexOf(gene.geneSymbol) === -1) {
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
return acc;
}, []);
(genesetName: any, genes: any) => async (dispatch: any, getState: any) => {
const state = getState();
const { obsCrossfilter: prevObsCrossfilter, annoMatrix } = state;
const { schema } = annoMatrix;
const varIndex = schema.annotations.var.index;
const df: Dataframe = await annoMatrix.fetch("var", varIndex);
const geneNames = df.col(varIndex).asArray();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
genes = genes.reduce((acc: any, gene: any) => {
if (geneNames.indexOf(gene.geneSymbol) === -1) {
postUserErrorToast(
`${gene.geneSymbol} doesn't appear to be a valid gene name.`
);
} else acc.push(gene);
return acc;
}, []);
const obsCrossfilter = dropGenesetSummaryDimension(
prevObsCrossfilter,
state,
genesetName
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
return dispatch({
type: "geneset: add genes",
genesetName,
genes,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
const obsCrossfilter = dropGenesetSummaryDimension(
prevObsCrossfilter,
state,
genesetName
);
dispatch({
type: "continuous metadata histogram cancel",
continuousNamespace: { isGeneSetSummary: true },
selection: genesetName,
});
return dispatch({
type: "geneset: add genes",
genesetName,
genes,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export const genesetDeleteGenes = (genesetName: any, geneSymbols: any) => (
export const genesetDeleteGenes =
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dispatch: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getState: any
) => {
const state = getState();
const obsCrossfilter = dropGeneset(dispatch, state, genesetName, geneSymbols);
return dispatch({
type: "geneset: delete genes",
genesetName,
geneSymbols,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
(genesetName: any, geneSymbols: any) => (dispatch: any, getState: any) => {
const state = getState();
const obsCrossfilter = dropGeneset(
dispatch,
state,
genesetName,
geneSymbols
);
return dispatch({
type: "geneset: delete genes",
genesetName,
geneSymbols,
obsCrossfilter,
annoMatrix: obsCrossfilter.annoMatrix,
});
};
/*
Private
+5 -6
View File
@@ -219,7 +219,7 @@ export default class AnnoMatrix {
**/
// @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) {
fetch(field, q): Dataframe {
/*
Return the given query on a single matrix field as a single dataframe.
Currently supports ONLY full column query.
@@ -248,7 +248,7 @@ export default class AnnoMatrix {
1. Fetch the "n_genes" column the "obs":
const df = await fetch("obs", "n_genes")
console.log("Largest number of genes is: ", df.summarize().max);
console.log("Largest number of genes is: ", df.summarizeContinuous().max);
2. Fetch two separate columns from obs. Returns a single dataframe containing
the columns:
@@ -278,7 +278,7 @@ export default class AnnoMatrix {
// @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) {
prefetch(field, q): void {
/*
Start a data fetch & cache fill. Identical to fetch() except it does
not return a value.
@@ -287,7 +287,6 @@ export default class AnnoMatrix {
overall component rendering latency.
*/
this._fetch(field, q);
return undefined;
}
/**
@@ -494,8 +493,8 @@ Return cache keys for columns associated with this query. May return
// @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) {
if (!AnnoMatrix.fields().includes(field)) return undefined;
async _fetch(field, q): Dataframe {
if (!AnnoMatrix.fields().includes(field)) return Dataframe.empty();
const queries = Array.isArray(q) ? q : [q];
queries.forEach(_queryValidate);
+2 -2
View File
@@ -196,7 +196,7 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
const data = (this as any)._cache.obs.col(col).asArray().slice();
for (let i = 0, len = rowIndices.length; i < len; i += 1) {
const idx = rowIndices[i];
if (idx === undefined) throw new Error("Unknown row label");
if (idx === -1) throw new Error("Unknown row label");
data[idx] = value;
}
@@ -299,8 +299,8 @@ export default class AnnoMatrixLoader extends AnnoMatrix {
default:
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
let result = matrixFBSToDataframe(buffer);
if (!result || result.isEmpty()) throw Error("Unknown field/col");
+10 -9
View File
@@ -1,3 +1,4 @@
import { Schema } from "../common/types/schema";
import { _getColumnSchema, _isIndex } from "./schema";
import catLabelSort from "../util/catLabelSort";
import {
@@ -5,12 +6,11 @@ import {
overflowCategoryLabel,
globalConfig,
} from "../globals";
import { Dataframe } from "../util/dataframe";
import { Dataframe, LabelType, DataframeColumn } from "../util/dataframe";
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function normalizeResponse(
field: string,
schema: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
schema: Schema,
response: Dataframe
): Dataframe {
/**
@@ -65,8 +65,7 @@ export function normalizeResponse(
return response;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
function castColumnToBoolean(df: Dataframe, label: any): Dataframe {
function castColumnToBoolean(df: Dataframe, label: LabelType): Dataframe {
const colData = df.col(label).asArray();
const newColData = new Array(colData.length);
for (let i = 0; i < colData.length; i += 1) newColData[i] = !!colData[i];
@@ -75,7 +74,10 @@ function castColumnToBoolean(df: Dataframe, label: any): Dataframe {
}
// 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, col: any) {
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.
col: DataframeColumn
) {
/*
Ensure all enum writable / categorical schema have a categories array, that
the categories array contains all unique values in the data array, AND that
@@ -91,12 +93,11 @@ export function normalizeWritableCategoricalSchema(colSchema: any, col: any) {
return colSchema;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function normalizeCategorical(
df: Dataframe,
colLabel: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
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.
) {
): Dataframe {
/*
If writable, ensure schema matches data and we have an unassigned label
+1 -1
View File
@@ -197,7 +197,7 @@ function _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) {
if (qmax > 1) qmax = 1;
if (qmin === 0 && qmax === 1) return colData;
const quantiles = df.col(colLabel).summarize().percentiles;
const quantiles = df.col(colLabel).summarizeContinuous().percentiles;
const lower = quantiles[100 * qmin];
const upper = quantiles[100 * qmax];
const clippedData = clip(colData.slice(), lower, upper, Number.NaN);
+16
View File
@@ -30,6 +30,22 @@ export type TypedArrayConstructor =
export type AnyArray = Array<unknown> | TypedArray;
export interface GenericArrayConstructor<T extends AnyArray> {
new (
...args: ConstructorParameters<
typeof Int8Array &
typeof Uint8Array &
typeof Int16Array &
typeof Uint16Array &
typeof Int32Array &
typeof Uint32Array &
typeof Float32Array &
typeof Float64Array &
typeof Array
>
): T;
}
export type NumberArray = Array<number> | TypedArray;
export type Int8 = Int8Array[0];
@@ -11,6 +11,7 @@ import Histogram from "./histogram";
import HistogramFooter from "./footer";
import StillLoading from "./loading";
import ErrorLoading from "./error";
import { Dataframe } from "../../util/dataframe";
const MARGIN = {
LEFT: 10, // Space for 0 tick label on X axis
@@ -116,9 +117,12 @@ class HistogramBrush extends React.PureComponent {
};
};
// @ts-expect-error ts-migrate(6133) FIXME: 'selection' is declared but its value is never rea... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
onBrushEnd = (selection: any, x: any) =>
onBrushEnd =
(
_selection: any, // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
x: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
) =>
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
() => {
const {
@@ -257,18 +261,18 @@ class HistogramBrush extends React.PureComponent {
const query = this.createQuery();
// @ts-expect-error ts-migrate(2488) FIXME: Type 'any[] | null' must have a '[Symbol.iterator]... Remove this comment to see the full error message
const df = await annoMatrix.fetch(...query);
const df: Dataframe = await annoMatrix.fetch(...query);
const column = df.icol(0);
// if we are clipped, fetch both our value and our unclipped value,
// as we need the absolute min/max range, not just the clipped min/max.
const summary = column.summarize();
const summary = column.summarizeContinuous();
const range = [summary.min, summary.max];
let unclippedRange = [...range];
if (isClipped) {
const parent = await annoMatrix.viewOf.fetch(...query);
const { min, max } = parent.icol(0).summarize();
const parent: Dataframe = await annoMatrix.viewOf.fetch(...query);
const { min, max } = parent.icol(0).summarizeContinuous();
unclippedRange = [min, max];
}
@@ -320,7 +324,8 @@ class HistogramBrush extends React.PureComponent {
recalculate expensive stuff, notably bins, summaries, etc.
*/
const histogramCache = {}; /* maybe change this so that it computes ... */
const summary = col.summarize(); /* this is memoized, so it's free the second time you call it */
const summary =
col.summarizeContinuous(); /* this is memoized, so it's free the second time you call it */
const { min: domainMin, max: domainMax } = summary;
const numBins = 40;
const { TOP: topMargin, LEFT: leftMargin } = newMargin;
@@ -333,7 +338,7 @@ class HistogramBrush extends React.PureComponent {
.range([leftMargin, leftMargin + newWidth]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
(histogramCache as any).bins = col.histogram(numBins, [
(histogramCache as any).bins = col.histogramContinuous(numBins, [
domainMin,
domainMax,
]);
@@ -26,6 +26,7 @@ import {
createColorQuery,
} from "../../../util/stateManager/colorHelpers";
import actions from "../../../actions";
import { Dataframe } from "../../../util/dataframe";
const LABEL_WIDTH = globals.leftSidebarWidth - 100;
const ANNO_BUTTON_WIDTH = 50;
@@ -165,8 +166,20 @@ class Category extends React.PureComponent {
};
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
async fetchData(annoMatrix: any, metadataField: any, colors: any) {
async fetchData(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
annoMatrix: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
metadataField: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colors: any
): Promise<
[
Dataframe,
ReturnType<typeof createCategorySummaryFromDfCol>,
Dataframe | null
]
> {
/*
fetch our data and the color-by data if appropriate, and then build a summary
of our category and a color table for the color-by annotation.
@@ -175,7 +188,7 @@ class Category extends React.PureComponent {
const { colorAccessor, colorMode } = colors;
// @ts-expect-error ts-migrate(2339) FIXME: Property 'genesets' does not exist on type 'Readon... Remove this comment to see the full error message
const { genesets } = this.props;
let colorDataPromise = Promise.resolve(null);
let colorDataPromise: Promise<Dataframe | null> = Promise.resolve(null);
if (colorAccessor) {
const query = createColorQuery(
colorMode,
@@ -185,10 +198,10 @@ class Category extends React.PureComponent {
);
if (query) colorDataPromise = annoMatrix.fetch(...query);
}
const [categoryData, colorData] = await Promise.all([
annoMatrix.fetch("obs", metadataField),
colorDataPromise,
]);
const [categoryData, colorData] = await Promise.all<
Dataframe,
Dataframe | null
>([annoMatrix.fetch("obs", metadataField), colorDataPromise]);
// our data
const column = categoryData.icol(0);
@@ -200,8 +213,8 @@ class Category extends React.PureComponent {
return [categoryData, categorySummary, colorData];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
updateColorTable(colorData: any) {
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types -- - FIXME: disabled temporarily on migrate to TS.
updateColorTable(colorData: Dataframe|null) {
// color table, which may be null
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
const { schema, colors, metadataField } = this.props;
@@ -25,6 +25,7 @@ import actions from "../../../actions";
import MiniHistogram from "../../miniHistogram";
import MiniStackedBar from "../../miniStackedBar";
import { CategoryCrossfilterContext } from "../categoryContext";
import { Dataframe, ContinuousHistogram } from "../../../util/dataframe";
const STACKED_BAR_HEIGHT = 11;
const STACKED_BAR_WIDTH = 100;
@@ -327,13 +328,11 @@ class CategoryValue extends React.Component<{}, State> {
createHistogramBins = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
metadataField: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryData: any,
categoryData: Dataframe,
// @ts-expect-error ts-migrate(6133) FIXME: 'colorAccessor' is declared but its value is never... Remove this comment to see the full error message
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colorAccessor: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colorData: any,
colorData: Dataframe,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryValue: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
@@ -348,17 +347,17 @@ class CategoryValue extends React.Component<{}, State> {
*/
const groupBy = categoryData.col(metadataField);
const col = colorData.icol(0);
const range = col.summarize();
const range = col.summarizeContinuous();
const histogramMap = col.histogram(
const histogramMap = col.histogramContinuousBy(
50,
[range.min, range.max],
groupBy
); /* Because the signature changes we really need different names for histogram to differentiate signatures */
);
const bins = histogramMap.has(categoryValue)
? histogramMap.get(categoryValue)
: new Array(50).fill(0);
? histogramMap.get(categoryValue) as ContinuousHistogram
: new Array<number>(50).fill(0);
const xScale = d3.scaleLinear().domain([0, bins.length]).range([0, width]);
@@ -377,12 +376,10 @@ class CategoryValue extends React.Component<{}, State> {
createStackedGraphBins = (
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
metadataField: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryData: any,
categoryData: Dataframe,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colorAccessor: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colorData: any,
colorData: Dataframe,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
categoryValue: any,
// @ts-expect-error ts-migrate(6133) FIXME: 'colorTable' is declared but its value is never re... Remove this comment to see the full error message
@@ -401,7 +398,7 @@ class CategoryValue extends React.Component<{}, State> {
const groupBy = categoryData.col(metadataField);
const occupancyMap = colorData
.col(colorAccessor)
.histogramCategorical(groupBy);
.histogramCategoricalBy(groupBy);
const occupancy = occupancyMap.get(categoryValue);
@@ -8,6 +8,8 @@ import {
Position,
} from "@blueprintjs/core";
import { Dataframe } from "../../../util/dataframe";
// @ts-expect-error ts-migrate(1238) FIXME: Unable to resolve signature of class decorator whe... Remove this comment to see the full error message
@connect((state) => ({
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
@@ -21,34 +23,32 @@ class Occupancy extends React.PureComponent {
_HEIGHT = 11;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
createHistogram = () => {
createHistogram = (): void => {
/*
Knowing that colorScale is based off continous data,
createHistogram fetches the continous data in relation to the cells releveant to the catagory value.
It then seperates that data into 50 bins for drawing the mini-histogram
Knowing that colorScale is based off continuous data,
createHistogram fetches the continuous data in relation to the cells relevant to the category value.
It then separates that data into 50 bins for drawing the mini-histogram
*/
const {
const { metadataField, categoryData, colorData, categoryValue } = this
.props as {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
metadataField,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'Re... Remove this comment to see the full error message
categoryData,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type 'Reado... Remove this comment to see the full error message
colorData,
metadataField;
categoryData: Dataframe;
colorData: Dataframe;
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryValue' does not exist on type 'R... Remove this comment to see the full error message
categoryValue,
} = this.props;
categoryValue;
};
if (!this.canvas) return;
const groupBy = categoryData.col(metadataField);
const col = colorData.icol(0);
const range = col.summarize();
const histogramMap = col.histogram(
const range = col.summarizeContinuous();
const histogramMap = col.histogramContinuousBy(
50,
[range.min, range.max],
groupBy
); /* Because the signature changes we really need different names for histogram to differentiate signatures */
);
const bins = histogramMap.has(categoryValue)
? histogramMap.get(categoryValue)
? (histogramMap.get(categoryValue) as number[])
: new Array(50).fill(0);
const xScale = d3
.scaleLinear()
@@ -71,36 +71,41 @@ class Occupancy extends React.PureComponent {
}
};
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
createOccupancyStack = () => {
createOccupancyStack = (): void => {
/*
Knowing that the color scale is based off of catagorical data,
createOccupancyStack obtains a map showing the number if cells per colored value
Using the colorScale a stack of colored bars is drawn representing the map
*/
const {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'metadataField' does not exist on type 'R... Remove this comment to see the full error message
metadataField,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryData' does not exist on type 'Re... Remove this comment to see the full error message
categoryData,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorAccessor' does not exist on type 'R... Remove this comment to see the full error message
colorAccessor,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'categoryValue' does not exist on type 'R... Remove this comment to see the full error message
categoryValue,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorTable' does not exist on type 'Read... Remove this comment to see the full error message
colorTable,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'schema' does not exist on type 'Readonly... Remove this comment to see the full error message
schema,
// @ts-expect-error ts-migrate(2339) FIXME: Property 'colorData' does not exist on type 'Reado... Remove this comment to see the full error message
colorData,
} = this.props;
} = this.props as {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
metadataField: any;
categoryData: Dataframe;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colorAccessor: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryValue: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colorTable: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema: any;
colorData: Dataframe;
};
const { scale: colorScale } = colorTable;
const ctx = this.canvas?.getContext("2d");
if (!ctx) return;
const groupBy = categoryData.col(metadataField);
const occupancyMap = colorData
.col(colorAccessor)
.histogramCategorical(groupBy);
.histogramCategoricalBy(groupBy);
const occupancy = occupancyMap.get(categoryValue);
if (occupancy && occupancy.size > 0) {
// not all categories have occupancy, so occupancy may be undefined.
@@ -119,7 +124,7 @@ class Occupancy extends React.PureComponent {
let value;
for (let i = 0, { length } = categoryValues; i < length; i += 1) {
value = categoryValues[i];
o = occupancy.get(value);
o = occupancy.get(value) as number;
scaledValue = x(o);
ctx.fillStyle = o
? colorScale(categories.indexOf(value))
@@ -121,6 +121,7 @@ const loadAllEmbeddingCounts = async ({ annoMatrix, available }: any) => {
return available.map((name, idx) => ({
embeddingName: name,
embedding: embeddings[idx],
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'unknown' is not assignable...
discreteCellIndex: getDiscreteCellEmbeddingRowIndex(embeddings[idx]),
}));
};
@@ -19,6 +19,7 @@ import {
import { memoize } from "../../../util/dataframe/util";
import parseBulkGeneString from "../../../util/parseBulkGeneString";
import { Dataframe } from "../../../util/dataframe";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const renderGene = (fuzzySortResult: any, { handleClick, modifiers }: any) => {
@@ -187,7 +188,7 @@ class AddGenes extends React.Component<{}, AddGenesState> {
this.setState({ status: "pending" });
try {
const df = await annoMatrix.fetch("var", varIndex);
const df: Dataframe = await annoMatrix.fetch("var", varIndex);
this.setState({
status: "success",
geneNames: df.col(varIndex).asArray(),
@@ -9,6 +9,7 @@ import Gene from "./gene";
import { postUserErrorToast } from "../framework/toasters";
import actions from "../../actions";
import { Dataframe, DataframeValue } from "../../util/dataframe";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const usePrevious = (value: any) => {
@@ -24,7 +25,7 @@ function QuickGene() {
const dispatch = useDispatch();
const [isExpanded, setIsExpanded] = useState(true);
const [geneNames, setGeneNames] = useState([]);
const [geneNames, setGeneNames] = useState([] as DataframeValue[]);
const [, setStatus] = useState("pending");
const { annoMatrix, userDefinedGenes, userDefinedGenesLoading } = useSelector(
@@ -50,9 +51,9 @@ function QuickGene() {
setStatus("pending");
try {
const df = await annoMatrix.fetch("var", varIndex);
const df: Dataframe = await annoMatrix.fetch("var", varIndex);
setStatus("success");
setGeneNames(df.col(varIndex).asArray());
setGeneNames(df.col(varIndex).asArray() as DataframeValue[]);
} catch (error) {
setStatus("error");
throw error;
@@ -95,7 +96,6 @@ function QuickGene() {
const gene = g.target;
if (userDefinedGenes.indexOf(gene) !== -1) {
postUserErrorToast("That gene already exists");
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'any' is not assignable to parame... Remove this comment to see the full error message
} else if (geneNames.indexOf(gene) === undefined) {
postUserErrorToast("That doesn't appear to be a valid gene name.");
} else {
+19 -19
View File
@@ -26,6 +26,7 @@ import {
flagSelected,
flagHighlight,
} from "../../util/glHelpers";
import { Dataframe } from "../../util/dataframe";
/*
Simple 2D transforms control all point painting. There are three:
@@ -558,7 +559,11 @@ class Graph extends React.Component<{}, GraphState> {
handleEnd = this.handleLassoEnd.bind(this);
handleCancel = this.handleLassoCancel.bind(this);
}
const { svg: newToolSVG, tool, container } = setupSVGandBrushElements(
const {
svg: newToolSVG,
tool,
container,
} = setupSVGandBrushElements(
selectionTool,
handleStart,
handleDrag,
@@ -592,8 +597,7 @@ class Graph extends React.Component<{}, GraphState> {
const positions = this.computePointPositions(X, Y, modelTF);
const colorTable = this.updateColorTable(colorsProp, colorDf);
const colors = this.computePointColors(colorTable.rgb);
const { colorAccessor } = colorsProp;
const colorByData = colorDf?.col(colorAccessor)?.asArray();
const colorByData = colorDf?.icol(0)?.asArray();
const {
metadataField: pointDilationCategory,
categoryField: pointDilationLabel,
@@ -627,7 +631,7 @@ class Graph extends React.Component<{}, GraphState> {
colors: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
pointDilation: any
) {
): Promise<[Dataframe, Dataframe | null, Dataframe | null]> {
/*
fetch all data needed. Includes:
- the color by dataframe
@@ -635,22 +639,18 @@ class Graph extends React.Component<{}, GraphState> {
- the point dilation dataframe
*/
const { metadataField: pointDilationAccessor } = pointDilation;
const promises = [];
// layout
promises.push(annoMatrix.fetch("emb", layoutChoice.current));
// color
const query = this.createColorByQuery(colors);
if (query) {
promises.push(annoMatrix.fetch(...query));
} else {
promises.push(Promise.resolve(null));
}
// point highlighting
if (pointDilationAccessor) {
promises.push(annoMatrix.fetch("obs", pointDilationAccessor));
} else {
promises.push(Promise.resolve(null));
}
const promises: [
Promise<Dataframe>,
Promise<Dataframe | null>,
Promise<Dataframe | null>
] = [
annoMatrix.fetch("emb", layoutChoice.current),
query ? annoMatrix.fetch(...query) : Promise.resolve(null),
pointDilationAccessor
? annoMatrix.fetch("obs", pointDilationAccessor)
: Promise.resolve(null),
];
return Promise.all(promises);
}
@@ -22,6 +22,7 @@ import {
flagSelected,
flagHighlight,
} from "../../util/glHelpers";
import { Dataframe, DataframeColumn } from "../../util/dataframe";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function createProjectionTF(viewportWidth: any, viewportHeight: any) {
@@ -33,9 +34,9 @@ function createProjectionTF(viewportWidth: any, viewportHeight: any) {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function getScale(col: any, rangeMin: any, rangeMax: any) {
function getScale(col: DataframeColumn, rangeMin: any, rangeMax: any) {
if (!col) return null;
const { min, max } = col.summarize();
const { min, max } = col.summarizeContinuous();
return d3.scaleLinear().domain([min, max]).range([rangeMin, rangeMax]);
}
const getXScale = memoize(getScale);
@@ -276,17 +277,13 @@ class Scatterplot extends React.PureComponent<{}, State> {
pointDilation,
} = props.watchProps;
const [
expressionXDf,
expressionYDf,
colorDf,
pointDilationDf,
] = await this.fetchData(
scatterplotXXaccessor,
scatterplotYYaccessor,
colorsProp,
pointDilation
);
const [expressionXDf, expressionYDf, colorDf, pointDilationDf] =
await this.fetchData(
scatterplotXXaccessor,
scatterplotYYaccessor,
colorsProp,
pointDilation
);
const colorTable = this.updateColorTable(colorsProp, colorDf);
const xCol = expressionXDf.icol(0);
@@ -383,37 +380,32 @@ class Scatterplot extends React.PureComponent<{}, State> {
colors: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
pointDilation: any
) {
): Promise<
[Dataframe, Dataframe, Dataframe | null, Dataframe | null]
> {
// @ts-expect-error ts-migrate(2339) FIXME: Property 'annoMatrix' does not exist on type 'Read... Remove this comment to see the full error message
const { annoMatrix } = this.props;
const { metadataField: pointDilationAccessor } = pointDilation;
const promises = [];
// X and Y dimensions
promises.push(
// @ts-expect-error ts-migrate(2488) FIXME: Type '(string | { where: { field: string; column: ... Remove this comment to see the full error message
annoMatrix.fetch(...this.createXQuery(scatterplotXXaccessor))
);
promises.push(
annoMatrix.fetch(...this.createXQuery(scatterplotYYaccessor))
);
// color
const query = this.createColorByQuery(colors);
if (query) {
promises.push(annoMatrix.fetch(...query));
} else {
promises.push(Promise.resolve(null));
}
// point highlighting
if (pointDilationAccessor) {
promises.push(annoMatrix.fetch("obs", pointDilationAccessor));
} else {
promises.push(Promise.resolve(null));
}
const promises: [Dataframe, Dataframe, Dataframe | null, Dataframe | null] =
[
// @ts-expect-error ts-migrate(2488) FIXME: Type '(string | { where: { field: string; column: ... Remove this comment to see the full error message
annoMatrix.fetch(...this.createXQuery(scatterplotXXaccessor)),
annoMatrix.fetch(...this.createXQuery(scatterplotYYaccessor)),
query ? annoMatrix.fetch(...query) : Promise.resolve(null),
pointDilationAccessor
? annoMatrix.fetch("obs", pointDilationAccessor)
: Promise.resolve(null),
];
return Promise.all(promises);
return Promise.all<
Dataframe,
Dataframe,
Dataframe | null,
Dataframe | null
>(promises);
}
renderCanvas = renderThrottle(() => {
+10 -18
View File
@@ -1,5 +1,6 @@
import quantile from "./quantile";
import { memoize } from "./dataframe/util";
import { Dataframe } from "./dataframe";
import { unassignedCategoryLabel } from "../globals";
import {
createCategorySummaryFromDfCol,
@@ -27,12 +28,10 @@ const getCoordinatesByLabel = (
schema: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryDf: any,
categoryDf: Dataframe,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutChoice: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutDf: any
layoutDf: Dataframe
) => {
const coordsByCategoryLabel = new Map();
// If the coloredBy is not a categorical col
@@ -50,11 +49,8 @@ const getCoordinatesByLabel = (
schema.annotations.obsByName[categoryName]
);
const {
isUserAnno,
categoryValueIndices,
categoryValueCounts,
} = categorySummary;
const { isUserAnno, categoryValueIndices, categoryValueCounts } =
categorySummary;
// Iterate over all cells
for (let i = 0, len = categoryArray.length; i < len; i += 1) {
@@ -114,12 +110,10 @@ const calcMedianCentroid = (
schema: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryDf: any,
categoryDf: Dataframe,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutChoice: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutDf: any
layoutDf: Dataframe
) => {
// generate a map describing the coordinates for each label within the given category
const dataMap = getCoordinatesByLabel(
@@ -159,13 +153,11 @@ const hashMedianCentroid = (
schema: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryName: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
categoryDf: any,
categoryDf: Dataframe,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutChoice: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
layoutDf: any
) => {
layoutDf: Dataframe
): string => {
const category = categoryDf.col(categoryName);
const layoutDimNames = layoutChoice.currentDimNames;
const layoutX = layoutDf.col(layoutDimNames[0]);
+5 -8
View File
@@ -10,21 +10,18 @@ objects.
*/
import { memoize } from "./util";
import Dataframe from "./dataframe";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function hashDataframe(df: any) {
function hashDataframe(df: Dataframe): string {
if (df.isEmpty()) return "";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return df.__columnsAccessor.map((c: any) => c.__id).join(",");
return df.__columnsAccessor.map((c) => c.__id).join(",");
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function noop(df: any) {
function noop(df: Dataframe): Dataframe {
return df;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
const dataframeMemo = (capacity = 100) =>
const dataframeMemo = (capacity = 100): ((df: Dataframe) => Dataframe) =>
memoize(noop, hashDataframe, capacity);
export default dataframeMemo;
+249 -224
View File
@@ -1,25 +1,44 @@
import { IdentityInt32Index, isLabelIndex } from "./labelIndex";
// weird cross-dependency that we should clean up someday...
import { callOnceLazy, memoize, __getMemoId } from "./util";
import { isTypedArray, isAnyArray } from "../../common/types/arraytypes";
import {
summarizeContinuous,
isTypedArray,
isAnyArray,
AnyArray,
GenericArrayConstructor,
} from "../../common/types/arraytypes";
import { IdentityInt32Index, LabelIndex, isLabelIndex } from "./labelIndex";
import {
summarizeContinuous as _summarizeContinuous,
summarizeCategorical as _summarizeCategorical,
} from "./summarize";
import {
histogramCategorical as _histogramCategorical,
histogramCategoricalBy as _histogramCategoricalBy,
hashCategorical,
histogramContinuous,
hashCategoricalBy,
histogramContinuous as _histogramContinuous,
histogramContinuousBy as _histogramContinuousBy,
hashContinuous,
hashContinuousBy,
} from "./histogram";
import {
DataframeValue,
DataframeValueArray,
DataframeColumn,
OffsetType,
OffsetArray,
LabelType,
LabelArray,
ContinuousHistogram,
ContinuousHistogramBy,
} from "./types";
/*
Dataframe is an immutable 2D matrix similiar to Python Pandas Dataframe,
Dataframe is an immutable 2D matrix similar to Python Pandas Dataframe,
but (currently) without all of the surrounding support functions.
Data is stored in column-major layout, and each column is monomorphic.
It supports:
* Relatively efficient creation, cloning and subsetting
* Relatively efficient create, clone and subset operations
* Very efficient columnar access (eg, sum down a column), and access
to the underlying column arrays.
* Data access by row/col offset or label. Labels are reasonably well
@@ -27,10 +46,10 @@ It supports:
It does not currently support:
* Views on matrix subset - for currently known access patterns,
it is more effiicent to copy on subsetting, optimizing for access
it is more efficient to copy on subsetting, optimizing for access
speed over memory use.
* JS iterators - they are too slow. Use explicit iteration over
offest or labels.
offset or labels.
Important assumptions embedded in the API:
* Columns are implicitly categorical if they are a JS Array and numeric
@@ -67,46 +86,55 @@ dominant pattern in cellxgene.
Dataframe
**/
interface DataframeConstructor {
new (...args: ConstructorParameters<typeof Dataframe>): Dataframe;
}
export type MapColumnsCallbackFn = (
data: DataframeValueArray,
idx: number,
df: Dataframe
) => DataframeValueArray;
/** @internal */
function raiseIsNotContinuous<R = void>(): R {
throw TypeError("Column is not a continuous data type.");
}
class Dataframe {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__columns: any;
/** @internal */
__columns: DataframeValueArray[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__columnsAccessor: any;
/** @internal */
__columnsAccessor: DataframeColumn[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__id: any;
__id: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colIndex: any;
colIndex: LabelIndex;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
dims: any;
dims: [number, number];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
length: any;
length: number;
rowIndex: LabelIndex;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
rowIndex: any;
/**
Constructors & factories
**/
constructor(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dims: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
columnarData: any,
rowIndex = null,
colIndex = null,
__columnsAccessor = [] // private interface
dims: [number, number],
columnarData: DataframeValueArray[],
rowIndex?: LabelIndex | null,
colIndex?: LabelIndex | null,
__columnsAccessor: (DataframeColumn | null)[] = [] // private interface
) {
/*
The base constructor is relatively hard to use - as an alternative,
see factory methods and clone/slice, below.
Parameters:
* dims - 2D array describing intendend dimensionality: [nRows,nCols].
* dims - 2D array describing intended dimensionality: [nRows,nCols].
* columnarData - JS array, nCols in length, containing array
or TypedArray of length nRows.
* rowIndex/colIndex - null (create default index using offsets as key),
@@ -121,11 +149,9 @@ class Dataframe {
throw new RangeError("Dataframe dimensions must be positive");
}
if (!rowIndex) {
// @ts-expect-error ts-migrate(2322) FIXME: Type 'IdentityInt32Index' is not assignable to typ... Remove this comment to see the full error message
rowIndex = new IdentityInt32Index(nRows);
}
if (!colIndex) {
// @ts-expect-error ts-migrate(2322) FIXME: Type 'IdentityInt32Index' is not assignable to typ... Remove this comment to see the full error message
colIndex = new IdentityInt32Index(nCols);
}
Dataframe.__errorChecks(dims, columnarData, rowIndex, colIndex);
@@ -141,17 +167,13 @@ class Dataframe {
Object.freeze(this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
/** @internal */
static __errorChecks(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dims: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
columnarData: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
rowIndex: any,
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
colIndex: any
) {
dims: [number, number],
columnarData: AnyArray[],
rowIndex: LabelIndex,
colIndex: LabelIndex
): void | never {
const [nRows, nCols] = dims;
/* check for expected types */
@@ -189,8 +211,12 @@ class Dataframe {
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
static __compileColumn(column: any, getRowByOffset: any, getRowByLabel: any) {
/** @internal */
static __compileColumn(
column: DataframeValueArray,
getRowOffset: (label: LabelType) => OffsetType | -1,
getRowLabel: (offset: number) => LabelType | undefined
): DataframeColumn {
/*
Each column accessor is a function which will lookup data by
index (ie, is equivalent to dataframe.get(row, col), where 'col'
@@ -223,16 +249,17 @@ class Dataframe {
*/
const { length } = column;
const __id = __getMemoId();
const isContinuous = isTypedArray(column);
/* get value by row label */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const get = function get(rlabel: any) {
return column[getRowByOffset(rlabel)];
const get = function get(rlabel: LabelType): DataframeValue | undefined {
const idx = getRowOffset(rlabel);
if (idx === -1) return undefined;
return column[idx];
};
/* get value by row offset */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const iget = function iget(roffset: any) {
const iget = function iget(roffset: OffsetType) {
return column[roffset];
};
@@ -242,14 +269,12 @@ class Dataframe {
};
/* test for row label inclusion in column */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const has = function has(rlabel: any) {
const offset = getRowByOffset(rlabel);
const has = function has(rlabel: LabelType) {
const offset = getRowOffset(rlabel);
return offset >= 0 && offset < length;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const ihas = function ihas(offset: any) {
const ihas = function ihas(offset: OffsetType) {
return offset >= 0 && offset < length;
};
@@ -260,84 +285,88 @@ class Dataframe {
NOTE: not found return is DIFFERENT than the default Array.indexOf as
-1 is a plausible Dataframe row/col label.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const indexOf = function indexOf(value: any) {
const offset = column.indexOf(value);
const _indexOf = function _indexOf(value: DataframeValue) {
let offset: number;
if (isTypedArray(column)) offset = column.indexOf(value as number);
else offset = column.indexOf(value);
if (offset === -1) {
return undefined;
}
return getRowByLabel(offset);
return getRowLabel(offset);
};
/*
Summarize the column data. Lazy eval, memoized
*/
const summarizeCategorical = callOnceLazy(() =>
get.summarizeCategorical = callOnceLazy(() =>
_summarizeCategorical(column)
);
const summarize = callOnceLazy(() =>
isTypedArray(column)
? summarizeContinuous(column)
: summarizeCategorical(column)
);
get.summarizeContinuous = isContinuous
? callOnceLazy(() => _summarizeContinuous(column))
: raiseIsNotContinuous;
/*
Create histogram bins for this column. Memoized.
*/
const _memoHistoCat = memoize(_histogramCategorical, hashCategorical);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const histogramCategorical = (by: any) => _memoHistoCat(get, by);
let histogram = null;
if (isTypedArray(column)) {
const mFn = memoize(histogramContinuous, hashContinuous);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
histogram = (bins: any, domain: any, by: any) =>
mFn(get, bins, domain, by);
} else {
histogram = histogramCategorical;
}
get.histogramContinuous = isContinuous
? (bins: number, domain: [number, number]): ContinuousHistogram =>
memoize(_histogramContinuous, hashContinuous)(get, bins, domain)
: raiseIsNotContinuous;
get.histogramContinuousBy = isContinuous
? (
bins: number,
domain: [number, number],
by: DataframeColumn
): ContinuousHistogramBy =>
memoize(_histogramContinuousBy, hashContinuousBy)(
get,
bins,
domain,
by
)
: raiseIsNotContinuous;
get.histogramCategorical = () =>
memoize(_histogramCategorical, hashCategorical)(get);
get.histogramCategoricalBy = (by: DataframeColumn) =>
memoize(_histogramCategoricalBy, hashCategoricalBy)(get, by);
get.summarize = summarize;
get.summarizeCategorical = summarizeCategorical;
get.histogram = histogram;
get.histogramCategorical = histogramCategorical;
get.asArray = asArray;
get.has = has;
get.ihas = ihas;
get.indexOf = indexOf;
get.indexOf = _indexOf;
get.iget = iget;
get.__id = __id;
get.isContinuous = isContinuous;
Object.freeze(get);
return get;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
__compile(accessors: any) {
/** @internal */
__compile(accessors: (DataframeColumn | null)[]): void {
/*
Compile data accessors for each column.
Use an existing accessor if provided, else compile a new one.
*/
const getRowByOffset = this.rowIndex.getOffset.bind(this.rowIndex);
const getRowByLabel = this.rowIndex.getLabel.bind(this.rowIndex);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.__columnsAccessor = this.__columns.map((column: any, idx: any) => {
if (accessors[idx]) {
return accessors[idx];
const getRowOffset = this.rowIndex.getOffset.bind(this.rowIndex);
const getRowLabel = this.rowIndex.getLabel.bind(this.rowIndex);
this.__columnsAccessor = this.__columns.map(
(column, idx): DataframeColumn => {
if (accessors[idx]) {
return accessors[idx] as DataframeColumn;
}
return Dataframe.__compileColumn(column, getRowOffset, getRowLabel);
}
return Dataframe.__compileColumn(column, getRowByOffset, getRowByLabel);
});
);
Object.freeze(this.__columnsAccessor);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
clone() {
clone(): Dataframe {
/*
Clone this dataframe
*/
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
this.dims,
[...this.__columns],
this.rowIndex,
@@ -346,8 +375,11 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withCol(label: any, colData: any, withRowIndex = null) {
withCol(
label: LabelType,
colData: DataframeValueArray,
withRowIndex?: LabelIndex
): Dataframe {
/*
Create a new DF, which is `this` plus the new column. Example:
const newDf = df.withCol("foo", [1,2,3]);
@@ -362,11 +394,10 @@ class Dataframe {
the rowIndex from `this` will be used (ie, the rowIndex is
unchanged).
*/
let dims;
let rowIndex;
let dims: [number, number];
let rowIndex: LabelIndex | null = null;
if (this.isEmpty()) {
dims = [colData.length, 1];
rowIndex = null;
} else {
dims = [this.dims[0], this.dims[1] + 1];
({ rowIndex } = this);
@@ -380,8 +411,7 @@ class Dataframe {
columns.push(colData);
const colIndex = this.colIndex.withLabel(label);
const columnsAccessor = [...this.__columnsAccessor];
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
dims,
columns,
rowIndex,
@@ -390,8 +420,10 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withColsFrom(dataframe: any, labels: any) {
withColsFrom(
dataframe: Dataframe,
labels?: Record<string | number, LabelType> | LabelType[]
): Dataframe {
/*
return a new dataframe containing all columns from both `this` and the
provided dataframe argument.
@@ -417,8 +449,8 @@ class Dataframe {
*/
// resolve the source and dest label names.
let srcLabels;
let dstLabels;
let srcLabels: LabelArray;
let dstLabels: LabelArray;
if (!labels) {
// combine all columns
dstLabels = dataframe.colIndex.labels();
@@ -453,12 +485,9 @@ class Dataframe {
return dataframe;
}
// otherwise, bulid a new dataframe combining columns from both
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const srcOffsets = srcLabels.map((l: any) =>
dataframe.colIndex.getOffset(l)
);
// otherwise, build a new dataframe combining columns from both
const srcOffsets = Array.from(dataframe.colIndex.getOffsets(srcLabels));
if (srcOffsets.some((i) => i === -1)) throw RangeError("Unknown label.");
// check for label collisions
if (dstLabels.some(this.hasCol, this)) {
@@ -466,22 +495,22 @@ class Dataframe {
}
// const dims = [this.dims[0], this.dims[1] + dataframe.dims[1]];
const dims = [this.dims[0], this.dims[1] + srcOffsets.length];
const dims: [number, number] = [
this.dims[0],
this.dims[1] + srcOffsets.length,
];
const { rowIndex } = this;
const columns = [
const columns: DataframeValueArray[] = [
...this.__columns,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
...srcOffsets.map((i: any) => dataframe.__columns[i]),
...srcOffsets.map((i) => dataframe.__columns[i]),
];
const colIndex = this.colIndex.withLabels(dstLabels);
const columnsAccessor = [
...this.__columnsAccessor,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
...srcOffsets.map((i: any) => dataframe.__columnsAccessor[i]),
...srcOffsets.map((i) => dataframe.__columnsAccessor[i]),
];
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
dims,
columns,
rowIndex,
@@ -490,15 +519,12 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
withColsFromAll(dataframes = []) {
withColsFromAll(dataframes: Dataframe[] = []): Dataframe {
dataframes = Array.isArray(dataframes) ? dataframes : [dataframes];
// @ts-expect-error ts-migrate(2554) FIXME: Expected 2 arguments, but got 1.
return dataframes.reduce((acc, df) => acc.withColsFrom(df), this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dropCol(label: any) {
dropCol(label: LabelType): Dataframe {
/*
Create a new dataframe, omitting one columns.
@@ -517,15 +543,15 @@ class Dataframe {
return Dataframe.empty();
}
const dims = [this.dims[0], this.dims[1] - 1];
const dims: [number, number] = [this.dims[0], this.dims[1] - 1];
const coffset = this.colIndex.getOffset(label);
if (coffset === -1) throw new RangeError("Unknown label.");
const columns = [...this.__columns];
columns.splice(coffset, 1);
const colIndex = this.colIndex.dropLabel(label);
const columnsAccessor = [...this.__columnsAccessor];
columnsAccessor.splice(coffset, 1);
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
dims,
columns,
this.rowIndex,
@@ -534,12 +560,12 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
renameCol(oldLabel: any, newLabel: any) {
renameCol(oldLabel: LabelType, newLabel: LabelType): Dataframe {
/*
Accelerator for dropping a column and then adding it again with a new label
*/
const coffset = this.colIndex.getOffset(oldLabel);
if (coffset === -1) throw new RangeError("Unknown label.");
const colIndex = this.colIndex.dropLabel(oldLabel).withLabel(newLabel);
const columns = [...this.__columns];
@@ -550,8 +576,7 @@ class Dataframe {
columnsAccessor.push(columnsAccessor[coffset]);
columnsAccessor.splice(coffset, 1);
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
this.dims,
columns,
this.rowIndex,
@@ -560,20 +585,21 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
replaceColData(label: any, newColData: any) {
replaceColData(label: LabelType, newColData: DataframeValueArray): Dataframe {
/*
Accelerator for dropping a column then adding it again with same
label and different values.
*/
const coffset = this.colIndex.getOffset(label);
if (coffset === -1) throw RangeError("Unknown column label.");
const columns = [...this.__columns];
columns[coffset] = newColData;
const columnsAccessor = [...this.__columnsAccessor];
const columnsAccessor: (DataframeColumn | null)[] = [
...this.__columnsAccessor,
];
columnsAccessor[coffset] = null;
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
this.dims,
columns,
this.rowIndex,
@@ -582,20 +608,19 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
static empty(rowIndex = null, colIndex = null) {
const dims = [
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
static empty(rowIndex?: LabelIndex, colIndex?: LabelIndex): Dataframe {
const dims: [number, number] = [
rowIndex ? rowIndex.size() : 0,
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
colIndex ? colIndex.size() : 0,
];
if (dims[0] && dims[1]) throw new Error("not an empty dataframe");
return new Dataframe(dims, new Array(dims[1]), rowIndex, colIndex);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
static create(dims: any, columnarData: any) {
static create(
dims: [number, number],
columnarData: DataframeValueArray[]
): Dataframe {
/*
Create a dataframe from raw columnar data. All column arrays
must have the same length. Identity indexing will be used.
@@ -603,12 +628,15 @@ class Dataframe {
Example:
const df = Dataframe.create([2,2], [new Uint32Array(2), new Float32Array(2)]);
*/
return new Dataframe(dims, columnarData, null, null);
return new Dataframe(dims, columnarData);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
__subset(newRowIndex: any, newColIndex: any) {
const dims = [...this.dims];
/** @internal */
__subset(
newRowIndex: LabelIndex | null,
newColIndex: LabelIndex | null
): Dataframe {
const dims: [number, number] = [...this.dims];
/* subset columns */
let { __columns, colIndex, __columnsAccessor } = this;
@@ -617,8 +645,10 @@ class Dataframe {
__columns = new Array(colOffsets.length);
__columnsAccessor = new Array(colOffsets.length);
for (let i = 0, l = colOffsets.length; i < l; i += 1) {
__columns[i] = this.__columns[colOffsets[i]];
__columnsAccessor[i] = this.__columnsAccessor[colOffsets[i]];
const colOffset = colOffsets[i];
if (colOffset === -1) throw new RangeError("Unexpected column offset.");
__columns[i] = this.__columns[colOffset];
__columnsAccessor[i] = this.__columnsAccessor[colOffset];
}
colIndex = newColIndex;
dims[1] = colOffsets.length;
@@ -627,11 +657,14 @@ class Dataframe {
let { rowIndex } = this;
if (newRowIndex) {
const rowOffsets = this.rowIndex.getOffsets(newRowIndex.labels());
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__columns = __columns.map((col: any) => {
const newCol = new col.constructor(rowOffsets.length);
__columns = __columns.map((col) => {
const newCol = new (col.constructor as GenericArrayConstructor<
typeof col
>)(rowOffsets.length);
for (let i = 0, l = rowOffsets.length; i < l; i += 1) {
newCol[i] = col[rowOffsets[i]];
const rowOffset = rowOffsets[i];
if (rowOffset === -1) throw new RangeError("Unexpected row offset.");
newCol[i] = col[rowOffset];
}
return newCol;
});
@@ -650,8 +683,11 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
subset(rowLabels: any, colLabels = null, withRowIndex = null) {
subset(
rowLabels: LabelArray | null,
colLabels: LabelArray | null,
withRowIndex?: LabelIndex | null
): Dataframe {
/*
Subset by row/col labels.
@@ -673,8 +709,11 @@ class Dataframe {
return this.__subset(rowIndex, colIndex);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubset(rowOffsets: any, colOffsets = null, withRowIndex = null) {
isubset(
rowOffsets: OffsetArray | null,
colOffsets: OffsetArray | null,
withRowIndex?: LabelIndex | null
): Dataframe {
/*
Subset by row/col offset.
@@ -683,14 +722,14 @@ class Dataframe {
indexing. If withRowIndex is a label index object, it will be used
for the new dataframe.
*/
let rowIndex = null;
let rowIndex: LabelIndex | null = null;
if (withRowIndex) {
rowIndex = withRowIndex;
} else if (rowOffsets) {
rowIndex = this.rowIndex.isubset(rowOffsets);
}
let colIndex = null;
let colIndex: LabelIndex | null = null;
if (colOffsets) {
colIndex = this.colIndex.isubset(colOffsets);
}
@@ -698,8 +737,11 @@ class Dataframe {
return this.__subset(rowIndex, colIndex);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubsetMask(rowMask: any, colMask = null, withRowIndex = null) {
isubsetMask(
rowMask: Uint8Array | boolean[] | null,
colMask: Uint8Array | boolean[] | null,
withRowIndex?: LabelIndex | null
): Dataframe {
/*
Subset on row/column based upon a truthy/falsey array (a mask).
@@ -711,15 +753,16 @@ class Dataframe {
const [nRows, nCols] = this.dims;
if (
(rowMask && rowMask.length !== nRows) ||
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
(colMask && colMask.length !== nCols)
) {
throw new RangeError("boolean arrays must match row/col dimensions");
}
/* convert masks to lists - method wastes space, but is fast */
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const toList = (mask: any, maxSize: any) => {
const toList = (
mask: Uint8Array | boolean[] | null | undefined,
maxSize: number
) => {
if (!mask) {
return null;
}
@@ -735,7 +778,6 @@ class Dataframe {
};
const rowOffsets = toList(rowMask, nRows);
const colOffsets = toList(colMask, nCols);
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'Int32Array | null' is not assign... Remove this comment to see the full error message
return this.isubset(rowOffsets, colOffsets, withRowIndex);
}
@@ -743,14 +785,12 @@ class Dataframe {
Data access with row/col.
**/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
columns() {
columns(): DataframeColumn[] {
/* return all column accessors as an array, in offset order */
return [...this.__columnsAccessor];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
col(columnLabel: any) {
col(columnLabel: LabelType): DataframeColumn {
/*
Return accessor bound to a column. Allows random row access
based upon the row indexing. Returns undefined if the
@@ -767,39 +807,44 @@ class Dataframe {
See __compile() for the functions available in a column accessor.
*/
const coff = this.colIndex.getOffset(columnLabel);
if (coff === -1) throw RangeError("Unknown label.");
return this.__columnsAccessor[coff];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
icol(columnOffset: any) {
icol(columnOffset: OffsetType): DataframeColumn {
/*
Return column accessor by offset.
*/
return Number.isInteger(columnOffset)
? this.__columnsAccessor[columnOffset]
: undefined;
if (
Number.isInteger(columnOffset) &&
columnOffset >= 0 &&
columnOffset < this.__columnsAccessor.length
) {
return this.__columnsAccessor[columnOffset];
}
throw new RangeError("Unknown offset.");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
at(r: any, c: any) {
at(r: LabelType, c: LabelType): DataframeValue {
/*
Access a single value, for a row/col label pair.
For performance reasons, there are no bounds or existance
For performance reasons, there are no bounds or existence
checks on labels, and no defined behavior when these are supplied.
May return undefined, throw an Error, or do something else for
non-existant labels. If you want predictable out-of-bounds
non-existent labels. If you want predictable out-of-bounds
behavior, use has(), eg,
const myVal = df.has(r,l) ? df.at(r,l) : undefined;
*/
const coff = this.colIndex.getOffset(c);
const roff = this.rowIndex.getOffset(r);
if (coff === undefined || roff === undefined)
throw new RangeError("Unknown row or column label.");
return this.__columns[coff][roff];
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
iat(r: any, c: any) {
iat(r: OffsetType, c: OffsetType): DataframeValue {
/*
Access a single value, for a row/col offset (integer) position.
@@ -809,11 +854,12 @@ class Dataframe {
const myVal = df.ihas(r, c) ? df.iat(r, c) : undefined;
*/
return this.__columns[c][r];
if (c >= 0 && c < this.dims[1] && r >= 0 && r < this.dims[0])
return this.__columns[c][r];
throw new RangeError("Unknown row or column index.");
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
has(r: any, c: any) {
has(r: LabelType, c: LabelType): boolean {
/*
Test if row/col labels exist in the dataframe - returns true/false
*/
@@ -823,8 +869,7 @@ class Dataframe {
return coff >= 0 && coff < nCols && roff >= 0 && roff < nRows;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
ihas(r: any, c: any) {
ihas(r: number, c: number): boolean {
/*
Test if row/col offset (integer) position exists in the
dataframe - returns true/false
@@ -840,16 +885,23 @@ class Dataframe {
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
hasCol(c: any) {
hasCol(c: LabelType): boolean {
/*
Test if col label exists - return true/false
*/
return !!this.col(c);
const coff = this.colIndex.getOffset(c);
return coff !== -1;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
isEmpty() {
ihasCol(i: number): boolean {
/*
Test if col offset exists - return true/false
*/
const [, nCols] = this.dims;
return i >= 0 && i < nCols;
}
isEmpty(): boolean {
/*
Return true if this is an empty dataframe, ie, has dimensions [0,0]
*/
@@ -864,24 +916,20 @@ class Dataframe {
add these as useful.
****/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
mapColumns(callback: any) {
mapColumns(callback: MapColumnsCallbackFn): Dataframe {
/*
map all columns in the dataframe, returning a new dataframe comprised of the
return values, with the same index as the original dataframe.
callback MUST not modify the column, but instead return a mutated copy.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const columns = this.__columns.map((colData: any, colIdx: any) =>
const columns = this.__columns.map((colData, colIdx) =>
callback(colData, colIdx, this)
);
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const columnsAccessor = columns.map((c: any, idx: any) =>
this.__columns[idx] === c ? this.__columnsAccessor[idx] : undefined
const columnsAccessor: (DataframeColumn | null)[] = columns.map((c, idx) =>
this.__columns[idx] === c ? this.__columnsAccessor[idx] : null
);
// @ts-expect-error ts-migrate(2351) FIXME: This expression is not constructable.
return new this.constructor(
return new (this.constructor as DataframeConstructor)(
this.dims,
columns,
this.rowIndex,
@@ -889,29 +937,6 @@ class Dataframe {
columnsAccessor
);
}
/*
Map & reduce of column or row
TODO remainder of map/reduce functions: mapCol, mapRow, reduceRow, ...
*/
/* comment out until we have a use for this
reduceCol(clabel, callback, initialValue) {
const coff = this.colIndex.getOffset(clabel);
const column = this.__columns[coff];
let start = 0;
let acc = initialValue;
if (initialValue === undefined) {
acc = column[0];
start = 1;
}
for (let i = start, l = column.length; i < l; i += 1) {
acc = callback(acc, column[i]);
}
return acc;
}
*/
}
export default Dataframe;
+50 -70
View File
@@ -1,16 +1,27 @@
/*
Dataframe histogram
*/
import { isTypedArray } from "../../common/types/arraytypes";
import { NumberArray } from "../../common/types/arraytypes";
import {
DataframeColumn,
ContinuousHistogram,
ContinuousHistogramBy,
CategoricalHistogram,
CategoricalHistogramBy,
} from "./types";
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _histogramContinuous(column: any, bins: any, min: any, max: any) {
export function histogramContinuous(
column: DataframeColumn,
bins: number,
domain: [number, number]
): ContinuousHistogram {
const valBins = new Array(bins).fill(0);
if (!column) {
return valBins;
}
const [min, max] = domain;
const binWidth = (max - min) / bins;
const colArray = column.asArray();
const colArray: NumberArray = column.asArray() as NumberArray;
for (let r = 0, len = colArray.length; r < len; r += 1) {
const val = colArray[r];
if (val <= max && val >= min) {
@@ -22,25 +33,20 @@ function _histogramContinuous(column: any, bins: any, min: any, max: any) {
return valBins;
}
function _histogramContinuousBy(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
column: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
bins: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
min: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
max: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
by: any
) {
export function histogramContinuousBy(
column: DataframeColumn,
bins: number,
domain: [number, number],
by: DataframeColumn
): ContinuousHistogramBy {
const byMap = new Map();
if (!column || !by) {
return byMap;
}
const [min, max] = domain;
const binWidth = (max - min) / bins;
const byArray = by.asArray();
const colArray = column.asArray();
const colArray = column.asArray() as NumberArray;
for (let r = 0, len = colArray.length; r < len; r += 1) {
const byBin = byArray[r];
let valBins = byMap.get(byBin);
@@ -58,8 +64,9 @@ function _histogramContinuousBy(
return byMap;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _histogramCategorical(column: any) {
export function histogramCategorical(
column: DataframeColumn
): CategoricalHistogram {
const valMap = new Map();
if (!column) {
return valMap;
@@ -76,8 +83,10 @@ function _histogramCategorical(column: any) {
return valMap;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function _histogramCategoricalBy(column: any, by: any) {
export function histogramCategoricalBy(
column: DataframeColumn,
by: DataframeColumn
): CategoricalHistogramBy {
const byMap = new Map();
if (!column || !by) {
return byMap;
@@ -101,67 +110,38 @@ function _histogramCategoricalBy(column: any, by: any) {
return byMap;
}
/*
Count category occupancy. Optional group-by category.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function histogramCategorical(column: any, by: any) {
if (by && isTypedArray(by)) {
throw new Error("Group by column must be categorical");
}
return by
? _histogramCategoricalBy(column, by)
: _histogramCategorical(column);
}
/*
Memoization hash for histogramCategorical()
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function hashCategorical(column: any, by: any) {
if (by) {
return `${column.__id}:${by.__id}`;
}
export function hashCategorical(column: DataframeColumn): string {
return `${column.__id}:`;
}
/*
Bin counts for continuous/scalar values, with optional group-by category.
Values outside domain are ignored.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function histogramContinuous(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
column: any,
bins = 40,
domain = [0, 1],
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
by: any
) {
if (by && isTypedArray(by)) {
throw new Error("Group by column must be categorical");
}
const [min, max] = domain;
return by
? _histogramContinuousBy(column, bins, min, max, by)
: _histogramContinuous(column, bins, min, max);
export function hashCategoricalBy(
column: DataframeColumn,
by: DataframeColumn
): string {
return `${column.__id}:${by.__id}`;
}
/*
Memoization hash for histogramContinuous
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function hashContinuous(
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
column: any,
bins = "",
domain = [0, 0],
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
by: any
) {
column: DataframeColumn,
bins: number,
domain: [number, number]
): string {
const [min, max] = domain;
if (by) {
return `${column.__id}:${bins}:${min}:${max}:${by.__id}`;
}
return `${column.__id}::${bins}:${min}:${max}`;
}
export function hashContinuousBy(
column: DataframeColumn,
bins: number,
domain: [number, number],
by: DataframeColumn
): string {
const [min, max] = domain;
return `${column.__id}:${bins}:${min}:${max}:${by.__id}`;
}
+13
View File
@@ -6,3 +6,16 @@ export {
isLabelIndex,
} from "./labelIndex";
export { default as dataframeMemo } from "./cache";
export type {
LabelType,
DataframeValue,
DataframeValueArray,
DataframeColumn,
ContinuousHistogram,
ContinuousHistogramBy,
CategoricalHistogram,
CategoricalHistogramBy,
ContinuousColumnSummary,
CategoricalColumnSummary,
} from "./types";
export type { LabelIndex } from "./labelIndex";
+234 -240
View File
@@ -1,4 +1,5 @@
/* eslint-disable max-classes-per-file -- Classes are interrelated*/
/**
Label indexing - map a label to & from an integer offset. See Dataframe
for how this is used.
@@ -6,45 +7,78 @@ for how this is used.
import { rangeFill as fillRange } from "../range";
import { __getMemoId } from "./util";
import { OffsetArray, LabelType, LabelArray, GenericLabelArray } from "./types";
/*
Private utility functions
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function extent(tarr: any) {
let min = 0x7fffffff;
let max = ~min; // eslint-disable-line no-bitwise -- Establishes 0 of same size
for (let i = 0, l = tarr.length; i < l; i += 1) {
const v = tarr[i];
if (v < min) {
min = v;
}
if (v > max) {
max = v;
}
export abstract class LabelIndexBase {
readonly __id: string; // memoization helper
constructor(id: string) {
this.__id = id;
}
return [min, max];
abstract labels(): LabelArray;
/**
* Look up the offset for the label.
*
* @param label - label to look up
* @returns - offset number or -1 if not found.
*/
abstract getOffset(label: LabelType): number;
getOffsets(labels: LabelArray): Int32Array {
// labels to offsets
const result = new Int32Array(labels.length);
for (let i = 0; i < labels.length; i += 1) {
result[i] = this.getOffset(labels[i]);
}
return result;
}
/**
* Look up the label for the offset.
*
* @param offset - offset to look up
* @returns - label or undefined if not found.
*/
abstract getLabel(offset: number): LabelType | undefined;
getLabels(offsets: OffsetArray): (LabelType | undefined)[] {
// offsets to labels
const result = new Array(offsets.length);
for (let i = 0; i < offsets.length; i += 1) {
result[i] = this.getLabel(offsets[i]);
}
return result;
}
abstract size(): number;
abstract subset(labels: LabelArray): LabelIndexBase;
abstract isubset(offsets: OffsetArray): LabelIndexBase;
abstract isubsetMask(mask: Uint8Array | boolean[]): LabelIndexBase;
abstract withLabel(label: LabelType): LabelIndexBase;
abstract withLabels(labels: LabelArray): LabelIndexBase;
abstract dropLabel(label: LabelType): LabelIndexBase;
}
class IdentityInt32Index {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
maxOffset: any;
export class IdentityInt32Index extends LabelIndexBase {
readonly maxOffset: number;
/*
identity/noop index, with small assumptions that labels are int32
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(maxOffset: any) {
constructor(maxOffset: number) {
super(`IdentityInt32Index_${maxOffset}`);
this.maxOffset = maxOffset;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
get __id() {
return `IdentityInt32Index_${this.maxOffset}`;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
labels() {
labels(): LabelArray {
// memoize
const k = fillRange(new Int32Array(this.maxOffset));
this.labels = function labels() {
@@ -53,77 +87,79 @@ class IdentityInt32Index {
return k;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getOffset(i: any) {
getOffset(label: LabelType): number {
// label to offset
return Number.isInteger(i) && i >= 0 && i < this.maxOffset ? i : undefined;
return Number.isInteger(label) && label >= 0 && label < this.maxOffset
? (label as number)
: -1;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getOffsets(arr: any) {
// labels to offsets
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((i: any) => this.getOffset(i));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getLabel(i: any) {
getLabel(offset: number): number | undefined {
// offset to label
return Number.isInteger(i) && i >= 0 && i < this.maxOffset ? i : undefined;
return Number.isInteger(offset) && offset >= 0 && offset < this.maxOffset
? offset
: undefined;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
getLabels(arr: any) {
// offsets to labels
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((i: any) => this.getLabel(i));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
size() {
size(): number {
return this.maxOffset;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
__promote(labelArray: any) {
/** @internal */
__promote(labelArray: LabelArray, allInts: boolean): LabelIndexBase {
/*
time/space decision - based on the resulting density
*/
const [minLabel, maxLabel] = extent(labelArray);
if (minLabel === 0 && maxLabel === labelArray.length - 1)
return new IdentityInt32Index(labelArray.length);
if (labelArray.length === 0) return new KeyIndex(Array.from(labelArray));
if (allInts) {
const [minLabel, maxLabel] = extent(
labelArray as GenericLabelArray<number> // safe, as allInts is true
);
if (minLabel === 0 && maxLabel === labelArray.length - 1)
return new IdentityInt32Index(labelArray.length);
const labelSpaceSize = maxLabel - minLabel + 1;
const density = labelSpaceSize / this.maxOffset;
/* 0.1 is a magic number, that needs testing to optimize */
if (density < 0.1) {
return new KeyIndex(labelArray);
const labelSpaceSize = maxLabel - minLabel + 1;
const density = labelSpaceSize / this.maxOffset;
/* 0.1 is a magic number which needs testing to optimize */
if (density < 0.1) {
return new KeyIndex(Array.from(labelArray));
}
return new DenseInt32Index(labelArray as GenericLabelArray<number>, [
minLabel,
maxLabel,
]);
}
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message
return new DenseInt32Index(labelArray, [minLabel, maxLabel]);
return new KeyIndex(Array.from(labelArray));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
subset(labels: any) {
subset(labels: LabelArray): LabelIndexBase {
/* validate subset */
const { maxOffset } = this;
for (let i = 0, l = labels.length; i < l; i += 1) {
const label = labels[i];
if (!Number.isInteger(label) || label < 0 || label >= maxOffset)
throw new RangeError(`offset or label: ${label}`);
throw new RangeError(`label: ${label}`);
}
return this.__promote(labels);
return this.__promote(labels, true);
}
/* identity index - labels are offsets */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubset(offsets: any) {
return this.subset(offsets);
isubset(offsets: OffsetArray): LabelIndexBase {
/* validate isubset */
const { maxOffset } = this;
for (let i = 0, l = offsets.length; i < l; i += 1) {
const offset = offsets[i];
if (!Number.isInteger(offset) || offset < 0 || offset >= maxOffset)
throw new RangeError(`offset: ${offset}`);
}
if (!(offsets instanceof Int32Array)) {
offsets = new Int32Array(offsets);
}
return this.__promote(offsets, true);
}
/* identity index - labels are offsets */
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubsetMask(mask: any) {
isubsetMask(mask: Uint8Array | boolean[]): LabelIndexBase {
let count = 0;
if (mask.length !== this.maxOffset) {
throw new RangeError("mask has invalid length for index");
@@ -139,54 +175,42 @@ class IdentityInt32Index {
return this.subset(labels);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabel(label: any) {
withLabel(label: LabelType): LabelIndexBase {
if (label === this.maxOffset) {
return new IdentityInt32Index(label + 1);
}
return this.__promote([...this.labels(), label]);
return this.__promote([...this.labels(), label], Number.isInteger(label));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabels(labels: any) {
return this.__promote([...this.labels(), ...labels]);
withLabels(labels: LabelArray): LabelIndexBase {
return this.__promote(
[...this.labels(), ...labels],
labels.every(Number.isInteger)
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dropLabel(label: any) {
dropLabel(label: LabelType): LabelIndexBase {
if (!Number.isInteger(label) || label < 0 || label > this.maxOffset - 1)
throw new RangeError("Invalid label.");
if (label === this.maxOffset - 1) {
return new IdentityInt32Index(label);
}
const labelArray = [...this.labels()];
labelArray.splice(labelArray.indexOf(label), 1);
return this.__promote(labelArray);
labelArray.splice(labelArray.indexOf(label as number), 1);
return this.__promote(labelArray, true);
}
}
class DenseInt32Index {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__id: any;
export class DenseInt32Index extends LabelIndexBase {
getLabel: (offset: number) => number | undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getLabel: any;
getOffset: (label: LabelType) => number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getLabels: any;
index: Int32Array;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getOffset: any;
minLabel: number;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getOffsets: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
index: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
minLabel: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
rindex: any;
rindex: Int32Array;
/*
DenseInt32Index indexes integer labels, and uses Int32Array typed arrays
@@ -194,17 +218,16 @@ class DenseInt32Index {
of the forward index labels must be known a priori (so that the index
array can be pre-allocated).
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(labels: any, labelRange = null) {
if (labels.constructor !== Int32Array) {
labels = new Int32Array(labels);
}
constructor(
labels: GenericLabelArray<number>,
labelRange?: [number, number]
) {
super(__getMemoId());
const int32Labels =
labels instanceof Int32Array ? labels : new Int32Array(labels);
if (!labelRange) {
// @ts-expect-error ts-migrate(2322) FIXME: Type 'number[]' is not assignable to type 'null'.
labelRange = extent(labels);
labelRange = extent(int32Labels);
}
// @ts-expect-error ts-migrate(2488) FIXME: Type 'null' must have a '[Symbol.iterator]()' meth... Remove this comment to see the full error message
const [minLabel, maxLabel] = labelRange;
const labelSpaceSize = maxLabel - minLabel + 1;
const index = new Int32Array(labelSpaceSize).fill(-1);
@@ -214,82 +237,70 @@ class DenseInt32Index {
}
this.minLabel = minLabel;
this.rindex = labels;
this.rindex = int32Labels;
this.index = index;
this.__id = __getMemoId();
this.__compile();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
__compile() {
const { minLabel, index, rindex } = this;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getOffset = function getOffset(l: any) {
if (!Number.isInteger(l)) return undefined;
const offset = index[l - minLabel];
return offset === -1 ? undefined : offset;
this.getOffset = function getOffset(label: LabelType) {
if (!Number.isInteger(label)) return -1;
const lblIdx: number = <number>label - minLabel;
if (lblIdx < 0 || lblIdx >= index.length) return -1;
const offset = index[lblIdx];
return offset;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getOffsets = function getOffsets(arr: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((i: any) => this.getOffset(i));
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getLabel = function getLabel(i: any) {
return Number.isInteger(i) ? rindex[i] : undefined;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getLabels = function getLabels(arr: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((i: any) => this.getLabel(i));
this.getLabel = function getLabel(offset: number) {
return Number.isInteger(offset) ? labels[offset] : undefined;
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
labels() {
labels(): LabelArray {
return this.rindex;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
size() {
size(): number {
return this.rindex.length;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
__promote(labelArray: any) {
/** @internal */
__promote(labelArray: LabelArray, allInts: boolean): LabelIndexBase {
/*
time/space decision - if we are going to use less than 10% of the
dense index space, switch to a KeyIndex (which is slower, but uses
less memory for sparse label spaces).
*/
const [minLabel, maxLabel] = extent(labelArray);
const labelSpaceSize = maxLabel - minLabel + 1;
const density = labelSpaceSize / this.rindex.length;
/* 0.1 is a magic number, that needs testing to optimize */
if (density < 0.1) {
return new KeyIndex(labelArray);
if (labelArray.length === 0) return new KeyIndex(Array.from(labelArray));
if (allInts) {
if (!(labelArray instanceof Int32Array)) {
labelArray = new Int32Array(labelArray as number[]);
}
const [minLabel, maxLabel] = extent(
labelArray as GenericLabelArray<number> // safe, as allInts is true
);
const labelSpaceSize = maxLabel - minLabel + 1;
const density = labelSpaceSize / this.rindex.length;
/* 0.1 is a magic number, that needs testing to optimize */
if (density < 0.1) {
return new KeyIndex(Array.from(labelArray));
}
return new DenseInt32Index(labelArray as GenericLabelArray<number>, [
minLabel,
maxLabel,
]);
}
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'number[]' is not assignable to p... Remove this comment to see the full error message
return new DenseInt32Index(labelArray, [minLabel, maxLabel]);
return new KeyIndex(Array.from(labelArray));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
subset(labels: any) {
subset(labels: LabelArray): LabelIndexBase {
/* validate subset */
for (let i = 0, l = labels.length; i < l; i += 1) {
const label = labels[i];
const offset = this.getOffset(label);
if (offset === undefined || offset === -1)
throw new RangeError(`unknown label: ${label}`);
const label = labels[i]; // if not a number, getOffset will error
const offset = this.getOffset(label as number);
if (offset === -1) throw new RangeError(`unknown label: ${label}`);
}
return this.__promote(labels);
return this.__promote(labels as GenericLabelArray<number>, true);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubset(offsets: any) {
isubset(offsets: OffsetArray): LabelIndexBase {
/* validate subset */
const { rindex } = this;
const maxOffset = rindex.length;
@@ -300,11 +311,10 @@ class DenseInt32Index {
throw new RangeError(`out of bounds offset: ${offset}`);
labels[i] = rindex[offset];
}
return this.__promote(labels);
return this.__promote(labels, true);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubsetMask(mask: any) {
isubsetMask(mask: Uint8Array | boolean[]): LabelIndexBase {
const { rindex } = this;
if (mask.length !== rindex.length)
throw new RangeError("mask has invalid length for index");
@@ -317,62 +327,55 @@ class DenseInt32Index {
}
}
labels = labels.slice(0, count);
return this.__promote(labels);
return this.__promote(labels, true);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabel(label: any) {
return this.__promote([...this.labels(), label]);
withLabel(label: LabelType): LabelIndexBase {
return this.__promote([...this.labels(), label], Number.isInteger(label));
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabels(labels: any) {
return this.__promote([...this.labels(), ...labels]);
withLabels(labels: LabelArray): LabelIndexBase {
return this.__promote(
[...this.labels(), ...labels],
labels.every(Number.isInteger)
);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dropLabel(label: any) {
dropLabel(label: LabelType): LabelIndexBase {
if (!Number.isInteger(label)) throw new RangeError("Invalid label.");
const labelArray = [...this.labels()];
labelArray.splice(labelArray.indexOf(label), 1);
return this.__promote(labelArray);
labelArray.splice(labelArray.indexOf(label as number), 1);
return this.__promote(
new Int32Array(labelArray as GenericLabelArray<number>),
true
);
}
}
class KeyIndex {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
__id: any;
export class KeyIndex extends LabelIndexBase {
getLabel: (offset: number) => LabelType | undefined;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getLabel: any;
getOffset: (label: LabelType) => number | -1;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getLabels: any;
index: Map<string | number, number>;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getOffset: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
getOffsets: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
index: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
rindex: any;
rindex: (string | number)[];
/*
KeyIndex indexes arbitrary JS primitive types, and uses a Map()
as its core data structure.
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
constructor(labels: any) {
const index = new Map();
constructor(labels: Array<string | number>) {
super(__getMemoId());
const index = new Map<string | number, number>();
if (labels === undefined) {
labels = [];
}
if (!Array.isArray(labels)) {
labels = Array.from(labels);
}
const rindex = labels;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
labels.forEach((v: any, i: any) => {
labels.forEach((v, i) => {
index.set(v, i);
});
@@ -383,48 +386,27 @@ class KeyIndex {
this.index = index;
this.rindex = rindex;
this.__id = __getMemoId();
this.__compile();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
__compile() {
const { index, rindex } = this;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getOffset = function getOffset(k: any) {
return index.get(k);
this.getOffset = function getOffset(label: LabelType) {
const offset = index.get(label);
if (offset === undefined) return -1;
return offset;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getOffsets = function getOffsets(arr: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((l: any) => this.getOffset(l));
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getLabel = function getLabel(i: any) {
return Number.isInteger(i) ? rindex[i] : undefined;
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
this.getLabels = function getLabels(arr: any) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return arr.map((i: any) => this.getLabel(i));
this.getLabel = function getLabel(offset: number) {
return Number.isInteger(offset) ? rindex[offset] : undefined;
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
labels() {
labels(): LabelArray {
return this.rindex;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
size() {
size(): number {
return this.rindex.length;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
subset(labels: any) {
subset(labels: (string | number)[]): LabelIndexBase {
/* validate subset */
for (let i = 0, l = labels.length; i < l; i += 1) {
const label = labels[i];
@@ -437,8 +419,7 @@ class KeyIndex {
return new KeyIndex(labels);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubset(offsets: any) {
isubset(offsets: OffsetArray): LabelIndexBase {
const { rindex } = this;
const maxOffset = rindex.length;
const labels = new Array(offsets.length);
@@ -452,8 +433,7 @@ class KeyIndex {
return new KeyIndex(labels);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
isubsetMask(mask: any) {
isubsetMask(mask: Uint8Array | boolean[]): LabelIndexBase {
const { rindex } = this;
if (mask.length !== rindex.length)
throw new RangeError("mask has invalid length for index");
@@ -469,18 +449,15 @@ class KeyIndex {
return new KeyIndex(labels);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabel(label: any) {
withLabel(label: LabelType): LabelIndexBase {
return new KeyIndex([...this.rindex, label]);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
withLabels(labels: any) {
withLabels(labels: LabelArray): LabelIndexBase {
return new KeyIndex([...this.rindex, ...labels]);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
dropLabel(label: any) {
dropLabel(label: LabelType): LabelIndexBase {
const idx = this.rindex.indexOf(label);
const labelArray = [...this.rindex];
labelArray.splice(idx, 1);
@@ -488,14 +465,31 @@ class KeyIndex {
}
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
function isLabelIndex(i: any) {
export type LabelIndex = LabelIndexBase;
export function isLabelIndex(i: unknown): i is LabelIndex {
return (
i instanceof LabelIndexBase ||
i instanceof IdentityInt32Index ||
i instanceof DenseInt32Index ||
i instanceof KeyIndex
);
}
export { DenseInt32Index, IdentityInt32Index, KeyIndex, isLabelIndex };
/** @internal */
function extent(tarr: GenericLabelArray<number>): [number, number] {
let min = 0x7fffffff;
let max = ~min; // eslint-disable-line no-bitwise -- Establishes 0 of same size
for (let i = 0, l = tarr.length; i < l; i += 1) {
const v = tarr[i];
if (v < min) {
min = v;
}
if (v > max) {
max = v;
}
}
return [min, max];
}
/* eslint-enable max-classes-per-file -- enable*/
+40 -40
View File
@@ -5,55 +5,56 @@ TODO / XXX: for scalar/continuous data, this uses a naive method
of computing quantiles. Would be good to switch from sort to
partition at some point.
*/
import {
AnyArray,
GenericArrayConstructor,
} from "../../common/types/arraytypes";
import { ContinuousColumnSummary, CategoricalColumnSummary } from "./types";
import quantile from "../quantile";
import { sortArray } from "../typedCrossfilter/sort";
// [ 0, 0.01, 0.02, ..., 1.0]
// @ts-expect-error ts-migrate(6133) FIXME: 'v' is declared but its value is never read.
const centileNames = new Array(101).fill(0).map((v, idx) => idx / 100);
const centileNames = new Array(101).fill(0).map((_v, idx) => idx / 100);
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function summarizeContinuous(col: any) {
let min;
let max;
export function summarizeContinuous(col: AnyArray): ContinuousColumnSummary {
let nan = 0;
let pinf = 0;
let ninf = 0;
let percentiles;
if (col) {
// -Inf < finite < Inf < NaN
const sortedCol = sortArray(new col.constructor(col));
// count non-finites, which are at each end of sorted data
for (let i = sortedCol.length - 1; i >= 0; i -= 1) {
if (!Number.isNaN(sortedCol[i])) {
nan = sortedCol.length - i - 1;
break;
}
}
for (let i = 0, l = sortedCol.length; i < l; i += 1) {
if (sortedCol[i] !== Number.NEGATIVE_INFINITY) {
ninf = i;
break;
}
}
for (let i = sortedCol.length - nan - 1; i >= 0; i -= 1) {
if (sortedCol[i] !== Number.POSITIVE_INFINITY) {
pinf = sortedCol.length - i - nan - 1;
break;
}
}
// -Inf < finite < Inf < NaN
const sortedCol = sortArray(
new (col.constructor as GenericArrayConstructor<typeof col>)(col)
);
// compute percentiles on finite data ONLY
const sortedColFiniteOnly = sortedCol.slice(
ninf,
sortedCol.length - nan - pinf
);
percentiles = quantile(centileNames, sortedColFiniteOnly, true);
min = percentiles[0];
max = percentiles[100];
// count non-finites, which are at each end of sorted data
for (let i = sortedCol.length - 1; i >= 0; i -= 1) {
if (!Number.isNaN(sortedCol[i])) {
nan = sortedCol.length - i - 1;
break;
}
}
for (let i = 0, l = sortedCol.length; i < l; i += 1) {
if (sortedCol[i] !== Number.NEGATIVE_INFINITY) {
ninf = i;
break;
}
}
for (let i = sortedCol.length - nan - 1; i >= 0; i -= 1) {
if (sortedCol[i] !== Number.POSITIVE_INFINITY) {
pinf = sortedCol.length - i - nan - 1;
break;
}
}
// compute percentiles on finite data ONLY
const sortedColFiniteOnly = sortedCol.slice(
ninf,
sortedCol.length - nan - pinf
);
const percentiles = quantile(centileNames, sortedColFiniteOnly, true);
const min = percentiles[0];
const max = percentiles[100];
return {
categorical: false,
min,
@@ -65,8 +66,7 @@ export function summarizeContinuous(col: any) {
};
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function summarizeCategorical(col: any) {
export function summarizeCategorical(col: AnyArray): CategoricalColumnSummary {
const categoryCounts = new Map();
if (col) {
for (let r = 0, l = col.length; r < l; r += 1) {
+139
View File
@@ -0,0 +1,139 @@
import { TypedArray } from "../../common/types/arraytypes";
export type LabelType = number | string;
type CommonProps<A, B> = {
[K in keyof A & keyof B]: A[K] | B[K];
};
export type GenericLabelArray<T> = CommonProps<Array<T>, Int32Array>;
export type LabelArray = GenericLabelArray<number | string>;
export type OffsetType = number;
export type OffsetArray =
| Int8Array
| Uint8Array
| Int16Array
| Uint16Array
| Int32Array
| Uint32Array
| number[];
export type ContinuousColumnSummary = {
categorical: false;
min: number;
max: number;
nan: number;
pinf: number;
ninf: number;
percentiles: number[];
};
export type CategoricalColumnSummary = {
categorical: true;
categories: (number | string | boolean)[];
categoryCounts: Map<number | string | boolean, number>;
numCategories: number;
};
export type ColumnSummary = ContinuousColumnSummary | CategoricalColumnSummary;
export type ContinuousHistogram = number[];
export type ContinuousHistogramBy = Map<DataframeValue, ContinuousHistogram>;
export type CategoricalHistogram = Map<DataframeValue, number>;
export type CategoricalHistogramBy = Map<DataframeValue, CategoricalHistogram>;
export type DataframeValue = number | string | boolean;
export type DataframeValueArray = DataframeValue[] | TypedArray;
export type DataframeColumnGetter = (
label: LabelType
) => DataframeValue | undefined;
/**
* Interface representing a Dataframe column. Eg, returned by
* Dataframe.col().
*/
export interface DataframeColumn extends DataframeColumnGetter {
/**
* __id is unique per Dataframe and DataframeColumn, and is used as a memoization key.
*/
readonly __id: string;
/**
* Boolean indicating if the underlying data supports continuous operations, eg,
* summarizeContinuous.
*/
isContinuous: boolean;
/**
* Return underlying column data as an array-like object.
*/
asArray: () => DataframeValueArray;
/**
* Continuous data summary. Will throw if !isContinuous.
*/
summarizeContinuous: () => ContinuousColumnSummary;
/**
* Categorical data summary.
*/
summarizeCategorical: () => CategoricalColumnSummary;
/**
* Continuous bin/histogram. Will throw if !isContinuous.
* @param bins - array of bin boundary fractions, in range [0., 1.]
* @param domain - data domain [min, max]
*/
histogramContinuous: (
bins: number,
domain: [number, number]
) => ContinuousHistogram;
/**
* Continuous bin/histogram, grouped by another categorical column. Will throw if !isContinuous.
* @param bins - array of bin boundary fractions, in range [0., 1.]
* @param domain - data domain [min, max]
* @param by - group by categorical column
*/
histogramContinuousBy: (
bins: number,
domain: [number, number],
by: DataframeColumn
) => ContinuousHistogramBy;
/**
* Categorical bin/histogram.
*/
histogramCategorical: () => CategoricalHistogram;
/**
* Categorical bin/histogram grouped by another column.
* @param by - group by categorical column
*/
histogramCategoricalBy: (by: DataframeColumn) => CategoricalHistogramBy;
/**
* Return true if the column contains the row label.
*/
has: (rlabel: LabelType) => boolean;
/**
* Return true if the column contains the row offset. Identical to
* (offset >= 0 && offset < dataframe.length)
*/
ihas: (offset: OffsetType) => boolean;
/**
* Return index of the value, as a _label_. Returns undefined if
* not present. *NOTE*: unlike Array.indexOf, does not return an
* offset.
*/
indexOf: (value: DataframeValue) => LabelType | undefined;
/**
* Return the value at the given offset, or undefined if not present.
*/
iget: (offset: OffsetType) => DataframeValue | undefined;
}
+20 -16
View File
@@ -2,19 +2,19 @@
Private utility code for dataframe
*/
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function callOnceLazy(f: any) {
export function callOnceLazy<
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- a legitimate use of any.
T extends (...args: any[]) => any = (...args: any[]) => any
>(fn: T): (...args: Parameters<T>) => ReturnType<T> {
/*
call function once, and save the result, regardless of arguments (this is not
the same as typical memoization).
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
let value: any;
let value: ReturnType<T>;
let calledOnce = false;
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const result = function result(...args: any[]) {
const result = function result(...args: Parameters<T>): ReturnType<T> {
if (!calledOnce) {
value = f(...args);
value = fn(...args);
calledOnce = true;
}
return value;
@@ -22,8 +22,14 @@ export function callOnceLazy(f: any) {
return result;
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function memoize(fn: any, hashFn: any, maxResultsCached = -1) {
export function memoize<
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- a legitimate use of any.
T extends (...args: any[]) => any = (...args: any[]) => any
>(
fn: T,
hashFn: (...args: Parameters<T>) => string,
maxResultsCached = -1
): (...args: Parameters<T>) => ReturnType<T> {
/*
function memoization, with user-provided hash. hashFn must return a
key which will be unique as a Map key (ie, obeys "sameValueZero" algorithm
@@ -31,8 +37,7 @@ export function memoize(fn: any, hashFn: any, maxResultsCached = -1) {
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map#Key_equality
*/
const cache = new Map();
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const wrap = function wrap(...args: any[]) {
const wrap = function wrap(...args: Parameters<T>): ReturnType<T> {
const key = hashFn(...args);
if (cache.has(key)) {
return cache.get(key);
@@ -57,12 +62,11 @@ export function memoize(fn: any, hashFn: any, maxResultsCached = -1) {
}
/**
memoization helpers - just a global counter.
**/
*memoization helpers - just a global counter.
*/
let __DataframeMemoId__ = 0;
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function __getMemoId() {
export function __getMemoId(): string {
const id = __DataframeMemoId__;
__DataframeMemoId__ += 1;
return id;
return id.toString();
}
+14 -5
View File
@@ -12,17 +12,26 @@ Arguments:
*/
import { NumberArray, TypedArrayConstructor } from "../common/types/arraytypes";
import { sortArray } from "./typedCrossfilter/sort";
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'quantArr' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export default function quantile(quantArr, tarr, sorted = false) {
export default function quantile(
quantArr: number[],
tarr: NumberArray,
sorted = false
): number[] {
/*
start with the naive (sort) implementation. Later, use a faster partition
*/
const arr = sorted ? tarr : sortArray(new tarr.constructor(tarr)); // copy
if (tarr.length === 0) {
return new Array(quantArr.length).fill(0);
}
const Ctor: TypedArrayConstructor = tarr.constructor as TypedArrayConstructor;
const arr = sorted ? tarr : sortArray(new Ctor(tarr)); // copy
const len = arr.length;
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'q' implicitly has an 'any' type.
return quantArr.map((q) => {
if (q === 1) {
return arr[len - 1];
@@ -4,6 +4,7 @@ See also reducers/annotations.js
*/
import { Schema } from "../../common/types/schema";
import { Dataframe, LabelType } from "../dataframe";
/*
There are a number of state constraints assumed throughout the
@@ -57,9 +58,12 @@ export function isUserAnnotation(annoMatrix, name) {
return _isUserAnnotation(annoMatrix.schema, name);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'df' implicitly has an 'any' type.
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
export function allHaveLabelByMask(df, colName, label, mask) {
export function allHaveLabelByMask(
df: Dataframe,
colName: LabelType,
label: string,
mask: Uint8Array
): boolean {
// return true if all rows as indicated by mask have the colname set to label.
// False if not.
const col = df.col(colName);
+12 -9
View File
@@ -7,6 +7,7 @@ import memoize from "memoize-one";
import * as globals from "../../globals";
import parseRGB from "../parseRGB";
import { range } from "../range";
import { Dataframe, LabelType } from "../dataframe";
/*
given a color mode & accessor, generate an annoMatrix query that will
@@ -95,18 +96,19 @@ Returns:
}
*/
function _createColorTable(
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colorMode: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colorByAccessor: any,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
colorByData: any,
colorMode: string | null,
colorByAccessor: LabelType | null,
colorByData: Dataframe | null,
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
schema: any,
userColors = null
) {
if (colorMode === null || colorByData === null)
return defaultColors(schema.dataframe.nObs);
switch (colorMode) {
case "color by categorical metadata": {
if (colorByAccessor === null) return defaultColors(schema.dataframe.nObs);
const data = colorByData.col(colorByAccessor).asArray();
// @ts-expect-error ts-migrate(2531) FIXME: Object is possibly 'null'.
if (userColors && colorByAccessor in userColors) {
@@ -115,18 +117,19 @@ function _createColorTable(
return createColorsByCategoricalMetadata(data, colorByAccessor, schema);
}
case "color by continuous metadata": {
if (colorByAccessor === null) return defaultColors(schema.dataframe.nObs);
const col = colorByData.col(colorByAccessor);
const { min, max } = col.summarize();
const { min, max } = col.summarizeContinuous();
return createColorsByContinuousMetadata(col.asArray(), min, max);
}
case "color by expression": {
const col = colorByData.icol(0);
const { min, max } = col.summarize();
const { min, max } = col.summarizeContinuous();
return createColorsByContinuousMetadata(col.asArray(), min, max);
}
case "color by geneset mean expression": {
const col = colorByData.icol(0);
const { min, max } = col.summarize();
const { min, max } = col.summarizeContinuous();
return createColorsByContinuousMetadata(col.asArray(), min, max);
}
default: {
+21 -27
View File
@@ -1,6 +1,10 @@
import { flatbuffers } from "flatbuffers";
import { NetEncoding } from "./matrix_generated";
import { isTypedArray, isFloatTypedArray } from "../../common/types/arraytypes";
import {
TypedArray,
isTypedArray,
isFloatTypedArray,
} from "../../common/types/arraytypes";
import {
Dataframe,
IdentityInt32Index,
@@ -94,14 +98,13 @@ function encodeTypedArray(builder: any, uType: any, uData: any) {
return builder.endObject();
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function encodeMatrixFBS(df: any) {
export function encodeMatrixFBS(df: Dataframe): Uint8Array {
/*
encode the dataframe as an FBS Matrix
*/
/* row indexing not supported currently */
if (df.rowIndex.constructor !== IdentityInt32Index) {
if (!(df.rowIndex instanceof IdentityInt32Index)) {
throw new Error("FBS does not support row index encoding at this time");
}
@@ -115,11 +118,9 @@ export function encodeMatrixFBS(df: any) {
let encColumns;
if (shape[0] > 0 && shape[1] > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const columns = df.columns().map((col: any) => col.asArray());
const columns = df.columns().map((col) => col.asArray());
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const cols = columns.map((carr: any) => {
const cols = columns.map((carr) => {
let uType;
let tarr;
if (isTypedArray(carr)) {
@@ -179,8 +180,7 @@ export function encodeMatrixFBS(df: any) {
return builder.asUint8Array();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
function promoteTypedArray(o: any) {
function promoteTypedArray(o: TypedArray) {
/*
Decide what internal data type to use for the data returned from
the server.
@@ -212,8 +212,9 @@ function promoteTypedArray(o: any) {
return new TypedArrayCtor(o);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any -- - FIXME: disabled temporarily on migrate to TS.
export function matrixFBSToDataframe(arrayBuffers: any) {
export function matrixFBSToDataframe(
arrayBuffers: ArrayBuffer | ArrayBuffer[]
): Dataframe {
/*
Convert array of Matrix FBS to a Dataframe.
@@ -229,37 +230,30 @@ export function matrixFBSToDataframe(arrayBuffers: any) {
arrayBuffers = [arrayBuffers];
}
if (arrayBuffers.length === 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
return (Dataframe as any).Dataframe.empty();
return Dataframe.empty();
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
const fbs = arrayBuffers.map((ab: any) => decodeMatrixFBS(ab, true)); // leave in place
const fbs = arrayBuffers.map((ab) => decodeMatrixFBS(ab, true)); // leave in place
/* check that all FBS have same row dimensionality */
const { nRows } = fbs[0];
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
fbs.forEach((b: any) => {
fbs.forEach((b) => {
if (b.nRows !== nRows)
throw new Error("FBS with inconsistent dimensionality");
});
const columns = fbs // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.map((fb: any) =>
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
fb.columns.map((c: any) => {
const columns = fbs
.map((fb) =>
fb.columns.map((c) => {
if (isFloatTypedArray(c) || Array.isArray(c)) return c;
return promoteTypedArray(c);
})
)
.flat();
// colIdx may be TypedArray or Array
const colIdx = fbs // eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
.map((b: any) =>
Array.isArray(b.colIdx) ? b.colIdx : Array.from(b.colIdx)
)
const colIdx = fbs
.map((b) => (Array.isArray(b.colIdx) ? b.colIdx : Array.from(b.colIdx)))
.flat();
const nCols = columns.length;
// @ts-expect-error ts-migrate(2345) FIXME: Argument of type 'KeyIndex' is not assignable to p... Remove this comment to see the full error message
const df = new Dataframe([nRows, nCols], columns, null, new KeyIndex(colIdx));
return df;
}
@@ -37,6 +37,7 @@ Views can be interogated for their type with the following:
import { clip, isubsetMask, isubset } from "../../annoMatrix";
import { memoize } from "../dataframe/util";
import { Dataframe, LabelIndex } from "../dataframe";
// @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.
@@ -101,7 +102,7 @@ export function _userResetSubsetAnnoMatrix(annoMatrix) {
// @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 _setEmbeddingSubset(annoMatrix, embeddingDf) {
export function _setEmbeddingSubset(annoMatrix, embeddingDf: Dataframe) {
/*
Set the embedding subset view. Only create a subset view for the embedding
when it is needed, ie, there are NaN values in the embeddings.
@@ -141,8 +142,10 @@ export function _setEmbeddingSubset(annoMatrix, embeddingDf) {
return annoMatrix;
}
// @ts-expect-error ts-migrate(6133) FIXME: 'baseRowIndex' is declared but its value is never ... Remove this comment to see the full error message
function _getEmbeddingRowOffsets(baseRowIndex, embeddingDf) {
function _getEmbeddingRowOffsets(
_baseRowIndex: LabelIndex,
embeddingDf: Dataframe
) {
/*
given a dataframe containing an embedding:
- if the embedding contains no NaN coordinates, return null
@@ -167,17 +170,16 @@ function _getEmbeddingRowOffsets(baseRowIndex, embeddingDf) {
return offsets.subarray(0, numOffsets);
}
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'embeddingDf' implicitly has an 'any' ty... 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 _getDiscreteCellEmbeddingRowIndex(embeddingDf) {
export function _getDiscreteCellEmbeddingRowIndex(
embeddingDf: Dataframe
): LabelIndex {
const idx = _getEmbeddingRowOffsets(embeddingDf.rowIndex, embeddingDf);
if (idx === null) return embeddingDf.rowIndex;
return embeddingDf.rowIndex.isubset(idx);
}
export const getDiscreteCellEmbeddingRowIndex = memoize(
_getDiscreteCellEmbeddingRowIndex,
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'df' implicitly has an 'any' type.
(df) => df.__id
(df: Dataframe) => df.__id
);
// @ts-expect-error ts-migrate(7006) FIXME: Parameter 'annoMatrix' implicitly has an 'any' typ... Remove this comment to see the full error message
@@ -12,9 +12,8 @@ import { makeSortIndex } from "./util";
import { isAnyArray } from "../../common/types/arraytypes";
class NotImplementedError extends Error {
// eslint-disable-next-line @typescript-eslint/no-explicit-any --- FIXME: disabled temporarily on migrate to TS.
constructor(...params: any[]) {
super(...params);
constructor(msg: string) {
super(msg);
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
@@ -63,8 +62,7 @@ export default class ImmutableTypedCrossfilter {
Object.preventExtensions(this);
}
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types --- FIXME: disabled temporarily on migrate to TS.
size() {
size(): number {
return this.data.length;
}