Population Pyramid

Data from the Minnesota Population Center. Use the arrow keys to change the displayed year. The blue bars are the male population for each five-year age bracket, while the pink bars are the female population; the bars are partially transparent so that you can see how they overlap, unlike the traditional side-by-side display which makes it difficult to compare the relative distribution of the sexes.

Source Code

  1 var w = 960,
  2     h = 500,
  3     x = d3.scale.linear().range([0, w]),
  4     y = d3.scale.linear().range([0, h - 40]);
  5 
  6 // An SVG element with a bottom-right origin.
  7 var svg = d3.select("#chart").append("svg:svg")
  8     .attr("width", w)
  9     .attr("height", h)
 10     .style("padding-right", "30px")
 11   .append("svg:g")
 12     .attr("transform", "translate(" + x(1) + "," + (h - 20) + ")scale(-1,-1)");
 13 
 14 // A sliding container to hold the bars.
 15 var body = svg.append("svg:g")
 16     .attr("transform", "translate(0,0)");
 17 
 18 // A container to hold the y-axis rules.
 19 var rules = svg.append("svg:g");
 20 
 21 // A label for the current year.
 22 var title = svg.append("svg:text")
 23     .attr("class", "title")
 24     .attr("dy", ".71em")
 25     .attr("transform", "translate(" + x(1) + "," + y(1) + ")scale(-1,-1)")
 26     .text(2000);
 27 
 28 d3.csv("population.csv", function(data) {
 29 
 30   // Convert strings to numbers.
 31   data.forEach(function(d) {
 32     d.people = +d.people;
 33     d.year = +d.year;
 34     d.age = +d.age;
 35   });
 36 
 37   // Compute the extent of the data set in age and years.
 38   var age0 = 0,
 39       age1 = d3.max(data, function(d) { return d.age; }),
 40       year0 = d3.min(data, function(d) { return d.year; }),
 41       year1 = d3.max(data, function(d) { return d.year; }),
 42       year = year1;
 43 
 44   // Update the scale domains.
 45   x.domain([0, age1 + 5]);
 46   y.domain([0, d3.max(data, function(d) { return d.people; })]);
 47 
 48   // Add rules to show the population values.
 49   rules = rules.selectAll(".rule")
 50       .data(y.ticks(10))
 51     .enter().append("svg:g")
 52       .attr("class", "rule")
 53       .attr("transform", function(d) { return "translate(0," + y(d) + ")"; });
 54 
 55   rules.append("svg:line")
 56       .attr("x2", w);
 57 
 58   rules.append("svg:text")
 59       .attr("x", 6)
 60       .attr("dy", ".35em")
 61       .attr("transform", "rotate(180)")
 62       .text(function(d) { return Math.round(d / 1e6) + "M"; });
 63 
 64   // Add labeled rects for each birthyear.
 65   var years = body.selectAll("g")
 66       .data(d3.range(year0 - age1, year1 + 5, 5))
 67     .enter().append("svg:g")
 68       .attr("transform", function(d) { return "translate(" + x(year1 - d) + ",0)"; });
 69 
 70   years.selectAll("rect")
 71       .data(d3.range(2))
 72     .enter().append("svg:rect")
 73       .attr("x", 1)
 74       .attr("width", x(5) - 2)
 75       .attr("height", 1e-6);
 76 
 77   years.append("svg:text")
 78       .attr("y", -6)
 79       .attr("x", -x(5) / 2)
 80       .attr("transform", "rotate(180)")
 81       .attr("text-anchor", "middle")
 82       .style("fill", "#fff")
 83       .text(String);
 84 
 85   // Add labels to show the age.
 86   svg.append("svg:g").selectAll("text")
 87       .data(d3.range(0, age1 + 5, 5))
 88     .enter().append("svg:text")
 89       .attr("text-anchor", "middle")
 90       .attr("transform", function(d) { return "translate(" + (x(d) + x(5) / 2) + ",-4)scale(-1,-1)"; })
 91       .attr("dy", ".71em")
 92       .text(String);
 93 
 94   // Nest by year then birthyear.
 95   data = d3.nest()
 96       .key(function(d) { return d.year; })
 97       .key(function(d) { return d.year - d.age; })
 98       .rollup(function(v) { return v.map(function(d) { return d.people; }); })
 99       .map(data);
100 
101   // Allow the arrow keys to change the displayed year.
102   d3.select(window).on("keydown", function() {
103     switch (d3.event.keyCode) {
104       case 37: year = Math.max(year0, year - 10); break;
105       case 39: year = Math.min(year1, year + 10); break;
106     }
107     redraw();
108   });
109 
110   redraw();
111 
112   function redraw() {
113     if (!(year in data)) return;
114     title.text(year);
115 
116     body.transition()
117         .duration(750)
118         .attr("transform", function(d) { return "translate(" + x(year - year1) + ",0)"; });
119 
120     years.selectAll("rect")
121         .data(function(d) { return data[year][d] || [0, 0]; })
122       .transition()
123         .duration(750)
124         .attr("height", y);
125   }
126 });
Copyright © 2011 Mike Bostock
Fork me on GitHub