2017-06-08 90 views
0

基本的に、時系列折れ線グラフの一定量の点に曲線平均線を描きたいと思います。javascript canvas:曲線付き移動平均線を描く

enter image description here

私はそれがチャートの全体の長さにまたがるにしたいが、私は平均が(と思う)のポイントになるので、開始点と終了点を計算する方法を見つけ出すことはできません。このような各セクションの中央。あなたは私がacheiveたいものを見ることができます移動平均と株価チャートを見て:

enter image description here

私は時間の期間に基づいてチャンクにデータ配列を分割することにより、第1の平均値を計算します。私はで始まるのであれば:これは私が不完全な行で終わるところ私は、チャートの先頭から開始does notのいずれかを試してみたものです

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

[ 
    { time: 1, value: 2 }, 
    { time: 2, value: 4 }, 
    { time: 3, value: 5 }, 
    { time: 4, value: 7 }, 
] 

私はを取得します最後に停止しませんが、最初の平均時間にチャート内の星と終わり:

  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(); 

どうやってやりますか?

答えて

1

移動平均を取得するには、現在のサンプルの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); 
} 

この方法では、各データポイントの最も正確な平均を与える前方の平均を引いていません。

この方法の問題点は、最初の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; 
} 

を減らすことができ加重平均を適用して平均値を引っ張ってくるの代替を行うことができます。

この例では、ランダムデータセットとその上にプロットされた2種類の手段が示されています。クリックすると新しい図が表示されます。赤い線が移動平均で、青が加重平均です。青い線がデータに少し遅れていく傾向があることに注意してください。 緑色の線は、他の2倍よりも4倍大きいサンプル範囲を持つ加重平均です。

// 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;
body { font-family : arial; } 
 
.red { color : #F50; } 
 
.blue { color : #0AF; } 
 
.green { color : #4C0; } 
 
canvas { position : absolute; top : 0px; left :130px; }
<canvas id="canvas"></canvas> 
 
<div class="red">Moving mean</div> 
 
<div class="blue">Weighted mean</div> 
 
<div class="green">Wide weighted mean</div> 
 
<div>Click for another sample</div>