2017-06-08 7 views
0

Also im Grunde möchte ich eine gekrümmte Durchschnittslinie über eine bestimmte Anzahl von Punkten eines Zeitreihen Liniendiagramm zeichnen. Wie folgt aus:Javascript Leinwand: zeichne bewegliche Durchschnittslinie mit Kurven

enter image description here

Ich will es über die gesamte Länge des Diagramms zu überspannen, aber ich kann nicht herausfinden, wie die Start- und Endpunkte zu berechnen, weil die durchschnittlichen würde (glaube ich) ein Punkt in die Mitte jedes Abschnitts. Mit Blick auf ein Lager Chart mit durchschnittlich bewegen können Sie sehen, was ich acheive wollen:

enter image description here

ich die Mittelwerte zunächst durch Aufteilen des Datenfeldes berechnen bis in Stücke auf einen bestimmten Zeitraum basiert. Also, wenn ich mit Start:

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

erreiche ich:

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

Dies ist, was ich versucht habe, wo ich mit einer unvollständigen Zeile am Ende, eine, die am Anfang des Diagramms beginnen tut und tut am Ende zu stoppen, aber Sterne und endet in der Tabelle auf der ersten Durchschnittszeit:

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

Wie würden Sie das tun?

Antwort

1

Um einen beweglichen Mittelwert zu erhalten, müssen Sie nur den Mittelwert von n Punkten auf beiden Seiten der aktuellen Probe ermitteln.

Zum Beispiel

// 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); 
} 

Diese Methode nicht den Mittelwert nach vorne zieht die genaueste Mittelwert für jeden Datenpunkt zu geben.

Das Problem mit dieser Methode ist, dass Sie keinen Mittelwert für die ersten n und letzten n Stichproben erhalten, wobei n die Anzahl der Stichproben auf jeder Seite des Mittelwerts ist.

Sie können eine Alternative tun, die den Mittelwert ein wenig nach vorne ziehen, sondern durch einen gewichteten Mittelwert Beantragen Sie die Vorspannung ein wenig

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; 
} 

Dieses Verfahren, dass ein engere reduzieren können auf die aktuellen Probenende bedeuten hält.

Das Beispiel zeigt einen zufälligen Datensatz und die beiden Arten von Mitteln, die darüber gezeichnet werden. Klicken Sie, um ein neues Grundstück zu erhalten. Die rote Linie ist das bewegte Mittel und das Blau ist das gewichtete Mittel. Beachten Sie, dass die blaue Linie dazu neigt, den Daten etwas langsam zu folgen. Die grüne Linie ist ein gewichtetes Mittel mit einem viermal größeren Stichprobenbereich als die anderen beiden.

// 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>

Verwandte Themen