mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-21 10:08:12 +08:00
* initial dataframe commit * initial dataframe port of core app * rename variables for clarity * remove unused import * comment out unused code * fix array handling bug in crossfilter dimension creation * allow creation of empty dataframes * handle non-existent columns * handle non-existent columns * revise tests for new dataframe * comments for clarity * comments for clarity * generate bulk add placeholder with real gene names * fix bug in gene name adding * more dataframe unit tests * fix bug - subset from current world, not universe * put cut and pasted code into a single function * improve caching of crossfilter * remove cascading update bug from graph * more performance work * improve state handling for scatterplot * performance optimization of critical path * add column summarization * dataframe utils * add callOnceLazy * fix tests * minor updates found during review * fix misspelling * remove RESTv02 from function names * comment cleanup * cut/icut col parameter defaults to null * break up large test * improve tests and comments on dataframe at/has functions * add Dataframe withCol/dropCol * expression varData now stored in a dataframe * dead code cleanup * use dataframe.summarize() * test cases for Dataframe.col.summarize * update test cases for new dataframe summarize * improve naming * use new hasCol API * add comments * add more Dataframe.withCol tests * add ability to specify row index in cut operation * retire subsetVarData function * correctly handle expression subsetting * lint and improve comments * rename cut to subset * changes based on PR review
181 lines
5.3 KiB
JavaScript
181 lines
5.3 KiB
JavaScript
import _ from "lodash";
|
|
import * as Universe from "../../../src/util/stateManager/universe";
|
|
import * as World from "../../../src/util/stateManager/world";
|
|
import * as Dataframe from "../../../src/util/dataframe";
|
|
import Crossfilter from "../../../src/util/typedCrossfilter";
|
|
import * as REST from "./sampleResponses";
|
|
import {
|
|
obsAnnoDimensionName,
|
|
layoutDimensionName
|
|
} from "../../../src/util/nameCreators";
|
|
|
|
/*
|
|
Helper - creates universe, world, corssfilter and dimensionMap from
|
|
the default REST test response.
|
|
*/
|
|
const defaultBigBang = () => {
|
|
/* create unverse, world, crossfilter and dimensionMap */
|
|
/* create universe */
|
|
const universe = Universe.createUniverseFromResponse(
|
|
REST.config,
|
|
REST.schema,
|
|
REST.annotationsObs,
|
|
REST.annotationsVar,
|
|
REST.layoutObs
|
|
);
|
|
/* create world */
|
|
const world = World.createWorldFromEntireUniverse(universe);
|
|
/* create crossfilter */
|
|
const crossfilter = Crossfilter(world.obsAnnotations);
|
|
/* create dimension map */
|
|
const dimensionMap = World.createObsDimensionMap(crossfilter, world);
|
|
|
|
return {
|
|
universe,
|
|
world,
|
|
crossfilter,
|
|
dimensionMap
|
|
};
|
|
};
|
|
|
|
describe("createWorldFromEntireUniverse", () => {
|
|
test("create from REST sample", () => {
|
|
const universe = Universe.createUniverseFromResponse(
|
|
REST.config,
|
|
REST.schema,
|
|
REST.annotationsObs,
|
|
REST.annotationsVar,
|
|
REST.layoutObs
|
|
);
|
|
expect(universe).toBeDefined();
|
|
|
|
const world = World.createWorldFromEntireUniverse(universe);
|
|
expect(world).toBeDefined();
|
|
|
|
expect(world).toMatchObject(
|
|
expect.objectContaining({
|
|
nObs: universe.nObs,
|
|
nVar: universe.nVar,
|
|
schema: universe.schema,
|
|
obsAnnotations: universe.obsAnnotations,
|
|
varAnnotations: universe.varAnnotations,
|
|
obsLayout: universe.obsLayout,
|
|
varData: expect.any(Dataframe.Dataframe)
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("createWorldFromCurrentSelection", () => {
|
|
test("create from REST sample", () => {
|
|
const {
|
|
universe,
|
|
world: originalWorld,
|
|
crossfilter,
|
|
dimensionMap
|
|
} = defaultBigBang();
|
|
|
|
/* mock a selection */
|
|
dimensionMap[obsAnnoDimensionName("field1")].filterRange([0, 5]);
|
|
dimensionMap[obsAnnoDimensionName("field3")].filterExact(false);
|
|
|
|
/* create the world from the selection */
|
|
const world = World.createWorldFromCurrentSelection(
|
|
universe,
|
|
originalWorld,
|
|
crossfilter
|
|
);
|
|
expect(world).toBeDefined();
|
|
expect(world.nObs).toEqual(crossfilter.countFiltered());
|
|
|
|
/*
|
|
calculate expected values and match against result
|
|
*/
|
|
|
|
/* matchFilter must match the dimension filters above */
|
|
const matchFilter = (df, row) => {
|
|
const field1 = df.at(row, "field1");
|
|
const field3 = df.at(row, "field3");
|
|
return field1 >= 0 && field1 < 5 && !field3;
|
|
};
|
|
const matchingIndices = _()
|
|
.range(universe.nObs)
|
|
.filter(idx => matchFilter(universe.obsAnnotations, idx))
|
|
.value();
|
|
|
|
expect(world).toMatchObject(
|
|
expect.objectContaining({
|
|
nObs: matchingIndices.length,
|
|
nVar: universe.nVar,
|
|
schema: universe.schema,
|
|
obsAnnotations: expect.any(Dataframe.Dataframe),
|
|
varAnnotations: universe.varAnnotations,
|
|
obsLayout: expect.any(Dataframe.Dataframe),
|
|
varData: expect.any(Dataframe.Dataframe)
|
|
})
|
|
);
|
|
|
|
expect(world.obsAnnotations.rowIndex.keys()).toEqual(
|
|
new Int32Array(matchingIndices)
|
|
);
|
|
expect(world.obsAnnotations.colIndex.keys()).toEqual(
|
|
universe.obsAnnotations.colIndex.keys()
|
|
);
|
|
expect(world.obsLayout.rowIndex.keys()).toEqual(
|
|
new Int32Array(matchingIndices)
|
|
);
|
|
expect(world.obsLayout.colIndex.keys()).toEqual(["X", "Y"]);
|
|
});
|
|
});
|
|
|
|
describe("createObsDimensionMap", () => {
|
|
test("when universe eq world", () => {
|
|
/*
|
|
check for:
|
|
- creates a dimension for all obsAnnotations, PLUS X/Y layout
|
|
- check that dimension typing is sane
|
|
*/
|
|
|
|
const { dimensionMap } = defaultBigBang();
|
|
const annotationNames = _.map(
|
|
REST.schema.schema.annotations.obs,
|
|
c => c.name
|
|
);
|
|
const schemaByObsName = _.keyBy(REST.schema.schema.annotations.obs, "name");
|
|
expect(dimensionMap).toBeDefined();
|
|
annotationNames.forEach(name => {
|
|
const dim = dimensionMap[obsAnnoDimensionName(name)];
|
|
if (name === "name") {
|
|
expect(dim).toBeUndefined();
|
|
} else {
|
|
const { type } = schemaByObsName[name];
|
|
if (type === "string" || type === "boolean" || type === "categorical") {
|
|
expect(dim).toBeInstanceOf(Crossfilter.EnumDimension);
|
|
} else {
|
|
expect(dim).toBeInstanceOf(Crossfilter.ScalarDimension);
|
|
}
|
|
}
|
|
});
|
|
expect(dimensionMap[layoutDimensionName("XY")]).toBeInstanceOf(
|
|
Crossfilter.SpatialDimension
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("createVarDataDimension", () => {
|
|
/* create default universe */
|
|
const { world, crossfilter } = defaultBigBang();
|
|
world.varData = world.varData.withCol(
|
|
"GENE",
|
|
Float32Array.from(_.range(world.nObs))
|
|
);
|
|
const result = World.createVarDataDimension(world, crossfilter, "GENE");
|
|
expect(result).toBeInstanceOf(Crossfilter.ScalarDimension);
|
|
});
|
|
|
|
describe("worldEqUniverse", () => {
|
|
const { universe, world } = defaultBigBang();
|
|
const result = World.worldEqUniverse(world, universe);
|
|
expect(result).toBe(true);
|
|
});
|