我需要用 D3.js 制作一个看起来很经典的人口金字塔。类似于这张图片的东西:
我发现了一些看起来非常好的示例(this和this),但它们比我正在寻找的要复杂。有谁知道我可以看的一个很好的简单例子?开始这个有什么建议吗?我应该只制作两个相邻的条形图,每个性别组一个吗?
进行这样的可视化的关键是在开始绘制任何东西之前花大量时间提前设置布局。
当需要为图表制作刻度和轴时,您将希望使用一些关键度量来使您的生活更轻松。然后,您可以使用<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 + ')');
这是一个很好的做法,因为它允许您在 svg 文档上为轴刻度、标签和标题留出空间,而无需在定位可视化本身的部分时考虑该空间。稍后,如果您发现需要更多空间,您可以只调整代码顶部的一个值,而不是重新定位整个图表。
其他重要的点是两个 y 轴的 x 坐标。换句话说,男性和女性人口为零的点(如下图红线所示)。这些将是距您的内部区域中心线的固定距离(如下所示的蓝线):
您可以将中间边距属性添加到之前创建的边距对象以引用此值:
margin.middle = 28;
您将需要创建变量来存储上面两条红线的位置。现在,我们称它们pointA
为(男性人口零线的 x 坐标)和pointB
(女性人口的对应点)。
var regionWidth = (width/2) - margin.middle;
var pointA = regionWidth,
pointB = width - regionWidth;
一旦你设置了这些点,事情就变得简单多了,因为你可以简单地将这些值插入到 svg 变换中,以将你创建的对象转换到这些位置。
最棘手的部分是两个中心 y 轴,它们共享一组标签。为此,您需要很好地掌握如何d3.svg.axis()
创建其文本标签以及可以操作哪些属性。为此需要了解四个重要的属性:
axis.orient(direction)
指定刻度指向的方向,从轴出来。在这种情况下这会有点混乱,因为我们希望刻度指向中心,所以左轴将有.orient('right')
,右轴将有.orient('left')
axis.tickSize(inner,outer)
设置从轴出来的刻度线的长度(以 svg 为单位)。“外部”标记是轴端点处的标记,在这种情况下可以设置为0
,因为我们使用的是带范围带的序数刻度,所以端点不代表值。为内部刻度选择一个较小的值,在此示例中,我将使用4
.
axis.tickPadding(padding)
设置标签文本的文本锚距刻度线末端的距离。我们希望将文本锚点放置在图像的中心,就在两个轴之间。由于我们已经知道中间边距(从中心线到每个轴的距离)并且我们知道tickSize
(刻度的长度),我们可以通过取这两个值的差来将它们放在中心.tickPadding(margin.middle - 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('');
然后,当实际绘制左 y 轴时,选择它的<text>
元素并将 text-anchor 设置为,middle
以便它们以锚为中心:
.call(yAxisLeft)
.selectAll('text')
.style('text-anchor', 'middle');
这里是使用一些人为数据的整个图表的简单示例。我试图尽可能多地评论一般结构以使其易于理解。我还制作了一个translation(x,y)
函数,使每个翻译的 x 和 y 坐标比使用字符串连接时更容易发现。您可以在脚本底部找到它的定义。
我希望这会有所帮助,如果您对具体细节有疑问,请告诉我。