D3.js中Population Pyramid详解

您所在的位置:网站首页 金字塔是什么立体图形 D3.js中Population Pyramid详解

D3.js中Population Pyramid详解

2024-02-24 11:29| 来源: 网络整理| 查看: 265

Population Pyramid

聊一聊人口金字塔图。

人口金字塔是按人口年龄和性别表示人口分布的特种塔状条形图,是形象地表示某一人口的年龄和性别构成的图形。——百度百科

一般的人口金字塔图如下图所示:

例如上图表示,2011年利比亚男女不同年龄阶段的比例分布情况。 而本篇要讲的Population Pyramid图,将男女人口数据画在了坐标轴的同一边,通过柱状图的覆盖来看不同年龄阶段的男女比例分布情况,如下图所示:

图中用粉色来标识女性的数据、蓝色标识男性的数据,数据重叠部分,由于粉色和蓝色重叠而呈现出紫色,例如70岁的人群当中,女性的比例比男性的多;而20岁的人群当中,男性的比例比女性的多。

接下来详细解释D3.js是如何实现这张人口金字塔图的。

index.html——源码 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; } // 定义相关尺寸 // 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); }); } });

至此,人口金字塔图的实现解释完毕。实现此人口金字塔图的重点: 一是通过d3.nest()数据处理方法,对官网给出的population.csv中的数据进行分组处理。 二是x,y坐标轴的刻度的计算方法。 今天尽管阳光明媚,但是空气冷凉,适合坐在室内窗边安安静静地就很美好。



【本文地址】


今日新闻


推荐新闻


    CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3