层级结构
🌐 Hierarchies
在你可以计算层次布局之前,你需要一个根节点。如果你的数据已经是层次结构格式,例如 JSON,你可以直接将其传递给hierarchy;否则,你可以使用stratify将表格数据(例如逗号分隔值 CSV)重新排列为层次结构。
🌐 Before you can compute a hierarchical layout, you need a root node. If your data is already in a hierarchical format, such as JSON, you can pass it directly to hierarchy; otherwise, you can rearrange tabular data, such as comma-separated values (CSV), into a hierarchy using stratify.
hierarchy(data, children)
示例 · 来源 · 从指定的分层数据构建一个根节点。指定的数据必须是表示根节点的对象。例如:
const data = {
name: "Eve",
children: [
{name: "Cain"},
{name: "Seth", children: [{name: "Enos"}, {name: "Noam"}]},
{name: "Abel"},
{name: "Awan", children: [{name: "Enoch"}]},
{name: "Azura"}
]
};构造层次结构:
🌐 To construct a hierarchy:
const root = d3.hierarchy(data);指定的 children 访问函数会对每个数据项调用,从根 data 开始,并且必须返回一个表示子项的数据可迭代对象(如果有的话)。如果未指定子项访问函数,则默认为:
🌐 The specified children accessor function is invoked for each datum, starting with the root data, and must return an iterable of data representing the children, if any. If the children accessor is not specified, it defaults to:
function children(d) {
return d.children;
}如果 data 是一个 Map,它会被隐式转换为条目 [undefined, data],并且 children 访问器默认改为:
🌐 If data is a Map, it is implicitly converted to the entry [undefined, data], and the children accessor instead defaults to:
function children(d) {
return Array.isArray(d) ? d[1] : null;
}这允许你将 group 或 rollup 的结果传递给层级结构。
🌐 This allows you to pass the result of group or rollup to hierarchy.
返回的根节点及其每个后代节点具有以下属性:
🌐 The returned root node and each descendant has the following properties:
- node.data - 作为传递给 hierarchy 的关联数据
- node.depth - 根节点为零,每向下一个子孙层级增加一
- node.height - 从任意子孙叶子到该节点的最大距离,叶子节点为零
- node.parent - 父节点,根节点则为 null
- node.children - 子节点数组,如果有的话;如果是叶节点,则为 undefined
- node.value - 节点及其子孙的可选求和值
此方法也可用于测试一个节点是否为 instanceof d3.hierarchy 并扩展节点原型。
🌐 This method can also be used to test if a node is an instanceof d3.hierarchy and to extend the node prototype.
node.ancestors()
来源 · 返回祖级节点的数组,从该节点开始,然后依次向上到每个父节点,直到根节点。
node.descendants()
来源 · 返回子孙节点的数组,从该节点开始,然后按拓扑顺序依次返回每个子节点。
node.leaves()
来源 · 返回按遍历顺序排列的叶子节点数组。叶子是指没有子节点的节点。
node.find(filter)
来源 · 返回从此节点开始层次结构中第一个使指定过滤器返回真值的节点。如果未找到这样的节点,则返回未定义。
node.path(target)
来源 · 返回从此节点到指定目标节点的层次结构中的最短路径。路径从此节点开始,上升到此节点与目标节点的最小公共祖级,然后下降到目标节点。这对于层次边缘打包很有用。
node.links()
源 · 返回此节点及其子孙节点的链接数组,其中每个链接是一个定义源和目标属性的对象。每个链接的源是父节点,目标是子节点。
node.sum(value)
示例 · 来源 · 对此 节点 及其每个子孙以后序遍历方式计算指定的 value 函数,并返回此 节点。每个节点的 node.value 属性被设置为指定函数返回的数值加上所有子节点的总和。该函数接收节点的数据作为参数,并且必须返回非负数。value 访问器会对 node 及其所有子孙节点进行计算,包括内部节点;如果你只想让叶子节点有内部值,则对任何含有子节点的节点返回零。例如,作为 node.count 的一种替代方法:
root.sum((d) => d.value ? 1 : 0);在调用需要 node.value 的层次布局(例如 treemap)之前,你必须先调用 node.sum 或 node.count。例如:
🌐 You must call node.sum or node.count before invoking a hierarchical layout that requires node.value, such as treemap. For example:
// Construct the treemap layout.
const treemap = d3.treemap();
treemap.size([width, height]);
treemap.padding(2);
// Sum and sort the data.
root.sum((d) => d.value);
root.sort((a, b) => b.height - a.height || b.value - a.value);
// Compute the treemap layout.
treemap(root);
// Retrieve all descendant nodes.
const nodes = root.descendants();由于该 API 支持方法链,你也可以说:
🌐 Since the API supports method chaining, you can also say:
d3.treemap()
.size([width, height])
.padding(2)
(root
.sum((d) => d.value)
.sort((a, b) => b.height - a.height || b.value - a.value))
.descendants()此示例假设节点数据具有值字段。
🌐 This example assumes that the node data has a value field.
node.count()
示例 · 来源 · 计算此节点下的叶子数,并将其分配给节点.value,对节点的每个后代同样处理。如果此节点是叶子,其计数为一。返回此节点。另见 节点.sum。
node.sort(compare)
示例 · 来源 · 对此节点的子节点(如果有的话)以及此节点的每个后代的子节点进行排序,使用指定的compare函数按先序遍历排序,并返回此节点。
指定的函数会传入两个节点 a 和 b 进行比较。如果 a 应该在 b 之前,函数必须返回小于零的值;如果 b 应该在 a 之前,函数必须返回大于零的值;否则,a 和 b 的相对顺序未指定。更多信息请参见 array.sort。
🌐 The specified function is passed two nodes a and b to compare. If a should be before b, the function must return a value less than zero; if b should be before a, the function must return a value greater than zero; otherwise, the relative order of a and b are not specified. See array.sort for more.
与 node.sum 不同,compare 函数传入的是两个 节点,而不是两个节点的数据。例如,如果数据有一个 value 属性,这会根据节点及其所有子节点的总和值降序对节点进行排序,这正是 圆形填充 推荐的方式:
🌐 Unlike node.sum, the compare function is passed two nodes rather than two nodes’ data. For example, if the data has a value property, this sorts nodes by the descending aggregate value of the node and all its descendants, as is recommended for circle-packing:
root
.sum((d) => d.value)
.sort((a, b) => b.value - a.value);同样,为了按节点高度降序(与任一后代叶子的最大距离)然后按值降序排序节点,正如对 treemaps 和 icicles 所推荐的:
🌐 Similarly, to sort nodes by descending height (greatest distance from any descendant leaf) and then descending value, as is recommended for treemaps and icicles:
root
.sum((d) => d.value)
.sort((a, b) => b.height - a.height || b.value - a.value);按照高度降序然后ID升序对节点进行排序,这正是对树和树状图推荐的做法:
🌐 To sort nodes by descending height and then ascending id, as is recommended for trees and dendrograms:
root
.sum((d) => d.value)
.sort((a, b) => b.height - a.height || d3.ascending(a.id, b.id));如果你希望新的排序顺序影响布局,在调用层次布局之前,必须先调用 node.sort;请参见 node.sum 获取示例。
🌐 You must call node.sort before invoking a hierarchical layout if you want the new sort order to affect the layout; see node.sum for an example.
nodeSymbol.iterator
🌐 node[Symbol.iterator]()
来源 · 返回一个按广度优先顺序遍历节点的子孙的迭代器。例如:
for (const descendant of node) {
console.log(descendant);
}node.each(function, that)
示例 · 来源 · 对指定的节点及其每个子孙按广度优先顺序调用指定的函数,这样只有在所有较浅层的节点以及同一深度的前置节点都已被访问后,给定的节点才会被访问。指定的函数会传入当前的子孙节点、从零开始的遍历索引以及该节点。如果指定了that,它将作为回调函数的 this 上下文。
node.eachAfter(function, that)
示例 · 来源 · 对指定的 节点 及其每个后代以后序遍历 的方式调用指定的 函数,这样只有在该 节点 的所有后代都已被访问后,才会访问该 节点。指定的函数会接收当前的 后代、从零开始的遍历 索引 以及该 节点。如果指定了 that,它将作为回调函数的 this 上下文。
node.eachBefore(function, that)
示例 · 来源 · 对指定的 节点 及其每个后代以先序遍历 的方式调用指定的 函数,这样只有在该 节点 的所有祖级已经被访问后,才会访问该 节点。指定的函数会接收当前的 后代、从零开始的遍历 索引 和该 节点。如果指定了 that,它就是回调函数的 this 上下文。
node.copy()
来源 · 返回从此节点开始的子树的深拷贝。(但是,返回的深拷贝共享相同的数据。)返回的节点是新树的根节点;返回节点的父节点总是 null,其深度总是零。