Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 

385 строки
11 KiB

  1. (function (global, factory) {
  2. if (typeof define === "function" && define.amd) {
  3. define(['module'], factory);
  4. } else if (typeof exports !== "undefined") {
  5. factory(module);
  6. } else {
  7. var mod = {
  8. exports: {}
  9. };
  10. factory(mod);
  11. global.regression = mod.exports;
  12. }
  13. })(this, function (module) {
  14. 'use strict';
  15. function _defineProperty(obj, key, value) {
  16. if (key in obj) {
  17. Object.defineProperty(obj, key, {
  18. value: value,
  19. enumerable: true,
  20. configurable: true,
  21. writable: true
  22. });
  23. } else {
  24. obj[key] = value;
  25. }
  26. return obj;
  27. }
  28. var _extends = Object.assign || function (target) {
  29. for (var i = 1; i < arguments.length; i++) {
  30. var source = arguments[i];
  31. for (var key in source) {
  32. if (Object.prototype.hasOwnProperty.call(source, key)) {
  33. target[key] = source[key];
  34. }
  35. }
  36. }
  37. return target;
  38. };
  39. function _toConsumableArray(arr) {
  40. if (Array.isArray(arr)) {
  41. for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) {
  42. arr2[i] = arr[i];
  43. }
  44. return arr2;
  45. } else {
  46. return Array.from(arr);
  47. }
  48. }
  49. var DEFAULT_OPTIONS = { order: 2, precision: 2, period: null };
  50. /**
  51. * Determine the coefficient of determination (r^2) of a fit from the observations
  52. * and predictions.
  53. *
  54. * @param {Array<Array<number>>} data - Pairs of observed x-y values
  55. * @param {Array<Array<number>>} results - Pairs of observed predicted x-y values
  56. *
  57. * @return {number} - The r^2 value, or NaN if one cannot be calculated.
  58. */
  59. function determinationCoefficient(data, results) {
  60. var predictions = [];
  61. var observations = [];
  62. data.forEach(function (d, i) {
  63. if (d[1] !== null) {
  64. observations.push(d);
  65. predictions.push(results[i]);
  66. }
  67. });
  68. var sum = observations.reduce(function (a, observation) {
  69. return a + observation[1];
  70. }, 0);
  71. var mean = sum / observations.length;
  72. var ssyy = observations.reduce(function (a, observation) {
  73. var difference = observation[1] - mean;
  74. return a + difference * difference;
  75. }, 0);
  76. var sse = observations.reduce(function (accum, observation, index) {
  77. var prediction = predictions[index];
  78. var residual = observation[1] - prediction[1];
  79. return accum + residual * residual;
  80. }, 0);
  81. return 1 - sse / ssyy;
  82. }
  83. /**
  84. * Determine the solution of a system of linear equations A * x = b using
  85. * Gaussian elimination.
  86. *
  87. * @param {Array<Array<number>>} input - A 2-d matrix of data in row-major form [ A | b ]
  88. * @param {number} order - How many degrees to solve for
  89. *
  90. * @return {Array<number>} - Vector of normalized solution coefficients matrix (x)
  91. */
  92. function gaussianElimination(input, order) {
  93. var matrix = input;
  94. var n = input.length - 1;
  95. var coefficients = [order];
  96. for (var i = 0; i < n; i++) {
  97. var maxrow = i;
  98. for (var j = i + 1; j < n; j++) {
  99. if (Math.abs(matrix[i][j]) > Math.abs(matrix[i][maxrow])) {
  100. maxrow = j;
  101. }
  102. }
  103. for (var k = i; k < n + 1; k++) {
  104. var tmp = matrix[k][i];
  105. matrix[k][i] = matrix[k][maxrow];
  106. matrix[k][maxrow] = tmp;
  107. }
  108. for (var _j = i + 1; _j < n; _j++) {
  109. for (var _k = n; _k >= i; _k--) {
  110. matrix[_k][_j] -= matrix[_k][i] * matrix[i][_j] / matrix[i][i];
  111. }
  112. }
  113. }
  114. for (var _j2 = n - 1; _j2 >= 0; _j2--) {
  115. var total = 0;
  116. for (var _k2 = _j2 + 1; _k2 < n; _k2++) {
  117. total += matrix[_k2][_j2] * coefficients[_k2];
  118. }
  119. coefficients[_j2] = (matrix[n][_j2] - total) / matrix[_j2][_j2];
  120. }
  121. return coefficients;
  122. }
  123. /**
  124. * Round a number to a precision, specificed in number of decimal places
  125. *
  126. * @param {number} number - The number to round
  127. * @param {number} precision - The number of decimal places to round to:
  128. * > 0 means decimals, < 0 means powers of 10
  129. *
  130. *
  131. * @return {numbr} - The number, rounded
  132. */
  133. function round(number, precision) {
  134. var factor = Math.pow(10, precision);
  135. return Math.round(number * factor) / factor;
  136. }
  137. /**
  138. * The set of all fitting methods
  139. *
  140. * @namespace
  141. */
  142. var methods = {
  143. linear: function linear(data, options) {
  144. var sum = [0, 0, 0, 0, 0];
  145. var len = 0;
  146. for (var n = 0; n < data.length; n++) {
  147. if (data[n][1] !== null) {
  148. len++;
  149. sum[0] += data[n][0];
  150. sum[1] += data[n][1];
  151. sum[2] += data[n][0] * data[n][0];
  152. sum[3] += data[n][0] * data[n][1];
  153. sum[4] += data[n][1] * data[n][1];
  154. }
  155. }
  156. var run = len * sum[2] - sum[0] * sum[0];
  157. var rise = len * sum[3] - sum[0] * sum[1];
  158. var gradient = run === 0 ? 0 : round(rise / run, options.precision);
  159. var intercept = round(sum[1] / len - gradient * sum[0] / len, options.precision);
  160. var predict = function predict(x) {
  161. return [round(x, options.precision), round(gradient * x + intercept, options.precision)];
  162. };
  163. var points = data.map(function (point) {
  164. return predict(point[0]);
  165. });
  166. return {
  167. points: points,
  168. predict: predict,
  169. equation: [gradient, intercept],
  170. r2: round(determinationCoefficient(data, points), options.precision),
  171. string: intercept === 0 ? 'y = ' + gradient + 'x' : 'y = ' + gradient + 'x + ' + intercept
  172. };
  173. },
  174. exponential: function exponential(data, options) {
  175. var sum = [0, 0, 0, 0, 0, 0];
  176. for (var n = 0; n < data.length; n++) {
  177. if (data[n][1] !== null) {
  178. sum[0] += data[n][0];
  179. sum[1] += data[n][1];
  180. sum[2] += data[n][0] * data[n][0] * data[n][1];
  181. sum[3] += data[n][1] * Math.log(data[n][1]);
  182. sum[4] += data[n][0] * data[n][1] * Math.log(data[n][1]);
  183. sum[5] += data[n][0] * data[n][1];
  184. }
  185. }
  186. var denominator = sum[1] * sum[2] - sum[5] * sum[5];
  187. var a = Math.exp((sum[2] * sum[3] - sum[5] * sum[4]) / denominator);
  188. var b = (sum[1] * sum[4] - sum[5] * sum[3]) / denominator;
  189. var coeffA = round(a, options.precision);
  190. var coeffB = round(b, options.precision);
  191. var predict = function predict(x) {
  192. return [round(x, options.precision), round(coeffA * Math.exp(coeffB * x), options.precision)];
  193. };
  194. var points = data.map(function (point) {
  195. return predict(point[0]);
  196. });
  197. return {
  198. points: points,
  199. predict: predict,
  200. equation: [coeffA, coeffB],
  201. string: 'y = ' + coeffA + 'e^(' + coeffB + 'x)',
  202. r2: round(determinationCoefficient(data, points), options.precision)
  203. };
  204. },
  205. logarithmic: function logarithmic(data, options) {
  206. var sum = [0, 0, 0, 0];
  207. var len = data.length;
  208. for (var n = 0; n < len; n++) {
  209. if (data[n][1] !== null) {
  210. sum[0] += Math.log(data[n][0]);
  211. sum[1] += data[n][1] * Math.log(data[n][0]);
  212. sum[2] += data[n][1];
  213. sum[3] += Math.pow(Math.log(data[n][0]), 2);
  214. }
  215. }
  216. var a = (len * sum[1] - sum[2] * sum[0]) / (len * sum[3] - sum[0] * sum[0]);
  217. var coeffB = round(a, options.precision);
  218. var coeffA = round((sum[2] - coeffB * sum[0]) / len, options.precision);
  219. var predict = function predict(x) {
  220. return [round(x, options.precision), round(round(coeffA + coeffB * Math.log(x), options.precision), options.precision)];
  221. };
  222. var points = data.map(function (point) {
  223. return predict(point[0]);
  224. });
  225. return {
  226. points: points,
  227. predict: predict,
  228. equation: [coeffA, coeffB],
  229. string: 'y = ' + coeffA + ' + ' + coeffB + ' ln(x)',
  230. r2: round(determinationCoefficient(data, points), options.precision)
  231. };
  232. },
  233. power: function power(data, options) {
  234. var sum = [0, 0, 0, 0, 0];
  235. var len = data.length;
  236. for (var n = 0; n < len; n++) {
  237. if (data[n][1] !== null) {
  238. sum[0] += Math.log(data[n][0]);
  239. sum[1] += Math.log(data[n][1]) * Math.log(data[n][0]);
  240. sum[2] += Math.log(data[n][1]);
  241. sum[3] += Math.pow(Math.log(data[n][0]), 2);
  242. }
  243. }
  244. var b = (len * sum[1] - sum[0] * sum[2]) / (len * sum[3] - Math.pow(sum[0], 2));
  245. var a = (sum[2] - b * sum[0]) / len;
  246. var coeffA = round(Math.exp(a), options.precision);
  247. var coeffB = round(b, options.precision);
  248. var predict = function predict(x) {
  249. return [round(x, options.precision), round(round(coeffA * Math.pow(x, coeffB), options.precision), options.precision)];
  250. };
  251. var points = data.map(function (point) {
  252. return predict(point[0]);
  253. });
  254. return {
  255. points: points,
  256. predict: predict,
  257. equation: [coeffA, coeffB],
  258. string: 'y = ' + coeffA + 'x^' + coeffB,
  259. r2: round(determinationCoefficient(data, points), options.precision)
  260. };
  261. },
  262. polynomial: function polynomial(data, options) {
  263. var lhs = [];
  264. var rhs = [];
  265. var a = 0;
  266. var b = 0;
  267. var len = data.length;
  268. var k = options.order + 1;
  269. for (var i = 0; i < k; i++) {
  270. for (var l = 0; l < len; l++) {
  271. if (data[l][1] !== null) {
  272. a += Math.pow(data[l][0], i) * data[l][1];
  273. }
  274. }
  275. lhs.push(a);
  276. a = 0;
  277. var c = [];
  278. for (var j = 0; j < k; j++) {
  279. for (var _l = 0; _l < len; _l++) {
  280. if (data[_l][1] !== null) {
  281. b += Math.pow(data[_l][0], i + j);
  282. }
  283. }
  284. c.push(b);
  285. b = 0;
  286. }
  287. rhs.push(c);
  288. }
  289. rhs.push(lhs);
  290. var coefficients = gaussianElimination(rhs, k).map(function (v) {
  291. return round(v, options.precision);
  292. });
  293. var predict = function predict(x) {
  294. return [round(x, options.precision), round(coefficients.reduce(function (sum, coeff, power) {
  295. return sum + coeff * Math.pow(x, power);
  296. }, 0), options.precision)];
  297. };
  298. var points = data.map(function (point) {
  299. return predict(point[0]);
  300. });
  301. var string = 'y = ';
  302. for (var _i = coefficients.length - 1; _i >= 0; _i--) {
  303. if (_i > 1) {
  304. string += coefficients[_i] + 'x^' + _i + ' + ';
  305. } else if (_i === 1) {
  306. string += coefficients[_i] + 'x + ';
  307. } else {
  308. string += coefficients[_i];
  309. }
  310. }
  311. return {
  312. string: string,
  313. points: points,
  314. predict: predict,
  315. equation: [].concat(_toConsumableArray(coefficients)).reverse(),
  316. r2: round(determinationCoefficient(data, points), options.precision)
  317. };
  318. }
  319. };
  320. function createWrapper() {
  321. var reduce = function reduce(accumulator, name) {
  322. return _extends({
  323. _round: round
  324. }, accumulator, _defineProperty({}, name, function (data, supplied) {
  325. return methods[name](data, _extends({}, DEFAULT_OPTIONS, supplied));
  326. }));
  327. };
  328. return Object.keys(methods).reduce(reduce, {});
  329. }
  330. module.exports = createWrapper();
  331. });