mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-10-04 19:48:11 +08:00
Redux refactor (#1571)
* refactor categorical controls state * lint * fix race condition in tests * fix typo * add missing update on subset * remove obsolete code * update jest and puppeteer major version; update all minors * update when label changes * remove lint from tests; increase timeouts in e2e tests * initial refactoring to new async annomatrix * refine error handling * fix bad merge * add continuous legend * lint * fix memoization in color table creators * partial implementation of user defined annotations * add new annotations action creator file * first pass at user annotations * additional user annotation bug fixes * user annotation auto-save * unit test cleanup * lint * refactor into multiple files * cleanup * add column GC * fix several bugs in user annotations * remove debug code * no anonymous functions * undo redo cleanup * file cleanup * scatterplot * performance * cleanup * remove old code * render in parallel with load * fix race condition * simply graph rendering * render throttle DRY * fix category label order * fix typo in e2e test setup * re-fix the e2e test setup * be more tolerant of races * anno matrix unit tests * temp disable reembedding * pilot port continuous histo to react-async * name change * lint * fix repaint bug * typo fix * update snap to match new ids * world/universe name cleanup * move annoMatrix to src dir * use private underscore naming convention * fix corner case in all selected * name cleanup * add layout control * init edge case * lint * port scatterplot * fix label indexing bug and improve tests * port category to react-async * fix user annotation labelling while subset * select all of prev layout on layout switch * fix race with crossfilter update * prettier lint * fix misleading comment * fix url composition in loader * first pass at crossfilter tests * lint * lint * fix typo * improved error handling for network errors * fix memoization bug * add memo * refactor for performnce * add missing single-value handling in select exact parser * small bugs discovered by tests * lint * additional crossfilter unit tests * remove extraneous comment * add support for automatic category determination * lint * fix render bug in category * take advantage of schema categories guarantee * lint * do not clear history when resetting * enhanced annomatrix gc * lint * finish renaming to follow conventions; fix clone race bug * lint * add priority based loading to improve initial data load UX * crossfilter cache perf * perf tuning * remove timers * documentation * PR review changes * PR review changes * more PR review edits * improve clarity of comment * more PR review fixes * port centroidLabels to use react-async * remove dead code * pr review updates * oops, remove logging
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
/* eslint-disable max-classes-per-file -- Classes are interrelated*/
|
||||
|
||||
/*
|
||||
Views on the annomatrix. all API here is defined in viewCreators.js and annoMatrix.js.
|
||||
*/
|
||||
import clip from "../util/clip";
|
||||
import AnnoMatrix from "./annoMatrix";
|
||||
import { _whereCacheCreate } from "./whereCache";
|
||||
import { _isContinuousType, _getColumnSchema } from "./schema";
|
||||
|
||||
class AnnoMatrixView extends AnnoMatrix {
|
||||
constructor(viewOf, rowIndex = null) {
|
||||
const nObs = rowIndex ? rowIndex.size() : viewOf.nObs;
|
||||
super(viewOf.schema, nObs, viewOf.nVar, rowIndex || viewOf.rowIndex);
|
||||
this.viewOf = viewOf;
|
||||
this.isView = true;
|
||||
}
|
||||
|
||||
addObsAnnoCategory(col, category) {
|
||||
const o = this._clone();
|
||||
o.viewOf = this.viewOf.addObsAnnoCategory(col, category);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
async removeObsAnnoCategory(col, category, unassignedCategory) {
|
||||
const o = this._clone();
|
||||
o.viewOf = await this.viewOf.removeObsAnnoCategory(
|
||||
col,
|
||||
category,
|
||||
unassignedCategory
|
||||
);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
dropObsColumn(col) {
|
||||
const o = this._clone();
|
||||
o.viewOf = this.viewOf.dropObsColumn(col);
|
||||
o._cache.obs = this._cache.obs.dropCol(col);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
addObsColumn(colSchema, Ctor, value) {
|
||||
const o = this._clone();
|
||||
o.viewOf = this.viewOf.addObsColumn(colSchema, Ctor, value);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
renameObsColumn(oldCol, newCol) {
|
||||
const o = this._clone();
|
||||
o.viewOf = this.viewOf.renameObsColumn(oldCol, newCol);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
async setObsColumnValues(col, rowLabels, value) {
|
||||
const o = this._clone();
|
||||
o.viewOf = await this.viewOf.setObsColumnValues(col, rowLabels, value);
|
||||
o._cache.obs = this._cache.obs.dropCol(col);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
|
||||
async resetObsColumnValues(col, oldValue, newValue) {
|
||||
const o = this._clone();
|
||||
o.viewOf = await this.viewOf.resetObsColumnValues(col, oldValue, newValue);
|
||||
o._cache.obs = this._cache.obs.dropCol(col);
|
||||
o.schema = o.viewOf.schema;
|
||||
return o;
|
||||
}
|
||||
}
|
||||
|
||||
class AnnoMatrixMapView extends AnnoMatrixView {
|
||||
/*
|
||||
A view which knows how to transform its data.
|
||||
*/
|
||||
constructor(viewOf, mapFn) {
|
||||
super(viewOf);
|
||||
this.mapFn = mapFn;
|
||||
}
|
||||
|
||||
async _doLoad(field, query) {
|
||||
const df = await this.viewOf._fetch(field, query);
|
||||
const dfMapped = df.mapColumns((colData, colIdx) => {
|
||||
const colLabel = df.colIndex.getLabel(colIdx);
|
||||
const colSchema = _getColumnSchema(this.schema, field, colLabel);
|
||||
return this.mapFn(field, colLabel, colSchema, colData, df);
|
||||
});
|
||||
const whereCacheUpdate = _whereCacheCreate(
|
||||
field,
|
||||
query,
|
||||
dfMapped.colIndex.labels()
|
||||
);
|
||||
return [whereCacheUpdate, dfMapped];
|
||||
}
|
||||
}
|
||||
|
||||
export class AnnoMatrixClipView extends AnnoMatrixMapView {
|
||||
/*
|
||||
A view which is a clipped transformation of its parent
|
||||
*/
|
||||
constructor(viewOf, qmin, qmax) {
|
||||
super(viewOf, (field, colLabel, colSchema, colData, df) =>
|
||||
_clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax)
|
||||
);
|
||||
this.isClipped = true;
|
||||
this.clipRange = [qmin, qmax];
|
||||
Object.seal(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class AnnoMatrixRowSubsetView extends AnnoMatrixView {
|
||||
/*
|
||||
A view which is a subset of total rows.
|
||||
*/
|
||||
constructor(viewOf, rowIndex) {
|
||||
super(viewOf, rowIndex);
|
||||
Object.seal(this);
|
||||
}
|
||||
|
||||
async _doLoad(field, query) {
|
||||
const df = await this.viewOf._fetch(field, query);
|
||||
|
||||
// don't try to row-subset the var dimension.
|
||||
if (field === "var") {
|
||||
return [null, df];
|
||||
}
|
||||
|
||||
const dfSubset = df.subset(null, null, this.rowIndex);
|
||||
const whereCacheUpdate = _whereCacheCreate(
|
||||
field,
|
||||
query,
|
||||
dfSubset.colIndex.labels()
|
||||
);
|
||||
return [whereCacheUpdate, dfSubset];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Utility functions below
|
||||
*/
|
||||
|
||||
function _clipAnnoMatrix(field, colLabel, colSchema, colData, df, qmin, qmax) {
|
||||
/* only clip obs and var scalar columns */
|
||||
if (field !== "obs" && field !== "X") return colData;
|
||||
if (!_isContinuousType(colSchema)) return colData;
|
||||
if (qmin < 0) qmin = 0;
|
||||
if (qmax > 1) qmax = 1;
|
||||
if (qmin === 0 && qmax === 1) return colData;
|
||||
|
||||
const quantiles = df.col(colLabel).summarize().percentiles;
|
||||
const lower = quantiles[100 * qmin];
|
||||
const upper = quantiles[100 * qmax];
|
||||
const clippedData = clip(colData.slice(), lower, upper, Number.NaN);
|
||||
return clippedData;
|
||||
}
|
||||
|
||||
/* eslint-enable max-classes-per-file -- enable*/
|
||||
Reference in New Issue
Block a user