Stack Layout

API ReferenceLayoutsStack Layout

The stack layout takes an array of layer objects, each having a series (array) of point objects as a member. The point objects contain a pair of ordinates (as a minimum) that map the horizontal position of each point and its vertical thickness. The output from the stack layout is the same array of layers, but with state added onto the point objects to facilitate the selected stacking strategy. The layout computes a baseline for the first layer which it then propagates to the above layers, so as to produce a stacked data set. Several baseline algorithms are supported, along with sorting heuristics to improve perception, as described in “Stacked Graphs—Geometry & Aesthetics” by Byron & Wattenberg.

stack

The stack layout operates in an arbitrary two-dimensional x and y coordinate space, similar to D3's other layouts, including tree. Thus, layers can be stacked vertically, horizontally, or even radially. While the "zero" offset is the default, a streamgraph can be generated using the "wiggle" offset, which attempts to minimize change in slope weighted by layer thickness.

d3.layout.stack()

Constructs a new stack layout with the default offset (zero) and order (null). The returned layout object is both an object and a function. That is: you can call the layout like any other function, and the layout has additional methods that change its behavior. Like other classes in D3, layouts follow the method chaining pattern where setter methods return the layout itself, allowing multiple setters to be invoked in a concise statement.

stack(layers[, index])

Computes the y-coordinate baseline for each layer in the layers array, and then propagates that baseline to the other layers. In the simplest case, layers is a two-dimensional array of point objects, all having the same length, and each having a vertical and horizontal ordinate value to define the y-thickness of each layer at the given x-position.

More complex structures are accepted by the layout, but only if an accessor function is passed to values, which abstracts the structure back to the simple case described above. In any case, the complexity is limited to an array of layer objects, each having a points array as a member. It is not possible, for example to use a series hash table (object) containing key value pairs representing the coordinates. Such a structure could be abstracted into the required format using an accessor function but, the object returned by the layout would not have the added offset state, as there is currently no means to abstract the output with layer awareness.

The default layout expects the point objects to carry x and y members, to which it will add a y0 member, to store the offset values produced by the selected baseline algorithm. If the coordinate properties (raw or abstracted) are not named x and y, then x and y accessors must be provided to complete the abstraction and deliver the above structure.

Thus, each point object has the following abstract structure:

  • x - the x-position of the value.
  • y - the y-thickness of the value.
  • y0 - the minimum y-position of the value (baseline).

The last two being physically added onto the point objects if required.

The optional index argument is not consumed by the default layout, but is made available to custom order and offset objects.

stack.values([accessor])

Specifies how to extract the points array from the layer elements of the layers array; accessor is a function which is invoked on each input layer passed to stack, equivalent to calling layers.map(accessor) before computing the stack layout. The default values function is the identity function. If accessor is not specified, returns the current values accessor.

The values accessor can be used to associate additional data per-layer, rather than per-point. For example, say your data were structured as follows:

var layers = [
  {
    "name": "apples",
    "values": [
      { "x": 0, "y":  91},
      { "x": 1, "y": 290}
    ]
  },
  {
    "name": "oranges",
    "values": [
      { "x": 0, "y":  9},
      { "x": 1, "y": 49}
    ]
  }
];

Specify a values accessor that retrieves the points for each layer:

var stack = d3.layout.stack()
    .offset("wiggle")
    .values(function(d) { return d.values; });

Then, if you wanted to add a tooltip for each layer, you might say:

svg.selectAll("path")
    .data(stack(layers))
  .enter().append("path")
    .attr("d", function(d) { return area(d.values); })
  .append("title")
    .text(function(d) { return d.name; });
stack.offset([offset])

If offset is specified, sets the stack offset algorithm to the specified value. If offset is not specified, returns the current offset algorithm. The following string values are supported:

  • silhouette - center the stream, as in ThemeRiver.
  • wiggle - minimize weighted change in slope.
  • expand - normalize layers to fill the range [0,1].
  • zero - use a zero baseline, i.e., the y-axis.

In addition to a string, offset may be specified as a function. The input to the offset function is the layer data, converted to a standardized representation: a two-dimensional array of values, where each value is represented as a two-element array [x, y]. The return value of the offset function must be an array of values which represents the y-coordinates of the baseline. For example, the default "zero" offset is implemented as:

function offset(data) {
  var j = -1,
      m = data[0].length,
      y0 = [];
  while (++j < m) y0[j] = 0;
  return y0;
}
stack.order([order])

If order is specified, sets the stack order to the specified value. If order is not specified, returns the current order. The following string values are supported:

  • inside-out - sort by index of maximum value, then use balanced weighting.
  • reverse - reverse the input layer order.
  • default - use the input layer order.

In addition to a string, order may be specified as a function. The input to the order function is the layer data, converted to the standardized representation: a two-dimensional array of values, where each value is represented as a two-element array [x, y]. The return value of the order function must be an array of indexes which represents the layer order. For example, the default order is implemented as:

function order(data) {
  return d3.range(data.length);
}

See also d3.range.

stack.x([accessor])

Specifies how to access the x-coordinate of each value’s position. If accessor is specified, sets the accessor to the specified function. If accessor is not specified, returns the current function, which by default assumes that each input value has an x attribute:

function x(d) {
  return d.x;
}

The x-accessor is invoked for each input value, for each input layer, being passed the current data (d) and index (i). The return value of the accessor must be a number.

The x-coordinate only affects the behavior of the “wiggle” offset; changing this accessor does not affect how data is grouped into stacks. Although the x-accessor is invoked for all layers (not just the bottommost layer), the stack layout assumes that the x-coordinates of all layers are homogenous and consistent. In other words, each layer must contain the same number of values, at the same x-coordinates, in the same order. If your data is irregular, you will need to reinterpolate or reorder the data before computing the stack.

stack.y([accessor])

Specifies how to access the y-coordinate of each value's thickness. If accessor is specified, sets the accessor to the specified function. If accessor is not specified, returns the current function, which by default assumes that each input value has a y attribute:

function y(d) {
  return d.y;
}

The y-accessor is invoked for each input value, for each input layer, being passed the current data (d) and index (i). The return value of the accessor must be a number. With the exception of the "expand" offset, the stack layout does not perform any automatic scaling of data. To simplify scaling, use this layout in conjunction with a linear scale or similar.

stack.out([setter])

Specifies how to propagate the computed baseline to above layers. If setter is specified, it is used as the output function. If setter is not specified, returns the current output function, which by default assumes that each input value has y and y0 attributes:

function out(d, y0, y) {
  d.y0 = y0;
  d.y = y;
}

The setter is invoked for each input value, for each input layer, being passed the current data (d), the computed y0 value, and the computed y-thickness. In all cases except the "expand" offset, the y-thickness is the same as the input value returned by y, and thus may be ignored.

© 2010–2016 Michael Bostock
Licensed under the BSD License.
https://github.com/d3/d3-3.x-api-reference/blob/master/Stack-Layout.md