javascript画布:用曲线绘制移动平均线

Chr*_*eid 0 javascript canvas stockquotes

因此,基本上,我想在时间序列折线图的一定数量的点上绘制一条弯曲的平均线。像这样:

在此处输入图片说明

我希望它跨越图表的整个长度,但是我无法弄清楚如何计算起点和终点,因为(我认为)平均值将是每个部分中间的一个点。查看带有移动平均线的股票图表,您可以看到我想要实现的目标:

在此处输入图片说明

我首先根据一段时间将数据数组分成多个块,以计算平均值。所以,如果我开始:

[
    { time: 1, value: 2 }, 
    { time: 2, value: 4 },
    { time: 3, value: 5 },
    { time: 4, value: 7 },
]
Run Code Online (Sandbox Code Playgroud)

我到达:

var averages = [
   {
      x: 1.5,
      y: 3,
   },
   {
       x: 3.5  (the average time)
       y: 6    (the average value)
   },
]
Run Code Online (Sandbox Code Playgroud)

这是我尝试过以不完整的行结束的方法,该行不从图表的开头开始,也不在图表的结尾处停止,而是在图表的第一个平均时间加星号和结尾:

            ctx.moveTo((averages[0].x), averages[0].y);

            for(var i = 0; i < averages.length-1; i ++)
            {

              var x_mid = (averages[i].x + averages[i+1].x) / 2;
              var y_mid = (averages[i].y + averages[i+1].y) / 2;
              var cp_x1 = (x_mid + averages[i].x) / 2;
              var cp_x2 = (x_mid + averages[i+1].x) / 2;
              ctx.quadraticCurveTo(cp_x1, averages[i].y ,x_mid, y_mid);
              ctx.quadraticCurveTo(cp_x2, averages[i+1].y ,averages[i+1].x, averages[i+1].y);
            }

            ctx.stroke();
Run Code Online (Sandbox Code Playgroud)

你会怎么做?

Bli*_*n67 5

要获得移动平均值,您只需获取当前样本两侧的n点平均值即可。

例如

// array of data points 
const movingMean = []; // the resulting means
const data = [12,345,123,53,134,...,219]; // data with index representing x axis
const sampleSize = 5;
for(var i = sampleSize; i < data.length - sampleSize; i++){
    var total = 0;
    for(var j = i- sampleSize; j < i + sampleSize;  j++){
         total += data[j];
    }
    movingMean[i] = total / (sampleSize * 2);
}
Run Code Online (Sandbox Code Playgroud)

此方法不会将均值向前拉,不会为每个数据点提供最准确的均值。

这种方法的问题在于,您没有获得前n个和后n个样本的均值,其中n是均值两侧的样本数。

您可以选择另一种方法,将均值向前拉一点,但是通过应用加权均值,可以稍微降低偏差

for(var i = sampleSize; i < data.length + Math.floor(sampleSize / 4); i++){
    var total = 0;
    var count = 0;
    for(var j = sampleSize; j > 0; j --){
        var index = i - (sampleSize - j);
        if(index < data.length){
             total += data[index] * j; // linear weighting
            count += j; 
        }
    }
    movingMean[i-Math.floor(sampleSize / 4)] = total / count;
}
Run Code Online (Sandbox Code Playgroud)

这种方法使该平均值更接近当前样本末端。

该示例显示了一个随机数据集,并在其上绘制了两种均值。单击以获取新图。红线是移动均值,蓝色线是加权均值。请注意,蓝线趋向于跟随数据有点慢。绿线是加权平均值,其样本范围是其他两个样本的4倍。

