JavaScript中的三次回归(最佳拟合线)

Cubic Regression (best fit line) in JavaScript

本文关键字:回归 最佳 三次 拟合 JavaScript      更新时间:2023-09-26

我正试图找到一个JavaScript代码,可以让我做三次回归最糟糕的时间。我想自己写,但我对多项式数学的理解是,嗯,次优的。

所以,这就是我要找的。给定一个数组的数组的输入,其中内部数组为[x,y],该函数将以具有四个参数的数组形式输出- [a,b,c,d],其中a,b,c和d是方程y = ax^3 + bx^2 + cx + d的参数。

的例子:输入数组这样的[[2、5],[5 10],[07年15],[12、20],[20 25],[32岁,30],[50岁,35]]。

本质上是表的表示:

<>之前| x | y ||-----------------|| 02 | 05 || 05 | 10 || 07 | 15 || 12 | 20 || 20 | 25 || 32 | 30 || 50 | 35 |之前

现在,输出将是[0.000575085,-0.058861065,2.183957502,1.127605507]。这些是三次函数的a、b、c和d参数。

(仅供参考,我通过使用Excel的LINEST函数并使用数组函数{1,2,3}在上述一组数字上运行它获得的输出)。

这是怎么做到的?非常感谢您的指导。

最好的,汤姆

这是一个真实的,工作的代码位来解决立方使用numeric.js库的uncmin无约束最小化作为最小二乘问题(jsbin在这里):

var data_x = [2,5,7,12,20,32,50];
var data_y = [5,10,15,20,25,30,35];
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]);
}

我得到了结果:

 0.0005750849851827991
-0.05886106462847641
 2.1839575020602164
 1.1276055079334206

解释一下:我们有一个函数'cubic',它对一组参数params和一个值x的一般三次函数求值。该函数被包装以创建目标函数,目标函数接受一组参数,并通过目标函数运行数据集中的每个x值,并计算平方和。这个函数通过一组初始值从numeric.js传递给uncmin;uncmin完成最困难的工作并返回一个对象,其solution属性包含优化的参数集。

要做到这一点,不需要全局变量(调皮!),你可以有一个目标函数工厂:

var makeObjective = function(targetFunc,xlist,ylist) {
  var objective = function(params) {
    var total = 0.0;
    for(var i=0; i < xlist.length; ++i) {
      var resultThisDatum = targetFunc(params, xlist[i]);
      var delta = resultThisDatum - ylist[i];
      total += (delta*delta);
    }
    return total;
  };
  return objective;
};

可以用来制造目标函数:

var objective = makeObjective(cubic, data_x, data_y); // then carry on as before
知道如何实际地做这件事对很多人都有很大的帮助,所以我很高兴这个问题被提出来了。

编辑:关于cubic的澄清

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

Cubic被定义为一个函数,它接受一个参数数组params和一个值x。给定params,我们可以定义函数f(x)。对于一个立方,也就是f(x) = a x^3 + b x^2 + c x + d,所以有4个参数([0][3]),给定这4个参数值,我们有一个单一的函数f(x)和一个输入x

代码的结构允许您用相同结构的另一个函数替换cubic;它可以是linear和2个参数:

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

代码的其余部分将查看params的长度,以便知道需要修改多少参数。

注意,这一整段代码试图找到一组参数值,产生最适合所有数据的曲线;如果您想找到适合某些数据的最后4个点,则只传递data_xdata_y中的这些值。

我将其表述为最小二乘问题。设Mn×4矩阵:

x_1^3  x_1^2  x_1  1
x_2^3  x_2^2  x_2  1
  ⋮       ⋮      ⋮
x_n^3  x_n^2  x_n  1

然后计算4×4矩阵<我> = <我><一口> T ⋅<我> 和4×1列向量b <我> = <我><一口> T ⋅y <我> 和解决线性方程组<我>ξ = <我> 。得到的向量ξ将包含系数ad

上面的描述可以很容易地从数学上理解发生了什么。对于实现,特别是对于非常大的n,上述方法可能是不可行的。在这些情况下,您可以直接构建Ab,而无需显式地构建M。例如A1,2= sum(x_i^3 * x_i^2 for all i)。因此,您可以遍历所有i并将相应的值添加到相应的矩阵和向量项中。