如何在动态增长的数据集上应用刷亮?

How to apply brushing on a dynamically growing dataset?

我有一个动态增长的时间序列,我需要在 zoomable/panable 图表中显示。

在这里试试(事实上:我的第一个 jsFiddle :)):

https://jsfiddle.net/Herkules001/L12k5zwx/29/

我尝试按照此处描述的方式进行操作:https://dc-js.github.io/dc.js/examples/replacing-data.html

但是,每次图表更新时,焦点图表上的缩放和过滤器都会丢失。 (然而,画笔保留在范围图表上。)

如何在不重置视图和不丢失缩放比例的情况下添加数据?

var chart = dc.lineChart("#test");
var zoom = dc.lineChart("#zoom");

//d3.csv("morley.csv", function(error, experiments) {
var experiments = d3.csvParse(d3.select('pre#data').text());
  experiments.forEach(function(x) {
    x.Speed = +x.Speed;
  });

  var ndx                 = crossfilter(experiments),
      runDimension        = ndx.dimension(function(d) {return +d.Run;}),
      speedSumGroup       = runDimension.group().reduceSum(function(d) {return d.Speed * d.Run / 1000;});

  chart
    .width(768)
    .height(400)
    .x(d3.scaleLinear().domain([6,20]))
    .brushOn(false)
    .yAxisLabel("This is the Y Axis!")
    .dimension(runDimension)
    .group(speedSumGroup)
    .rangeChart(zoom);

  zoom
    .width(768)
    .height(80)
    .x(d3.scaleLinear().domain([6,20]))
    .brushOn(true)
    .yAxisLabel("")
    .dimension(runDimension)
    .group(speedSumGroup);

    zoom.render();
    chart.render();

  var run = 21;
  setInterval(
    () => {

      var chartfilter = chart.filters();
      var zoomfilter = zoom.filters();

      chart.filter(null);
      zoom.filter(null);

      ndx.add([{Expt: 6, Run: run++, Speed: 100 + 5 * run}]);
      chart.x(d3.scaleLinear().domain([6,run]));
      zoom.x(d3.scaleLinear().domain([6,run]));

      chart.filter([chartfilter]);
      zoom.filter([zoomfilter]);

      chart.render();
      zoom.render();
    },
    1000);  

//});

在这种情况下,如果您只是添加数据,则不需要执行您引用的示例中演示的复杂的过滤器清除和恢复操作。

该部分是必需的,因为 crossfilter.remove() 最初会删除与当前过滤器匹配的数据。一个尴尬的界面,几乎不是你想要的。

如果您只是添加数据,则不必担心这些:

setInterval(
() => {
  ndx.add([{Expt: 6, Run: run++, Speed: 5000 + 5 * run}]);
  chart.redraw();
  zoom.redraw();
},
5000);  

请注意,通过使用 redraw 而不是 render,您将获得更少的闪烁和良好的动画过渡。我还添加了 evadeDomainFilter 以避免在图表边缘之前截断线条。

Fork of your fiddle

正在删除数据

如果您使用 crossfilter.remove() 的谓词形式,您不必担心保存和恢复过滤器:

  ndx.remove(d => d.Run < run-20);

然而,这确实暴露了 dc.js 中的其他错误。似乎 elasticY 不起作用,类似于 in this issue. And you get some weird animations.

所描述的

Here's a demo with remove enabled.

最后,dc.js 有一些非常简洁的功能,通常有办法让它做你想做的事,但它确实很古怪。这是一个非常复杂的领域,根据我的经验,您会在任何功能齐全的图表库中发现其中的一些怪癖。

更新:我修复了 replacing data example,那个现在只有 ndx.remove(() => true)

缩放问题

正如 Joerg 在评论中指出的那样,

  • 当图表未缩放时,最好让它在新数据到达时也能显示出来
  • 如果焦点到达图表的原始域之外,X 域将被剪裁甚至反转

我们可以通过添加 preRedraw event handler 来解决这些问题。那是调整域的理想位置;例如,如果需要,您可以手动实施 elasticX 。 (您马上就会看到,我们做到了!)

首先,简单易懂的幼稚尝试:

chart.on('preRedraw', () => {
  chart.elasticX(!zoom.filters().length);
});

我们可以根据范围图表是否具有活动过滤器来打开和关闭 elasticX

这行得通而且简单又好用,但是当您尝试关注原始图表中不存在的域时,为什么图表会变得如此混乱?

Welp,它记录了原始域 (source)。这样它可以在焦点被清除时恢复到该域,并且还可以阻止您缩放或平移超过图形的边缘。

但是从上面的来源 link 可以看出我们有一个逃生口。设置X尺度时,记录原始域。所以,不用设置elasticX,我们可以计算数据的范围,设置比例尺的域,告诉图表比例尺是新的:

chart.on('preRedraw', () => {
  if(!zoom.filters().length) {
    var xExtent = d3.extent(speedSumGroup.all(), kv => kv.key);
    chart.x(chart.x().domain(xExtent));
  }
});

New fiddle with zooming issues fixed.

Joerg 指出还有一个问题:如果您在数据输入时移动画笔,画笔手柄偶尔会偏离画笔的末端。根据我的经验,这类故障在 D3(以及一般的动态图表)中非常常见,因为很难考虑用户交互期间数据的变化。它可能可以在库中修复(也许是中断的转换?)但我不打算在这里讨论。