The range of the data a scale has seen, trained a batch of values at a time: a continuous range,
the lowest and highest finite number (train_continuous/2), or a discrete one, the levels seen,
in order (train_discrete/3); with the key that orders values that have no levels of their own
(level_key/1) and the key a value is matched by (key/1).
A range is :none until it has seen a value: a continuous one is then {low, high}, and a
discrete one {levels, factor}, factor saying whether its first values were a factor's,
whose levels keep the order they came in.
Summary
Functions
The key a value is matched by: a number as a float, so an integer and the float equal to it share one, and a negative zero is the zero it equals; any other value as it is.
The key that orders values with no levels of their own: nil after every value, and the values
in Erlang's term order, so numbers come first, strings sort by their bytes and false comes
before true. A factor's values are ordered by its levels instead.
The range, :none for none, widened to cover the finite numbers among values, R's
train_continuous(): nil, :infinity, :neg_infinity and :nan widen nothing.
The discrete range {levels, factor}, {[], :none} before any, with values merged into its
levels, R's train_discrete() with drop = TRUE; levels are the levels of the factor
values are of, or :none for values with none of their own.
Functions
The key a value is matched by: a number as a float, so an integer and the float equal to it share one, and a negative zero is the zero it equals; any other value as it is.
Examples
iex> Scales.DataRange.key(4) === Scales.DataRange.key(4.0)
true
iex> Scales.DataRange.key(-0.0)
0.0
The key that orders values with no levels of their own: nil after every value, and the values
in Erlang's term order, so numbers come first, strings sort by their bytes and false comes
before true. A factor's values are ordered by its levels instead.
Examples
iex> Enum.sort_by([nil, "b", 2, "a", true], &Scales.DataRange.level_key/1)
[2, true, "a", "b", nil]
The range, :none for none, widened to cover the finite numbers among values, R's
train_continuous(): nil, :infinity, :neg_infinity and :nan widen nothing.
Examples
iex> Scales.DataRange.train_continuous(:none, [nil, 3, :infinity, -2])
{-2, 3}
iex> Scales.DataRange.train_continuous({-2, 3}, [7])
{-2, 7}
@spec train_discrete({list(), :none | boolean()}, list(), list() | :none) :: {list(), :none | boolean()}
The discrete range {levels, factor}, {[], :none} before any, with values merged into its
levels, R's train_discrete() with drop = TRUE; levels are the levels of the factor
values are of, or :none for values with none of their own.
A factor's values bring the levels they hold, in the levels' order, the missing value after
them; other values bring their distinct values sorted by level_key/1, nil last. Values are
matched by key/1, so 4 and 4.0 are one level. A factor's levels lead the rest; and once a
range's first values were a factor's, new values follow its levels, where otherwise every merge
sorts them all again. No values merge nothing.
Examples
iex> Scales.DataRange.train_discrete({[], :none}, ["y", nil, "x"], :none)
{["x", "y", nil], false}
iex> Scales.DataRange.train_discrete({["x", "y", nil], false}, ["z", "x"], :none)
{["x", "y", "z", nil], false}
iex> Scales.DataRange.train_discrete({[], :none}, ["b", "a"], ["c", "b", "a"])
{["b", "a"], true}