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 });