Skip to content

Instantly share code, notes, and snippets.

@tianhuil
Last active July 26, 2016 21:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tianhuil/7936887 to your computer and use it in GitHub Desktop.
Save tianhuil/7936887 to your computer and use it in GitHub Desktop.
Grouped Horizontal Bar Graph in d3. This is simply reversing the x and y axes of http://bl.ocks.org/mbostock/3887051 This version has abstracted the data and graphics layers so that it is easy to reuse.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.y.axis path {
display: none;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://underscorejs.org/underscore.js"></script>
<script>
var data = data;
d3.csv("data.csv", function(error, mydata) {
var ageNames = d3.keys(mydata[0]).filter(function(key) { return key !== "State"; });
mydata.forEach(function(d) {
d.subgroups = ageNames.map(function(ageName) { return {subgroupLabel: ageName, value: +d[ageName]}; });
d.groupLabel = d.State
});
horizontalGroupedBarGraph(mydata, ageNames);
data = mydata;
});
// data is a list of objects d
// d.groupLabel -> label for the group
// d.subgroups is a list of subgroup objects
// subgroup.subgroupLabel -> label
// subgroup.value -> value
function horizontalGroupedBarGraph(data) {
var subgroupLabels = _.union.apply(this, _.map(data, function(d) {
return _.map(d.subgroups, function(g) { return g.subgroupLabel; });
}));
d3.keys(data[0]).filter(function(key) { return key !== "State"; });
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.range([0, width]);
var y0 = d3.scale.ordinal()
.rangeRoundBands([height, 0], .1);
var y1 = d3.scale.ordinal();
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickFormat(d3.format(".2s"));
var yAxis = d3.svg.axis()
.scale(y0)
.orient("left");
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
y0.domain(data.map(function(d) { return d.groupLabel; }));
y1.domain(subgroupLabels).rangeRoundBands([0, y0.rangeBand()]);
x.domain([0, d3.max(data, function(d) { return d3.max(d.subgroups, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("x", width)
.attr("dy", "-.71em")
.style("text-anchor", "end")
.text("Population");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var group = svg.selectAll(".group")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(0," + y0(d.groupLabel) + ")"; });
group.selectAll("rect")
.data(function(d) { return d.subgroups; })
.enter().append("rect")
.attr("height", y1.rangeBand())
.attr("x", 0)
.attr("y", function(d) { return y1(d.subgroupLabel); })
.attr("width", function(d) { return x(d.value); })
.style("fill", function(d) { return color(d.subgroupLabel); });
var legend = svg.selectAll(".legend")
.data(subgroupLabels)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
}
</script>
@PeterVermont
Copy link

PeterVermont commented Jul 26, 2016

On line 105 shouldn't the class be "group" to match the selectAll(".group"). It doesn't really matter here because you only have the enter() handler but cause trouble if you later wanted to handle update or exit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment