Tibbles print numbers with three significant digits by default, switching to scientific notation if the available space is too small. Underlines are used to highlight groups of three digits. This display works for many, but not for all use cases.
library(pillar)
library(tibble)
The new num()
constructor allows creating vectors that behave like numbers but allow customizing their display.
num(-1:3, notation = "sci")
#> <pillar_num(sci)[5]>
#> [1] -1e0 0 1e0 2e0 3e0
tibble(
x4 = num(8:12 * 100 + 0.5, digits = 4),
x1 = num(8:12 * 100 + 0.5, digits = -1),
usd = num(8:12 * 100 + 0.5, digits = 2, label = "USD"),
percent = num(8:12 / 100 + 0.0005, label = "%", scale = 100),
eng = num(10^(-3:1), notation = "eng", fixed_exponent = -Inf),
si = num(10^(-3:1) * 123, notation = "si")
)#> # A tibble: 5 × 6
#> x4 x1 usd percent eng si
#> <num:.4!> <num:.1> USD % <eng> <si>
#> 1 800.5000 800.5 800.50 8.05 1e-3 123 m
#> 2 900.5000 900.5 900.50 9.05 10e-3 1.23
#> 3 1000.5000 1000.5 1000.50 10.05 100e-3 12.3
#> 4 1100.5000 1100.5 1100.50 11.05 1000e-3 123
#> 5 1200.5000 1200.5 1200.50 12.05 10000e-3 1.23k
num
Formatting numbers is useful for presentation of results. If defined early on in the analysis, the formatting options survive most operations. It is worth defining output options that suit your data once early on in the process, to benefit from the formatting throughout the analysis. We are working on seamlessly applying this formatting to the final presentation (plots, tables, …).
num(1) + 2
#> <pillar_num[1]>
#> [1] 3
1 + num(2)
#> <pillar_num[1]>
#> [1] 3
+ num(2)
1L #> <pillar_num[1]>
#> [1] 3
num(3.23456, sigfig = 4) - num(2)
#> <pillar_num:4[1]>
#> [1] 1.235
num(4, sigfig = 2) * num(3, digits = 2)
#> <pillar_num:2[1]>
#> [1] 12
num(3, digits = 2) * num(4, sigfig = 2)
#> <pillar_num:.2![1]>
#> [1] 12.00
-num(2)
#> <pillar_num[1]>
#> [1] -2
min(num(1:3, label = "$"))
#> <pillar_num{$}[1]>
#> [1] 1
mean(num(1:3, notation = "eng"))
#> <pillar_num(eng)[1]>
#> [1] 2e0
sin(num(1:3, label = "%", scale = 100))
#> <pillar_num{%}*100[3]>
#> [1] 84.14710 90.92974 14.11200
The var()
function is one of the examples where the formatting is lost:
<- num(c(1, 2, 4), notation = "eng")
x var(x)
#> [1] 2.333333
One way to recover is to apply num()
to the result:
num(var(x), notation = "eng")
#> <pillar_num(eng)[1]>
#> [1] 2.333333e0
For automatic recovery, we can also define our version of var()
, or even overwrite the base implementation. Note that this pattern is still experimental and may be subject to change:
<- function(x, ...) {
var_ <- var(vctrs::vec_proxy(x), ...)
out ::vec_restore(out, x)
vctrs
}var_(x)
#> <pillar_num(eng)[1]>
#> [1] 2.333333e0
This pattern can be applied to all functions that lose the formatting. The make_restore()
function defined below is a function factory that consumes a function and returns a derived function:
<- function(fun) {
make_restore force(fun)
function(x, ...) {
<- fun(vctrs::vec_proxy(x), ...)
out ::vec_restore(out, x)
vctrs
}
}
<- make_restore(var)
var_ <- make_restore(sd)
sd_
var_(x)
#> <pillar_num(eng)[1]>
#> [1] 2.333333e0
sd_(x)
#> <pillar_num(eng)[1]>
#> [1] 1.527525e0
library(units)
#> udunits database from /Library/Frameworks/R.framework/Versions/4.1-arm64/Resources/library/units/share/udunits/udunits2.xml
<- function(x, ...) {
set_units.pillar_num <- x
unclassed class(unclassed) <- NULL
set_units(unclassed, ...)
}
<- set_units(1:3, m)
m <- set_units(1:3, km)
km
tibble(
sci_int = set_num_opts(m + km, notation = "sci"),
digits_int = set_num_opts(km + m, digits = 4),
sci_ext = set_units(num(1:3 + 0.001, notation = "sci"), km)
)#> # A tibble: 3 × 3
#> sci_int digits_int sci_ext
#> [m] [km] [km]
#> 1 1.001e3 1.0010 1.001e0
#> 2 2.002e3 2.0020 2.001e0
#> 3 3.003e3 3.0030 3.001e0
tibble(
sci_int = set_num_opts(m, notation = "sci") + km,
digits_int = set_num_opts(km, digits = 4) + m,
sci_ext = set_units(num(1:3, notation = "sci"), m) + km
)#> # A tibble: 3 × 3
#> sci_int digits_int sci_ext
#> [m] [km] [m]
#> 1 1.001e3 1.0010 1.001e3
#> 2 2.002e3 2.0020 2.002e3
#> 3 3.003e3 3.0030 3.003e3
library(formattable)
<- function(x, ...) {
pillar_shaft.formattable ::new_pillar_shaft_simple(format(x), align = "right")
pillar
}
<- function(x, ...) {
pillar_shaft.formattable_currency <- attr(x, "formattable", exact = TRUE)
formattable
pillar_shaft(num(unclass(x), digits = formattable$digits))
}
<- function(x, ...) {
pillar_shaft.formattable_percent <- attr(x, "formattable", exact = TRUE)
formattable
pillar_shaft(num(unclass(x), digits = formattable$digits, label = "%", scale = 100))
}
<- function(x, ...) {
pillar_shaft.formattable_scientific pillar_shaft(num(unclass(x), notation = "sci"))
}
<- function(x) {
type_sum.formattable <- attr(x, "formattable", exact = TRUE)
formattable
if (inherits(x, "formattable_currency")) {
I(sub("^formattable_", "", class(x)[[1]]))
else if (inherits(x, "formattable_percent")) {
} I("%")
else {
} abbreviate(sub("^formattable_", "", class(x)[[1]]), 4)
}
}
num_currency(1:3 * 100 + 0.1)
#> Error in num_currency(1:3 * 100 + 0.1): could not find function "num_currency"
num_percent(1:3 * 0.1 + 0.001)
#> Error in num_percent(1:3 * 0.1 + 0.001): could not find function "num_percent"
num_scientific(1:3 * 0.1 + 0.001)
#> Error in num_scientific(1:3 * 0.1 + 0.001): could not find function "num_scientific"
tibble(
currency = num_currency(1:3 * 100 + 0.1),
percent = num_percent(1:3 * 0.1 + 0.001),
scientific = num_scientific(1:3 * 0.1 + 0.001)
)#> Error in num_currency(1:3 * 100 + 0.1): could not find function "num_currency"
library(scales)
#>
#> Attaching package: 'scales'
#> The following objects are masked from 'package:formattable':
#>
#> comma, percent, scientific
<- num(1:10 / 100, label = "%", scale = 100)
x
::squish(x)
scales#> <pillar_num{%}*100[10]>
#> [1] 1 2 3 4 5 6 7 8 9 10
< 0
x #> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
< 0L
x #> [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
::cscale(x, scales::rescale_pal())
scales#> Error in UseMethod("rescale"): no applicable method for 'rescale' applied to an object of class "c('pillar_num', 'pillar_vctr', 'vctrs_vctr', 'double')"
library(ggplot2)
<- function(x, ...) {
scale_type.pillar_num "continuous"
}
data.frame(x = x, y = 1:10) %>%
ggplot(aes(x = x, y = y)) %>%
+ geom_point()
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following object is masked from 'package:pillar':
#>
#> dim_desc
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
<-
data_units ::penguins %>%
palmerpenguinsmutate(across(ends_with("_mm"), set_units, "mm")) %>%
mutate(across(ends_with("_g"), set_units, "g"))
%>%
data_units mutate(bill_area = bill_length_mm * bill_depth_mm, .after = island)
#> # A tibble: 344 × 9
#> species island bill_area bill_length_mm bill_depth_mm flipper_length_mm
#> <fct> <fct> [mm^2] [mm] [mm] [mm]
#> 1 Adelie Torgersen 731.17 39.1 18.7 181
#> 2 Adelie Torgersen 687.3 39.5 17.4 186
#> 3 Adelie Torgersen 725.4 40.3 18 195
#> 4 Adelie Torgersen NA NA NA NA
#> 5 Adelie Torgersen 708.31 36.7 19.3 193
#> 6 Adelie Torgersen 809.58 39.3 20.6 190
#> 7 Adelie Torgersen 692.42 38.9 17.8 181
#> 8 Adelie Torgersen 768.32 39.2 19.6 195
#> 9 Adelie Torgersen 617.21 34.1 18.1 193
#> 10 Adelie Torgersen 848.4 42 20.2 190
#> # … with 334 more rows, and 3 more variables: body_mass_g [g], sex <fct>,
#> # year <int>
<-
data_decor %>%
data_units decorate(year, digits = 0) %>%
decorate(where(is.numeric), digits = 3)
%>%
data_decor mutate(bill_area = bill_length_mm * bill_depth_mm, .after = island)
#> # A tibble: 344 × 9
#> species island bill_area bill_length_mm bill_depth_mm flipper_length_mm
#> <fct> <fct> [mm^2] [mm] [mm] [mm]
#> 1 Adelie Torgersen 731.170 39.100 18.700 181.000
#> 2 Adelie Torgersen 687.300 39.500 17.400 186.000
#> 3 Adelie Torgersen 725.400 40.300 18.000 195.000
#> 4 Adelie Torgersen NA NA NA NA
#> 5 Adelie Torgersen 708.310 36.700 19.300 193.000
#> 6 Adelie Torgersen 809.580 39.300 20.600 190.000
#> 7 Adelie Torgersen 692.420 38.900 17.800 181.000
#> 8 Adelie Torgersen 768.320 39.200 19.600 195.000
#> 9 Adelie Torgersen 617.210 34.100 18.100 193.000
#> 10 Adelie Torgersen 848.400 42.000 20.200 190.000
#> # … with 334 more rows, and 3 more variables: body_mass_g [g], sex <fct>,
#> # year <int>
`````