Works out the transform, limits and breaks for a set of values, without
drawing anything. map_surface() calls it; call it directly to see what a
map is going to do before it does it, or to fix a scale across several
figures that would otherwise each choose their own.
Usage
surface_scale(values, transform = "auto", limits = NULL, probs = c(0, 0.99))Arguments
- values
The numbers to be coloured.
- transform
"auto"to choose by the rule below, or any transform nameggplot2::continuous_scale()accepts –"identity","log","sqrt".- limits
NULLto take them fromprobs, or a length-2 vector to fix them.- probs
The quantiles the limits are taken from. The default keeps the bottom of the range and cuts the top percentile, because that is the tail that flattens a skewed surface.
Value
A list with transform, limits, breaks, labels, squished
(whether anything falls outside limits) and note (the legend
annotation, or NULL).
Choosing a transform
Only when transform = "auto", and by one rule: the ratio of the 99th
percentile to the median. Below 10 the values are drawn linearly. At or
above 10 the top of the range is more than an order of magnitude above the
middle, a linear ramp spends most of its colours on values almost nothing
has, and the scale becomes logarithmic.
A logarithmic scale cannot show a zero, and a density surface is full of
them. Rather than dropping those cells or adding 1 to everything – which
means something different depending on whether the units are animals per
km2 or per m2 – the transform is log(x + offset), with offset set to
the 5th percentile of the positive values and reported. It is a real number
from the data with a stated provenance, not a constant that happens to make
the arithmetic work.
Any automatic choice is reported with a message, because a default that
varies with the data is a default that has to be visible. It also means two
figures of different data can end up on different scales: when they must
match, fix transform and limits, or draw them with map_panels(), which
does that for you.
Examples
skewed <- c(rep(0, 40), rexp(200, rate = 20))
surface_scale(skewed)
#> $transform
#> [1] "identity"
#>
#> $limits
#> [1] 0.0000000 0.2073073
#>
#> $breaks
#> list()
#> attr(,"class")
#> [1] "waiver"
#>
#> $labels
#> function (x)
#> {
#> lab <- format(signif(x, 3), scientific = FALSE, trim = TRUE,
#> drop0trailing = TRUE, big.mark = ",")
#> if (!squished)
#> return(lab)
#> at_top <- !is.na(x) & abs(x - limits[2]) < 1e-09 * max(1,
#> abs(limits[2]))
#> lab[at_top] <- paste0("≥ ", lab[at_top])
#> lab
#> }
#> <bytecode: 0x55a219665390>
#> <environment: 0x55a2166ecfa8>
#>
#> $squished
#> [1] TRUE
#>
#> $note
#> NULL
#>
# a quantity that does not need it
surface_scale(runif(100, 8, 12))
#> $transform
#> [1] "identity"
#>
#> $limits
#> [1] 8.046092 11.833802
#>
#> $breaks
#> list()
#> attr(,"class")
#> [1] "waiver"
#>
#> $labels
#> function (x)
#> {
#> lab <- format(signif(x, 3), scientific = FALSE, trim = TRUE,
#> drop0trailing = TRUE, big.mark = ",")
#> if (!squished)
#> return(lab)
#> at_top <- !is.na(x) & abs(x - limits[2]) < 1e-09 * max(1,
#> abs(limits[2]))
#> lab[at_top] <- paste0("≥ ", lab[at_top])
#> lab
#> }
#> <bytecode: 0x55a219665390>
#> <environment: 0x55a2165f7e58>
#>
#> $squished
#> [1] TRUE
#>
#> $note
#> NULL
#>