7

I'm trying to calculate a best-fit curve for data using a 3-6 order polynomial. I found this tutorial: Cubic Regression (best fit line) in JavaScript

First, I can't seem to get my outputs to remotely match the curve. jsbin here: http://jsbin.com/qukuqigobu/1/edit?html,js,console,output

var data_x = [500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000];
var data_y = [50,80,100,160,210,265,340,390,440,470,500,500,495,460];

var cubic = function(params,x) {
  return params[0] * x*x*x +
    params[1] * x*x +
    params[2] * x +
    params[3];
};

var objective = function(params) {
  var total = 0.0;
  for(var i=0; i < data_x.length; ++i) {
    var resultThisDatum = cubic(params, data_x[i]);
    var delta = resultThisDatum - data_y[i];
    total += (delta*delta);
  }
  return total;
};

var initial = [1,1,1,1];
var minimiser = numeric.uncmin(objective,initial);

console.log("initial:");
for(var j=0; j<initial.length; ++j) {
  console.log(initial[j]);  
}

console.log("minimiser:");
for(var j=0; j<minimiser.solution.length; ++j) {
  console.log(minimiser.solution[j]);
}

My output coefficients are:

1
-17358.001260500238
80977844.06724495
-96625621220.328

However, using LINEST in excel, they are:

-4.68257E-09
4.26789E-05
-0.01
45.39760539

I calculated Y values from the Xs in Excel using this to confirm a good correlation. The minimizer results do not work.

This is the first step, but ideally I'd want to be able to do the same thing for 4th, 5th and 6th order polynomials as well.

Any help would be much appreciated. Thanks.

Community
  • 1
  • 1
ProDarwin
  • 141
  • 1
  • 6

3 Answers3

7

Figured it out using Matrix Algebra:

var x = [500,1000,1500,2000,2500,3000,3500,4000,4500,5000,5500,6000,6500,7000];
var y = [50,80,100,160,210,265,340,390,440,470,500,500,495,460];

order = 3;

var xMatrix = [];
var xTemp = [];
var yMatrix = numeric.transpose([y]);

for (j=0;j<x.length;j++)
{
    xTemp = [];
    for(i=0;i<=order;i++)
    {
        xTemp.push(1*Math.pow(x[j],i));
    }
    xMatrix.push(xTemp);
}

var xMatrixT = numeric.transpose(xMatrix);
var dot1 = numeric.dot(xMatrixT,xMatrix);
var dotInv = numeric.inv(dot1);
var dot2 = numeric.dot(xMatrixT,yMatrix);
var solution = numeric.dot(dotInv,dot2);
console.log("Coefficients a + bx^1 + cx^2...")
console.log(solution);

jsbin: http://jsbin.com/yoqiqanofo/1/

ProDarwin
  • 141
  • 1
  • 6
  • Hey but what to do after getting those numbers? How can I plugged them into the formula to perform prediction? – QWERTY Oct 23 '17 at 13:32
  • @hyperfkcb I know I am a few months late but it would probably give you a better understanding if you read: https://en.wikipedia.org/wiki/Polynomial_regression his code is an implementation of just one method of calculating a "best bit" for an nth order polynomial. – theshadow124 Jan 08 '18 at 07:46
  • @hyperfkcb Which library did you use? "numeric is not defined"... – Digerkam Jun 02 '20 at 12:55
  • @theshadow Do you know which library it is? – Digerkam Jun 02 '20 at 12:56
  • 1
    @Digerkam if you look at the jsbin they linked, they are importing http://www.numericjs.com/lib/numeric-1.2.6.min.js in the html – theshadow124 Jun 03 '20 at 18:16
  • The numericjs.com website is down but you can still get the code on GitHub: github.com/sloisel/numeric – Jacob Philpott Jan 27 '21 at 01:24
1

You might be intersted in MLjs's Polynomial Regression library.

Sample usage copied from its documentation:

import PolynomialRegression from 'ml-regression-polynomial';

const x = [50, 50, 50, 70, 70, 70, 80, 80, 80, 90, 90, 90, 100, 100, 100];
const y = [3.3, 2.8, 2.9, 2.3, 2.6, 2.1, 2.5, 2.9, 2.4, 3.0, 3.1, 2.8, 3.3, 3.5, 3.0];
const degree = 5; // setup the maximum degree of the polynomial

const regression = new PolynomialRegression(x, y, degree);
console.log(regression.predict(80)); // Apply the model to some x value. Prints 2.6.
console.log(regression.coefficients); // Prints the coefficients in increasing order of power (from 0 to degree).
console.log(regression.toString(3)); // Prints a human-readable version of the function.
console.log(regression.toLaTeX());
console.log(regression.score(x, y));
Maxime Pacary
  • 22,336
  • 11
  • 85
  • 113
0

Alglib.js can be used for cubic spline fitting. You can use it on any other functional form for that matter.

https://jsfiddle.net/webalizer12/46tcf8w9/26/

var f = function(a_n, x){
    return a_n[3]*Math.pow(x, 3)+a_n[2]*Math.pow(x, 2)+a_n[1]*Math.pow(x, 1)+a_n[0];
}

var data = [[500,50],
[1000,80],
[1500,100],
[2000,160],
[2500,210],
[3000,265],
[3500,340],
[4000,390],
[4500,440],
[5000,470],
[5500,500],
[6000,500],
[6500,495],
[7000,460]];

var fn1 = function(a){
    let sum = 0
    for (let i = 0; i < data.length; ++i) {
        sum = sum + Math.pow(data[i][1] - f(a, data[i][0]), 2)
    }
    let sse = Math.sqrt(sum)
    return sse
}

let solver = new Alglib()
solver.add_function(fn1) //Add the first equation to the solver.

solver.promise.then(function(result) { 
    let x_guess = [0,0,0,0] //Guess the initial values of the solution.
let x_scale = [10,1e-1, 1e-4, 1e-8] //Set the scale of the values in the function only positive values here.
let max_iterations=50000
let penalty=50.0
let radius=0.1
let diffstep=0.000001
let stop_threshold=0.000001
solver.solve("min", x_guess, x_scale, max_iterations, penalty, radius, diffstep, stop_threshold) //Solve the equation

let a=solver.get_results()
console.log(solver.get_results())})