@formant/ava
Version:
A framework for automated visual analytics.
88 lines (87 loc) • 3.84 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.lowess = exports.weightedLinearRegression = exports.bisquareWeightFunction = exports.tricubeWeightFunction = void 0;
var lodash_1 = require("lodash");
var utils_1 = require("../utils");
var base_1 = require("./base");
var constants_1 = require("./constants");
var vector_1 = require("./vector");
var tricubeWeightFunction = function (x) {
if (!(0, lodash_1.isNumber)(x) || Math.abs(x) >= 1)
return 0;
return Math.pow((1 - Math.pow(Math.abs(x), 3)), 3);
};
exports.tricubeWeightFunction = tricubeWeightFunction;
var bisquareWeightFunction = function (x) {
if (!(0, lodash_1.isNumber)(x) || Math.abs(x) >= 1)
return 0;
return Math.pow((1 - Math.pow(x, 2)), 2);
};
exports.bisquareWeightFunction = bisquareWeightFunction;
var weightedLinearRegression = function (x, y, w) {
/** dimension: 2*n */
var xt = [(0, utils_1.nOnes)(x.length), x];
/** dimension: n*2 */
var xMatrix = (0, vector_1.matrixTranspose)(xt);
/** dimension: n*1 */
var yMatrix = (0, vector_1.matrixTranspose)([y]);
/** diagonal matrix with w as the main diagonal */
var wMatrix = (0, vector_1.constructDiagonalMatrix)(w);
/**
* (X^(T)wX)^(-1)
* - 2*n, n*n, n*2 => 2*2
* */
var factor = (0, vector_1.inverseSecondOrderMatrix)((0, vector_1.multiMatrixMultiply)([xt, wMatrix, xMatrix]));
/**
* (X^(T)wX)^(-1) X^(T)wY
* - 2*2, 2*n, n*1 => 2*1
* */
return (0, vector_1.multiMatrixMultiply)([factor, xt, wMatrix, yMatrix]);
};
exports.weightedLinearRegression = weightedLinearRegression;
/**
* Locally weighted regression
* - General idea: perform weighted linear regression on localized subsets of the data point by point to calculate fitted value.
* - Reference: William S. Cleveland. Robust Locally Weighted Regression and Smoothing Scatterplots. Journal of the American Statistical Association. 1979. Vol. 74(368):829-836.
* */
var lowess = function (x, y, options) {
var xLength = x.length;
var mergeOptions = (0, lodash_1.mergeWith)(constants_1.DEFAULT_LOWESS_OPTIONS, options, function (defaultValue, inputValue) {
return inputValue !== null && inputValue !== void 0 ? inputValue : defaultValue;
});
// length of subset
var r = Math.ceil(xLength * mergeOptions.f);
// for each i, hi be the distance from xi to the rth nearest neighbor of xi
var h = x.map(function (xi) {
// rth smallest number among |xi - xj|, for j = 1, ..., n
return x.map(function (xj) { return Math.abs(xi - xj); }).sort(function (a, b) { return a - b; })[r];
});
// weight, n*n
var w = x.map(function (xi, i) {
// wk(xi) = weightFunc((xk - xi)/hi), for k = 1, ..., n
return x.map(function (xk) { return (0, exports.tricubeWeightFunction)((xk - xi) / h[i]); });
});
var robustCoefficient = (0, utils_1.nOnes)(xLength);
/** fitted values */
var yFit = (0, utils_1.nZeros)(xLength);
var _loop_1 = function (iter) {
for (var i = 0; i < xLength; i += 1) {
var robustWeight = w[i].map(function (wik, k) { return wik * robustCoefficient[k]; });
var beta = (0, exports.weightedLinearRegression)(x, y, robustWeight);
yFit[i] = beta[0][0] + beta[1][0] * x[i];
}
var residuals = (0, vector_1.vectorSubtract)(y, yFit);
/** median of |residuals| */
var s = (0, base_1.median)(residuals.map(function (res) { return Math.abs(res); }));
residuals.forEach(function (res, i) {
robustCoefficient[i] = (0, exports.bisquareWeightFunction)(res / (6 * s));
});
};
for (var iter = 0; iter < mergeOptions.nSteps; iter += 1) {
_loop_1(iter);
}
return {
y: yFit,
};
};
exports.lowess = lowess;