@visactor/vdataset
Version:
data processing tool
50 lines (48 loc) • 4.24 kB
JavaScript
import { clamp, isArray, isNil, quantileSorted } from "@visactor/vutils";
export const boxplot = (data, options) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r;
const field = null == options ? void 0 : options.field;
if (!field) return [];
const groupField = null == options ? void 0 : options.groupField;
let whiskers = null !== (_a = null == options ? void 0 : options.whiskers) && void 0 !== _a ? _a : 1.5;
const includeValues = !!(null == options ? void 0 : options.includeValues), names = null !== (_b = null == options ? void 0 : options.outputNames) && void 0 !== _b ? _b : {}, keyName = null !== (_c = names.key) && void 0 !== _c ? _c : isArray(groupField) ? null : null !== (_d = groupField) && void 0 !== _d ? _d : "key", countName = null !== (_e = names.count) && void 0 !== _e ? _e : "count", meanName = null !== (_f = names.mean) && void 0 !== _f ? _f : "mean", q1Name = null !== (_g = names.q1) && void 0 !== _g ? _g : "q1", medianName = null !== (_h = names.median) && void 0 !== _h ? _h : "median", q3Name = null !== (_j = names.q3) && void 0 !== _j ? _j : "q3", iqrName = null !== (_k = names.iqr) && void 0 !== _k ? _k : "iqr", minName = null !== (_l = names.min) && void 0 !== _l ? _l : "min", maxName = null !== (_m = names.max) && void 0 !== _m ? _m : "max", lowerWhiskerName = null !== (_o = names.lowerWhisker) && void 0 !== _o ? _o : "lowerWhisker", upperWhiskerName = null !== (_p = names.upperWhisker) && void 0 !== _p ? _p : "upperWhisker", outliersName = null !== (_q = names.outliers) && void 0 !== _q ? _q : "outliers", valuesName = null !== (_r = names.values) && void 0 !== _r ? _r : "values", groups = new Map, rawValues = new Map, keyToGroup = new Map, n = data.length;
for (let i = 0; i < n; i++) {
const d = data[i], v = d[field];
if (isNil(v)) continue;
const num = +v;
if (!Number.isFinite(num)) continue;
let key;
key = isArray(groupField) ? groupField.map((f => String(d[f]))).join("||") : groupField ? String(d[groupField]) : "___all",
groups.has(key) || (groups.set(key, []), includeValues && rawValues.set(key, []),
isArray(groupField) ? keyToGroup.set(key, Object.fromEntries(groupField.map((f => [ f, d[f] ])))) : keyToGroup.set(key, groupField ? d[groupField] : null));
const arr = groups.get(key);
if (arr && arr.push(num), includeValues) {
const rv = rawValues.get(key);
rv && rv.push(d);
}
}
if (isArray(whiskers)) {
whiskers = [ clamp(Math.min.apply(null, whiskers), 0, 1), clamp(Math.max.apply(null, whiskers), 0, 1) ];
}
const out = [];
for (const [key, vals] of groups) {
if (!vals || 0 === vals.length) continue;
const sorted = vals.slice().sort(((a, b) => a - b)), count = sorted.length, dataMin = sorted[0], dataMax = sorted[sorted.length - 1];
let sum = 0;
for (let i = 0; i < sorted.length; i++) sum += sorted[i];
const mean = sum / count, q1 = quantileSorted(sorted, .25), median = quantileSorted(sorted, .5), q3 = quantileSorted(sorted, .75), iqr = q3 - q1, lowerBound = isArray(whiskers) ? quantileSorted(sorted, whiskers[0]) : q1 - whiskers * iqr, upperBound = isArray(whiskers) ? quantileSorted(sorted, whiskers[1]) : q3 + whiskers * iqr, outliers = [];
for (let i = 0; i < sorted.length; i++) (sorted[i] < lowerBound || sorted[i] > upperBound) && outliers.push(sorted[i]);
const obj = {}, representative = keyToGroup.get(key);
if (null !== keyName) obj[keyName] = representative; else if (isArray(groupField)) {
const groupObj = representative || {};
for (const f of groupField) obj[f] = groupObj[f];
}
obj[countName] = count, obj[meanName] = mean, obj[q1Name] = q1, obj[medianName] = median,
obj[q3Name] = q3, obj[iqrName] = iqr, obj[minName] = dataMin, obj[maxName] = dataMax,
obj[lowerWhiskerName] = lowerBound, obj[upperWhiskerName] = upperBound, obj[outliersName] = outliers,
includeValues && (obj[valuesName] = rawValues.get(key) || []), out.push(obj);
}
return out;
};
export default boxplot;
//# sourceMappingURL=boxplot.js.map