【问题标题】:Interactive Charts (by dc.js) not updating each other correctly交互式图表(由 dc.js 提供)不能正确地相互更新
【发布时间】:2016-04-21 15:22:55
【问题描述】:

我正在尝试使用 dc.js 进行交互式数据可视化。

我的数据基本上是两个系列的值,由为每一行指定的id 属性区分。这是我如何生成它的示例:

var data = [];
var n = 10000.;
for (var i = 0; i < n; i++) {
    data.push({id: 0, "i": i, x: Math.random()});
    data.push({id: 1, "i": i, x: (Math.random()+i/n)});
}

然后我将这个数据集放入一个交叉过滤器对象并创建两个维度和两个组。一个显示每个id 沿系列的分箱值的总和,一个显示每个id 的总选定值的总和:

var cf = crossfilter(data),
    series = cf.dimension(function(d) {return [d.id, d.i];}),
    series_grouped = series
        .group(function(d){return [d[0], Math.floor(d[1]/100.)*100.];})
        .reduceSum(function(d) { return d.x; }),
    id = cf.dimension(function(d) {return d.id;}),
    id_grouped = id.group().reduceSum(function(d){return d.x;});

我设法使用以下代码创建了我想要的图表。我无法做到的是交互行为:

  • 当我在系列图中选择一个范围时,左侧的条形图没有更新(但应该更新)。
  • 当我在系列图表中选择一个范围,然后选择一个条形时,条形消失。我无法从该状态中恢复(通过重新加载页面以外的方式)。 (我没有尝试在这里触发cf.filterAll(),因为我认为这不能解决我的根本问题。)

如何获取左侧的条形图,当我在右侧的系列图表中选择一个范围时进行更新?我做错了吗?

我使用的是 Firefox 45.0.2,这些是我的库版本:

  • dc.js: 2.0.0-beta.26
  • crossfilter.js: 1.3.12
  • d3.js: 3.5.16

这是完整的文档:

<!DOCTYPE html>
<meta charset="utf-8">

<head>
    <script src="crossfilter.js"></script>
    <script src="d3.js"></script>
    <script src="dc.js"></script>
    <link rel="stylesheet" type="text/css" href="dc.css" />
    <style>
        body { width: 960px; }
        .chart.left { width: 25%; }
        .chart.right { width: 75%; float: right; }
    </style>
</head>

<body>
    <div id="charts">
        <div id="chart_a" class="chart right"></div>
        <div id="chart_b" class="chart left"></div>
    </div>

    <script>
    // generate data
    var data = [];
    var n = 10000.;
    for (var i = 0; i < n; i++) {
        data.push({id: 0, "i": i, x: Math.random()});
        data.push({id: 1, "i": i, x: (Math.random()+i/n)});
    }

    // do some crossfilter stuff
    var cf = crossfilter(data),
        series = cf.dimension(function(d) {return [d.id, d.i];}),
        series_grouped = series
            .group(function(d){return [d[0], Math.floor(d[1]/100.)*100.];})
            .reduceSum(function(d) { return d.x; }),
        id = cf.dimension(function(d) {return d.id;}),
        id_grouped = id.group().reduceSum(function(d){return d.x;});

    // generate charts
    var chart_width = 960, chart_height = 200;
    dc.seriesChart("#chart_a").height(chart_height).width(.74*chart_width)
        .chart(function(c) { return dc.lineChart(c).renderArea(true); })
        .x(d3.scale.linear().domain([0,n]))
        .dimension(series)
        .group(series_grouped)
        .seriesAccessor(function(d) {return d.key[0];})
        .keyAccessor(function(d) {return d.key[1];})
        .valueAccessor(function(d) {return d.value;})
        .xAxis();
    dc.barChart("#chart_b").height(chart_height).width(.24*chart_width)
        .dimension(id)
        .group(id_grouped)
        .x(d3.scale.ordinal().domain([0,1]))
        .xUnits(dc.units.ordinal)
        .xAxis();

    dc.renderAll();
    </script>
