D3.js中Population Pyramid详解
Posted wan353694124
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了D3.js中Population Pyramid详解相关的知识,希望对你有一定的参考价值。
Population Pyramid
聊一聊人口金字塔图。
人口金字塔是按人口年龄和性别表示人口分布的特种塔状条形图,是形象地表示某一人口的年龄和性别构成的图形。——百度百科
一般的人口金字塔图如下图所示:
例如上图表示,2011年利比亚男女不同年龄阶段的比例分布情况。
而本篇要讲的Population Pyramid图,将男女人口数据画在了坐标轴的同一边,通过柱状图的覆盖来看不同年龄阶段的男女比例分布情况,如下图所示:
图中用粉色来标识女性的数据、蓝色标识男性的数据,数据重叠部分,由于粉色和蓝色重叠而呈现出紫色,例如70岁的人群当中,女性的比例比男性的多;而20岁的人群当中,男性的比例比女性的多。
接下来详细解释D3.js是如何实现这张人口金字塔图的。
index.html——源码
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg
font: 10px sans-serif;
.y.axis path
display: none;
.y.axis line
stroke: #fff;
stroke-opacity: .2;
shape-rendering: crispEdges;
.y.axis .zero line
stroke: #000;
stroke-opacity: 1;
.title
font: 300 78px Helvetica Neue;
fill: #666;
.birthyear,
.age
text-anchor: middle;
.birthyear
fill: #fff;
rect
fill-opacity: .6;
fill: #e377c2;
rect:first-child
fill: #1f77b4;
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
// 定义相关尺寸
// margin定义svg画图的上 、右、下、左的外边距
var margin = top: 20, right: 40, bottom: 30, left: 20,
// 计算宽度
width = 960 - margin.left - margin.right,
// 计算高度
height = 500 - margin.top - margin.bottom,
// 计算柱状条的宽度,其中19由于分了19个年龄段
barWidth = Math.floor(width / 19) - 1;
// 为x轴定义线性比例尺,值域range的定义可以看出,x轴的刻度尺都会位于柱状图的底部中间位置
var x = d3.scale.linear()
.range([barWidth / 2, width - barWidth / 2]);
// 为y轴定义线性比例尺,值域为height到0
var y = d3.scale.linear()
.range([height, 0]);
// 定义y坐标轴
var yAxis = d3.svg.axis()
// 设置y轴的比例尺
.scale(y)
// y轴坐标刻度文字在右侧
.orient("right")
// 这里设置为“-width”,个人理解为,y轴刻度线本应该在轴的右边,设置为负数,刻度线绘制在y轴的左边
// 而且刻度线的长度为图形的宽度,表现在图上就是那些横穿柱状条的白色线,看不见白色线的部分是因为
// 图背景和刻度线都是白色
.tickSize(-width)
// 设置y轴刻度的格式
.tickFormat(function(d) return Math.round(d / 1e6) + "M"; );
// An SVG element with a bottom-right origin.
// 定义svg画布
var svg = d3.select("body").append("svg")
// 设置svg画布的宽度
.attr("width", width + margin.left + margin.right)
// 设置svg画布的高度
.attr("height", height + margin.top + margin.bottom)
.append("g")
// 定位svg画布
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// A sliding container to hold the bars by birthyear.
// 定义表示 出生年 的元素
var birthyears = svg.append("g")
.attr("class", "birthyears");
// A label for the current year.
// 绘制当年的年份文字,即图中左上角的 2000字样
var title = svg.append("text")
.attr("class", "title")
.attr("dy", ".71em")
.text(2000);
// 处理数据
d3.csv("population.csv", function(error, data)
// Convert strings to numbers.
// 将csv数据文件中的pepole,yaer,age字段的值转换成数字类型
data.forEach(function(d)
d.people = +d.people;
d.year = +d.year;
d.age = +d.age;
);
// Compute the extent of the data set in age and years.
// 计算年龄和年份数据集的范围
// 获取最大年龄
var age1 = d3.max(data, function(d) return d.age; ),
// 获取最小年份
year0 = d3.min(data, function(d) return d.year; ),
// 获取最大年份
year1 = d3.max(data, function(d) return d.year; ),
// 设置year为最大年份
year = year1;
// Update the scale domains.
// 上面在定义x,y的比例尺时没有设置“定义域”,此处开始设置
// 设置x比例尺的定义域,可以看出,x轴表示年龄的变化
x.domain([year1 - age1, year1]);
// 设置y比例尺的定义域,可以看出,y轴表示人口数量的变化
y.domain([0, d3.max(data, function(d) return d.people; )]);
// Produce a map from year and birthyear to [male, female].
// d3.nest()函数用来将数据分组为任意层次结构
// d3.nest().key(fun)用来对每数据以fun函数返回的键值来进行分组,此处以year来进行分组
// 后,返回的是以year作为键的不同的数组;再以year-age作为键值进行第二次分组;
// rollup()函数将用返回的值d.people来替换key所对应的值
// d3.nest().map()返回最终的分组后的层次结构的数据
// 可以通过在浏览器中调试状态下看到最终返回的data数组是以年份进行第一层分组,每个年份下又以
// d.year -d.age进行了第二层的分组,第二层分组对应的数据为rollup中指定的d.people。
data = d3.nest()
.key(function(d) return d.year; )
.key(function(d) return d.year - d.age; )
.rollup(function(v) return v.map(function(d) return d.people; ); )
.map(data);
// Add an axis to show the population values.
// 绘制y轴
svg.append("g")
.attr("class", "y axis")
// 将y轴定位到画布右侧
.attr("transform", "translate(" + width + ",0)")
// 对该g元素执行yAxis定义的操作
.call(yAxis)
.selectAll("g")
// 筛选出 value为空的
.filter(function(value) return !value; )
// 将筛选出的value为空的元素,为期添加zero样式类
.classed("zero", true);
// Add labeled rects for each birthyear (so that no enter or exit is required).
// 为表示出生年份的元素绑定数据,定义年份步长为5年
var birthyear = birthyears.selectAll(".birthyear")
.data(d3.range(year0 - age1, year1 + 1, 5))
.enter().append("g")
.attr("class", "birthyear")
// 定位年份的位置,通过上面定义的x()比例尺函数来计算
.attr("transform", function(birthyear) return "translate(" + x(birthyear) + ",0)"; );
// 绘制柱状条
birthyear.selectAll("rect")
// 获取2000这一年里,出生年份为birthyear的分组
.data(function(birthyear) return data[year][birthyear] || [0, 0]; )
.enter().append("rect")
.attr("x", -barWidth / 2)
.attr("width", barWidth)
// 设置y位置通过y比例尺来计算
.attr("y", y)
// 设置柱状条的高度
.attr("height", function(value) return height - y(value); );
// Add labels to show birthyear.
// 添加出生年份文字
birthyear.append("text")
.attr("y", height - 4)
.text(function(birthyear) return birthyear; );
// Add labels to show age (separate; not animated).
// 添加年龄文字
svg.selectAll(".age")
// 为年龄文字绑定数据,年龄步长为5
.data(d3.range(0, age1 + 1, 5))
.enter().append("text")
.attr("class", "age")
.attr("x", function(age) return x(year - age); )
.attr("y", height + 4)
.attr("dy", ".71em")
.text(function(age) return age; );
// Allow the arrow keys to change the displayed year.
// 通过方向键“←”和“→”来查滑动年份窗口,查看更多年份的人口分布情况
// 用focus()方法可把键盘焦点给予当前窗口
window.focus();
//为方向键“←”和“→”操作绑定动作
d3.select(window).on("keydown", function()
switch (d3.event.keyCode)
// 若为向左←,则将当前年份倒退10年
case 37: year = Math.max(year0, year - 10); break;
// 若为向右→,则将当前年份向前推进10年
case 39: year = Math.min(year1, year + 10); break;
// 对图进行更新
update();
);
// 定义更改年份窗口后,对图进行更新的操作
function update()
// 若更改年份窗口后,data中无当前年份的数据,则不进行任何操作,直接返回
if (!(year in data)) return;
// 托更改年份窗口后,data中有当前年份数据,则首先更新左上角显示的年份
title.text(year);
// 更新出生年份,此处定义更新过渡动画
birthyears.transition()
// 动作持续750毫秒
.duration(750)
// 定义更新动作
.attr("transform", "translate(" + (x(year1) - x(year)) + ",0)");
// 更新柱状条
birthyear.selectAll("rect")
// 绑定新的年份窗口数据
.data(function(birthyear) return data[year][birthyear] || [0, 0]; )
// 定义过渡动画
.transition()
.duration(750)
.attr("y", y)
.attr("height", function(value) return height - y(value); );
);
</script>
至此,人口金字塔图的实现解释完毕。实现此人口金字塔图的重点:
一是通过d3.nest()数据处理方法,对官网给出的population.csv中的数据进行分组处理。
二是x,y坐标轴的刻度的计算方法。
今天尽管阳光明媚,但是空气冷凉,适合坐在室内窗边安安静静地就很美好。
以上是关于D3.js中Population Pyramid详解的主要内容,如果未能解决你的问题,请参考以下文章
D3.js的v5版本入门教程(第十二章)—— D3.js中各种精美的图形