From 88ae34058d6bf80de5f95cdea3c3337f5c8fc601 Mon Sep 17 00:00:00 2001 From: EscapedGibbon Date: Fri, 3 May 2024 10:16:57 +0200 Subject: [PATCH 1/2] feat!: throw an error if not sufficient number of points --- src/regression/poly-fit-regression2d.js | 40 +++++++++---------------- 1 file changed, 14 insertions(+), 26 deletions(-) diff --git a/src/regression/poly-fit-regression2d.js b/src/regression/poly-fit-regression2d.js index 75717c3..242099c 100644 --- a/src/regression/poly-fit-regression2d.js +++ b/src/regression/poly-fit-regression2d.js @@ -2,7 +2,7 @@ import { Matrix, SVD } from 'ml-matrix'; import BaseRegression from 'ml-regression-base'; const defaultOptions = { - order: 2 + order: 2, }; // Implements the Kernel ridge regression algorithm. // http://www.ics.uci.edu/~welling/classnotes/papers_class/Kernel-Ridge.pdf @@ -57,7 +57,7 @@ export default class PolynomialFitRegression2D extends BaseRegression { if (X.columns !== 2) { throw new RangeError( - `You give X with ${X.columns} columns and it must be 2` + `You give X with ${X.columns} columns and it must be 2`, ); } if (X.rows !== y.rows) { @@ -66,29 +66,19 @@ export default class PolynomialFitRegression2D extends BaseRegression { var examples = X.rows; var coefficients = ((this.order + 2) * (this.order + 1)) / 2; + if (examples < coefficients) { + throw new Error( + 'Insufficient number of points to create regression model.', + ); + } this.coefficients = new Array(coefficients); var x1 = X.getColumnVector(0); var x2 = X.getColumnVector(1); - var scaleX1 = - 1.0 / - x1 - .clone() - .abs() - .max(); - var scaleX2 = - 1.0 / - x2 - .clone() - .abs() - .max(); - var scaleY = - 1.0 / - y - .clone() - .abs() - .max(); + var scaleX1 = 1.0 / x1.clone().abs().max(); + var scaleX2 = 1.0 / x2.clone().abs().max(); + var scaleY = 1.0 / y.clone().abs().max(); x1.mulColumn(0, scaleX1); x2.mulColumn(0, scaleX2); @@ -109,7 +99,7 @@ export default class PolynomialFitRegression2D extends BaseRegression { var svd = new SVD(A.transpose(), { computeLeftSingularVectors: true, computeRightSingularVectors: true, - autoTranspose: false + autoTranspose: false, }); var qqs = Matrix.rowVector(svd.diagonal); @@ -128,9 +118,7 @@ export default class PolynomialFitRegression2D extends BaseRegression { var U = svd.rightSingularVectors; var V = svd.leftSingularVectors; - this.coefficients = V.mmul(qqs.transpose()) - .mmul(U.transpose()) - .mmul(y); + this.coefficients = V.mmul(qqs.transpose()).mmul(U.transpose()).mmul(y); col = 0; @@ -143,7 +131,7 @@ export default class PolynomialFitRegression2D extends BaseRegression { (this.coefficients.get(col, 0) * Math.pow(scaleX1, i) * Math.pow(scaleX2, j)) / - scaleY + scaleY, ); col++; } @@ -172,7 +160,7 @@ export default class PolynomialFitRegression2D extends BaseRegression { return { name: 'polyfit2D', order: this.order, - coefficients: this.coefficients + coefficients: this.coefficients, }; } From 29b027c5e10bbbe26f92eb98369f39f1f9b2d2ce Mon Sep 17 00:00:00 2001 From: EscapedGibbon Date: Fri, 3 May 2024 10:17:17 +0200 Subject: [PATCH 2/2] test: add testing case on error throw --- src/__tests__/2d-polinomial-fit.js | 43 +++++++++++++++++------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/__tests__/2d-polinomial-fit.js b/src/__tests__/2d-polinomial-fit.js index 2b4b579..cc1825b 100644 --- a/src/__tests__/2d-polinomial-fit.js +++ b/src/__tests__/2d-polinomial-fit.js @@ -9,20 +9,18 @@ describe('2D polinomial fit', () => { } const pf = new Polyfit(X, y, { - order: 2 + order: 2, }); it('Training coefficients', () => { const estimatedCoefficients = [ - 1.5587e1, - 3.8873e-1, - 5.2582e-3, - 4.8498e-1, - 2.1127e-3, - -7.3709e-3 + 1.5587e1, 3.8873e-1, 5.2582e-3, 4.8498e-1, 2.1127e-3, -7.3709e-3, ]; for (let i = 0; i < estimatedCoefficients.length; ++i) { - expect(pf.coefficients.get(i, 0)).toBeCloseTo(estimatedCoefficients[i], 1e-2); + expect(pf.coefficients.get(i, 0)).toBeCloseTo( + estimatedCoefficients[i], + 1e-2, + ); } }); @@ -44,16 +42,8 @@ describe('2D polinomial fit', () => { it('Other function test', () => { var testValues = [ - 15.041667, - 9.375, - 5.041667, - 2.041667, - 0.375, - 0.041667, - 1.041667, - 3.375, - 7.041667, - 12.041667 + 15.041667, 9.375, 5.041667, 2.041667, 0.375, 0.041667, 1.041667, 3.375, + 7.041667, 12.041667, ]; var len = 21; @@ -67,7 +57,7 @@ describe('2D polinomial fit', () => { } var polyFit = new Polyfit(X, y, { - order: 2 + order: 2, }); var test = 10; @@ -85,4 +75,19 @@ describe('2D polinomial fit', () => { expect(predict[i]).toBeCloseTo(testValues[i], 1e-2); } }); + it('must throw error', () => { + const X = new Array(5); + const y = new Array(5); + for (let i = 0; i < 5; ++i) { + X[i] = [i, i + 10]; + y[i] = i + 20; + } + + expect(() => { + const polyfit = new Polyfit(X, y, { + order: 4, + }); + return polyfit; + }).toThrow('Insufficient number of points to create regression model.'); + }); });