跳至内容

堆叠

示例 · 堆叠将长度转换为连续的位置区间。例如,每月销售额的条形图可以按类别细分为多系列条形图,垂直堆叠条形图并应用类别颜色编码。堆叠图可以同时显示总值和每个类别的值;但是,由于只有堆叠的底层对齐,因此通常难以跨类别进行比较。因此,请仔细选择 堆叠顺序,并考虑使用 流图。(另请参见 分组图表。)

饼图生成器 一样,堆叠生成器不会直接生成形状。相反,它计算位置,然后可以将这些位置传递给 区域生成器 或直接使用,例如用于定位条形图。

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 解析的

js
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 一个堆叠或列),我们可以 索引 datefruit 的数据,计算数据集中的不同 fruit 名称,最后获取每个 datefruitsales 值。

js
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));

提示

请参见来自 d3-array 的 unionindex

生成的数组每个 series 包含一个元素。每个系列每个月有一个点,每个点都有一个下限值和上限值,定义基线和顶线

js
[
  [[   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
]

然后通常将每个系列传递给 区域生成器 以渲染面积图,或用于构建条形图的矩形。

js
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 是上限值(顶线);y0y1 之间的差对应于为此点计算的 。每个系列的键作为 series.key 可用,索引 作为 series.index 可用。每个点的输入数据元素作为 point.data 可用。

stack.keys(keys)

源代码 · 如果指定了 keys,则将键访问器设置为指定的函数或数组,并返回此堆叠生成器。

js
const stack = d3.stack().keys(["apples", "bananas", "cherries", "durians"]);

如果未指定 keys,则返回当前键访问器。

js
stack.keys() // () => ["apples", "bananas", "cherries", "durians"]

键访问器默认为空数组。每个键都会 生成 一个系列(层)。键通常是字符串,但它们可能是任意值;请参见 InternMap。系列的键以及每个数据点一起传递给 值访问器,以计算点的值。

stack.value(value)

源代码 · 如果指定了 value,则将值访问器设置为指定的函数或数字,并返回此堆叠生成器。

js
const stack = d3.stack().value((d, key) => d[key]);

如果未指定 value,则返回当前值访问器。

js
stack.value() // (d, key) => d[key]

值访问器默认为

js
function value(d, key) {
  return d[key];
}

注意

默认值访问器假设输入数据是一个对象数组,这些对象公开具有数值的命名属性。这是数据的“宽”而不是“整齐”表示,不再推荐。请参见 stack 以了解使用整齐数据的示例。

stack.order(order)

源代码 · 如果指定了 order,则将顺序访问器设置为指定的函数或数组,并返回此堆叠生成器。

js
const stack = d3.stack().order(d3.stackOrderNone);

如果 order 是一个函数,则它会传递生成的系列数组,并且必须返回一个表示堆叠顺序的数字索引数组。例如,要使用反向键顺序

js
const stack = d3.stack().order(series => d3.range(series.length).reverse());

堆叠顺序是在 偏移 之前计算的;因此,在计算顺序时,所有点的下限值都为零。每个系列的 index 属性也是在计算顺序之后设置的。

如果未指定 order,则返回当前顺序访问器。

js
stack.order() // d3.stackOrderNone

顺序访问器默认为 stackOrderNone;这使用由 键访问器 给出的顺序。请参见 堆叠顺序,了解内置顺序。

stack.offset(offset)

源代码 · 如果指定了 offset,则将偏移访问器设置为指定的函数,并返回此堆叠生成器。

js
const stack = d3.stack().offset(d3.stackOffsetExpand);

偏移函数将传递生成的系列数组和顺序索引数组;然后它负责更新系列数组中的下限值和上限值。请参见内置偏移量,了解参考实现。

如果未指定 offset,则返回当前偏移访问器。

js
stack.offset() // d3.stackOffsetExpand

偏移访问器默认为 stackOffsetNone;这使用零基线。请参见 堆叠偏移量,了解内置偏移量。

堆叠顺序

堆叠顺序通常不会直接使用,而是传递给 stack.order

stackOrderAppearance(series)

js
const stack = d3.stack().order(d3.stackOrderAppearance);

源代码 · 返回一个系列顺序,使得最早的系列(根据最大值)位于底部。

stackOrderAscending(series)

js
const stack = d3.stack().order(d3.stackOrderAscending);

源代码 · 返回一个系列顺序,使得最小的系列(根据值的总和)位于底部。

stackOrderDescending(series)

js
const stack = d3.stack().order(d3.stackOrderDescending);

源代码 · 返回一个系列顺序,使得最大的系列(根据值的总和)位于底部。

stackOrderInsideOut(series)

js
const stack = d3.stack().order(d3.stackOrderInsideOut);

源代码 · 返回一个系列顺序,使得最早的系列(根据最大值)位于内部,而后面的系列位于外部。建议将此顺序用于流图,并结合使用 抖动偏移。有关更多信息,请参见 堆叠图 - 几何图形与美学 by Byron & Wattenberg。

stackOrderNone(series)

js
const stack = d3.stack().order(d3.stackOrderNone);

源代码 · 返回给定的系列顺序 [0, 1, … n - 1],其中 nseries 中元素的数量。因此,堆叠顺序由 键访问器 给出。

stackOrderReverse(series)

js
const stack = d3.stack().order(d3.stackOrderReverse);

源代码 · 返回给定序列顺序的反转 [n - 1, n - 2, … 0],其中 nseries 中元素的数量。因此,堆叠顺序由 键访问器 的反转给出。

堆叠偏移量

堆叠偏移量通常不会直接使用,而是传递给 stack.offset

stackOffsetExpand(series, order)

js
const stack = d3.stack().offset(d3.stackOffsetExpand);

源代码 · 应用零基线并对每个点的值进行归一化,使得顶线始终为 1。

stackOffsetDiverging(series, order)

js
const stack = d3.stack().offset(d3.stackOffsetDiverging);

源代码 · 正值堆叠在零之上,负值 堆叠在零之下,零值堆叠在零处。

stackOffsetNone(series, order)

js
const stack = d3.stack().offset(d3.stackOffsetNone);

源代码 · 应用零基线。

stackOffsetSilhouette(series, order)

js
const stack = d3.stack().offset(d3.stackOffsetSilhouette);

源代码 · 向下移动基线,使得流图的中心始终位于零处。

stackOffsetWiggle(series, order)

js
const stack = d3.stack().offset(d3.stackOffsetWiggle);

源代码 · 移动基线以最小化层的加权摆动。此偏移量推荐用于与 内向外顺序 结合使用的流图。有关更多信息,请参见 Bryon 和 Wattenberg 的 堆叠图 - 几何形状和美学