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