Reputation: 63
My requirement is to draw a category-grouped bar chart in which each category has a different number of groups, using pure d3. I have no idea how to take domain and range to meet my requirement.
I tried in the way given in the answer to d3 nested grouped bar chart, but it did not work in my case.
Here my graph structure is like:
Upvotes: 2
Views: 4455
Reputation: 2229
The issue with the plunker of the answer that you mention is that it will just work for values that have the same children. In order to handle the dynamic children values I took this approach
Lets create the color mapping for our groups:
var color = {
Mechanical: '#4A7B9D',
Electrical: '#54577C',
Hydraulic: '#ED6A5A'
};
We also need a structure with nested values that will be the inner groups:
// Simulated data structure
var data = [{
key: 'Mechanical',
values: [{
key: 'Gear',
value: 11
}, {
key: 'Bearing',
value: 8
}, {
key: 'Motor',
value: 3
}]
}];
I created a barPadding
value which will dictate the separation between bars:
var barPadding = 120;
We are going to need a dummy scale
to get the rangeBand
of the bars, lets do that:
// dummy array
var rangeBands = [];
// cummulative value to position our bars
var cummulative = 0;
data.forEach(function(val, i) {
val.cummulative = cummulative;
cummulative += val.values.length;
val.values.forEach(function(values) {
rangeBands.push(i);
})
});
// set scale to cover whole svg
var x_category = d3.scale.linear()
.range([0, width]);
// create dummy scale to get rangeBands (width/childrenValues)
var x_defect = d3.scale.ordinal().domain(rangeBands)
.rangeRoundBands([0, width], .1);
var x_category_domain = x_defect.rangeBand() * rangeBands.length;
x_category.domain([0, x_category_domain]);
Then lets add all our category groups g elements:
var category_g = svg.selectAll(".category")
.data(data)
.enter().append("g")
.attr("class", function(d) {
return 'category category-' + d.key;
})
.attr("transform", function(d) { // offset by inner group size
var x_group = x_category((d.cummulative * x_defect.rangeBand()));
return "translate(" + x_group + ",0)";
})
.attr("fill", function(d) { // make child elements of group "inherit" this fill
return color[d.key];
});
Adding our inner groups g elements:
var defect_g = category_g.selectAll(".defect")
.data(function(d) {
return d.values;
})
.enter().append("g")
.attr("class", function(d) {
return 'defect defect-' + d.key;
})
.attr("transform", function(d, i) { // offset by index
return "translate(" + x_category((i * x_defect.rangeBand())) + ",0)";
});
Having our g elements lets add the labels:
var category_label = category_g.selectAll(".category-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
console.log(d)
return 'category-label category-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((d.values.length * x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 30;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
var defect_label = defect_g.selectAll(".defect-label")
.data(function(d) {
return [d];
})
.enter().append("text")
.attr("class", function(d) {
console.log(d)
return 'defect-label defect-label-' + d.key;
})
.attr("transform", function(d) {
var x_label = x_category((x_defect.rangeBand() + barPadding) / 2);
var y_label = height + 10;
return "translate(" + x_label + "," + y_label + ")";
})
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle');
And finally our rects:
var rects = defect_g.selectAll('.rect')
.data(function(d) {
return [d];
})
.enter().append("rect")
.attr("class", "rect")
.attr("width", x_category(x_defect.rangeBand() - barPadding))
.attr("x", function(d) {
return x_category(barPadding);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
Here's the above code in plnkr: https://plnkr.co/edit/L0eQwtEMQ413CpoS5nvo?p=preview
Upvotes: 2