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

Shape-preserving downsampling (spec/01 §2.4): the two reducers finishing uses to bring a
delivery within a budget.

- `lttb/3`, Largest-Triangle-Three-Buckets, keeps about `n` points chosen to keep a
  series' visual shape;
- `m4/3` keeps, per pixel column, the first, last, least and greatest points, which a
  line drawn that wide needs to draw the same pixels as all of them.

Both treat a datum whose `x` or `y` is not a number as a gap, kept in place, so a line
over the result still breaks where the input broke. Ported from visualize's
`Visualize.Data.lttb/3` and `m4/3`.

# `lttb`

```elixir
@spec lttb([any()], pos_integer(), (any() -&gt; {term(), term()}) | nil) :: [any()]
```

Largest-Triangle-Three-Buckets: about `n` of the elements, chosen to keep the series'
visual shape (Steinarsson 2013).

The accessor returns `{x, y}`; without one the elements are `{x, y}` tuples. A datum
whose `x` or `y` is not a number is a gap: the defined data are reduced run by run
between the gaps, and each run of gaps keeps its first element in place, so a line over
the result still breaks where the input broke. Input of `n` or fewer elements is
returned unchanged; without gaps the result is exactly `n` elements, the first and the
last among them. `n` must be at least 3.

## Examples

    iex> Macaw.Data.Reduce.lttb([{0, 0}, {1, 1}, {2, 0}, {3, 5}, {4, 0}, {5, 1}, {6, 0}], 4)
    [{0, 0}, {2, 0}, {3, 5}, {6, 0}]

    iex> Macaw.Data.Reduce.lttb([{0, 0}, {1, 3}, {2, nil}, {3, 4}, {4, 1}], 9)
    [{0, 0}, {1, 3}, {2, nil}, {3, 4}, {4, 1}]

# `m4`

```elixir
@spec m4([any()], pos_integer(), (any() -&gt; {term(), term()}) | nil) :: [any()]
```

M4: per pixel column, the first, last, least-`y` and greatest-`y` elements — the
elements a line drawn `width` pixels wide needs to draw the same pixels as all of them
(Jugel et al. 2014).

The columns split the extent of the defined `x` readings evenly. The accessor, the gaps
and the runs are as `lttb/3`'s: each run of gaps keeps its first element in place, and
each stretch of one run in one column keeps at most four elements, deduplicated and in
input order. `width` must be at least 1.

## Examples

    iex> Macaw.Data.Reduce.m4([{0, 1}, {1, 5}, {2, 0}, {3, 2}, {4, 3}, {5, 9}, {6, 4}, {7, 6}], 2)
    [{0, 1}, {1, 5}, {2, 0}, {3, 2}, {4, 3}, {5, 9}, {7, 6}]

    iex> Macaw.Data.Reduce.m4([{0, 1}, {1, 2}, {2, nil}, {3, nil}, {4, 3}], 1)
    [{0, 1}, {1, 2}, {2, nil}, {4, 3}]

---

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