# `Drafter.Visualization.Reduce`
[🔗](https://github.com/jaman/drafter/blob/main/lib/drafter/visualization/reduce.ex#L1)

Reduces long series to the number of columns a plot can show, keeping peaks.

Items are split into `columns` buckets of consecutive items, in order. Each
function decides what a bucket keeps:

  * `peaks/3` — one item per bucket, the one furthest from zero
  * `min_max/3` — the smallest and largest item per bucket, in their original order
  * `m4/2` — first, smallest, largest and last `{x, y}` point per bucket, in x order

Items come back unchanged, so tuples such as `{value, weight}` survive when a
`value_fun` extracts the number. Lists no longer than `columns` are returned as-is.

A `nil` item is a gap and is never passed to `value_fun`. A bucket holding only
gaps keeps a gap in place of its items; otherwise its gaps are ignored.

# `value_fun`

```elixir
@type value_fun() :: (term() -&gt; number())
```

# `aligned_peaks`

```elixir
@spec aligned_peaks([[term()]], pos_integer(), value_fun()) :: [[term()]]
```

Reduces several series at the same indices, so stacked layers stay aligned.

Each index is a column of one item per series; the column kept per bucket is the
one whose summed magnitude is largest. Series are cut to the length of the
shortest. Returns one list per input series.

## Examples

    iex> Drafter.Visualization.Reduce.aligned_peaks([[1, 5, 1, 1], [1, 1, 1, 9]], 2)
    [[5, 1], [1, 9]]

# `m4`

```elixir
@spec m4([{number(), number()}], pos_integer()) :: [{number(), number()}]
```

M4 reduction of `{x, y}` points: first, minimum, maximum and last per bucket.

Points must be sorted by x. Returns at most `4 * columns` points, in x order,
with duplicates within a bucket removed. A line drawn through the result covers
the same pixels as a line through every input point at `columns` pixels wide.

## Examples

    iex> Drafter.Visualization.Reduce.m4([{0, 1}, {1, 7}, {2, 0}, {3, 4}], 1)
    [{0, 1}, {1, 7}, {2, 0}, {3, 4}]

    iex> Drafter.Visualization.Reduce.m4([{0, 1}, {1, 7}, {2, 0}, {3, 4}, {4, 2}, {5, 3}], 1)
    [{0, 1}, {1, 7}, {2, 0}, {5, 3}]

# `min_max`

```elixir
@spec min_max([term()], pos_integer(), value_fun()) :: [term()]
```

Keeps the smallest and the largest item per bucket, in their original order.

Returns exactly `2 * columns` items when the list is longer than `columns`, so
series reduced to the same `columns` line up index for index. A bucket whose
minimum and maximum are the same item contributes it twice.

## Examples

    iex> Drafter.Visualization.Reduce.min_max([5, 1, 9, 2, 8, 3], 2)
    [1, 9, 2, 8]

    iex> Drafter.Visualization.Reduce.min_max([4, 4, 4], 1)
    [4, 4]

# `peaks`

```elixir
@spec peaks([term()], pos_integer(), value_fun()) :: [term()]
```

Keeps one item per bucket: the one whose value has the largest magnitude.

Returns at most `columns` items.

## Examples

    iex> Drafter.Visualization.Reduce.peaks([1, 9, 2, 3, -7, 1], 3)
    [9, 3, -7]

    iex> Drafter.Visualization.Reduce.peaks([{1, 0.5}, {4, 1.0}], 1, &elem(&1, 0))
    [{4, 1.0}]

    iex> Drafter.Visualization.Reduce.peaks([nil, nil, 3, nil], 2)
    [nil, 3]

---

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