Files
cellxgene/client/src/util/dataframe/histogram.js
T

136 lines
3.4 KiB
JavaScript

/*
Dataframe histogram
*/
import { isTypedArray } from "./util";
function _histogramContinuous(column, bins, min, max) {
const valBins = new Array(bins).fill(0);
if (!column) {
return valBins;
}
const binWidth = (max - min) / bins;
const colArray = column.asArray();
for (let r = 0, len = colArray.length; r < len; r += 1) {
const val = colArray[r];
if (val <= max && val >= min) {
// ensure test excludes NaN values
const valBin = Math.min(Math.floor((val - min) / binWidth), bins - 1);
valBins[valBin] += 1;
}
}
return valBins;
}
function _histogramContinuousBy(column, bins, min, max, by) {
const byMap = new Map();
if (!column || !by) {
return byMap;
}
const binWidth = (max - min) / bins;
const byArray = by.asArray();
const colArray = column.asArray();
for (let r = 0, len = colArray.length; r < len; r += 1) {
const byBin = byArray[r];
let valBins = byMap.get(byBin);
if (valBins === undefined) {
valBins = new Array(bins).fill(0);
byMap.set(byBin, valBins);
}
const val = colArray[r];
if (val <= max && val >= min) {
// ensure test excludes NaN values
const valBin = Math.min(Math.floor((val - min) / binWidth), bins - 1);
valBins[valBin] += 1;
}
}
return byMap;
}
function _histogramCategorical(column) {
const valMap = new Map();
if (!column) {
return valMap;
}
const colArray = column.asArray();
for (let r = 0, len = colArray.length; r < len; r += 1) {
const valBin = colArray[r];
let curCount = valMap.get(valBin);
if (curCount === undefined) {
curCount = 0;
}
valMap.set(valBin, curCount + 1);
}
return valMap;
}
function _histogramCategoricalBy(column, by) {
const byMap = new Map();
if (!column || !by) {
return byMap;
}
const byArray = by.asArray();
const colArray = column.asArray();
for (let r = 0, len = colArray.length; r < len; r += 1) {
const byBin = byArray[r];
let valMap = byMap.get(byBin);
if (valMap === undefined) {
valMap = new Map();
byMap.set(byBin, valMap);
}
const valBin = colArray[r];
let curCount = valMap.get(valBin);
if (curCount === undefined) {
curCount = 0;
}
valMap.set(valBin, curCount + 1);
}
return byMap;
}
/*
Count category occupancy. Optional group-by category.
*/
export function histogramCategorical(column, by) {
if (by && isTypedArray(by)) {
throw new Error("Group by column must be categorical");
}
return by
? _histogramCategoricalBy(column, by)
: _histogramCategorical(column);
}
/*
Memoization hash for histogramCategorical()
*/
export function hashCategorical(column, by) {
if (by) {
return `${column.__id}:${by.__id}`;
}
return `${column.__id}:`;
}
/*
Bin counts for continuous/scalar values, with optional group-by category.
Values outside domain are ignored.
*/
export function histogramContinuous(column, bins = 40, domain = [0, 1], by) {
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);
}
/*
Memoization hash for histogramContinuous
*/
export function hashContinuous(column, bins = "", domain = [0, 0], by) {
const [min, max] = domain;
if (by) {
return `${column.__id}:${bins}:${min}:${max}:${by.__id}`;
}
return `${column.__id}::${bins}:${min}:${max}`;
}