用D3.js创建人口金字塔

bai*_*ley 5 javascript population census d3.js

我需要用D3.js制作一个经典的人口金字塔.与此图像类似的东西: 在此输入图像描述

我发现了一些看起来非常好的例子(这个这个),但它们比我正在寻找的更复杂.有谁知道我能看到的一个很好的SIMPLE例子?有什么建议吗?我应该只是制作两个彼此相邻的条形图,每个性别组一个吗?

jsh*_*ley 21

制作这样的可视化的关键是在绘制任何东西之前花费大量时间预先设置布局.

当您需要为图表制作刻度和轴时,您需要使用一些关键测量值来使您的生活更轻松.然后,您可以使用<g>具有翻译的元素,以便在图表的每个"部分"中,您可以在本地坐标中工作.

目标是创建两个镜像区域,如下所示:

两个镜像区域

这将允许您为两个区域之间的x和y维度共享相同的比例.

要定义这些区域,您可以先为svg内部的边距设置一些值,然后创建一个组以放在这些边距内.这是d3可视化中的常见做法,如果您还不熟悉它,那么这是一个学习的好时机.它通常看起来像这样:

var width = 400,
    height = 300;

var margin = {
  top: 20,
  right: 10
  bottom: 40,
  left: 10
};

var svg = d3.select('body').append('svg')
  .attr('width', margin.left + width + margin.right)
  .attr('height', margin.top + height + margin.bottom)
  .append('g')
    .attr('class', 'inner-region')
    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
Run Code Online (Sandbox Code Playgroud)

这是一个很好的做法,因为它允许您在svg文档上为轴标记,标签和标题留出空间,而无需在定位可视化本身的部分时考虑该空间.然后,如果您发现需要更多空间,可以在代码顶部调整一个值,而不是重新定位整个图表.

其他重要的点是两个y轴的x坐标.换句话说,男性和女性的人口为零(以下显示为红线).这些距离是您内部区域中心线的一个设定距离(如下面的蓝线所示):

中间边缘

您可以将中间边距属性添加到先前创建的边距对象以引用此值:

margin.middle = 28;
Run Code Online (Sandbox Code Playgroud)

您将需要创建变量来存储上面两条红线的位置.现在,让我们称呼它们pointA(男性人口零线的x坐标)和pointB(女性人口的对应点).

var regionWidth = (width/2) - margin.middle;

var pointA = regionWidth,
    pointB = width - regionWidth;
Run Code Online (Sandbox Code Playgroud)

一旦你设置了这些点,事情变得更加简单,因为你可以简单地将这些值插入到svg变换中,以将你创建的对象转换为这些位置.

最棘手的部分是两个中心y轴,它们共享一组标签.为此,您需要很好地处理如何d3.svg.axis()创建文本标签以及可以操作哪些属性.有四个重要的属性要知道:

  1. axis.orient(direction)指定刻度线从轴出来的方向.在这种情况下,这会有点混乱,因为我们希望刻度线指向中心,因此左轴将具有.orient('right'),右轴将具有.orient('left')

  2. axis.tickSize(inner,outer)设置从轴出来的刻度线的长度(以svg为单位)."外部"标记是轴的端点处的标记,并且0在这种情况下可以设置为因为我们使用带有范围带的序数标度,因此端点不表示值.为内部刻度选择一个小值,我将使用这个例子4.

  3. axis.tickPadding(padding)设置标记文本的文本锚所放置的刻度线末尾的距离.我们希望将文本锚点放在图像的中心,就在两个轴之间.由于我们已经知道中间边界(从中心线到每个轴的距离)并且我们知道tickSize(刻度线的长度),我们可以通过取这两个值的差异将它们放在中心:.tickPadding(margin.middle - 4).

  4. axis.tickFormat(format)指定刻度标签的格式.可以将其设置为空字符串以从其中一个轴中删除刻度标签,从而不会发生重叠:.tickFormat('')

因此,两个y轴可以这样定义:

var yAxisLeft = d3.svg.axis()
  .scale(yScale)
  .orient('right')
  .tickSize(4,0)
  .tickPadding(margin.middle - 4);

var yAxisRight = d3.svg.axis()
  .scale(yScale)
  .orient('left')
  .tickSize(4,0)
  .tickFormat('');
Run Code Online (Sandbox Code Playgroud)

然后,当实际绘制左侧y轴时,选择其<text>元素并将text-anchor设置为middle使它们以锚点为中心:

.call(yAxisLeft)
  .selectAll('text')
  .style('text-anchor', 'middle');
Run Code Online (Sandbox Code Playgroud)

是使用一些人为数据的整个图表的简单示例.我试图尽可能多地评论一般结构,以便于遵循.我还创建了一个translation(x,y)函数,使每个平移的x和y坐标比使用字符串连接时更容易发现.您可以在脚本底部找到它的定义.

我希望这有帮助,如果您对细节有疑问,请告诉我.