</body>

【问题讨论】:

    标签: javascript d3.js charts dc.js crossfilter


    【解决方案1】:

    至少您需要覆盖系列图表上的过滤器处理程序。现在,假设您在系列图表中选择 i = 20 到 400。在 Crossfilter 上写着series.filter([20,400])。但是您的系列维度值看起来像[0,150],那么评估20 &lt;= [0,150] &amp;&amp; [0,150] &lt;= 400 意味着什么?很难说,几乎可以肯定不是你的意思。使用 Crossfilter 的自动类型转换,它可能正在评估 "20" &lt; "0,150" &amp;&amp; "0,150" &lt; "400"。相反,您可能希望它评估 20 &lt;= [0,150][1] &amp;&amp; [0,150][1] &lt;= 400,您可以强制它在自定义过滤器处理程序中执行此操作。

    Here 是使用自定义过滤器处理程序“工作”的版本:

    dc.seriesChart("#chart_a").height(chart_height).width(.74 * chart_width)
      .chart(function(c) {
        return dc.lineChart(c).renderArea(true)
          .filterHandler(function(dimension, filter) {
            if (filter[0]) {
              dimension.filterFunction(function(d) {
                return d[1] > filter[0][0] && d[1] < filter[0][1];
              });
            } else {
              dimension.filterAll();
            }
            setTimeout(dc.redrawAll, 0);
            return filter;
          });
      })
      .x(d3.scale.linear().domain([0, n]))
      .dimension(series)
      .group(series_grouped)
      .seriesAccessor(function(d) {
        return d.key[0];
      })
      .keyAccessor(function(d) {
        return d.key[1];
      })
      .valueAccessor(function(d) {
        return d.value;
      });
    

    但是,您可能会看到,在 Crossfilter 中使用数组作为维度键是一个非常糟糕的主意(除非您在 Crossfilter2 中使用数组类型,这不是您想要的)。维度必须自然排序,而数组在排序时会以令人惊讶的方式表现,正如我希望上面的解释所证明的那样。

    那你是做什么的?我建议将您的数据转换为最佳选择:

    var data = [];
    var n = 10000.;
    for (var i = 0; i < n; i++) {
        data.push({"i": i, x0: Math.random(), x1:(Math.random()+i/n)});
    }
    

    利用这些数据,生成对 x0 和 x1 求和的组,然后使用堆栈混合(包含在标准 dc.lineChart 中)来显示每个系列的线。

    另一种但更痛苦的方法是自己处理维度键与字符串之间的序列化和反序列化。只是要非常小心地考虑排序,这意味着您可能需要对值进行零填充,并且在序列化之前显式地舍入浮点值可能是一个好主意。排序方面,如果您想要过滤,您应该使维度值按i 而不是id 排序。

    【讨论】:

    • 感谢您的意见。我的实现基于给定here 的多线图示例。如果我按照你的建议重组我的数据,我想我会遇到生成每个 id 条形图的问题。我想我没有完全理解我将如何实现和应用自定义过滤器处理程序。 The dc.js documentation 在这件事上并没有真正的帮助(也许那是因为我还没有完全理解过滤器是如何工作的)。您能否对此进行扩展,或提供一个示例?
    • 嗯。我有点惊讶这个例子有效,但如果这就是你所遵循的,那是公平的。这里可能会发生一些不同的事情。你能把你的代码的工作示例放在一起吗?我很乐意仔细研究一下。这是您可以使用的已加载 DC.js 和 Crossfilter 的模板:jsfiddle.net/esjewett/jusjkm8j/2
    • 这是一个使用 filterHandler 的“工作”版本。查看该系列图表 dc.js 示例,我很确定它与您遇到的过滤问题相同。我个人会远离它,但也许@gordonwoodhull 会有更多的见解。 jsfiddle.net/jusjkm8j/7
    猜你喜欢
    • 2017-01-22
    • 1970-01-01
    • 2017-05-01
    • 1970-01-01
    • 2017-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多