问题
I have the following below code and I would like to plot date on the x-axis and y-axis should have the values of total and tip on two separate series. Is this possible from DC?
I can't find a way to determine series using a separate column instead of using a column to determine the series.
Thank you
var data = [
{date: "2011-11-14T16:17:54Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:20:19Z", quantity: 2, total: 190, tip: 100, type: "tab"},
{date: "2011-11-14T16:28:54Z", quantity: 1, total: 300, tip: 200, type: "visa"},
{date: "2011-11-14T16:30:43Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:48:46Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:53:41Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T16:54:06Z", quantity: 1, total: 100, tip: 0, type: "cash"},
{date: "2011-11-14T16:58:03Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:07:21Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:22:59Z", quantity: 2, total: 90, tip: 0, type: "tab"},
{date: "2011-11-14T17:25:45Z", quantity: 2, total: 200, tip: 0, type: "cash"},
{date: "2011-11-14T17:29:52Z", quantity: 1, total: 200, tip: 100, type: "visa"}
];
data.forEach(function(d){
var tempDate = new Date(d.date);
d.date = tempDate;
})
var facts = crossfilter(data);
var dateDimension = facts.dimension(function(d){ return d.date; });
var dateGroup = dateDimension.group().reduceSum(function(d){ return d.total; });
var minDate = dateDimension.bottom(1)[0].date;
var maxDate = dateDimension.top(1)[0].date;
var runDimension = facts.dimension(function(d){ return [d.quantity, d.total, d.type]; });
var runGroup = runDimension.group().reduceSum(function(d) { return d.tip; });
var series = dc.seriesChart("#chart")
.width(1360)
.height(300)
.margins({top:40,bottom:60,right:80, left:60})
.chart(function(cht){ return dc.lineChart(cht).renderArea(true).interpolate('basis'); }) //change how it looks here
.dimension(dateDimension)
.group(runGroup)
.keyAccessor(function(d){ return d.key[1];}) //x axis
.valueAccessor(function(d){ return d.value;}) //y axis
.seriesAccessor(function(d){ return d.key[2];}) //group
.legend(dc.legend().x(100).y(200).itemHeight(13).gap(5).horizontal(2).legendWidth(1360).itemWidth(70))
.x(d3.time.scale().domain([minDate,maxDate]));
dc.renderAll();
回答1:
You are correct, crossfilter doesn't provide ways to expand rows into multiple group bins.
When you want to transform your data into a different shape for dc.js to read it, a "fake group" is the usual method.
In this case, we can create two groups, one for tips and one for totals, and then combine them using a fake group.
Our main dimension is time, so there's no need to muddle the keys with quantity, total, or type.
var totalGroup = dateDimension.group().reduceSum(function(d){ return d.total; });
var tipGroup = dateDimension.group().reduceSum(function(d){ return d.tip; });
Here's a generator for the fake group:
function combine_groups(groups) {
return {
all: function() {
var parts = Object.keys(groups).map(function(gk) {
return groups[gk].all().map(function(kv) {
return {key: [gk, kv.key], value: kv.value};
})
});
return Array.prototype.concat.apply([], parts);
}
};
}
All fake groups are objects that follow the crossfilter group convention of an object with a single method .all()
which returns the data. Conveniently, this means the data will be retrieved and aggregated each time the chart is drawn, so that it can react to filters in other charts.
Here we iterate over the names of groups supplied to the function. For each group, we call group.all()
and then map the key/value pairs so that the new key will have the group name as the first component, and the old key (date in this example) as the second component.
Construct the fake group like so:
var seriesGroup = combine_groups({
total: totalGroup,
tip: tipGroup
});
Supply the fake group to the series chart, and tell the chart how to read the series and key (X-value) components of the results:
series
.keyAccessor(function(d){ return d.key[1];}) //x axis
.seriesAccessor(function(d){ return d.key[0];}) //group
There's no need to supply a value accessor, because looking for kv.value
is the default.
Here's the result. (You might want to reconsider using area charts in a series chart, since the areas will overlap.)
Example fiddle.
Note: Another, equivalent, way to accomplish this would be simply to use a composite chart; most of what the series chart does is pull apart the data intended for each series, and then instantiate a bunch of child charts for the composite chart.
Or you might simply want to use a stacked chart. But you asked about series charts, and it's fun, so here you are.
来源:https://stackoverflow.com/questions/48313909/dc-js-create-series-plot-using-multiple-columns