# `Giggly.Mapping`
[🔗](https://github.com/giggleton/giggly/blob/v0.1.0/lib/giggly/mapping.ex#L1)

What a mapping maps each aesthetic to: a column, an expression computed from columns in each
row, or a constant. A mapping is a map from aesthetic to value:

    %{x: :displ, y: {&Kernel.//2, [:hwy, :cty]}, colour: {:constant, "smooth"}}

## Values

  * **A column** is named by an atom or a string, compared with the table's column names as a
    string, so `:hwy` and `"hwy"` name the same column. `true`, `false` and `nil` name none.
  * **An expression**, `{function, [value, ...]}`, is the function applied in each row to what
    its arguments are in that row, so `{&Kernel.//2, [:hwy, :cty]}` divides each row's `hwy` by
    its `cty`. Each argument is a value, so it may be a column, a constant or another expression,
    and the function takes as many arguments as it is given.
  * **A function by name**, `{:log10, [value]}`, is a common function Elixir lacks, computed by
    the engine holding the table: `log10` is the base-10 logarithm, minus infinity for 0, NaN
    for a negative number and missing for a missing value. Its value is a column, a constant
    or another function by name, never a capture or a closure, which the engine cannot run, so
    the logarithm of what a capture computes is a capture too, `{&:math.log10/1, [value]}`.
    Any other function is written as a capture.
  * **A constant**, `{:constant, term}`, is the term in every row. A number, a boolean, a date or
    a time is a constant written alone; a string or an atom would name a column, so a constant
    one is written `{:constant, "smooth"}`.
  * **A factor**, `{:factor, [value]}`, is the value read as discrete, so a column of numbers
    maps as levels: a discrete scale whatever the values, its levels in order of value (dates by
    date) with a missing one last, each written as text (a whole number without a decimal
    point, `4`; a boolean as `TRUE` or `FALSE`), and the rows grouped by them.
    `{:factor, [value], levels: [level, ...]}` gives the levels and their order, each named as
    the value holds it (`[8, 4, 6]` for numbers), a value not among them missing, so the axis,
    the legend, the groups and stacking follow that order; `ordered: true` marks the factor's
    levels as ordered. A factor reads a whole column, so it is the whole of a mapping value, a
    factor's own or an after_stat's, never an expression's argument.
  * **A reordering**, `{:reorder, [value, by]}`, is `value` read as discrete, as a factor reads
    it, its levels ordered by the mean of `by` over each one's rows, the lowest first: levels
    whose means tie keep the order the factor of `value` gives them (its own levels where the
    table's column is a factor), and a level whose mean is missing, since its rows hold a
    missing or NaN `by` or there are none, goes last. The mean is R's `mean()`, worked out as
    R works it out, digit for digit. Each of `value` and `by` is a column or a function by
    name, which the engine holding the table computes, as it computes the means, and `by` is
    numbers; the levels are ordered, as `ordered: true` marks a factor's, where `value` is an
    ordered factor. A reordering reads whole columns, as a factor does, so it goes where a
    factor goes.
  * **An after_stat**, `{:after_stat, [value]}`, is the value computed from what the layer's stat
    computes: its names are the stat's computed variables (counting's are `count` and `prop`),
    and it is evaluated once the stat has run, so it takes no part in grouping or in what the
    stat computes. Its value is put through its aesthetic's scale. It is the whole of a mapping
    value, never inside another, so the share as a percentage is written
    `{:after_stat, [{&Kernel.*/2, [:prop, 100]}]}`.

A constant is a value a column can hold: `nil`, a list, and a value with no column type of its
own (a map, a tuple, a struct other than a date or a time) are refused, and so are a function
alone, a tuple that is neither an expression nor a constant, and a function given a number of
arguments it does not take. A function by name is refused where the table's engine cannot run
it, and `log10` where its value is a column or a constant that is not numbers, a factor's
column among them. A factor's levels are refused unless they are a list of one or more
values a column can hold, of one type, none of them `nil` and none given twice (`4` and `4.0`
are one); levels of another type than the value's are refused when the plot is run. A
reordering is refused where either of its values is a constant, a capture or a closure, a factor
or another reordering, or where `by` is a column that is not numbers. A layer
whose stat computes nothing, and so documents no variables, refuses every after_stat.

    iex> plot =
    ...>   %{"hwy" => [29, 24], "cty" => [18, 16]}
    ...>   |> Giggly.Plot.new(%{x: :cty, y: {&Kernel.//2, [:hwy, :cty]}})
    ...>   |> Giggly.Plot.geom_point(%{colour: {:constant, "smooth"}})
    iex> frame = Giggly.layer_data(plot)
    iex> Giggly.DataFrame.pull(frame, :y)
    [1.6111111111111112, 1.5]
    iex> Giggly.DataFrame.pull(frame, :color)
    ["#F8766D", "#F8766D"]

Each class's share of all the rows, counted as one group:

    iex> plot =
    ...>   %{"class" => ["suv", "compact", "suv", "midsize"]}
    ...>   |> Giggly.Plot.new(%{x: :class, y: {:after_stat, [:prop]}, group: 1})
    ...>   |> Giggly.Plot.geom_bar()
    iex> frame = Giggly.layer_data(plot)
    iex> Giggly.DataFrame.pull(frame, :y)
    [0.25, 0.25, 0.5]

The cylinders in the order given, so the axis reads 8, 4, 6:

    iex> plot =
    ...>   %{"cyl" => [4, 6, 8, 4], "mpg" => [26.0, 19.7, 15.0, 30.4]}
    ...>   |> Giggly.Plot.new(%{x: {:factor, [:cyl], levels: [8, 4, 6]}, y: :mpg})
    ...>   |> Giggly.Plot.geom_point()
    iex> Giggly.panel_scales(plot, 1).x.levels
    [8, 4, 6]

The classes in order of their mean highway mileage, 19 for the SUVs, 27 and 28:

    iex> plot =
    ...>   %{"class" => ["suv", "compact", "suv", "midsize"], "hwy" => [18, 28, 20, 27]}
    ...>   |> Giggly.Plot.new(%{x: {:reorder, [:class, :hwy]}, y: :hwy})
    ...>   |> Giggly.Plot.geom_point()
    iex> Giggly.panel_scales(plot, 1).x.levels
    ["suv", "midsize", "compact"]

## Titles

A guide is titled by what its aesthetic is mapped to:

  * a column by its name, `cty`;
  * an expression by its text, as Elixir writes the call: a capture of one of `Kernel`'s
    operators as the operator, `hwy / cty`, and any other capture as a call to the function it
    names, `:math.log10(carat)`, as `inspect/1` names it;
  * a function by name as a call to it, `log10(carat)`;
  * an expression whose function is a closure, such as `fn hwy -> hwy * 2 end`, has no text, so
    its guide has no title until `Giggly.Plot.labs/2` gives it one;
  * a factor by its value's text inside `factor()`, `factor(cyl)`, and its options as R writes
    them, `factor(cyl, levels = c(8, 4, 6), ordered = TRUE)`;
  * a reordering as a call to R's `reorder()`, `reorder(class, hwy)`;
  * an after_stat by its value's text, `prop`;
  * a constant by its aesthetic's name, `color`, unless another layer maps the aesthetic to a
    column or an expression, whose title wins.

So a capture is the form to write where the title should say what was computed, and a closure
the form to write inline, with a title from `labs/2`.

    iex> svg =
    ...>   %{"carat" => [0.23, 1.5], "price" => [326, 18823]}
    ...>   |> Giggly.Plot.new(%{x: {:log10, [:carat]}, y: {:log10, [:price]}})
    ...>   |> Giggly.Plot.geom_point()
    ...>   |> Giggly.to_svg()
    ...>   |> IO.iodata_to_binary()
    iex> svg =~ ">log10(carat)</text>" and svg =~ ">log10(price)</text>"
    true

    iex> svg =
    ...>   %{"hwy" => [29, 24], "cty" => [18, 16]}
    ...>   |> Giggly.Plot.new(%{x: :cty, y: {&Kernel.//2, [:hwy, :cty]}})
    ...>   |> Giggly.Plot.geom_point()
    ...>   |> Giggly.to_svg()
    ...>   |> IO.iodata_to_binary()
    iex> svg =~ ">hwy / cty</text>"
    true

# `after_stat`

```elixir
@type after_stat() :: {:after_stat, [value()]}
```

An after_stat: a value computed from the stat's computed variables.

# `column`

```elixir
@type column() :: atom() | String.t()
```

A column, named by an atom or a string.

# `constant`

```elixir
@type constant() ::
  {:constant, term()}
  | number()
  | boolean()
  | Date.t()
  | Time.t()
  | NaiveDateTime.t()
  | DateTime.t()
```

A constant: `{:constant, term}`, or a number, a boolean, a date or a time written alone.

# `expression`

```elixir
@type expression() :: {function(), [value()]} | {:log10, [value()]}
```

An expression: a function and the values it is applied to in each row, or a function by name
and the value it is computed from.

# `factor`

```elixir
@type factor() ::
  {:factor, [value()]}
  | {:factor, [value()], levels: [term()], ordered: boolean()}
```

A factor: a value read as discrete, with the levels it holds and their order.

# `reordering`

```elixir
@type reordering() :: {:reorder, [column() | expression()]}
```

A reordering: a value read as discrete, its levels ordered by another's means.

# `resolved`

```elixir
@type resolved() ::
  String.t()
  | {:constant, term()}
  | {:factor, [resolved()]}
  | {:factor, [resolved()], levels: [term()], ordered: boolean()}
  | {:reorder, [resolved()]}
  | {:after_stat, [resolved()]}
  | {function(), [resolved()]}
  | {:log10, [resolved()]}
```

A value as a plot holds it: a column by its name as a string, a constant as `{:constant, term}`,
and an expression as its function and its arguments, each held so.

# `t`

```elixir
@type t() :: %{optional(atom()) =&gt; resolved()}
```

A mapping as a plot holds it: aesthetic to resolved value.

# `value`

```elixir
@type value() ::
  column() | expression() | constant() | factor() | reordering() | after_stat()
```

A value as written: a column, an expression, a constant, a factor, a reordering or an
after_stat.

---

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