Labels for a scale's breaks: numbers written plainly (plain/1), in decimals (number/2),
with a comma between thousands (comma/2), as percentages (percent/2), as amounts of money
(currency/2), in scientific notation (scientific/2), in whichever way suits their size
(number_auto/1), as powers (log/2, or as text, format_log/2), as bytes (bytes/2), as
ordinals (ordinal/2), as p-values (pvalue/2), as spans of time (timespan/2), and dates,
date-times and times of day in a format (date/2, time/2) or by what changed from the break
before (date_short/2); and text broken into lines (wrap/2) or looked up in a dictionary
(dictionary/2).
Each labeller is handed every break an axis or a legend draws and returns a label for each, in
order, so the labels can share their decimals: 0.5, 1 and 1.25 are labelled 0.50,
1.00 and 1.25. A label is text, but for log/2's powers (power_label/0). A missing
break, nil, has no label, nil, as R's NA has none, but where number_auto/1 writes it
"NA". One that takes options is handed on inside a function:
fn breaks -> Scales.Labels.comma(breaks, accuracy: 0.1) endLabellers are composed by piping one's labels into the next, where R composes them with
compose_label():
fn breaks ->
breaks
|> Scales.Labels.number(accuracy: 0.1)
|> Enum.map(fn label -> label && label <> " m" end)
endplain/1 writes numbers as Elixir shows them, with what an axis read at a glance needs on top;
the others write them as R's scales does, each a label to the character.
How a label is written
Each number is multiplied by scale:, then rounded to a multiple of accuracy:, a half going
to the even neighbour (2.5 to 2, 3.5 to 4). A number that rounds to zero has no sign.
One that scale: takes past the largest float is labelled Inf or -Inf, without
prefix: and suffix: where rounding takes it past (and inside them where only the scaling
does), and takes no part in the others' accuracy, cents or step.
It is written in plain decimals, with as many decimals as the accuracy has (0.01 writes two,
1 or more none) up to twenty, or more if a label needs them to show its first seven
significant digits (an accuracy of 0.25 writes 0.25 and 0.50); every label shares the
most any needs. Only labels more than 310 characters longer than their scientific form, past
about 1e290, are written in scientific notation, as R writes them. big_mark: goes between
each three digits of the whole part
and decimal_mark: before the decimals; then prefix: and suffix: go around the number,
and the sign outside the prefix (-$13.50).
Left out, the accuracy is worked out from the breaks themselves: the power of ten at or below the smallest gap between two of them when every break is a multiple of it, a tenth of it otherwise, and never more than 1. One break, or breaks closer than about 1.5e-8, take an accuracy of 1.
Every formatter takes the same options, listed with its own defaults in its documentation.
Shortening by steps
scale_cut: divides each label by the largest step it reaches and writes the step's suffix
after the number, before suffix:: under cut_short_scale/1, 1500 is written 1.5K and two
million 2M. A label that is not a whole number of its step, but is a whole number of the step
below, is written in the step below when the two steps are not a power of ten apart: under
cut_time_scale/1, 90 seconds is 90s, not 1.5m. Labels in each step share their decimals,
worked out from that step's labels alone when accuracy: is not given. The steps must include
one at threshold 0, whose suffix is written after the labels below every other step, and no
threshold may be negative or repeated. cut_short_scale/1, cut_long_scale/1, cut_si/1
and cut_time_scale/1 give the common sets.
Summary
Types
A power log/2 labels a break with, its base and its exponent each written as text: 10^3
is {:power, "10", "3"}.
A label of log/2: a power, a power with a sign before it (-10^3 is
{:signed, "-", {:power, "10", "3"}}), the text "0" for a signed zero, or nil for a
missing break.
A step for scale_cut:: the suffix written after a label, and the threshold it starts at.
Functions
Labels numbers of bytes, R's label_bytes(): in the unit that suits each, 1500 is 2 kB, or
all in one unit.
Labels numbers with a comma between each three digits of the whole part, R's label_comma().
Labels amounts of money, R's label_currency(), in dollars unless prefix: and suffix: say
otherwise.
The long scale's steps for scale_cut:, R's cut_long_scale(): thousands K, millions M,
billions B (1e12) and trillions T (1e18).
The short scale's steps for scale_cut:, R's cut_short_scale(): thousands K, millions M,
billions B (1e9) and trillions T (1e12).
The SI prefixes' steps for scale_cut:, R's cut_si(), from yocto (1e-24) to yotta (1e24),
each written with a space and unit after it (" kg"), and unit alone below a yocto and
from 1 to 1000.
Steps of time for scale_cut:, R's cut_time_scale(), for labels in seconds: nanoseconds ns,
microseconds μs, milliseconds ms, seconds s, minutes m, hours h, days d and weeks
w.
Labels dates and date-times in a format, R's label_date(), by Calendar.strftime/3, but for
%Y, the year unpadded (the year 900 is 900).
Labels dates and date-times with what changed from the break before, R's
label_date_short(): each part, a year, a month, a day and a time of day, written where it or
a coarser part changed, finest first, one part to a line.
Labels values by a dictionary, R's label_dictionary(): each value the dictionary holds takes
its label, and each it lacks, a missing one included, nomatch:'s; with no nomatch:, a string
it lacks is its own label and a missing value stays missing.
Writes numbers as powers of base: in text, R's format_log(): log/2's labels each written
base^exponent, its exponent as the exponents are written together, padded on the left to the
widest, in up to digits: significant digits.
Labels numbers as powers of base:, R's label_log(): 1000 is {:power, "10", "3"}, drawn
as a superscript.
Labels numbers in decimals, R's label_number(), with a space between each three digits of the
whole part.
Labels numbers in whichever way suits their size, R's label_number_auto().
Labels numbers as ordinals, R's label_ordinal(): 1st, 2nd, 3rd, 4th.
English ordinals' rules for ordinal/2, R's ordinal_english(): st after a 1, nd after a
2 and rd after a 3, but for 11, 12 and 13, and th after anything else.
Labels proportions as percentages, R's label_percent(): each is multiplied by 100 and written
with a %.
Labels numbers plainly, as Elixir shows a float, with what an axis read at a glance needs on
top: every label gets as many decimals as the most precise one needs, none when every break is
whole, never scientific notation and no thousands separators. Each break is first rounded to 15
significant digits, which drops the rounding error that adding up steps leaves in the last
digits (0.3, not 0.30000000000000004), and a negative zero is written 0. R's default
labels come from format() instead, which writes 1e+05 where this writes 100000.
Labels p-values, R's label_pvalue(): each rounded to accuracy: as number/2 rounds it,
one below the accuracy written as less than it (<0.001) and one above 1 less the accuracy
as more than that (>0.999).
Labels numbers in scientific notation, R's label_scientific().
Labels times of day and date-times in a format, R's label_time(), by Calendar.strftime/3.
Labels spans of time, R's label_timespan(): each break, counted in unit:, turned into
seconds and written as number/2 writes it under cut_time_scale/1's steps, 90 minutes
being 90m and 120 2h.
Breaks text into lines shorter than width: columns, R's label_wrap(), as R's strwrap()
breaks it, the lines joined by "\n". A missing label, nil, is written "NA".
Types
A power log/2 labels a break with, its base and its exponent each written as text: 10^3
is {:power, "10", "3"}.
A label of log/2: a power, a power with a sign before it (-10^3 is
{:signed, "-", {:power, "10", "3"}}), the text "0" for a signed zero, or nil for a
missing break.
A step for scale_cut:: the suffix written after a label, and the threshold it starts at.
Functions
Labels numbers of bytes, R's label_bytes(): in the unit that suits each, 1500 is 2 kB, or
all in one unit.
Each label is written as number/2 writes it, the unit its suffix. Under :auto_si and
:auto_binary, each break takes the largest unit it reaches, as scale_cut: does, and plain
bytes are B, a break that is not a whole number of its unit but is of plain bytes written in
bytes (1500 is 1 500 B under :auto_binary); in one unit, scale: is divided by the
unit's size. Labels are rounded to whole units unless accuracy: says otherwise.
Options
:units-:auto_si, each label in the SI unit, a power of 1000, that suits it;:auto_binary, in the binary unit, a power of 1024; or a unit every label is written in, spelt as R spells it:"kB","MB","GB","TB","PB","EB","ZB","YB","kiB","MiB","GiB","TiB","PiB","EiB","ZiB","YiB". The default value is:auto_si.:accuracy- A positive number each label is rounded to a multiple of, in its unit, ornilto work it out from the breaks (see the module's documentation). The default value is1.:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is" ".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.
Examples
iex> Scales.Labels.bytes([0, 1500, 2.0e6, nil])
["0 B", "2 kB", "2 MB", nil]
iex> Scales.Labels.bytes([512, 1024, 1_048_576], units: :auto_binary)
["512 B", "1 kiB", "1 MiB"]
iex> Scales.Labels.bytes([1500, 2500, 1.0e6], accuracy: nil)
["1.5 kB", "2.5 kB", "1 MB"]
iex> Scales.Labels.bytes([1.0e6, 2.5e6], units: "kB", big_mark: ",")
["1,000 kB", "2,500 kB"]
Labels numbers with a comma between each three digits of the whole part, R's label_comma().
Options
:accuracy- A positive number each label is rounded to a multiple of, in the label's own units, afterscale:; worked out from the breaks when not given (see the module's documentation).:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:suffix(String.t/0) - Text written after the number. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is",".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.:scale_cut- Steps to shorten each label by, a list of{suffix, threshold}pairs (see the module's documentation).
Examples
iex> Scales.Labels.comma([-100_000, 1_234_567.891, 0.125])
["-100,000", "1,234,568", "0"]
iex> Scales.Labels.comma([0.5, 1, 1.25])
["0.50", "1.00", "1.25"]
iex> Scales.Labels.comma([0, 1250, 2500], accuracy: 0.1, scale: 0.001, suffix: "k")
["0.0k", "1.2k", "2.5k"]
iex> Scales.Labels.comma([0, 1500, 1.0e6], scale_cut: Scales.Labels.cut_short_scale())
["0", "2K", "1M"]
Labels amounts of money, R's label_currency(), in dollars unless prefix: and suffix: say
otherwise.
Without accuracy: or scale_cut:, labels show cents when some amount is not whole and none
is larger than largest_with_fractional:, and whole amounts otherwise. When both marks would
be a comma, the thousands are marked with a space instead.
Options
:accuracy- A positive number each label is rounded to a multiple of, in the label's own units, afterscale:; worked out from the breaks when not given (see the module's documentation).:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"$".:suffix(String.t/0) - Text written after the number. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is",".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.:scale_cut- Steps to shorten each label by, a list of{suffix, threshold}pairs (see the module's documentation).:largest_with_fractional- The largest amount that shows cents when neitheraccuracy:norscale_cut:is given. The default value is100000.
Examples
iex> Scales.Labels.currency([0, 50, 100])
["$0", "$50", "$100"]
iex> Scales.Labels.currency([-13.5, 0, 13.5])
["-$13.50", "$0.00", "$13.50"]
iex> Scales.Labels.currency([1234.5, 1], big_mark: ".", decimal_mark: ",", prefix: "", suffix: " €")
["1.234,50 €", "1,00 €"]
iex> Scales.Labels.currency([0, 2.5e5, 5.0e5, 1.0e6], scale_cut: Scales.Labels.cut_short_scale())
["$0", "$250K", "$500K", "$1M"]
The long scale's steps for scale_cut:, R's cut_long_scale(): thousands K, millions M,
billions B (1e12) and trillions T (1e18).
Options
:space(boolean/0) -trueputs a space before each suffix (2 K). The default value isfalse.
Examples
iex> Scales.Labels.currency([0, 1.0e9, 2.0e12], scale_cut: Scales.Labels.cut_long_scale())
["$0", "$1,000M", "$2B"]
The short scale's steps for scale_cut:, R's cut_short_scale(): thousands K, millions M,
billions B (1e9) and trillions T (1e12).
Options
:space(boolean/0) -trueputs a space before each suffix (2 K). The default value isfalse.
Examples
iex> Scales.Labels.cut_short_scale()
[{"", 0}, {"K", 1000}, {"M", 1000000}, {"B", 1000000000}, {"T", 1000000000000}]
iex> Scales.Labels.comma([-2000, 0, 2000], scale_cut: Scales.Labels.cut_short_scale(space: true))
["-2 K", "0 ", "2 K"]
The SI prefixes' steps for scale_cut:, R's cut_si(), from yocto (1e-24) to yotta (1e24),
each written with a space and unit after it (" kg"), and unit alone below a yocto and
from 1 to 1000.
Examples
iex> Scales.Labels.comma([0.001, 1, 1500], scale_cut: Scales.Labels.cut_si("g"))
["1 mg", "1 g", "2 kg"]
Steps of time for scale_cut:, R's cut_time_scale(), for labels in seconds: nanoseconds ns,
microseconds μs, milliseconds ms, seconds s, minutes m, hours h, days d and weeks
w.
Options
:space(boolean/0) -trueputs a space before each suffix (2 K). The default value isfalse.
Examples
iex> Scales.Labels.comma([0, 45, 90, 120, 5400], scale_cut: Scales.Labels.cut_time_scale())
["0", "45s", "90s", "2m", "90m"]
@spec date([Date.t() | NaiveDateTime.t() | DateTime.t() | nil], keyword()) :: [ String.t() | nil ]
Labels dates and date-times in a format, R's label_date(), by Calendar.strftime/3, but for
%Y, the year unpadded (the year 900 is 900).
A DateTime is shifted into tz: first, UTC unless given; a NaiveDateTime is written as it
is, its zone UTC, and a Date at midnight UTC, so %H writes 00 and %Z UTC.
Options
:format- The format each break is written in, asCalendar.strftime/3takes it, but for%Y, the year unpadded. The default value is"%Y-%m-%d".:tz(String.t/0) - The time zone eachDateTimeis shifted into before it is written, onetime_zone_database:knows; aDateis written at midnight UTC and aNaiveDateTimeorTimeas it is. The default value is"Etc/UTC".:time_zone_database(atom/0) - The time zone database that readstz:, such asTz.TimeZoneDatabase; Elixir's own, which knows UTC alone, when not given. The default value isCalendar.UTCOnlyTimeZoneDatabase.
Examples
iex> Scales.Labels.date([~D[2020-01-01], nil, ~D[2021-06-15]])
["2020-01-01", nil, "2021-06-15"]
iex> Scales.Labels.date([~N[2020-03-05 14:07:09]], format: "%d %b %H:%M")
["05 Mar 14:07"]
iex> Scales.Labels.date([~U[2021-03-14 06:30:00Z], ~U[2021-03-14 07:30:00Z]],
...> format: "%H:%M %Z",
...> tz: "America/New_York",
...> time_zone_database: Tz.TimeZoneDatabase
...> )
["01:30 EST", "03:30 EDT"]
@spec date_short([Date.t() | NaiveDateTime.t() | DateTime.t() | nil], keyword()) :: [ String.t() | nil ]
Labels dates and date-times with what changed from the break before, R's
label_date_short(): each part, a year, a month, a day and a time of day, written where it or
a coarser part changed, finest first, one part to a line.
The first break, and one after a missing break, show every part. The time is left out where
every break is at midnight, then the day where every break is a 1st, then the month where every
break is in January. A break with no part to write, as a date repeated, is written in full,
2020-01-05, or 2020-01-05 10:00:00 where one such break is not at midnight in tz:.
What changed is read on each break's own clock, a DateTime in its own zone; each part is
written as date/2 writes it, a DateTime shifted into tz:.
Options
:format(keyword/0) - Each part's format, asCalendar.strftime/3takes it, but for%Y, the year unpadded; a part left out keeps its default. The default value is[].:year- The year's format, ornilfor a year never written. The default value is"%Y".:month- The month's format, ornilfor a month never written. The default value is"%b".:day- The day's format, ornilfor a day never written. The default value is"%d".:time- The time's format, ornilfor a time never written. The default value is"%H:%M".
:sep(String.t/0) - The text written between the parts. The default value is"\n".:leading(String.t/0) - The text that replaces a0at the start of each part:" "writes the 5th" 5",""writes it"5". The default value is"0".:tz(String.t/0) - The time zone eachDateTimeis shifted into before it is written, onetime_zone_database:knows; aDateis written at midnight UTC and aNaiveDateTimeorTimeas it is. The default value is"Etc/UTC".:time_zone_database(atom/0) - The time zone database that readstz:, such asTz.TimeZoneDatabase; Elixir's own, which knows UTC alone, when not given. The default value isCalendar.UTCOnlyTimeZoneDatabase.
Examples
iex> Scales.Labels.date_short([~D[2019-11-01], ~D[2019-12-01], ~D[2020-01-01], ~D[2020-02-01]])
["Nov\n2019", "Dec", "Jan\n2020", "Feb"]
iex> Scales.Labels.date_short([~N[2020-01-01 18:00:00], ~N[2020-01-01 21:00:00], ~N[2020-01-02 00:00:00]])
["18:00\n01\nJan\n2020", "21:00", "00:00\n02"]
iex> Scales.Labels.date_short([~D[2020-01-05], ~D[2020-01-06], ~D[2020-02-07]],
...> format: [month: "%B"],
...> sep: " ",
...> leading: ""
...> )
["5 January 2020", "6", "7 February"]
Labels values by a dictionary, R's label_dictionary(): each value the dictionary holds takes
its label, and each it lacks, a missing one included, nomatch:'s; with no nomatch:, a string
it lacks is its own label and a missing value stays missing.
A value is matched by Scales.DataRange.key/1, so a key of 2 names 2.0, and strings match
exactly, case and accents as written; a key matches a value of its own type, so "1" names the
string and never the number 1. A value that is not a string, which the dictionary lacks,
needs a nomatch:, or raises.
Options
:dictionary- Required. A map of values to their labels, each a string ornilfor none.:nomatch- The label of a value the dictionary lacks, a missing one included: a string, ornilfor none;:nonekeeps a string as its own label and a missing value missing. The default value is:none.
Examples
iex> Scales.Labels.dictionary(["a", "b", "c", nil], dictionary: %{"a" => "Apple", "b" => "Banana"})
["Apple", "Banana", "c", nil]
iex> Scales.Labels.dictionary(["a", "c", nil], dictionary: %{"a" => "Apple"}, nomatch: "?")
["Apple", "?", "?"]
iex> Scales.Labels.dictionary([4.0, 6.0, 8.0], dictionary: %{4 => "four", 6 => "six", 8 => "eight"})
["four", "six", "eight"]
Writes numbers as powers of base: in text, R's format_log(): log/2's labels each written
base^exponent, its exponent as the exponents are written together, padded on the left to the
widest, in up to digits: significant digits.
Options
:base- The base of the logarithm, a positive number other than 1. The default value is10.:signed- Whether each label is signed,+or-before its power and a zero"0";:autosigns them when any break is zero or negative. Unsigned, a break at or below zero raises. The default value is:auto.:digits- An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is7.
Examples
iex> Scales.Labels.format_log([0.01, 1, 100])
["10^-2", "10^ 0", "10^ 2"]
iex> Scales.Labels.format_log([0.5, 2, 8], digits: 3)
["10^-0.301", "10^ 0.301", "10^ 0.903"]
iex> Scales.Labels.format_log([-100, 0, 100])
["-10^2", "0", "+10^2"]
@spec log([number() | nil], keyword()) :: [power_label()]
Labels numbers as powers of base:, R's label_log(): 1000 is {:power, "10", "3"}, drawn
as a superscript.
Each break's exponent, its logarithm in base:, is rounded to the seventh significant digit
of the largest exponent's, then the exponents are written together in up to digits:
significant digits, sharing their decimals, and each is read back and written alone in up to
15, its zeros after the last significant digit dropped: 2 is {:power, "10", "0.301"}, and
1 beside it {:power, "10", "0"}. No exponent shows more than 7 significant digits. The
base is written in up to 15 significant digits too, in plain or scientific notation,
whichever is shorter ("2.71828182845905" for e, "1e+06").
Signed, each power has its sign before it, {:signed, "-", power} or {:signed, "+", power},
and a zero is labelled "0". A missing break has no label.
Options
:base- The base of the logarithm, a positive number other than 1. The default value is10.:signed- Whether each label is signed,+or-before its power and a zero"0";:autosigns them when any break is zero or negative. Unsigned, a break at or below zero raises. The default value is:auto.:digits- An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is3.
Examples
iex> Scales.Labels.log([0.01, 1, 100])
[{:power, "10", "-2"}, {:power, "10", "0"}, {:power, "10", "2"}]
iex> Scales.Labels.log([1, 2, 8], base: 2)
[{:power, "2", "0"}, {:power, "2", "1"}, {:power, "2", "3"}]
iex> Scales.Labels.log([-100, 0, 100])
[{:signed, "-", {:power, "10", "2"}}, "0", {:signed, "+", {:power, "10", "2"}}]
Labels numbers in decimals, R's label_number(), with a space between each three digits of the
whole part.
Options
:accuracy- A positive number each label is rounded to a multiple of, in the label's own units, afterscale:; worked out from the breaks when not given (see the module's documentation).:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:suffix(String.t/0) - Text written after the number. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is" ".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.:scale_cut- Steps to shorten each label by, a list of{suffix, threshold}pairs (see the module's documentation).
Examples
iex> Scales.Labels.number([-2.5, 0, 1.0e6])
["-2.5", "0.0", "1 000 000.0"]
iex> Scales.Labels.number([1, nil, 1.5])
["1.0", nil, "1.5"]
iex> Scales.Labels.number([0.001, 0.0025], scale: 1000, suffix: " ms")
["1.0 ms", "2.5 ms"]
Labels numbers in whichever way suits their size, R's label_number_auto().
The way is chosen by the largest and smallest magnitudes of the breaks other than zero:
- any above a million: each label in whole numbers with a space between each three digits, as
number(breaks, accuracy: 1)writes it, or in scientific notation, whichever is shorter, the whole number on a tie; - any below 0.001: each in plain decimals or in scientific notation, whichever is shorter, plain on a tie;
- all positive, from 1000 to 2200, as years are: in plain decimals, with no mark between thousands;
- any above 1000: as
number(breaks, accuracy: 1)writes them; - otherwise in plain decimals, or in scientific notation where that is narrower for all.
Plain decimals and scientific notation are written with every label sharing its decimals, in
up to 7 significant digits, each way on its own when the shorter of two is taken. A missing
break is labelled with the text "NA" in the years' way and the last, and has no label in the
others.
Examples
iex> Scales.Labels.number_auto([0, 2.5e6, 5.0e6])
["0", "2.5e+06", "5.0e+06"]
iex> Scales.Labels.number_auto([1990, 2000, 2010])
["1990", "2000", "2010"]
iex> Scales.Labels.number_auto([0, 5000, 10_000, nil])
["0", "5 000", "10 000", nil]
iex> Scales.Labels.number_auto([0, 0.5, nil])
["0.0", "0.5", "NA"]
Labels numbers as ordinals, R's label_ordinal(): 1st, 2nd, 3rd, 4th.
Each break is rounded to a whole number, a half to the even one, and written as number/2
writes it with an accuracy of 1, then the suffix of the first rule whose regex matches the
whole number's digits: unmarked and unpadded, a negative's with a - before them ("-12").
R matches the text it writes a number in alone, which for a round number is scientific
notation wherever that is shorter (2e+05); the digits are matched here, so a rule reading the
last digit sees 200000's 0, and English ordinals from 1e21 up, which R writes ...st from
1e+21, are ...th. A break no rule matches raises, so the rules end with one every break
matches, as ordinal_english/0's do.
Options
:rules- A list of{suffix, regex}pairs, each break taking the suffix of the first whose regex matches it (see the function's documentation);ordinal_english/0's when not given.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:suffix(String.t/0) - Text written after the ordinal's. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is" ".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.
Examples
iex> Scales.Labels.ordinal([1, 2, 3, 4, 11, 12, 13, 21, nil])
["1st", "2nd", "3rd", "4th", "11th", "12th", "13th", "21st", nil]
iex> Scales.Labels.ordinal([1, 1001, 2.5], big_mark: ",", prefix: "#")
["#1st", "#1,001st", "#2nd"]
iex> Scales.Labels.ordinal([1, 2], rules: [{"º", ~r/./}])
["1º", "2º"]
English ordinals' rules for ordinal/2, R's ordinal_english(): st after a 1, nd after a
2 and rd after a 3, but for 11, 12 and 13, and th after anything else.
Examples
iex> Scales.Labels.ordinal_english() |> Enum.map(fn {suffix, _rule} -> suffix end)
["st", "nd", "rd", "th", "th", "th"]
Labels proportions as percentages, R's label_percent(): each is multiplied by 100 and written
with a %.
Thousands are marked with a space (1 234%).
Options
:accuracy- A positive number each label is rounded to a multiple of, in the label's own units, afterscale:; worked out from the breaks when not given (see the module's documentation).:scale- A positive number each break is multiplied by before it is written. The default value is100.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:suffix(String.t/0) - Text written after the number. The default value is"%".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is" ".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.:scale_cut- Steps to shorten each label by, a list of{suffix, threshold}pairs (see the module's documentation).
Examples
iex> Scales.Labels.percent([0, 0.25, 0.5, 0.75, 1])
["0%", "25%", "50%", "75%", "100%"]
iex> Scales.Labels.percent([0.1234, 0.5], accuracy: 0.1)
["12.3%", "50.0%"]
iex> Scales.Labels.percent([-0.5, 0.125, 12.345])
["-50%", "12%", "1 234%"]
Labels numbers plainly, as Elixir shows a float, with what an axis read at a glance needs on
top: every label gets as many decimals as the most precise one needs, none when every break is
whole, never scientific notation and no thousands separators. Each break is first rounded to 15
significant digits, which drops the rounding error that adding up steps leaves in the last
digits (0.3, not 0.30000000000000004), and a negative zero is written 0. R's default
labels come from format() instead, which writes 1e+05 where this writes 100000.
Examples
iex> Scales.Labels.plain([0.0, 2.5, 5.0])
["0.0", "2.5", "5.0"]
iex> Scales.Labels.plain([0.0, 250_000.0])
["0", "250000"]
Labels p-values, R's label_pvalue(): each rounded to accuracy: as number/2 rounds it,
one below the accuracy written as less than it (<0.001) and one above 1 less the accuracy
as more than that (>0.999).
Options
:accuracy- A positive number no larger than 0.5 each label is rounded to a multiple of; below it a p-value is written as less than it, and above 1 less it as more than that. The default value is0.001.:decimal_mark- Text written before the decimals, at least one character. The default value is".".:prefix- Three strings,{below, between, above}, written before a p-value below the accuracy, one between, and one above 1 less it;{"<", "", ">"}when not given.:add_p(boolean/0) -truewrites the prefixes{"p<", "p=", "p>"}; not taken withprefix:. The default value isfalse.
Examples
iex> Scales.Labels.pvalue([0.0001, 0.05, 0.5, 0.9999, nil])
["<0.001", "0.050", "0.500", ">0.999", nil]
iex> Scales.Labels.pvalue([0.001, 0.123, 0.999], accuracy: 0.01, add_p: true)
["p<0.01", "p=0.12", "p>0.99"]
iex> Scales.Labels.pvalue([0.0001, 0.05], prefix: {"less than ", "", "more than "})
["less than 0.001", "0.050"]
Labels numbers in scientific notation, R's label_scientific().
Each break is multiplied by scale: and rounded to digits: significant digits, a half to the
even neighbour of the number as a float scaled into its digits (0.15 to one digit is 2e-01).
The labels are then written together, their mantissas sharing the most decimals any needs, in
at most 7 significant digits: digits: above 7 shows 7. They are never padded. A break that
scale: takes past the largest float is written Inf or -Inf.
Options
:digits- An integer from 1 to 22, the most significant digits shown, though no label shows more than 7 (see the function's documentation). The default value is3.:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number. The default value is"".:suffix(String.t/0) - Text written after the number. The default value is"".:decimal_mark- Text written before the decimals, at least one character. The default value is".".
Examples
iex> Scales.Labels.scientific([0.15, 1.5, 12345])
["1.50e-01", "1.50e+00", "1.23e+04"]
iex> Scales.Labels.scientific([1, 10, nil, 100])
["1e+00", "1e+01", nil, "1e+02"]
iex> Scales.Labels.scientific([1234, 5], decimal_mark: ",", suffix: " m")
["1,23e+03 m", "5,00e+00 m"]
@spec time([NaiveDateTime.t() | DateTime.t() | Time.t() | nil], keyword()) :: [ String.t() | nil ]
Labels times of day and date-times in a format, R's label_time(), by Calendar.strftime/3.
A DateTime is shifted into tz: first, UTC unless given; a NaiveDateTime is written as it
is, its zone UTC, and a Time as it is. A format writing a part of a date (%Y, %d) raises
on a Time.
Options
:format- The format each break is written in, asCalendar.strftime/3takes it, but for%Y, the year unpadded. The default value is"%H:%M:%S".:tz(String.t/0) - The time zone eachDateTimeis shifted into before it is written, onetime_zone_database:knows; aDateis written at midnight UTC and aNaiveDateTimeorTimeas it is. The default value is"Etc/UTC".:time_zone_database(atom/0) - The time zone database that readstz:, such asTz.TimeZoneDatabase; Elixir's own, which knows UTC alone, when not given. The default value isCalendar.UTCOnlyTimeZoneDatabase.
Examples
iex> Scales.Labels.time([~T[00:00:00], ~T[01:01:01], ~T[23:59:59], nil])
["00:00:00", "01:01:01", "23:59:59", nil]
iex> Scales.Labels.time([~T[00:00:59], ~T[00:59:59.9]], format: "%H:%M")
["00:00", "00:59"]
iex> Scales.Labels.time([~U[2020-01-01 03:00:00Z]],
...> tz: "America/New_York",
...> time_zone_database: Tz.TimeZoneDatabase
...> )
["22:00:00"]
Labels spans of time, R's label_timespan(): each break, counted in unit:, turned into
seconds and written as number/2 writes it under cut_time_scale/1's steps, 90 minutes
being 90m and 120 2h.
Options
:unit- The unit the breaks are counted in,:second,:minute,:hour,:dayor:week. The default value is:second.:space(boolean/0) -trueputs a space before each unit's suffix (90 s). The default value isfalse.:accuracy- A positive number each label is rounded to a multiple of, in the label's own units, afterscale:; worked out from the breaks when not given (see the module's documentation).:scale- A positive number each break is multiplied by before it is written. The default value is1.:prefix(String.t/0) - Text written before the number, after a negative's sign. The default value is"".:suffix(String.t/0) - Text written after the number. The default value is"".:big_mark(String.t/0) - Text written between each three digits of the whole part. The default value is" ".:decimal_mark- Text written before the decimals, at least one character. The default value is".".:trim(boolean/0) -falsepads each number on the left with spaces to the width of the widest, before its prefix and its sign:"-$ 1"beside"$1,000". The default value istrue.:style_negative- How a negative is marked::hyphen(-1),:minus, a true minus sign (−1), or:parens((1)). The default value is:hyphen.:style_positive- How a positive is marked::none(1),:plus(+1) or:space, a figure space as wide as a digit ("\u20071"). The default value is:none.
Examples
iex> Scales.Labels.timespan([0, 30, 90, 3600, 86_400])
["0", "30s", "90s", "1h", "1d"]
iex> Scales.Labels.timespan([0.5, 1.5, 36], unit: :hour)
["30m", "90m", "36h"]
iex> Scales.Labels.timespan([1, 60, nil], space: true, suffix: " ago")
["1 s ago", "1 m ago", nil]
Breaks text into lines shorter than width: columns, R's label_wrap(), as R's strwrap()
breaks it, the lines joined by "\n". A missing label, nil, is written "NA".
A word goes on the line before it while the line stays shorter than the width, and a word wider
than that has a line of its own, never split. Whitespace between words (spaces, tabs and line
breaks, nothing else) becomes one space, or two after a word ending a sentence, in ., ? or
! and then at most one of ), " and ', where two or more whitespace characters followed
it. A line holding only whitespace starts a new paragraph, an empty line before it.
A character is as wide as R counts it: most Chinese, Japanese and Korean characters, fullwidth forms and emoji take two columns, and combining marks, zero-width spaces and joiners none, so a word made of those alone is dropped as whitespace is.
Options
:width(pos_integer/0) - Required. How many columns each line of a label is kept under.
Examples
iex> Scales.Labels.wrap(["Conservation Dependent", "Least concern", nil], width: 10)
["Conservation\nDependent", "Least\nconcern", "NA"]
iex> Scales.Labels.wrap(["Mr. Smith went to Washington."], width: 20)
["Mr. Smith went to\nWashington."]
iex> Scales.Labels.wrap(["日本語 日本語"], width: 10)
["日本語\n日本語"]