| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- import { determination } from "./utils/determination";
- import { interpose } from "./utils/interpose";
- import { points, visitPoints } from "./utils/points";
- import linear from "./linear";
- import quad from "./quadratic";
- // Adapted from regression-js by Tom Alexander
- // Source: https://github.com/Tom-Alexander/regression-js/blob/master/src/regression.js#L246
- // License: https://github.com/Tom-Alexander/regression-js/blob/master/LICENSE
- // ...with ideas from vega-statistics by Jeffrey Heer
- // Source: https://github.com/vega/vega/blob/f21cb8792b4e0cbe2b1a3fd44b0f5db370dbaadb/packages/vega-statistics/src/regression/poly.js
- // License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
- export default function(){
- let x = d => d[0],
- y = d => d[1],
- order = 3,
- domain;
-
- function polynomial(data) {
- // Use more efficient methods for lower orders
- if (order === 1) {
- const o = linear().x(x).y(y).domain(domain)(data);
- o.coefficients = [o.b, o.a];
- delete o.a; delete o.b;
- return o;
- }
- if (order === 2) {
- const o = quad().x(x).y(y).domain(domain)(data);
- o.coefficients = [o.c, o.b, o.a];
- delete o.a; delete o.b; delete o.c;
- return o;
- }
-
- const [xv, yv, ux, uy] = points(data, x, y),
- n = xv.length,
- lhs = [],
- rhs = [],
- k = order + 1;
- let Y = 0, n0 = 0,
- xmin = domain ? +domain[0] : Infinity,
- xmax = domain ? +domain[1] : -Infinity;
-
- visitPoints(data, x, y, (dx, dy) => {
- ++n0
- Y += (dy - Y) / n0;
- if (!domain){
- if (dx < xmin) xmin = dx;
- if (dx > xmax) xmax = dx;
- }
- });
- let i, j, l, v, c;
- for (i = 0; i < k; ++i) {
- for (l = 0, v = 0; l < n; ++l) {
- v += Math.pow(xv[l], i) * yv[l];
- }
- lhs.push(v);
- c = new Float64Array(k);
- for (j=0; j<k; ++j) {
- for (l=0, v=0; l<n; ++l) {
- v += Math.pow(xv[l], i + j);
- }
- c[j] = v;
- }
- rhs.push(c);
- }
- rhs.push(lhs);
- const coef = gaussianElimination(rhs),
- fn = x => {
- x -= ux;
- let y = uy + coef[0] + coef[1] * x + coef[2] * x * x;
- for (i = 3; i < k; ++i) y += coef[i] * Math.pow(x, i);
- return y;
- },
- out = interpose(xmin, xmax, fn);
- out.coefficients = uncenter(k, coef, -ux, uy);
- out.predict = fn;
- out.rSquared = determination(data, x, y, Y, fn);
-
- return out;
- }
- polynomial.domain = function(arr){
- return arguments.length ? (domain = arr, polynomial) : domain;
- }
- polynomial.x = function(fn){
- return arguments.length ? (x = fn, polynomial) : x;
- }
- polynomial.y = function(fn){
- return arguments.length ? (y = fn, polynomial) : y;
- }
- polynomial.order = function(n){
- return arguments.length ? (order = n, polynomial) : order;
- }
-
- return polynomial;
- }
- function uncenter(k, a, x, y) {
- const z = Array(k);
- let i, j, v, c;
- // initialize to zero
- for (i = 0; i < k; ++i) z[i] = 0;
- // polynomial expansion
- for (i = k - 1; i >= 0; --i) {
- v = a[i];
- c = 1;
- z[i] += v;
- for (j = 1; j <= i; ++j) {
- c *= (i + 1 - j) / j; // binomial coefficent
- z[i-j] += v * Math.pow(x, j) * c;
- }
- }
- // bias term
- z[0] += y;
- return z;
- }
- // Given an array for a two-dimensional matrix and the polynomial order,
- // solve A * x = b using Gaussian elimination.
- function gaussianElimination(matrix) {
- const n = matrix.length - 1,
- coef = [];
- let i, j, k, r, t;
- for (i = 0; i < n; ++i) {
- r = i; // max row
- for (j = i + 1; j < n; ++j) {
- if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][r])) {
- r = j;
- }
- }
- for (k = i; k < n + 1; ++k) {
- t = matrix[k][i];
- matrix[k][i] = matrix[k][r];
- matrix[k][r] = t;
- }
- for (j = i + 1; j < n; ++j) {
- for (k = n; k >= i; k--) {
- matrix[k][j] -= (matrix[k][i] * matrix[i][j]) / matrix[i][i];
- }
- }
- }
- for (j = n - 1; j >= 0; --j) {
- t = 0;
- for (k = j + 1; k < n; ++k) {
- t += matrix[k][j] * coef[k];
- }
- coef[j] = (matrix[n][j] - t) / matrix[j][j];
- }
- return coef;
- }
|