UNPKG

@visactor/vgrammar-core

Version:

VGrammar is a visual grammar library

81 lines (78 loc) 5.42 kB
import { PointService, array, isNumber, quantileSorted } from "@visactor/vutils"; const defaultBins = 256, defaultAs = [ "x", "kde" ], defaultAs2d = [ "x", "y", "kde" ], SQRT2PI = Math.sqrt(2 * Math.PI), SQRT2PI2 = Math.sqrt((2 * Math.PI) ** 2), gaussKernel = (x, dimension = 1) => { const sp = 1 === dimension ? SQRT2PI : 2 === dimension ? SQRT2PI2 : Math.sqrt((2 * Math.PI) ** dimension); return Math.exp(-(x ** 2) / 2) / sp; }, ruleOfThumbBandwidth = (data, dimension = 1) => { const n = data.length, mean = data.reduce(((sum, datum) => sum + datum.x), 0) / n, sd = Math.sqrt(data.reduce(((v, datum) => v + (datum.x - mean) ** 2), 0) / n), sortedData = data.sort(((a, b) => a.x - b.x)).map((datum => datum.x)), q1 = quantileSorted(sortedData, .25), iqr = quantileSorted(sortedData, .75) - q1; return .9 * Math.min(sd, iqr / 1.34) * n ** -.2; }, scottBandwidth = (data, dimension = 1) => data.length ** (-1 / (dimension + 4)), kde1d = (targetDatum, data, bandwidth) => { const n = data.length; return data.reduce(((v, datum) => { const distance = Math.abs(targetDatum.x - datum.x); return v + gaussKernel(distance / bandwidth, 1); }), 0) / (n * bandwidth); }, kde2d = (targetDatum, data, bandwidth) => { const n = data.length; return data.reduce(((v, datum) => { const distance = PointService.distancePP(targetDatum, datum); return v + gaussKernel(distance / bandwidth ** 2, 1); }), 0) / (n * bandwidth ** 2); }; export const transform = (options, upstreamData) => { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w; if (!upstreamData || 0 === upstreamData.length) return upstreamData; const dimension = null !== (_a = options.dimension) && void 0 !== _a ? _a : "1d", field = array(options.field), bins = array(null !== (_b = options.bins) && void 0 !== _b ? _b : 256), as = array(options.as); if ("1d" === dimension) { const data = upstreamData.map((datum => ({ x: datum[field[0]] }))), bandwidth = null !== (_c = options.bandwidth) && void 0 !== _c ? _c : ruleOfThumbBandwidth(data), min = data.reduce(((min, datum) => Math.min(min, datum.x)), data[0].x), max = data.reduce(((max, datum) => Math.max(max, datum.x)), data[0].x), extentMin = null !== (_e = null === (_d = options.extent) || void 0 === _d ? void 0 : _d[0]) && void 0 !== _e ? _e : min, extentMax = null !== (_g = null === (_f = options.extent) || void 0 === _f ? void 0 : _f[1]) && void 0 !== _g ? _g : max, extent = [ isNumber(extentMin) ? extentMin : null !== (_h = extentMin.x) && void 0 !== _h ? _h : min, isNumber(extentMax) ? extentMax : null !== (_j = extentMax.x) && void 0 !== _j ? _j : max ], step = (extent[1] - extent[0]) / bins[0], kdeResult = new Array(bins[0]).fill(0).map(((v, index) => { var _a, _b; const value = Math.min(extent[0] + step * (index + .5), extent[1]); return { [null !== (_a = as[0]) && void 0 !== _a ? _a : defaultAs[0]]: value, [null !== (_b = as[1]) && void 0 !== _b ? _b : defaultAs[0]]: kde1d({ x: value }, data, bandwidth) }; })); return kdeResult; } if ("2d" === dimension) { const data = upstreamData.map((datum => ({ x: datum[field[0]], y: datum[field[1]] }))), bandwidth = options.bandwidth || scottBandwidth(data, 2), min = data.reduce(((min, datum) => ({ x: Math.min(min.x, datum.x), y: Math.min(min.y, datum.y) })), data[0]), max = data.reduce(((max, datum) => ({ x: Math.max(max.x, datum.x), y: Math.max(max.y, datum.y) })), data[0]), extentMin = null !== (_l = null === (_k = options.extent) || void 0 === _k ? void 0 : _k[0]) && void 0 !== _l ? _l : min, extentMax = null !== (_o = null === (_m = options.extent) || void 0 === _m ? void 0 : _m[1]) && void 0 !== _o ? _o : max, extent = [ isNumber(extentMin) ? { x: extentMin, y: extentMin } : { x: null !== (_p = extentMin.x) && void 0 !== _p ? _p : min.x, y: null !== (_q = extentMin.y) && void 0 !== _q ? _q : min.y }, isNumber(extentMax) ? { x: extentMax, y: extentMax } : { x: null !== (_r = extentMax.x) && void 0 !== _r ? _r : max.x, y: null !== (_s = extentMax.y) && void 0 !== _s ? _s : max.y } ], binsX = bins[0], binsY = null !== (_t = bins[1]) && void 0 !== _t ? _t : bins[0], stepX = (extent[1].x - extent[0].x) / binsX, stepY = (extent[1].y - extent[0].y) / binsY, kdeResult = []; for (let yIndex = 0; yIndex < binsY; yIndex++) for (let xIndex = 0; xIndex < binsX; xIndex++) { const x = Math.min(extent[0].x + stepX * (xIndex + .5), extent[1].x), y = Math.min(extent[0].y + stepY * (yIndex + .5), extent[1].y); kdeResult.push({ [null !== (_u = as[0]) && void 0 !== _u ? _u : defaultAs2d[0]]: x, [null !== (_v = as[1]) && void 0 !== _v ? _v : defaultAs2d[1]]: y, [null !== (_w = as[2]) && void 0 !== _w ? _w : defaultAs2d[2]]: kde2d({ x: x, y: y }, data, bandwidth) }); } return kdeResult; } return []; }; //# sourceMappingURL=kde.js.map