Population Pyramid

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