# `Macaw.Data.Stack`
[🔗](https://github.com/dcoai/macaw_data/blob/v0.5.3/lib/macaw/data/stack.ex#L1)

d3-shape's stack (spec/04 §4.8): a baseline for each datum, so layers of data stack on
one another. The engine of the `stack` transform, ported from visualize's
`Visualize.Shape.Stack`.

## Examples

    data = [
      %{month: "Jan", apples: 10, oranges: 20, bananas: 15},
      %{month: "Feb", apples: 15, oranges: 25, bananas: 10},
      %{month: "Mar", apples: 20, oranges: 15, bananas: 25}
    ]

    stack = Macaw.Data.Stack.new()
      |> Macaw.Data.Stack.keys([:apples, :oranges, :bananas])

    series = Macaw.Data.Stack.generate(stack, data)
    # Returns one series per key, in key order, each with :key, :index (its position
    # in the stacking order) and :points. Points have :data, :y0 (baseline), :y1 (top).

# `point`

```elixir
@type point() :: %{
  key: any(),
  index: non_neg_integer(),
  data: any(),
  value: number(),
  y0: number(),
  y1: number()
}
```

# `series`

```elixir
@type series() :: %{key: any(), index: non_neg_integer(), points: [point()]}
```

# `t`

```elixir
@type t() :: %Macaw.Data.Stack{
  keys: [any()],
  offset: :none | :expand | :diverging | :silhouette | :wiggle,
  order:
    :none | :ascending | :descending | :reverse | :insideout | {:keys, [any()]},
  value: (map(), any() -&gt; number()) | nil
}
```

# `generate`

```elixir
@spec generate(t(), Macaw.Data.Table.source()) :: [series()]
```

Generates the stacked series data.

Returns one series per key, **in key order**, each a map with:
- `:key` - The series key
- `:index` - The series' position in the stacking order (0 is the bottom)
- `:points` - One point per datum with `:data`, `:value`, `:y0`, `:y1`, `:index`

# `keys`

```elixir
@spec keys(t(), [any()]) :: t()
```

Sets the keys to stack.

Keys determine the layers in the stack, typically field names.

# `new`

```elixir
@spec new() :: t()
```

Creates a new stack generator

# `offset`

```elixir
@spec offset(t(), atom()) :: t()
```

Sets the stack offset.

- `:none` - Zero baseline (default)
- `:expand` - Normalize to 0-1 (100% stacked)
- `:diverging` - Positive values stack up from zero, negative values stack down from zero
- `:silhouette` - Center the stack around zero
- `:wiggle` - Minimize weighted wiggle (for streamgraphs)

# `order`

```elixir
@spec order(t(), atom() | {:keys, [any()]}) :: t()
```

Sets the stack order.

- `:none` - Use key order (default)
- `:ascending` - Smallest series on bottom
- `:descending` - Largest series on bottom
- `:reverse` - Reverse key order
- `:insideout` - d3's `stackOrderInsideOut`: the earliest-peaking series in the middle,
  later ones outward on whichever side has the smaller sum (good for streamgraphs)
- `{:keys, list}` - The listed keys bottom first, then the unlisted keys in key order;
  a listed key the stack does not have is ignored (spec/04 §4.8)

Every order but `{:keys, list}` is computed from the data `generate/2` is given, so over
a stream of data it can change between frames; `{:keys, list}` is fixed.

    iex> alias Macaw.Data.Stack
    iex> Stack.new()
    ...> |> Stack.keys([:a, :b, :c])
    ...> |> Stack.order({:keys, [:c, :x, :a]})
    ...> |> Stack.generate([%{a: 1, b: 2, c: 3}])
    ...> |> Enum.map(&{&1.key, &1.index})
    [a: 1, b: 2, c: 0]

# `value`

```elixir
@spec value(t(), (map(), any() -&gt; number())) :: t()
```

Sets the value accessor function.

The function receives (datum, key) and returns a number.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
