polynomial.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import { determination } from "./utils/determination";
  2. import { interpose } from "./utils/interpose";
  3. import { points, visitPoints } from "./utils/points";
  4. import linear from "./linear";
  5. import quad from "./quadratic";
  6. // Adapted from regression-js by Tom Alexander
  7. // Source: https://github.com/Tom-Alexander/regression-js/blob/master/src/regression.js#L246
  8. // License: https://github.com/Tom-Alexander/regression-js/blob/master/LICENSE
  9. // ...with ideas from vega-statistics by Jeffrey Heer
  10. // Source: https://github.com/vega/vega/blob/f21cb8792b4e0cbe2b1a3fd44b0f5db370dbaadb/packages/vega-statistics/src/regression/poly.js
  11. // License: https://github.com/vega/vega/blob/f058b099decad9db78301405dd0d2e9d8ba3d51a/LICENSE
  12. export default function(){
  13. let x = d => d[0],
  14. y = d => d[1],
  15. order = 3,
  16. domain;
  17. function polynomial(data) {
  18. // Use more efficient methods for lower orders
  19. if (order === 1) {
  20. const o = linear().x(x).y(y).domain(domain)(data);
  21. o.coefficients = [o.b, o.a];
  22. delete o.a; delete o.b;
  23. return o;
  24. }
  25. if (order === 2) {
  26. const o = quad().x(x).y(y).domain(domain)(data);
  27. o.coefficients = [o.c, o.b, o.a];
  28. delete o.a; delete o.b; delete o.c;
  29. return o;
  30. }
  31. const [xv, yv, ux, uy] = points(data, x, y),
  32. n = xv.length,
  33. lhs = [],
  34. rhs = [],
  35. k = order + 1;
  36. let Y = 0, n0 = 0,
  37. xmin = domain ? +domain[0] : Infinity,
  38. xmax = domain ? +domain[1] : -Infinity;
  39. visitPoints(data, x, y, (dx, dy) => {
  40. ++n0
  41. Y += (dy - Y) / n0;
  42. if (!domain){
  43. if (dx < xmin) xmin = dx;
  44. if (dx > xmax) xmax = dx;
  45. }
  46. });
  47. let i, j, l, v, c;
  48. for (i = 0; i < k; ++i) {
  49. for (l = 0, v = 0; l < n; ++l) {
  50. v += Math.pow(xv[l], i) * yv[l];
  51. }
  52. lhs.push(v);
  53. c = new Float64Array(k);
  54. for (j=0; j<k; ++j) {
  55. for (l=0, v=0; l<n; ++l) {
  56. v += Math.pow(xv[l], i + j);
  57. }
  58. c[j] = v;
  59. }
  60. rhs.push(c);
  61. }
  62. rhs.push(lhs);
  63. const coef = gaussianElimination(rhs),
  64. fn = x => {
  65. x -= ux;
  66. let y = uy + coef[0] + coef[1] * x + coef[2] * x * x;
  67. for (i = 3; i < k; ++i) y += coef[i] * Math.pow(x, i);
  68. return y;
  69. },
  70. out = interpose(xmin, xmax, fn);
  71. out.coefficients = uncenter(k, coef, -ux, uy);
  72. out.predict = fn;
  73. out.rSquared = determination(data, x, y, Y, fn);
  74. return out;
  75. }
  76. polynomial.domain = function(arr){
  77. return arguments.length ? (domain = arr, polynomial) : domain;
  78. }
  79. polynomial.x = function(fn){
  80. return arguments.length ? (x = fn, polynomial) : x;
  81. }
  82. polynomial.y = function(fn){
  83. return arguments.length ? (y = fn, polynomial) : y;
  84. }
  85. polynomial.order = function(n){
  86. return arguments.length ? (order = n, polynomial) : order;
  87. }
  88. return polynomial;
  89. }
  90. function uncenter(k, a, x, y) {
  91. const z = Array(k);
  92. let i, j, v, c;
  93. // initialize to zero
  94. for (i = 0; i < k; ++i) z[i] = 0;
  95. // polynomial expansion
  96. for (i = k - 1; i >= 0; --i) {
  97. v = a[i];
  98. c = 1;
  99. z[i] += v;
  100. for (j = 1; j <= i; ++j) {
  101. c *= (i + 1 - j) / j; // binomial coefficent
  102. z[i-j] += v * Math.pow(x, j) * c;
  103. }
  104. }
  105. // bias term
  106. z[0] += y;
  107. return z;
  108. }
  109. // Given an array for a two-dimensional matrix and the polynomial order,
  110. // solve A * x = b using Gaussian elimination.
  111. function gaussianElimination(matrix) {
  112. const n = matrix.length - 1,
  113. coef = [];
  114. let i, j, k, r, t;
  115. for (i = 0; i < n; ++i) {
  116. r = i; // max row
  117. for (j = i + 1; j < n; ++j) {
  118. if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][r])) {
  119. r = j;
  120. }
  121. }
  122. for (k = i; k < n + 1; ++k) {
  123. t = matrix[k][i];
  124. matrix[k][i] = matrix[k][r];
  125. matrix[k][r] = t;
  126. }
  127. for (j = i + 1; j < n; ++j) {
  128. for (k = n; k >= i; k--) {
  129. matrix[k][j] -= (matrix[k][i] * matrix[i][j]) / matrix[i][i];
  130. }
  131. }
  132. }
  133. for (j = n - 1; j >= 0; --j) {
  134. t = 0;
  135. for (k = j + 1; k < n; ++k) {
  136. t += matrix[k][j] * coef[k];
  137. }
  138. coef[j] = (matrix[n][j] - t) / matrix[j][j];
  139. }
  140. return coef;
  141. }