@nova-ui/dashboards
Version:
Nova Dashboards is a framework designed to provide feature developers with a common solution for presenting data coming from various sources within a single view, as well as a set of predefined widget visualizations that are 100% configuration-driven and
224 lines • 8.79 kB
JavaScript
;
// © 2022 SolarWinds Worldwide, LLC. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
Object.defineProperty(exports, "__esModule", { value: true });
exports.loess = void 0;
// Based on org.apache.commons.math.analysis.interpolation.LoessInterpolator
// from http://commons.apache.org/math/
// Extracted from https://github.com/jasondavies/science.js
function loess() {
let bandwidth = 0.3, robustnessIters = 2, accuracy = 1e-12;
function smooth(xval, yval, weights) {
let n = xval.length, i;
if (n !== yval.length) {
throw new Error("Mismatched array lengths");
}
if (n === 0) {
throw new Error("At least one point required.");
}
if (arguments.length < 3) {
weights = [];
i = -1;
while (++i < n) {
weights[i] = 1;
}
}
science_stats_loessFiniteReal(xval);
science_stats_loessFiniteReal(yval);
science_stats_loessFiniteReal(weights);
science_stats_loessStrictlyIncreasing(xval);
if (n === 1) {
return [yval[0]];
}
if (n === 2) {
return [yval[0], yval[1]];
}
const bandwidthInPoints = Math.floor(bandwidth * n);
if (bandwidthInPoints < 2) {
throw new Error("Bandwidth too small.");
}
const res = [], residuals = [], robustnessWeights = [];
// Do an initial fit and 'robustnessIters' robustness iterations.
// This is equivalent to doing 'robustnessIters+1' robustness iterations
// starting with all robustness weights set to 1.
i = -1;
while (++i < n) {
res[i] = 0;
residuals[i] = 0;
robustnessWeights[i] = 1;
}
let iter = -1;
while (++iter <= robustnessIters) {
const bandwidthInterval = [0, bandwidthInPoints - 1];
// At each x, compute a local weighted linear regression
let x;
i = -1;
while (++i < n) {
x = xval[i];
// Find out the interval of source points on which
// a regression is to be made.
if (i > 0) {
science_stats_loessUpdateBandwidthInterval(xval, weights, i, bandwidthInterval);
}
const ileft = bandwidthInterval[0], iright = bandwidthInterval[1];
// Compute the point of the bandwidth interval that is
// farthest from x
const edge = xval[i] - xval[ileft] > xval[iright] - xval[i]
? ileft
: iright;
// Compute a least-squares linear fit weighted by
// the product of robustness weights and the tricube
// weight function.
// See http://en.wikipedia.org/wiki/Linear_regression
// (section "Univariate linear case")
// and http://en.wikipedia.org/wiki/Weighted_least_squares
// (section "Weighted least squares")
let sumWeights = 0, sumX = 0, sumXSquared = 0, sumY = 0, sumXY = 0, denom = Math.abs(1 / (xval[edge] - x));
for (let k = ileft; k <= iright; ++k) {
const xk = xval[k], yk = yval[k], dist = k < i ? x - xk : xk - x, w = science_stats_loessTricube(dist * denom) *
robustnessWeights[k] *
weights[k], xkw = xk * w;
sumWeights += w;
sumX += xkw;
sumXSquared += xk * xkw;
sumY += yk * w;
sumXY += yk * xkw;
}
const meanX = sumX / sumWeights, meanY = sumY / sumWeights, meanXY = sumXY / sumWeights, meanXSquared = sumXSquared / sumWeights;
const beta = Math.sqrt(Math.abs(meanXSquared - meanX * meanX)) < accuracy
? 0
: (meanXY - meanX * meanY) /
(meanXSquared - meanX * meanX);
const alpha = meanY - beta * meanX;
res[i] = beta * x + alpha;
residuals[i] = Math.abs(yval[i] - res[i]);
}
// No need to recompute the robustness weights at the last
// iteration, they won't be needed anymore
if (iter === robustnessIters) {
break;
}
// Recompute the robustness weights.
// Find the median residual.
const medianResidual = median(residuals);
if (Math.abs(medianResidual) < accuracy) {
break;
}
let arg, w;
i = -1;
while (++i < n) {
arg = residuals[i] / (6 * medianResidual);
robustnessWeights[i] = arg >= 1 ? 0 : (w = 1 - arg * arg) * w;
}
}
return res;
}
smooth.bandwidth = function (x) {
if (!arguments.length) {
return x;
}
bandwidth = x;
return smooth;
};
smooth.robustnessIterations = function (x) {
if (!arguments.length) {
return x;
}
robustnessIters = x;
return smooth;
};
smooth.accuracy = function (x) {
if (!arguments.length) {
return x;
}
accuracy = x;
return smooth;
};
return smooth;
}
exports.loess = loess;
function science_stats_loessFiniteReal(values) {
let n = values.length, i = -1;
while (++i < n) {
if (!isFinite(values[i])) {
return false;
}
}
return true;
}
function science_stats_loessStrictlyIncreasing(xval) {
// eslint-disable-next-line prefer-const
let n = xval.length, i = 0;
while (++i < n) {
if (xval[i - 1] >= xval[i]) {
return false;
}
}
return true;
}
// Compute the tricube weight function.
// http://en.wikipedia.org/wiki/Local_regression#Weight_function
function science_stats_loessTricube(x) {
return (x = 1 - x * x * x) * x * x;
}
// Given an index interval into xval that embraces a certain number of
// points closest to xval[i-1], update the interval so that it embraces
// the same number of points closest to xval[i], ignoring zero weights.
function science_stats_loessUpdateBandwidthInterval(xval, weights, i, bandwidthInterval) {
const left = bandwidthInterval[0], right = bandwidthInterval[1];
// The right edge should be adjusted if the next point to the right
// is closer to xval[i] than the leftmost point of the current interval
const nextRight = science_stats_loessNextNonzero(weights, right);
if (nextRight < xval.length &&
xval[nextRight] - xval[i] < xval[i] - xval[left]) {
const nextLeft = science_stats_loessNextNonzero(weights, left);
bandwidthInterval[0] = nextLeft;
bandwidthInterval[1] = nextRight;
}
}
function science_stats_loessNextNonzero(weights, i) {
let j = i + 1;
while (j < weights.length && weights[j] === 0) {
j++;
}
return j;
}
function median(x) {
return quantiles(x, [0.5])[0];
}
// Uses R's quantile algorithm type=7.
function quantiles(d, quantiles) {
d = d.slice().sort(ascending);
const n1 = d.length - 1;
return quantiles.map(function (q) {
if (q === 0) {
return d[0];
}
else if (q === 1) {
return d[n1];
}
const index = 1 + q * n1, lo = Math.floor(index), h = index - lo, a = d[lo - 1];
return h === 0 ? a : a + h * (d[lo] - a);
});
}
function ascending(a, b) {
return a - b;
}
//# sourceMappingURL=loess.js.map