层次结构(Hierarchies)
分析或可视化数据时的一种常用技术是将数据组织成组。(分组)
可以将层次结构视为树状结构,其中根项目(或“节点”)拆分为顶级组。每个顶级组拆分为二级组,依此类推。最顶层的项目(或节点)称为根节点。最底部的项目称为叶子或叶子节点。
有几种方法可以可视化分层数据,包括树、树图、压缩圆图和旭日图。
-
从数据数组创建层次结构:使用 D3 的
.rollup
函数按任何分类属性对数据进行分组。第一个参数是要分组的数组;第二个参数是一个reduce函数,这是一个接受值数组并输出单个值的函数;其余参数是指定要分组的属性的函数。可以使用.get
检查返回的map对象
function sumWorldwideGross(group) {
return d3.sum(group, function(d) {
return d.Worldwide_Gross;
});
}
let groups = d3.rollup(data,
sumWorldwideGross,
function(d) { return d.Distributor; },
function(d) { return d.Genre; }
);
// Get Sony Pictures
groups.get('Sony Pictures'); // {"Comedy" => 22498520, "Action" => 315268353, "Drama" => 93905424}
// Get Drama within Sony Pictures
groups.get('Sony Pictures').get('Drama'); // 93905424
-
d3.层次结构:通过调用
d3.hierarchy
并传入生成的map对象来创建的d3.rollup
function sumWorldwideGross(group) {
return d3.sum(group, function(d) {
return d.Worldwide_Gross;
});
}
let groups = d3.rollup(data,
sumWorldwideGross,
function(d) { return d.Distributor; },
function(d) { return d.Genre; }
);
let root = d3.hierarchy(groups);
d3.hierarchy
输出的是一个嵌套对象,类似于:
{
data: [undefined, Map(3)],
children: [
{
data: ["Sony Pictures", Map(3)],
children: [...],
depth: 1,
height: 1,
parent: {...} // this item's parent node
}.
{
data: ["Walt Disney Pictures", Map(2)],
children: [...],
depth: 1,
height: 1,
parent: {...} // this item's parent node
}.
{
data: ["Warner Bros.", Map(3)],
children: [...],
depth: 1,
height: 1,
parent: {...} // this item's parent node
}
],
depth: 0,
height: 2,
parent: null
}
层次结构中的每个项目(或节点)都有属性:data
, children
, depth
, height
and parent
.
data
是传入的关联项目中的map或对象。通常,不需要访问该值,因为层次结构通过其children
和value
属性使该数据可用。
children
是一个包含节点子节点的数组。
depth
和height
指示层次结构中节点的深度和高度。
parent
引用节点的父节点。
-
可视化层次结构
-
树形布局:首先使用创建树布局函数
d3.tree()
;可以命令配置树的大小.size
;然后,可以调用treeLayout
,传入定义的层次结构对象root
var treeLayout = d3.tree(); treeLayout.size([400, 200]); treeLayout(root);
这将在
root
的每个节点上写入x
和y
值。绘制节点:
- 用于
root.descendants()
获取所有节点的数组 - 将此数组加入圆(或任何其他类型的 SVG 元素)
- 使用
x
和y
定位圆圈
要绘制链接:
- 用于
root.links()
获取所有链接的数组 - 将数组连接到行(或路径)元素
- 使用
x
和y
链接的source
和target
属性来定位线
// Nodes d3.select('svg g.nodes') .selectAll('circle.node') .data(root.descendants()) .join('circle') .classed('node', true) .attr('cx', function(d) {return d.x;}) .attr('cy', function(d) {return d.y;}) .attr('r', 4); // Links d3.select('svg g.links') .selectAll('line.link') .data(root.links()) .join('line') .classed('link', true) .attr('x1', function(d) {return d.source.x;}) .attr('y1', function(d) {return d.source.y;}) .attr('x2', function(d) {return d.target.x;}) .attr('y2', function(d) {return d.target.y;});
-
集群布局:与
tree
布局非常相似,主要区别在于所有叶节点都放置在相同的深度。
var clusterLayout = d3.cluster() .size([400, 200]); var root = d3.hierarchy(data); clusterLayout(root);
-
树状图布局:用于直观地表示每个项目都有关联值的层次结构。
通过调用创建树图布局函数
d3.treemap()
;可以配置布局;在将此布局应用于层次结构之前,必须在层次结构上运行.sum()
。调用treemapLayout
,传入root
之前定义的层次结构对象.
var treemapLayout = d3.treemap(); treemapLayout .size([400, 200]) .paddingOuter(10); root.sum(function(d) { return d.value; }); treemapLayout(root);
树形图布局函数向每个节点添加 4 个属性
x0
、x1
、y0
、y1
。它们指定树形图中每个矩形的尺寸。d3.select('svg g') .selectAll('rect') .data(root.descendants()) .join('rect') .attr('x', function(d) { return d.x0; }) .attr('y', function(d) { return d.y0; }) .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; })
如果想在每个矩形中添加标签,可以将
g
元素加入数组并添加rect
和text
元素到每个g
:var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .join('g') .attr('transform', function(d) {return 'translate(' + [d.x0, d.y0] + ')'}) nodes .append('rect') .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; }) nodes .append('text') .attr('dx', 4) .attr('dy', 14) .text(function(d) { return d.data.name; })
treemap
可以通过多种方式配置布局:- 可以使用设置节点子节点周围的填充
.paddingOuter
- 兄弟节点之间的填充可以使用
.paddingInner
- 可以使用同时设置外部和内部填充
.padding
- 外部填充也可以使用,``.paddingTop
、
.paddingBottom、
.paddingLeft、
.paddingRight`进行微调
树形图有不止一种排列矩形的策略。D3 有一些内置的:
treemapBinary
,treemapDice
,treemapSlice
、treemapSliceDice
、treemapSquarify
- 包布局:类似于树布局,但用圆圈表示节点。
var packLayout = d3.pack(); packLayout.size([300, 300]); rootNode.sum(function(d) { return d.value; }); packLayout(rootNode); d3.select('svg g') .selectAll('circle') .data(rootNode.descendants()) .join('circle') .attr('cx', function(d) { return d.x; }) .attr('cy', function(d) { return d.y; }) .attr('r', function(d) { return d.r; })
可以通过
g
为每个后代创建元素来添加标签:var nodes = d3.select('svg g') .selectAll('g') .data(rootNode.descendants()) .join('g') .attr('transform', function(d) {return 'translate(' + [d.x, d.y] + ')'}) nodes .append('circle') .attr('r', function(d) { return d.r; }) nodes .append('text') .attr('dy', 4) .text(function(d) { return d.children === undefined ? d.data.name : ''; })
可以使用以下方式配置每个圆圈周围的填充
packLayout.padding(10)
- 分区布局:将一个矩形空间细分为层,每个层代表层次结构中的一个层。对于层中的每个节点,每一层进一步细分
var partitionLayout = d3.partition(); partitionLayout.size([400, 200]); rootNode.sum(function(d) { return d.value; }); partitionLayout(rootNode); d3.select('svg g') .selectAll('rect') .data(rootNode.descendants()) .join('rect') .attr('x', function(d) { return d.x0; }) .attr('y', function(d) { return d.y0; }) .attr('width', function(d) { return d.x1 - d.x0; }) .attr('height', function(d) { return d.y1 - d.y0; }); partitionLayout.padding(2);
如果您想更改分区布局的方向,以便图层从左到右运行,您可以在定义元素时交换
x0
、x1
和交换:y0
、y1
.attr('x', function(d) { return d.y0; }) .attr('y', function(d) { return d.x0; }) .attr('width', function(d) { return d.y1 - d.y0; }) .attr('height', function(d) { return d.x1 - d.x0; });
还可以将
x
尺寸映射到旋转角度和y
半径以创建旭日图:var data = { "name": "A1", "children": [ { "name": "B1", "children": [ { "name": "C1", "value": 100 }, { "name": "C2", "value": 300 }, { "name": "C3", "value": 200 } ] }, { "name": "B2", "value": 200 } ] }; var radius = 150; var partitionLayout = d3.partition() .size([2 * Math.PI, radius]); var arcGenerator = d3.arc() .startAngle(function(d) { return d.x0; }) .endAngle(function(d) { return d.x1; }) .innerRadius(function(d) { return d.y0; }) .outerRadius(function(d) { return d.y1; }); var rootNode = d3.hierarchy(data) rootNode.sum(function(d) { return d.value; }); partitionLayout(rootNode); d3.select('svg g') .selectAll('path') .data(rootNode.descendants()) .join('path') .attr('d', arcGenerator);
-
树形布局:首先使用创建树布局函数