Reputation: 55
I have a d3 bar chart that lists data based on date in order by day. This works great, but now I need to display two sets of data per day, in a grouped bar chart format or just side-by-side bars.
I'm thinking this would take a bit of refactoring but after fooling with it and referencing a bunch of examples I've found on the net, the fact that the chart is using a timescale and linear scale throws a wrench in my understanding of this. I'm hoping someone can help show me the way into making this chart show 2 bars for each day in the chart.
My current x axis setup:
var xTimeScale = d3.time.scale().
domain([new Date(data[0].date), d3.time.day.offset(new Date(data[data.length - 1].date), 1)])
.range([0, width]);
var xScale = d3.scale.linear()
.domain([0, data.length])
.range([0, width]);
My fiddle: http://jsfiddle.net/MmEjF/45/
So from two separate sets of data, I need to have 2 bars per day - each bar representing a value from each data set. How can I accomplish displaying grouped bars? And would it be better if the datasets were combined into one?
Upvotes: 3
Views: 2168
Reputation: 8503
First, lets think about two separate graphs of 2 sets of data. The scales are the same. It will look like this:
OK. That's all well and good, but how do we get from there to a stacked chart?
Well, we need to make sure each tick has both pieces of data.
Then, when plotting each tick, it should have the first piece of data plotted from 0, and the second should be plotted not from zero, but offset by the amount of the first data.
With that in mind, other pieces fall into place: the y axis must be from 0 to max of the sum of the two pieces of data.
var data = [{"year":2013,"month":3,"day":5,"count1":18, "count2":3},
{"year":2013,"month":3,"day":6,"count1":3, "count2":11},
{"year":2013,"month":3,"day":7,"count1":10,"count2": 8},
{"year":2013,"month":3,"day":8,"count1":18, "count2": 4}];
var maxValue = d3.max(data, function (d) {
return d.count1 + d.count2;
});
var yScale = d3.scale.linear()
.domain([0, maxValue])
.range([0, height]);
Now, it's easier to treat each set of bars as a group, and add the bars to that group:
var bars = svg.selectAll(".bar")
.data(data).enter()
.append("g")
.attr("class","bar")
.attr("transform", function(d){
return "translate("+xTimeScale(d.date)+",0)"
});
var count1Bars = bars.append("rect")
.attr("class", "count1")
.attr("height", function(d){return yScale(d.count1)})
.attr("y", function(d){return height - yScale(d.count1)})
.attr("width", barWidth);
var count2Bars = bars.append("rect")
.attr("class", "count2")
.attr("height", function(d){return yScale(d.count2)})
.attr("y", function(d){return height - yScale(d.count1) -yScale(d.count2)})
.attr("width", barWidth);
var count1text = bars.append("text")
.text(function(d){return d.count1})
.attr("y", function(d){return height})
.attr("x", barWidth / 2);
var count2text = bars.append("text")
.text(function(d){return d.count2})
.attr("y", function(d){return height - yScale(d.count1)})
.attr("x", barWidth / 2);
Now, this is just a rough example to get you on your way: http://jsfiddle.net/MmEjF/49/
Good luck! Just think about what you need, and things tend to fall into place from there!
Upvotes: 3