javascript - How to reduce a data graph but keeping the extremes -


i have database has got month full of datasets in 10min intervals. (so dataset every 10min)

now want show data on 3 graphs: last 24 hours, last 7 days , last 30 days.

the data looks this:

{ "data" : 278, "date" : isodate("2016-08-31t01:51:05.315z") } { "data" : 627, "date" : isodate("2016-08-31t01:51:06.361z") } { "data" : 146, "date" : isodate("2016-08-31t01:51:07.938z") } // etc 

for 24h graph output data last 24h, that's easy.

for other graphs thin data:

const data = {}; //data database let newdata = []; const interval = 7; //for 7 days interval 7, 30 days it's 30  for( let = 0; < data.length; += interval ) {     newdata.push( data[ ] ); }; 

this works fine extreme events data 0 or differs other values average, can lost depending on time search data. not thinning out data result in large sum of data points sent on pipe , have processed on front end. i'd avoid that.

now question

how can reduce data 7 day period while keeping extremes in it? what's efficient way here?

additions: in essence think i'm trying simplify graph reduce points keep overall shape. (if @ pure image perspective)

something implementation of douglas–peucker algorithm in node? enter image description here

as mention in comments, ramer-douglas-peucker (rdp) algorithm used process data points in 2d figures want use graph data x values fixed. modified this javascript implementation of algorithm provided m oehm consider vertical (y) distance in calculations.

on other hand, data smoothing suggested reduce number of data points in graph (see this post csgillespie).

in order compare 2 methods, made small test program. reset button creates new test data. algorithm can selected , applied obtain reduced number of points, separated specified interval. in case of rdp algorithm however, resulting points not evenly spaced. same number of points specified interval, run calculations iteratively, adjusting espilon value each time until correct number of points reached.

from tests, rdp algorithm gives better results. downside spacing between points varies. don't think can avoided, given want keep extreme points not evenly distributed in original data.

here code snippet, better seen in full page mode:

