@visactor/vdataset
Version:
data processing tool
131 lines (128 loc) • 7.96 kB
JavaScript
import { isNil, isArray } from "@visactor/vutils";
const subBin = (data, options) => {
var _a, _b, _c, _d;
const {numBins: numBins, thresholds: thresholds, countName: countName, percentageName: percentageName, valuesName: valuesName, countField: countField, field: field, n: n, x0Name: x0Name, x1Name: x1Name} = options, groupField = options.groupField, usingGroup = Array.isArray(groupField) ? groupField.length > 0 : !!groupField, out = [];
if (!usingGroup) for (let i = 0; i < numBins; i++) {
const rec = {
[x0Name]: thresholds[i],
[x1Name]: thresholds[i + 1],
[countName]: 0
};
options.includeValues && (rec[valuesName] = []), out.push(rec);
}
const binGroupCounts = usingGroup ? new Array(numBins).fill(0).map((() => new Map)) : [], binGroupValues = usingGroup ? new Array(numBins).fill(0).map((() => new Map)) : [], groupKeyOrder = [], groupKeySet = new Set, groupRepr = new Map;
for (let i = 0; i < n; i++) {
const v = data[i][field];
if (null == v) continue;
const num = +v;
if (Number.isFinite(num)) for (let j = 0; j < numBins; j++) {
const left = thresholds[j], right = thresholds[j + 1];
if (num >= left && num < right || j === numBins - 1 && num <= right) {
const datumCount = null !== (_a = data[i][countField]) && void 0 !== _a ? _a : 1;
if (usingGroup) {
let gk;
gk = isArray(groupField) ? groupField.map((f => String(data[i][f]))).join("||") : String(data[i][groupField]);
const m = binGroupCounts[j], prev = null !== (_b = m.get(gk)) && void 0 !== _b ? _b : 0;
if (m.set(gk, prev + datumCount), groupKeySet.has(gk) || (groupKeySet.add(gk), groupKeyOrder.push(gk),
isArray(groupField) ? groupRepr.set(gk, Object.fromEntries(groupField.map((f => [ f, data[i][f] ])))) : groupRepr.set(gk, data[i][groupField])),
options && options.includeValues) {
const vv = binGroupValues[j];
vv.has(gk) || vv.set(gk, []);
const arr = vv.get(gk);
arr && arr.push(data[i]);
}
} else out[j][countName] += datumCount;
options && options.includeValues && !usingGroup && out[j][valuesName].push(data[i]);
break;
}
}
}
let totalCount = 0;
const finalOut = [];
if (usingGroup) {
for (let j = 0; j < numBins; j++) for (const gk of groupKeyOrder) {
const sum = null !== (_c = binGroupCounts[j].get(gk)) && void 0 !== _c ? _c : 0, rec = {
[x0Name]: thresholds[j],
[x1Name]: thresholds[j + 1],
[countName]: sum
}, repr = null !== (_d = groupRepr.get(gk)) && void 0 !== _d ? _d : {};
if (isArray(groupField)) for (const f of groupField) rec[f] = repr[f]; else groupField && (rec[groupField] = repr);
options && options.includeValues && (rec[valuesName] = binGroupValues[j].get(gk) || []),
finalOut.push(rec), totalCount += sum;
}
const denominator = totalCount;
for (const r of finalOut) r[percentageName] = denominator > 0 ? r[countName] / denominator : 0;
} else {
for (let i = 0, len = out.length; i < len; i++) totalCount += out[i][countName];
for (let i = 0, len = out.length; i < len; i++) out[i][percentageName] = totalCount > 0 ? out[i][countName] / totalCount : 0,
finalOut.push(out[i]);
}
return finalOut;
};
export const bin = (data, options) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
const field = null == options ? void 0 : options.field;
if (!field) return [];
const countField = options.countField, n = data.length;
let thresholds, min = 1 / 0, max = -1 / 0;
if (options.extent) min = options.extent[0], max = options.extent[1]; else for (let i = 0; i < n; i++) {
const v = data[i][field];
if (isNil(v)) continue;
const num = +v;
Number.isFinite(num) && (num < min && (min = num), num > max && (max = num));
}
if (!Number.isFinite(min) || !Number.isFinite(max) || 0 === n) return [];
if (options.thresholds && options.thresholds.length) thresholds = options.thresholds.slice(),
thresholds.sort(((a, b) => a - b)); else if ("number" == typeof options.step && options.step > 0) {
const stepSize = options.step;
let startMin = min;
for (options.extent || (startMin = Math.floor(min / stepSize) * stepSize), thresholds = [ startMin ]; startMin <= max; ) startMin += stepSize,
thresholds.push(startMin);
} else {
let bins = options.bins && options.bins > 0 ? Math.floor(options.bins) : 10;
if (max - min > 1) {
const start = Math.floor(min), stepSizeInt = Math.ceil((max - start) / bins);
thresholds = new Array(bins + 1);
for (let i = 0; i <= bins; i++) if (thresholds[i] = start + stepSizeInt * i, thresholds[i] > max) {
bins = i, thresholds.length = bins + 1;
break;
}
} else {
const stepSize = (max - min) / bins;
thresholds = new Array(bins + 1);
for (let i = 0; i <= bins; i++) if (thresholds[i] = min + stepSize * i, thresholds[i] > max) {
bins = i, thresholds.length = bins + 1;
break;
}
}
}
const numBins = Math.max(0, thresholds.length - 1);
if (0 === numBins) return [];
const x0Name = null !== (_b = null === (_a = options.outputNames) || void 0 === _a ? void 0 : _a.x0) && void 0 !== _b ? _b : "x0", x1Name = null !== (_d = null === (_c = options.outputNames) || void 0 === _c ? void 0 : _c.x1) && void 0 !== _d ? _d : "x1", countName = null !== (_f = null === (_e = options.outputNames) || void 0 === _e ? void 0 : _e.count) && void 0 !== _f ? _f : "count", valuesName = null !== (_h = null === (_g = options.outputNames) || void 0 === _g ? void 0 : _g.values) && void 0 !== _h ? _h : "values", percentageName = null !== (_k = null === (_j = options.outputNames) || void 0 === _j ? void 0 : _j.percentage) && void 0 !== _k ? _k : "percentage", facetField = isArray(null == options ? void 0 : options.facetField) ? null == options ? void 0 : options.facetField : (null == options ? void 0 : options.facetField) ? [ options.facetField ] : [], groupField = isArray(null == options ? void 0 : options.groupField) ? null == options ? void 0 : options.groupField : (null == options ? void 0 : options.groupField) ? [ options.groupField ] : [], normalizedGroupField = groupField.length ? groupField : void 0, subViewOptions = Object.assign(Object.assign({}, options), {
numBins: numBins,
thresholds: thresholds,
countName: countName,
percentageName: percentageName,
valuesName: valuesName,
countField: countField,
field: field,
n: n,
x0Name: x0Name,
x1Name: x1Name,
groupField: normalizedGroupField
});
if (!facetField.length) return subBin(data, subViewOptions);
const subViewMap = {};
return data.forEach((dataItem => {
const subViewKey = facetField.map((field => null == dataItem ? void 0 : dataItem[field])).join("-&&-");
subViewMap[subViewKey] ? subViewMap[subViewKey].push(dataItem) : subViewMap[subViewKey] = [ dataItem ];
})), Object.values(subViewMap).map((subDataset => {
const combinedGroupField = [ ...groupField, ...facetField ];
return subBin(subDataset, Object.assign(Object.assign({}, subViewOptions), {
groupField: combinedGroupField.length ? combinedGroupField : void 0,
n: subDataset.length
}));
})).flat();
};
export default bin;
//# sourceMappingURL=bin.js.map