How can I fill gaps in a dc.js series chart?

风流意气都作罢 提交于 2020-05-28 07:34:09

问题


I have the following dc.js series chart:

var data = [
  {day: 1, service: 'ABC', count: 100},
  {day: 2, service: 'ABC', count: 80},
  {day: 4, service: 'ABC', count: 10},
  {day: 7, service: 'XYZ', count: 380},
  {day: 8, service: 'XYZ', count: 400}
];
var ndx = crossfilter(data);
var dim = ndx.dimension(function (d) { return [d.service, d.day]; });
var grp = dim.group().reduceSum(function(d) { return +d.count; });
var chart = dc.seriesChart('#graph')
  .width(620)
  .height(175)
  .chart(function(c) { return dc.lineChart(c).renderArea(true);})
  .dimension(dim)
  .group(grp)
  .seriesAccessor(function(d) { return d.key[0]; })
  .keyAccessor(function(d) { return d.key[1]; })
  .x(d3.scaleLinear().domain([1, 8]))
  .legend(dc.legend().horizontal(true).x(80))
  .render();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.css"/>
<div id="graph"></div>

It works as expected... but since there are "gaps" in my data, it doesn't look so good. Is there any way to tell dc.js to default to 0 the points where there isn't data for a particular series?

Basically, I want this result, but without having to create a different group for each series and stacking them together myself:

var data = [
  {day: 1, service: 'ABC', count: 100},
  {day: 2, service: 'ABC', count: 80},
  {day: 4, service: 'ABC', count: 10},
  {day: 7, service: 'XYZ', count: 380},
  {day: 8, service: 'XYZ', count: 400}
];
var ndx = crossfilter(data);
var dim = ndx.dimension(function (d) { return d.day; });
var grpABC = dim.group().reduceSum(function(d) { return d.service === 'ABC' ? +d.count : 0; });
var grpXYZ = dim.group().reduceSum(function(d) { return d.service === 'XYZ' ? +d.count : 0; });
var chart = dc.lineChart('#graph')
  .width(620)
  .height(175)
  .dimension(dim)
  .group(grpABC)
  .stack(grpXYZ)
  .renderArea(true)
  .x(d3.scaleLinear().domain([1, 8]))
  .legend(dc.legend().horizontal(true).x(80))
  .render();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.css"/>
<div id="graph"></div>

Is there any way to achieve the same with the seriesChart of the first example?

I have seen this question which might be similar, but they're using NVD3.js and Underscore. We have jQuery, d3.js and dc.js instead.


回答1:


The dc.js FAQ contains a section on preprocessing data. In this case, you want to ensure that any empty values are filled with zero, and ensure_group_bins is pretty close to what you want.

Here I've adapted it to take the required bins as an array instead of as arguments, because we need to require a lot of bins - the Cartesian product of all the services and all the days. Handily, D3v4 contains d3.cross for exactly this purpose.

Then we just wrap our group with the fake group produced by ensure_group_bins and we are off!

Interestingly, this exposes a detail about your data which was obscured before - since the line chart only draws points where it has data, it was previously not clear that service ABC / day 3 is zero.

function ensure_group_bins(source_group, bins) {
return {
    all:function () {
        var result = source_group.all().slice(0), // copy original results (we mustn't modify them)
            found = {};
        result.forEach(function(d) {
            found[d.key] = true;
        });
        bins.forEach(function(d) {
            if(!found[d])
                result.push({key: d, value: 0});
        });
        return result;
    }
};
};
var needed = d3.cross(['ABC', 'XYZ'], d3.range(1,8));

var data = [
  {day: 1, service: 'ABC', count: 100},
  {day: 2, service: 'ABC', count: 80},
  {day: 4, service: 'ABC', count: 10},
  {day: 7, service: 'XYZ', count: 380},
  {day: 8, service: 'XYZ', count: 400}
];
var ndx = crossfilter(data);
var dim = ndx.dimension(function (d) { return [d.service, d.day]; });
var grp = dim.group().reduceSum(function(d) { return +d.count; });
grp = ensure_group_bins(grp, needed);
var chart = dc.seriesChart('#graph')
  .width(620)
  .height(175)
  .chart(function(c) { return dc.lineChart(c).renderArea(true);})
  .dimension(dim)
  .group(grp)
  .seriesAccessor(function(d) { return d.key[0]; })
  .keyAccessor(function(d) { return d.key[1]; })
  .x(d3.scaleLinear().domain([1, 8]))
  .legend(dc.legend().horizontal(true).x(80))
  .render();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.12/crossfilter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dc/3.0.4/dc.css"/>
<div id="graph"></div>


来源:https://stackoverflow.com/questions/51095906/how-can-i-fill-gaps-in-a-dc-js-series-chart

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!