最小二乘法拟合

使用工具

p5.js 是开源的设计师工具,专攻画图
下面是文档、工具包和官网(本文只用了其中的p5.min.js)
链接:https://pan.baidu.com/s/1i5D0OpZ 密码:e04v
https://p5js.org/

拟合直线

最终效果

两个点
三个点
多个点

数学公式

∑ i = 0 n ( x − x ‾ ) ( y − y ‾ ) ∑ i = 0 n ( x − x ‾ ) ( x − x ‾ ) \frac{\sum_{i=0}^n (x-\overline x)(y-\overline y)}{\sum_{i=0}^n(x-\overline x)(x-\overline x)} i=0n(xx)(xx)i=0n(xx)(yy)

代码 JS

var data=[];//点坐标
var a=1;//拟合直线的x系数
var b=0;//拟合直线的常熟b
/**
 * 初始函数
 */
function setup() {//p5.js的初始方法
    createCanvas(400,400);
    background(0,3,3);
}
/**
 * 一次函数线性回归(系数项和常数项)
 */
function linearRegression(){
    var xsum=0;//x的多项和
    var ysum=0;//y的多项和
    for(var i=0;i<data.length;i++){
        xsum+=data[i].x;
        ysum+=data[i].y;
    }
    var xmean=xsum/data.length;//x的平均数
    var ymean=ysum/data.length;//y的平均数
    var num=0;//多项式和【(x-x的均值)*(y-y的均值)】
    var den=0;//多项式和【(x-x的均值)*(x-x的均值)】
    for(var i=0;i<data.length;i++){
        var x=data[i].x;
        var y=data[i].y;
        num+=(x-xmean)*(y-ymean);
        den+=(x-xmean)*(x-xmean);
    }
    a=num/den;//y=ax+b 的 系数a
    b=ymean-a*xmean;//y=ax+b 的 系数b
}
/**
 * 鼠标点击
 */
function mousePressed(){
    var x=map(mouseX,0,width,0,100);
    var y=map(mouseY,0,height,100,0);
    var point = createVector(x,y);
    data.push(point);
}
/**
 * 画直线
 */
function drawLine(){
    var x1=0;//得到(x1,y1),(x2,y2),即可画直线
    var x2=100;
    var y1=a*x1+b;
    var y2=a*x2+b;
    x1=map(x1,0,100,0,width);
    x2=map(x2,0,100,0,width);
    y1=map(y1,0,100,height,0);
    y2=map(y2,0,100,height,0);
    stroke(255);
    line(x1,y1,x2,y2);
}
/**
 * 绘画(点和线)
 */
function draw() {
    background(50);
    for(var i=0;i<data.length;i++){
        var x=map(data[i].x,0,100,0,width);
        var y=map(data[i].y,0,100,height,0);
        fill(255,0,0);//设置填充颜色
        stroke(255);//设置边框颜色
        strokeWeight(2);//设置点的宽度
        ellipse(x,y,10,10);//椭圆
    }
    if(data.length>1){
        linearRegression();
        drawLine();
    }
}

代码 html

<!DOCTYPE html>
<html>
  <head>
    <meta name="viewport" width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0>
    <style> body {padding: 0; margin: 0;} </style>
    <script src="libs/p5.min.js"></script>
    <script src="sketch.js"></script>
  </head>
  <body>
  </body>
</html>

曲线拟合(二次)

最终效果

三个点
四个点
多个点

数学公式 这个公式有点复杂 上个截图(注意其中的p(x),α,β,a)

二次型和多次型的公式是一致的。二次函数拟合做成后可以延伸至多次
曲线拟合公式
二次拟合公式

代码 JS

var data=[];//点坐标
/**
 * 初始函数
 */
function setup() {
    createCanvas(400,400);
    background(0,3,3);
}
var alpha0=0;
var alpha1=0;
var a0=0;
var a1=0;
var a2=0;
var beta0=0;
/**
 * 二次函数
 */
