UNPKG

@formant/ava

Version:

A framework for automated visual analytics.

88 lines (87 loc) 3.84 kB
"use strict"; 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;