是否可以使用 dygraphs 显示插值数据?

Is it possible to show interpolated data using dygraphs?

过去几个月我一直在研究 dygraphs。这是一个令人难以置信的库,我得到了很好的结果,但我在找到将来自不同信号的数据插值以显示在同一张图表中的方法时遇到了一些问题。

我从不同的传感器接收到的数据对于不同的样本没有相同的时间戳,所以对于 x ax 时间戳的大部分点我只有一个信号的值。图表绘制完美,但我想在我指向的那个 x 点上查看其余信号的内插值。下面是我得到的图表。

阅读 dygraph documentation 我已经看到,当你有独立的系列时,对于 x 轴的那个点没有数据的信号,至少可以看到值 "undefined"。

我用来绘制数据的 csv 如下所示。它与 dygraph 文档中指示的结构相同,但我也没有得到这个未定义的标签。

TIME,LH_Fuel_Qty,L_Left_Sensor_NP 1488801288048,,1.4411650490795007 1488801288064,0.478965502446834, 1488801288133,,0.6372882768113235 1488801288139,1.131315227899919, 1488801288190,1.847605177130475, 1488801288207,,0.49655791428536067 1488801288258,0.45488168748987334, 1488801288288,,1.3756073145270766 1488801288322,0.5636921255908185, 1488801288358,,1.1193344122758362

提前致谢。

似乎最好的方法是在将数据提交给 dygraph 调用之前对其进行处理。这意味着以下步骤: 1)将csv文件解析成数组数组。 2)遍历数组的每一行以找到孔的位置 3)插值以填补那些洞 4) 修改构造好的数组以供dygraph显示 5) 调用 dygraph

不是最吸引人的代码,但似乎有效...

function findNextValueIndex(data, column, start) {
    var rows = data.length;

    for(var i=start;i<rows;i++) {
        if(data[i][column].length>0) return(i);
    }
    return(-1);
}

function interpolate(t0, t1, tmid, v0, v1) {
    return((v0 + (tmid-t0)/(t1-t0) * (v1-v0)).toString());
}

function parseCSV(string) {

    var data = [];

    // first get the number of lines:

    var lines = string.split('\n');

    // now split the first line to retrieve the headings

    var headings = lines[0].split(",");
    var cols = headings.length;

    // now get the data

    var rows=0;
    for(var i=1;i<lines.length;i++) {
        if(lines[i].length>0) {
            data[rows] = lines[i].split(",");
            rows++;
        }
    }

    // now, fill in the blanks - start by finding the first value for each column of data

    var vals = [];
    var times = [];

    for(var j=1;j<cols;j++) {
        var index = findNextValueIndex(data,j,0);
        vals[j] = parseFloat(data[index][j]);
    }

    // now put those start values at the beginning of the array 
    // there is no way to calculate the previous value of the sensor missing from the first sample 
    // so we use the first reading and duplicate it
    for(var j=1;j<cols;j++) {
        data[0][j] = vals[j].toString();
        times[j] = parseInt(data[0][0]);
    }

    // now step through the rows and interpolate the missing values
    for(var i=1;i<rows;i++) {
        for(var j=1;j<cols;j++) {
            if(data[i][j].length>0) {
                vals[j] = parseFloat(data[i][j]);
                times[j] = parseInt(data[i][0]);
            }
            else {
                var index = findNextValueIndex(data,j,i);
                if(index<0) // no more data in this column
                    data[i][j] = vals[j].toString();
                else
                    data[i][j] = interpolate(times[j],parseInt(data[index][0]),parseInt(data[i][0]),vals[j],data[index][j]);
            }
        }        
    }
    // now convert from strings to integers and floats so dygraph can handle it
    // I've also changed the time value so that it is relative to the first element
    // it will be shown in milliseconds
    var time0 = parseInt(data[0][0]);
    for(var i=0;i<rows;i++) {
        data[i][0] = parseInt(data[i][0]) - time0;
        for(var j=1;j<cols;j++) {
            data[i][j] = parseFloat(data[i][j]);           
        } 
    }
    var obj = {
        labels: headings,
        data: data
    }

    return(obj);
}

