打赏

JS 对数据进行 正态分布拟合对比(echarts 做图)

对一组看似正态分布的数据进行标准正态分布的拟合,用来做对比

准备:math.js

给定一组数据

yData = [
53, 53, 58.5, 78, 115, 154.5, 200, 300.5, 383.5, 518, 871.5, 1382.5, 2192.5, 3340.5, 5249, 8979.5, 15448, 26225, 44057.5, 71392, 109113, 159006, 224595.5, 307191.5, 405623, 520332, 646965.5, 785170.5, 930962.5, 1078572.5, 1227179.5, 1373870, 1522723.5, 1671622.5, 1812839.5, 1944963, 2068185, 2180604.5, 2280685.5, 2361196.5, 2417123.5, 2457786, 2483891, 2494890, 2496943.5, 2498862.5, 2500857.5, 2500175, 2501485, 2499141, 2492862, 2478390, 2459869.5, 2443707, 2430140, 2421345.5, 2404805, 2377592.5, 2334130, 2263164, 2163510.5, 2031018.5, 1872739.5, 1698482, 1507286, 1306381.5, 1114865.5, 938499, 771654.5, 619227.5, 488654.5, 379699.5, 289925.5, 218837.5, 165510.5, 126806, 97552, 75560, 59062, 46304.5, 36476, 28583, 22280.5, 16965.5, 12631.5, 9265.5, 6629, 4548.5, 3091, 2102.5, 1396.5, 921, 580, 345, 203.5, 137, 87, 46, 27.5, 12, 6
]

xData=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

绘制的图形如下

 

 

 现在要对这个曲线进行正态分布的拟合

  1.求出均值

    

var yArr = [];var sumFuc = (s, c) => formulaCalcByFunc(s + c, 6);//求和匿名函数,formulaCalc为了减少精度误差,可以自己写var ysum = yData.reduce(sumFuc, 0);
for (var n = 0; n < yData.length; n++) {
var y = yData[n] / ysum;
yArr.push(y);
}
var avg = math.mean(yArr);

 

  2.编写最小二乘法高斯拟合函数

function gaussFit(xOriginal, yOriginal, average) {
    var x = [];
    var y = [];
    // 过滤平滑部分
    for (var i = 0; i < yOriginal.length; i++)
        if (yOriginal[i] > average) {
            x.push(xOriginal[i]);
            y.push(yOriginal[i]);
        }

    var zMatrix = math.matrix(math.log(y));
    var zMatrixT = math.transpose(zMatrix);
    var xMatrix = math.ones([y.length, 3]);

    for (var j = 0; j < y.length; j++) {
        xMatrix[j][1] = x[j];
        xMatrix[j][2] = x[j] * x[j];
    }
    // 最小二乘法
    var xMatrixT = math.transpose(xMatrix);
    var bMatrix = math.multiply(math.multiply(math.inv(math.multiply(xMatrixT,xMatrix)), xMatrixT), zMatrixT);

    var b2 = math.subset(bMatrix, math.index(2));
    var b1 = math.subset(bMatrix, math.index(1));
    var b0 = math.subset(bMatrix, math.index(0));

    var s = -1 / b2;
    var xMaxi = s * b1 / 2;//均值
    var yMaxi = math.exp(b0 + xMaxi * xMaxi / s);//正态分布归一系数

    var yFit = []
    for (var n = 0; n < yOriginal.length; n++) {
        yFit.push(gaussFunc(xOriginal[n], xMaxi, yMaxi, s));
    }

    return yFit;
}

//正态分布公式
function gaussFunc(x, xMaxi, yMaxi, s) {
return yMaxi * math.exp(-((x - xMaxi) * (x - xMaxi) / s));
}

  3.求出y轴数据的系数,并还原出原始数据

datas2 = gaussFit(xArr, yArr, avg);
for (var k = 0; k < datas2.length; k++) {
    var l = datas2[k] / yArr[k];
    var y = yData[k] * l;
    yData2.push(y);
}

 

 

最终绘制的图形如下:

 第二种方法,使用标准正态分布公式实现(结果将会偏向原始数据峰值):

/**
* 正态分布函数
* @@param x 数据
* @@param mean 平均数
* @@param stdev 标准差
*/
function normalDistributionfun(x, mean, stdev) {
    return (1 / (Math.sqrt(2 * Math.PI) * stdev)) * Math.exp(-1 * ((x - mean) * (x - mean)) / (2 * stdev * stdev));
}


function calcNormallineDta(xData, yData) {
            var result = [];
         
            var totalCount = 0;

            var firstMode = 0;//峰值起始位置
            var modeCount = 0;//找到目前数据的峰值
        
            for (var i = 0; i < yData.length;i++) {
                totalCount += yData[i];
                if (yData[i] > modeCount) {
                    modeCount = yData[i];
                    firstMode = i;
                }
             
            }
            //找出出x轴左右范围内的均值(关键代码)
            var mode = 0;
            var modeDuplicates = 0;
        
            var fellOffTop = true;

            for (var j = firstMode; j < yData.length; j ++) {
                if (yData[j] > yData[firstMode] - (yData[firstMode]/10)) {//10:分布线系数
                    mode += j ;
                    modeDuplicates++;
                }
                else {
                    fellOffTop = false;
                    break;
                }
            }

            var fellOffBottom = true;
        
            for (var k=firstMode-1;k>=0;k--) {
                if (yData[k] > yData[firstMode] - (yData[firstMode] / 10)) {//10:分布线系数
                    mode += k;
                    modeDuplicates++;
                }
                else {
                    fellOffBottom = false;
                    break;
                }
            }
            var mean;
            if (fellOffBottom || fellOffTop) {
               
                mean = firstMode;
            }
            else {
                mean = mode/ modeDuplicates;
            }
            //求出标准差
          
            var stdev = 0;
        
            for (var n = 0; n < yData.length;n++) {
                stdev += Math.pow((n - mean), 2) * yData[n];
            }
        
            stdev /= totalCount-1;
        
            stdev = Math.sqrt(stdev);
            //带入正态分布公式
            for (var m = 0; m < yData.length;m++) {
                var probability =normalDistributionfun(m,mean,stdev);
                result.push(Math.round(probability * totalCount*100)/100);
            }
            return result;
        }
      

 

 

 

 

 完整代码在echart作品上查看

作品地址:https://www.makeapie.com/editor.html?c=xBtzYvoXr2

 

posted @ 2020-04-11 14:07  Bear.Tirisfal  阅读(9766)  评论(2编辑  收藏  举报