mirror of
https://github.com/chanzuckerberg/cellxgene.git
synced 2026-09-28 06:48:11 +08:00
* Add numeric inputs for percentiles * Define initial values for percentile cutoffs in world reducer * add percentil to crossfilter dimensions * worldEqUniverse now handles cloned worlds * add Dataframe.mapColumns * Wire up handlers for percentile inputs * World reducer and stateManager know about continuousPercentileMin/Max * Create world as universe clone (not pointer) to avoid clobbering vals * Define basic actions for setting continuousPercentileMin/Max * Under the hood, deal with percentiles between 0 and 1 * Move percentile inputs to visualization settings menu * Fix padding for undo/redo buttons * Trigger world rebuild from percentile actions * BROKEN - pseudocode for clamping dataframe by percentiles upon world rebuild * fix error handling on clip quantiles; start world clipping implementation * more unclipped reorg * rename crossfilter.percentile to quantile * simplify schema access * update continuous legend when scale changes * update color cache when clip changes * clip obs annotations and var data when clip quantile changes * use own fromEntries * fix tests * stable non-finite float sort/search * clarify comments * fix syntax typo * use new stand-alone clip * clip expresssion data * add select tests for non-finite scalars * basic styles * clip UI now requires explicit commit * reset enable/disable accounts for clip percentiles * better error messages * fix bug in undo interaction with programatic min brush selection * small refactoring * support clipping of int data * do not perform unnecessary summarizations * improve caching of dataframe compiled columns * add percentile precompute to Dataframe.summarize * use Dataframe.summarize for clip percentiles * remove obsolete quantile code from corssfilter * histogram scale and label Y axis, add unclipped X range labels * layout tweaks * scatterplot now updates when clip changes * improve comments * remove debugging comment * rework clip number entry validation for usability * ui tweaks to histogram colors and layout * enable undo/redo for clip user action * refine UI on clip value entry * api cleanup * update confusing comment * clarify purpose of isValidDigitKeyEvent * fix misleading comment * apply appropriate button-group classes; do not mix span and div * variable name and comment changes suggested in PR review * rename sort to sortArray; remove unused and dead code path * naming changes suggested in PR review * code review improvements for clarity * more small changes from PR review * lint fixes for PR review * fix spelling error * clarify that function performs in-place modification of world * add comment to clarify intent of range operation * fix bad indents in comments * clean up __columnsAccessor comments and code * improve comments around clipPredicate * field name consistency * improve comment on quantiles params
83 lines
2.0 KiB
JavaScript
83 lines
2.0 KiB
JavaScript
/*
|
|
Private dataframe support functions
|
|
|
|
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 quantile from "../quantile";
|
|
import { sortArray } from "../typedCrossfilter/sort";
|
|
|
|
// [ 0, 0.01, 0.02, ..., 1.0]
|
|
const centileNames = new Array(101).fill(0).map((v, idx) => idx / 100);
|
|
|
|
export function summarizeContinuous(col) {
|
|
let min;
|
|
let max;
|
|
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;
|
|
}
|
|
}
|
|
|
|
// 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];
|
|
}
|
|
return {
|
|
categorical: false,
|
|
min,
|
|
max,
|
|
nan,
|
|
pinf,
|
|
ninf,
|
|
percentiles
|
|
};
|
|
}
|
|
|
|
export function summarizeCategorical(col) {
|
|
const categoryCounts = new Map();
|
|
if (col) {
|
|
for (let r = 0, l = col.length; r < l; r += 1) {
|
|
const val = col[r];
|
|
let curCount = categoryCounts.get(val);
|
|
if (curCount === undefined) curCount = 0;
|
|
categoryCounts.set(val, curCount + 1);
|
|
}
|
|
}
|
|
return {
|
|
categorical: true,
|
|
categories: [...categoryCounts.keys()],
|
|
categoryCounts,
|
|
numCategories: categoryCounts.size
|
|
};
|
|
}
|