window.onload = function () {

    var data_obj = parseCSV(document.getElementById('csvdata').innerHTML);


      new Dygraph(
        document.getElementById('graph'), data_obj.data,
        {
          labels: data_obj.labels,
          connectSeparatedPoints: true,
          drawPoints: true
        }
      );
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.js"></script>
<div id="graph" style="height:200px;"></div>

<pre id="csvdata" style="display:none">
TIME,LH_Fuel_Qty,L_Left_Sensor_NP
1488801288048,,1.4411650490795007
1488801288064,0.478965502446834,
1488801288133,,0.6372882768113235
1488801288139,1.131315227899919,
1488801288190,1.847605177130475,
1488801288207,,0.49655791428536067
1488801288258,0.45488168748987334,
1488801288288,,1.3756073145270766
1488801288322,0.5636921255908185,
1488801288358,,1.1193344122758362
</pre>

是否

connectSeparatedPoints: true

不满足你的需求?

这种方法不会向您的 csv 数据添加任何数据,并且在您四处移动鼠标时仍会为所有列提供内插值。它向 dygraph 中的 mousemove 事件添加一个侦听器,并为所有数据插入最近的点。目前,我只是在图表后的额外 DIV 中显示了它,但您可以根据需要显示它:

function findNextValueIndex(data, column, start) {
  var rows = data.length;

  for (var i = start; i < rows; i++) {
    if (data[i][column] != null) return (i);
  }
  return (-1);
}

function findPrevValueIndex(data, column, start) {

  for (var i = start; i >= 0; i--) {
    if (data[i][column] != null) return (i);
  }
  return (-1);
}

function interpolate(t0, t1, tmid, v0, v1) {
  return (v0 + (tmid - t0) / (t1 - t0) * (v1 - v0));
}

function showValues(headers, colors, vals) {
  var el = document.getElementById("info");
  var str = "";
  for (j = 1; j < headers.length; j++) {
    str += '<p style="color:' + colors[j] + '";>' + headers[j] + ": " + vals[j] + "</p>";
  }
  el.innerHTML = str;
  document.getElementById("hiddenDiv").style.display = "none";
}

function movecustom(event, dygraph, point) {
  var time = dygraph.lastx_;
  var row = dygraph.lastRow_;
  var vals = [];
  var headers = [];
  var colors = [];
  var cols = dygraph.rawData_[0].length;
  
  // draw a line on the chart showing the selected location
  var canvas = dygraph.canvas_;
  var ctx = canvas.getContext("2d");

  ctx.beginPath();
  ctx.lineWidth = 1;
  ctx.strokeStyle = "rgba(0,200,200,0.1)";
  ctx.moveTo( dygraph.selPoints_[0].canvasx, 0);
  ctx.lineTo( dygraph.selPoints_[0].canvasx, 1000);
  ctx.stroke();
  
  
  for (var j = 1; j < cols; j++) {
    colors[j] = dygraph.colors_[j - 1];
    if (dygraph.rawData_[row][j] == null) {
      var prev = findPrevValueIndex(dygraph.rawData_, j, row - 1);
      var next = findNextValueIndex(dygraph.rawData_, j, row + 1);
      if (prev < 0)
        vals[j] = dygraph.rawData_[next][j];
      else if (next < 0)
        vals[j] = dygraph.rawData_[prev][j];
      else {
        vals[j] = interpolate(dygraph.rawData_[prev][0], dygraph.rawData_[next][0], time, dygraph.rawData_[prev][j], dygraph.rawData_[next][j]);
      }
    } else {
      vals[j] = dygraph.rawData_[row][j];
    }
  }

  headers = Object.keys(dygraph.setIndexByName_);

  showValues(headers, colors, vals);
}

window.onload = function() {

  new Dygraph(
    document.getElementById('graph'), document.getElementById('csvdata').innerHTML, {
      connectSeparatedPoints: true,
      drawPoints: true,
      labelsDiv: "hiddenDiv",
      interactionModel: {
        'mousemove': movecustom
      }
    }
  );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/dygraph/2.0.0/dygraph.js"></script>

<div id="graph" style="height:120px;"></div>

<div id="info"></div>
<div id="hiddenDiv" style="display:none"></div>

<pre id="csvdata" style="display:none">
TIME,LH_Fuel_Qty,L_Left_Sensor_NP
1488801288048,,1.4411650490795007
1488801288064,0.478965502446834,
1488801288133,,0.6372882768113235
1488801288139,1.131315227899919,
1488801288190,1.847605177130475,
1488801288207,,0.49655791428536067
1488801288258,0.45488168748987334,
1488801288288,,1.3756073145270766
1488801288322,0.5636921255908185,
1488801288358,,1.1193344122758362
</pre>