堆叠
示例 · 堆叠将长度转换为连续的位置区间。例如,每月销售额的条形图可以按类别细分为多系列条形图,垂直堆叠条形图并应用类别颜色编码。堆叠图可以同时显示总值和每个类别的值;但是,由于只有堆叠的底层对齐,因此通常难以跨类别进行比较。因此,请仔细选择 堆叠顺序,并考虑使用 流图。(另请参见 分组图表。)
与 饼图生成器 一样,堆叠生成器不会直接生成形状。相反,它计算位置,然后可以将这些位置传递给 区域生成器 或直接使用,例如用于定位条形图。
stack()
源代码 · 使用默认设置构造新的堆叠生成器。有关用法,请参见 stack。
stack(data, ...arguments)
源代码 · 为给定的 data 数组生成堆叠,并返回一个数组,表示每个系列。任何额外的 arguments 都是任意的;它们与 this
对象一起传播到访问器。
例如,考虑这个每月水果销售额的整齐表格
日期 | 水果 | 销量 |
---|---|---|
1/2015 | 苹果 | 3840 |
1/2015 | 香蕉 | 1920 |
1/2015 | 樱桃 | 960 |
1/2015 | 榴莲 | 400 |
2/2015 | 苹果 | 1600 |
2/2015 | 香蕉 | 1440 |
2/2015 | 樱桃 | 960 |
2/2015 | 榴莲 | 400 |
3/2015 | 苹果 | 640 |
3/2015 | 香蕉 | 960 |
3/2015 | 樱桃 | 640 |
3/2015 | 榴莲 | 400 |
4/2015 | 苹果 | 320 |
4/2015 | 香蕉 | 480 |
4/2015 | 樱桃 | 640 |
4/2015 | 榴莲 | 400 |
这可以用 JavaScript 表示为一个对象数组,可能是从 CSV 解析的
const data = [
{date: new Date("2015-01-01"), fruit: "apples", sales: 3840},
{date: new Date("2015-01-01"), fruit: "bananas", sales: 1920},
{date: new Date("2015-01-01"), fruit: "cherries", sales: 960},
{date: new Date("2015-01-01"), fruit: "durians", sales: 400},
{date: new Date("2015-02-01"), fruit: "apples", sales: 1600},
{date: new Date("2015-02-01"), fruit: "bananas", sales: 1440},
{date: new Date("2015-02-01"), fruit: "cherries", sales: 960},
{date: new Date("2015-02-01"), fruit: "durians", sales: 400},
{date: new Date("2015-03-01"), fruit: "apples", sales: 640},
{date: new Date("2015-03-01"), fruit: "bananas", sales: 960},
{date: new Date("2015-03-01"), fruit: "cherries", sales: 640},
{date: new Date("2015-03-01"), fruit: "durians", sales: 400},
{date: new Date("2015-04-01"), fruit: "apples", sales: 320},
{date: new Date("2015-04-01"), fruit: "bananas", sales: 480},
{date: new Date("2015-04-01"), fruit: "cherries", sales: 640},
{date: new Date("2015-04-01"), fruit: "durians", sales: 400}
];
要计算堆叠系列(每个 fruit 一个系列或层;每个 date 一个堆叠或列),我们可以 索引 date 和 fruit 的数据,计算数据集中的不同 fruit 名称,最后获取每个 date 和 fruit 的 sales 值。
const series = d3.stack()
.keys(d3.union(data.map(d => d.fruit))) // apples, bananas, cherries, …
.value(([, group], key) => group.get(key).sales)
(d3.index(data, d => d.date, d => d.fruit));
生成的数组每个 series 包含一个元素。每个系列每个月有一个点,每个点都有一个下限值和上限值,定义基线和顶线
[
[[ 0, 3840], [ 0, 1600], [ 0, 640], [ 0, 320]], // apples
[[3840, 5760], [1600, 3040], [ 640, 1600], [ 320, 800]], // bananas
[[5760, 6720], [3040, 4000], [1600, 2240], [ 800, 1440]], // cherries
[[6720, 7120], [4000, 4400], [2240, 2640], [1440, 1840]] // durians
]
然后通常将每个系列传递给 区域生成器 以渲染面积图,或用于构建条形图的矩形。
svg.append("g")
.selectAll("g")
.data(series)
.join("g")
.attr("fill", d => color(d.key))
.selectAll("rect")
.data(D => D)
.join("rect")
.attr("x", d => x(d.data[0]))
.attr("y", d => y(d[1]))
.attr("height", d => y(d[0]) - y(d[1]))
.attr("width", x.bandwidth());
系列由 键访问器 确定;返回的数组中每个系列 i 对应于 i 个键。每个系列都是一个点的数组,其中每个点 j 对应于输入 data 中的 j 个元素。最后,每个点都表示为一个数组 [y0, y1],其中 y0 是下限值(基线),y1 是上限值(顶线);y0 和 y1 之间的差对应于为此点计算的 值。每个系列的键作为 series.key 可用,索引 作为 series.index 可用。每个点的输入数据元素作为 point.data 可用。
stack.keys(keys)
源代码 · 如果指定了 keys,则将键访问器设置为指定的函数或数组,并返回此堆叠生成器。
const stack = d3.stack().keys(["apples", "bananas", "cherries", "durians"]);
如果未指定 keys,则返回当前键访问器。
stack.keys() // () => ["apples", "bananas", "cherries", "durians"]
键访问器默认为空数组。每个键都会 生成 一个系列(层)。键通常是字符串,但它们可能是任意值;请参见 InternMap。系列的键以及每个数据点一起传递给 值访问器,以计算点的值。
stack.value(value)
源代码 · 如果指定了 value,则将值访问器设置为指定的函数或数字,并返回此堆叠生成器。
const stack = d3.stack().value((d, key) => d[key]);
如果未指定 value,则返回当前值访问器。
stack.value() // (d, key) => d[key]
值访问器默认为
function value(d, key) {
return d[key];
}
注意
默认值访问器假设输入数据是一个对象数组,这些对象公开具有数值的命名属性。这是数据的“宽”而不是“整齐”表示,不再推荐。请参见 stack 以了解使用整齐数据的示例。
stack.order(order)
源代码 · 如果指定了 order,则将顺序访问器设置为指定的函数或数组,并返回此堆叠生成器。
const stack = d3.stack().order(d3.stackOrderNone);
如果 order 是一个函数,则它会传递生成的系列数组,并且必须返回一个表示堆叠顺序的数字索引数组。例如,要使用反向键顺序
const stack = d3.stack().order(series => d3.range(series.length).reverse());
堆叠顺序是在 偏移 之前计算的;因此,在计算顺序时,所有点的下限值都为零。每个系列的 index 属性也是在计算顺序之后设置的。
如果未指定 order,则返回当前顺序访问器。
stack.order() // d3.stackOrderNone
顺序访问器默认为 stackOrderNone;这使用由 键访问器 给出的顺序。请参见 堆叠顺序,了解内置顺序。
stack.offset(offset)
源代码 · 如果指定了 offset,则将偏移访问器设置为指定的函数,并返回此堆叠生成器。
const stack = d3.stack().offset(d3.stackOffsetExpand);
偏移函数将传递生成的系列数组和顺序索引数组;然后它负责更新系列数组中的下限值和上限值。请参见内置偏移量,了解参考实现。
如果未指定 offset,则返回当前偏移访问器。
stack.offset() // d3.stackOffsetExpand
偏移访问器默认为 stackOffsetNone;这使用零基线。请参见 堆叠偏移量,了解内置偏移量。
堆叠顺序
堆叠顺序通常不会直接使用,而是传递给 stack.order。
stackOrderAppearance(series)
const stack = d3.stack().order(d3.stackOrderAppearance);
源代码 · 返回一个系列顺序,使得最早的系列(根据最大值)位于底部。
stackOrderAscending(series)
const stack = d3.stack().order(d3.stackOrderAscending);
源代码 · 返回一个系列顺序,使得最小的系列(根据值的总和)位于底部。
stackOrderDescending(series)
const stack = d3.stack().order(d3.stackOrderDescending);
源代码 · 返回一个系列顺序,使得最大的系列(根据值的总和)位于底部。
stackOrderInsideOut(series)
const stack = d3.stack().order(d3.stackOrderInsideOut);
源代码 · 返回一个系列顺序,使得最早的系列(根据最大值)位于内部,而后面的系列位于外部。建议将此顺序用于流图,并结合使用 抖动偏移。有关更多信息,请参见 堆叠图 - 几何图形与美学 by Byron & Wattenberg。
stackOrderNone(series)
const stack = d3.stack().order(d3.stackOrderNone);
源代码 · 返回给定的系列顺序 [0, 1, … n - 1],其中 n 是 series 中元素的数量。因此,堆叠顺序由 键访问器 给出。
stackOrderReverse(series)
const stack = d3.stack().order(d3.stackOrderReverse);
源代码 · 返回给定序列顺序的反转 [n - 1, n - 2, … 0],其中 n 是 series 中元素的数量。因此,堆叠顺序由 键访问器 的反转给出。
堆叠偏移量
堆叠偏移量通常不会直接使用,而是传递给 stack.offset。
stackOffsetExpand(series, order)
const stack = d3.stack().offset(d3.stackOffsetExpand);
源代码 · 应用零基线并对每个点的值进行归一化,使得顶线始终为 1。
stackOffsetDiverging(series, order)
const stack = d3.stack().offset(d3.stackOffsetDiverging);
源代码 · 正值堆叠在零之上,负值 堆叠在零之下,零值堆叠在零处。
stackOffsetNone(series, order)
const stack = d3.stack().offset(d3.stackOffsetNone);
源代码 · 应用零基线。
stackOffsetSilhouette(series, order)
const stack = d3.stack().offset(d3.stackOffsetSilhouette);
源代码 · 向下移动基线,使得流图的中心始终位于零处。
stackOffsetWiggle(series, order)
const stack = d3.stack().offset(d3.stackOffsetWiggle);
源代码 · 移动基线以最小化层的加权摆动。此偏移量推荐用于与 内向外顺序 结合使用的流图。有关更多信息,请参见 Bryon 和 Wattenberg 的 堆叠图 - 几何形状和美学。