问题
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