function unLinearRegression(){
    var xsum=0;//x的和
    var ysum=0;//y的和

    for(var i=0;i<data.length;i++){
        xsum+=data[i].x;
        ysum+=data[i].y;
    }
    var length=data.length;//p0(x)=1,所以p0(x)的和就是数据长度
    alpha0=xsum/data.length;//α0
    var p1data=[];//p1(x)
    var p1p1sum=0;//p1*p1 多项式的和
    var xp1p1sum=0;//x*p1*p1 多项式的和
    for(var i=0;i<data.length;i++){
        var p1=data[i].x-alpha0;
        p1p1sum+=p1*p1;
        xp1p1sum+=data[i].x*p1*p1;
        p1data.push(p1);
    }
    alpha1=xp1p1sum/p1p1sum;//α1
    beta0=p1p1sum/length;//β0
    var p2data=[];//p2(x)
    var p2p2sum=0;//p2*p2 多项式的和
    var xp2p2sum=0;//x*p2*p2 多项式的和
    for(var i=0;i<data.length;i++){
        var p2=(data[i].x-alpha1)*p1data[i]-beta0;
        p2data.push(p2);
        p2p2sum+=p2*p2;
        xp2p2sum+=data[i].x*p2*p2;
    }
    //var alpha2=xp2p2sum/p2p2sum;
    var fp0=ysum;//y*p0 多项式的和
    var fp1=0;//y*p1 多项式的和
    var fp2=0;//y*p2 多项式的和
    for(var i=0;i<data.length;i++){
        fp1+=data[i].y*p1data[i];
        fp2+=data[i].y*p2data[i];
    }
    a0=fp0/length;//g(x)=a0*p0(x)+a1*p1(x)+a2*p2(x) 二次多项式拟合公式
    a1=fp1/p1p1sum;
    a2=fp2/p2p2sum;
}
/**
 * 画曲线
 */
function drawCurve(){
    var newdata=[];//二次多项式 点数据
    for(var i=0;i<100;i++){//生成100个点画曲线
        var x=i*100;
        var pp1x=x-alpha0;
        var pp2x=(x-alpha1)*(x-alpha0)-beta0;
        var y=(a0+a1*pp1x+a2*pp2x);
        /*var y=x*x+90;*/
        var point=createVector(x,y);
        newdata.push(point);
    }
    //b=x*x;
    var i=0;
    noFill();
    //三个点即可形成一条曲线
    if(i<newdata.length){
        var p1x=newdata[i].x;
        var p1y=newdata[i].y;
        p1x=map(p1x,0,100,0,width);
        p1y=map(p1y,0,100,height,0);
        i++;
        var p2x=newdata[i].x;
        var p2y=newdata[i].y;
        p2x=map(p2x,0,100,0,width);
        p2y=map(p2y,0,100,height,0);
        i++;
        var p3x=newdata[i].x;
        var p3y=newdata[i].y;
        p3x=map(p3x,0,100,0,width);
        p3y=map(p3y,0,100,height,0);
        i++;
/*      var p4x=newdata[i].x;
        var p4y=newdata[i].y;
        p4x=map(p4x,0,100,0,width);
        p4y=map(p4y,0,100,height,0);
        i++;*/
        var p1 = {x: p1x, y: p1y};
        var p2 = {x: p2x, y: p2y};
        var p3 = {x: p3x, y: p3y};
        //var p4 = {x: p4x, y: p4y};
        noFill();
        stroke(138, 43, 226);//曲线颜色 - 紫罗兰色
        strokeWeight(4);//曲线宽度
        curve(p1.x, p1.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
    }
}
/**
 * 鼠标点击
 */
function mousePressed(){
    var x=map(mouseX,0,width,0,100);
    var y=map(mouseY,0,height,100,0);
    var point = createVector(x,y);
    data.push(point);
}
/**
 * 绘画(点和线)
 */
function draw() {
    background(50);
    for(var i=0;i<data.length;i++){
        var x=map(data[i].x,0,100,0,width);
        var y=map(data[i].y,0,100,height,0);
        fill(255,0,0);//设置填充颜色
        stroke(255);//设置边框颜色
        strokeWeight(2);//设置点的宽度
        ellipse(x,y,10,10);//椭圆
    }
    if(data.length>2){
        unLinearRegression();
        drawCurve();
    }
}

代码 HTML 和直线一致

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