// array of data points 
const movingMean = []; // the resulting means
const data = [12,345,123,53,134,...,219]; // data with index representing x axis
const sampleSize = 5;
for(var i = sampleSize; i < data.length - sampleSize; i++){
    var total = 0;
    for(var j = i- sampleSize; j < i + sampleSize;  j++){
         total += data[j];
    }
    movingMean[i] = total / (sampleSize * 2);
}
Run Code Online (Sandbox Code Playgroud)
for(var i = sampleSize; i < data.length + Math.floor(sampleSize / 4); i++){
    var total = 0;
    var count = 0;
    for(var j = sampleSize; j > 0; j --){
        var index = i - (sampleSize - j);
        if(index < data.length){
             total += data[index] * j; // linear weighting
            count += j; 
        }
    }
    movingMean[i-Math.floor(sampleSize / 4)] = total / count;
}
Run Code Online (Sandbox Code Playgroud)
// helper functions
const doFor = (count, callback) => {var i = 0; while (i < count) { callback(i ++) } };
const setOf = (count, callback) => {var a = [],i = 0; while (i < count) { a.push(callback(i ++)) } return a };
const rand  = (min, max = min + (min = 0)) => Math.random() * (max - min) + min;
const randG = (dis, min, max) => {var r = 0; doFor(dis,()=>r+=rand(min,max)); return r / dis};
function getMinMax(data){
    var min = data[0];
    var max = data[0];
    doFor(data.length - 1, i => {
        min = Math.min(min,data[i+1]);
        max = Math.max(max,data[i+1]);
    });
    var range = max-min;
    return {min,max,range};
}
function plotData(data,minMax){
   ctx.beginPath();
   for(var i = 0; i < data.length; i++){
      if(data[i] !== undefined){
          var y = (data[i] - minMax.min) / minMax.range;
          y = y *(ctx.canvas.height - 2) + 1;
          ctx.lineTo(i/2,y);
      }
   }
   ctx.stroke();
}
function getMovingMean(data,sampleSize){ 
    const movingMean = []; // the resulting means
    for(var i = sampleSize; i < data.length - sampleSize; i++){
        var total = 0;
        for(var j = i- sampleSize; j < i + sampleSize;  j++){
             total += data[j];
        }
        movingMean[i] = total / (sampleSize * 2);
    }
    return movingMean[i];
} 

function getMovingMean(data,sampleSize){ 
    const movingMean = []; // the resulting means
    for(var i = sampleSize; i < data.length - sampleSize; i++){
        var total = 0;
        for(var j = i- sampleSize; j < i + sampleSize;  j++){
             total += data[j];
        }
        movingMean[i] = total / (sampleSize * 2);
    }
    return movingMean;
} 

function getWeightedMean(data,sampleSize){
    const weightedMean = [];
    for(var i = sampleSize; i < data.length+Math.floor(sampleSize/4); i++){
        var total = 0;
        var count = 0;
        for(var j = sampleSize; j > 0; j --){
            var index = i - (sampleSize - j);
            if(index < data.length){
                total += data[index] * j; // linear weighting
                count += j; 
            }

        }
        weightedMean[i-Math.floor(sampleSize/4)] = total / count;
    }
    return weightedMean;
}
const dataSize = 1000;
const sampleSize = 50;
canvas.width = dataSize/2;
canvas.height = 200;
const ctx = canvas.getContext("2d");
function displayData(){
    ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
    var dataPoint = 100;
    var distribution = Math.floor(rand(1,8));
    var movement = rand(2,20);
    const data = setOf(dataSize,i => dataPoint += randG(distribution, -movement, movement));
    const movingMean = getMovingMean(data, sampleSize);
    const weightedMean = getWeightedMean(data, sampleSize*2);
    const weightedMean1 = getWeightedMean(data, sampleSize*8);
    var minMax = getMinMax(data);
    ctx.strokeStyle = "#ccc";
    plotData(data,minMax);
    ctx.strokeStyle = "#F50";
    plotData(movingMean,minMax);
    ctx.strokeStyle = "#08F";
    plotData(weightedMean,minMax);
    ctx.strokeStyle = "#4C0";
    plotData(weightedMean1,minMax);
}
displayData();
document.onclick = displayData;
Run Code Online (Sandbox Code Playgroud)