var svgns = 'http://www.w3.org/2000/svg';  var graph = document.getelementbyid('graph1');  var grprawdata = document.getelementbyid('grprawdata');  var grpcalculateddata = document.getelementbyid('grpcalculateddata');    var btnreset = document.getelementbyid('btnreset');  var cmbmethod = document.getelementbyid('cmbmethod');  var btnaddcalculated = document.getelementbyid('btnaddcalculated');  var btnclearcalculated = document.getelementbyid('btnclearcalculated');    var data = [];  var calculatedcount = 0;  var colors = ['black', 'red', 'green', 'blue', 'orange', 'purple'];    var getperiod = function () {      return parseint(document.getelementbyid('txtperiod').value, 10);  };    var cleargroup = function (grp) {      while (grp.lastchild) {          grp.removechild(grp.lastchild);      }  };    var showpoints = function (grp, pts, markersize, color) {      var i, point;      (i = 0; < pts.length; i++) {          point = pts[i];          var marker = document.createelementns(svgns, 'circle');          marker.setattributens(null, 'cx', point.x);          marker.setattributens(null, 'cy', point.y);          marker.setattributens(null, 'r', markersize);          marker.setattributens(null, 'fill', color);          grp.appendchild(marker);      }  };    // create , display test data  var showrawdata = function () {      var i, x, y;      var r = 0;      data = [];      (i = 1; < 500; i++) {          x = i;          r += 15.0 * (math.random() * math.random() - 0.25);          y = 150 + 30 * math.sin(x / 200) * math.sin((x - 37) / 61) + 2 * math.sin((x - 7) / 11) + r;          data.push({ x: x, y: y });      }      showpoints(grprawdata, data, 1, '#888');  };    // gaussian kernel smoother  var creategaussiankerneldata = function () {      var i, x, y;      var r = 0;      var result = [];      var period = getperiod();      (i = math.floor(period / 2) ; < data.length; += period) {          x = data[i].x;          y = gaussiankernel(i);          result.push({ x: x, y: y });      }      return result;  };    var gaussiankernel = function (index) {      var halfrange = math.floor(getperiod() / 2);      var distance, factor;      var totalvalue = 0;      var totalfactor = 0;      (i = index - halfrange; <= index + halfrange; i++) {          if (0 <= && < data.length) {              distance = math.abs(i - index);              factor = math.exp(-math.pow(distance, 2));              totalfactor += factor;              totalvalue += data[i].y * factor;          }      }      return totalvalue / totalfactor;  };    // ramer-douglas-peucker algorithm  var ramerdouglaspeuckerrecursive = function (pts, first, last, eps) {      if (first >= last - 1) {          return [pts[first]];      }        var slope = (pts[last].y - pts[first].y) / (pts[last].x - pts[first].x);        var x0 = pts[first].x;      var y0 = pts[first].y;        var imax = first;      var max = -1;      var p, dy;        // calculate vertical distance      (var = first + 1; < last; i++) {          p = pts[i];          y = y0 + slope * (p.x - x0);          dy = math.abs(p.y - y);            if (dy > max) {              max = dy;              imax = i;          }      }        if (max < eps) {          return [pts[first]];      }        var p1 = ramerdouglaspeuckerrecursive(pts, first, imax, eps);      var p2 = ramerdouglaspeuckerrecursive(pts, imax, last, eps);        return p1.concat(p2);  }    var internalramerdouglaspeucker = function (pts, eps) {      var p = ramerdouglaspeuckerrecursive(data, 0, pts.length - 1, eps);      return p.concat([pts[pts.length - 1]]);  }    var createramerdouglaspeuckerdata = function () {      var finalpointcount = math.round(data.length / getperiod());      var epsilon = getperiod();      var pts = internalramerdouglaspeucker(data, epsilon);      var iteration = 0;      // iterate until correct number of points obtained      while (pts.length != finalpointcount && iteration++ < 20) {          epsilon *= math.sqrt(pts.length / finalpointcount);          pts = internalramerdouglaspeucker(data, epsilon);      }      return pts;  };    // event handlers  btnreset.addeventlistener('click', function () {      calculatedcount = 0;      cleargroup(grprawdata);      cleargroup(grpcalculateddata);      showrawdata();  });    btnclearcalculated.addeventlistener('click', function () {      calculatedcount = 0;      cleargroup(grpcalculateddata);  });    btnaddcalculated.addeventlistener('click', function () {      switch (cmbmethod.value) {          case "gaussian":              showpoints(grpcalculateddata, creategaussiankerneldata(), 2, colors[calculatedcount++]);              break;          case "rdp":              showpoints(grpcalculateddata, createramerdouglaspeuckerdata(), 2, colors[calculatedcount++]);              return;      }  });    showrawdata();
div  {      margin-bottom: 6px;  }
<div>      <button id="btnreset">reset</button>&nbsp;      <select id="cmbmethod">          <option value="rdp">ramer-douglas-peucker</option>          <option value="gaussian">gaussian kernel</option>      </select>&nbsp;      <label for="txtperiod">interval: </label>      <input id="txtperiod" type="text" style="width: 36px;" value="7" />  </div>  <div>      <button id="btnaddcalculated">add calculated points</button>      <button id="btnclearcalculated">clear calculated points</button>  </div>  <svg id="svg1" width="765" height="450" viewbox="0 0 510 300">      <g id="graph1" transform="translate(0,300) scale(1,-1)">          <rect width="500" height="300" stroke="black" fill="#eee"></rect>          <g id="grprawdata"></g>          <g id="grpcalculateddata"></g>      </g>  </svg>


Comments

Popular posts from this blog

javascript - Thinglink image not visible until browser resize -

firebird - Error "invalid transaction handle (expecting explicit transaction start)" executing script from Delphi -

mongodb - How to keep track of users making Stripe Payments -