formatdown v0.1.4

R
engineering notation
scientific notation
units
rmarkdown
quarto

New functionality for formatting power-of-ten notation in R markdown or Quarto markdown documents.

Author

Richard Layton

Published

2024–05–20

Summary

In response to issues raised by users, formatdown has new functionality (and bug fixes) with additional arguments affording greater control over the appearance of the formatted objects.

R code
library(formatdown)
library(data.table)
library(knitr)
library(units)

Background

My original motive in developing formatdown was to easily and repeatably produce power-of-ten notation in R markdown documents for single numbers in an inline math markup and for columns of numbers in a table.

I realized that some of the functions in the previous release were actually special cases of the power-of-ten function. So I recast the power-of-ten function as the more general format_numbers()—of which scientific notation, engineering notation, and decimal notation are special cases. I deprecated the earlier stand-alone functions, replacing them with new functions that wrap format_numbers() and take advantage of the extended set of arguments.

Additionally, I added format_text() so that columns of text in a table can be rendered with the same font face and size as the formatted numerical columns.

Finally, in response to a user’s request, I added an option for changing the default decimal marker from a period (.) to a comma (,). It seemed reasonable to make this an option that can be changed globally to avoid having to type this argument in every function call. This led me to consider what other arguments a user might want to assign globally—which are now implemented in the new formatdown_options() function.

format_numbers()

Useful for formatting a single number in a sentence. For example, the inline code chunk,

\(\quad\) `r format_numbers(6.0221E+23, digits = 5, format = "sci")`

produces the following math markup,

    $6.0221 \times 10^{23}$.

When this markup is rendered inline, as shown in the sentence below, we obtain the desired scientific notation.

The Avogadro constant, \(\small N_A =\) \(\small 6.0221 \times 10^{23}\), is the number of constituent particles (usually molecules, atoms, or ions) per mole.

format_numbers() is also useful for columns in a table. For example, the elastic modulus of several metals are tabulated below in engineering notation:

R code
# Data set included in formatdown
data("metals")

# Extract two columns (data.table syntax)
DT <- metals[, .(metal, elast_mod)]

# Format one column
DT$elast_mod <- format_numbers(DT$elast_mod, digits = 3, format = "engr")

# Display the table
knitr::kable(DT, col.names = c("metal", "modulus (Pa)"))
metal modulus (Pa)
aluminum 6061 \(\small 73.1 \times 10^{9}\)
copper \(\small 117 \times 10^{9}\)
lead \(\small 13.8 \times 10^{9}\)
platinum \(\small 147 \times 10^{9}\)
steel 1020 \(\small 207 \times 10^{9}\)
titanium \(\small 102 \times 10^{9}\)

format_text()

The table above also illustrates the difference in typefaces between a formatted numerical column and an unformatted text column. To format the text in a matching typeface, we use format_text().

R code
# Format the text column
DT$metal <- format_text(DT$metal)

# Display the table
knitr::kable(DT, col.names = c("metal", "modulus (Pa)"))
metal modulus (Pa)
\(\small \mathrm{aluminum\>6061}\) \(\small 73.1 \times 10^{9}\)
\(\small \mathrm{copper}\) \(\small 117 \times 10^{9}\)
\(\small \mathrm{lead}\) \(\small 13.8 \times 10^{9}\)
\(\small \mathrm{platinum}\) \(\small 147 \times 10^{9}\)
\(\small \mathrm{steel\>1020}\) \(\small 207 \times 10^{9}\)
\(\small \mathrm{titanium}\) \(\small 102 \times 10^{9}\)

Like the numerical column, the text column is also math delimited. Thus the elements of the “metal” column have a markup similar to:

    $\mathrm{aluminum\>6061}$

The whitespace macro (\>) preserves the horizontal space between words.

formatdown_options()

Global options are provided for arguments that users would be likely to prefer to set once in a document instead of repeating in every function call. Globally-set arguments can be overridden locally by assigning them in a function call.

formatdown_options() can be used to view current settings, assign new settings, or reset to defaults. Arguments include:

  • delim: left and right math markup delimiters.
  • size: font size “scriptsize”, “small”, “normalsize”, etc.
  • decimal_mark: a period “.” or a comma “,”.
  • big_mark and big_interval: For numbers with many digits to the left of the decimal, readability can sometimes be improved by inserting a thin space between groups of digits. big_mark defines the thin space and big_interval defines the number of digits in a group.
  • small_mark and small_interval: Similar to above but to the right of the decimal.
  • whitespace: Preserve horizontal spaces when text appears in a math-delimited output.

To “get” the current settings,

    formatdown_options("delim", "size")

To “set” new settings,

    formatdown_options(delim = "\\(", size = "small")

To reset to the default settings,

    formatdown_options(reset = TRUE)

For examples of how changing the options changes the appearance of the output, I recommend the Global settings vignette at the package website.

Wrappers

As mentioned earlier, we can use format_numbers() to format scientific, engineering, or decimal notation, or we can use one of the new convenience functions format_sci(), format_engr(), or format_dcml() which simply wrap format_numbers() with some pre–set arguments.

This first example compares scientific notation markups, and shows that the results are identical. When rendered, both p and q produce \(\small 6.022 \times 10^{23}\).

    # Avogadro constant
    L <- 6.0221e+23
    p <- format_numbers(L, format = "sci")
    q <- format_sci(L)
    all.equal(p, q)
    #> [1] TRUE

We get a similar result for engineering notation; here, p and q both produce \(\small 602.2 \times 10^{21}\).

    p <- format_numbers(L, format = "engr")
    q <- format_engr(L)
    all.equal(p, q)
    #> [1] TRUE

Units

Unit-handling has been scaled back in this version of formatdown, leaving all unit manipulation to the user and the units R package. For example, creating three units-class values \((x, y, z)\) is performed with units functions.

R code
# Numeric value
x <- 10320
units(x) <- "m"

# The units package reflects operations in units
y <- x^2

# The units package supports unit conversion
z <- y
units(z) <- "ft^2"

The units class, however, is still accommodated by formatdown. If an input argument to format_numbers() (or its convenience functions) is of class “units”, formatdown attempts to extract the units character string, format the number in the expected way, and append a units character string to the result. For example, the three values \((x, y, z)\) from the previous code chunk, when formatted, yield the following markups,

    format_sci(x)
    #> [1] "$1.032 \\times 10^{4}\\>\\mathrm{m}$"
    
    format_sci(y)
    #> [1] "$1.065 \\times 10^{8}\\>\\mathrm{m^{2}}$"
    
    format_sci(z)
    #> [1] "$1.146 \\times 10^{9}\\>\\mathrm{ft^{2}}$"

which render as

  • \(x=\) \(\small 1.032 \times 10^{4}\>\mathrm{m}\)
  • \(y=\) \(\small 1.065 \times 10^{8}\>\mathrm{m^{2}}\)
  • \(z=\) \(\small 1.146 \times 10^{9}\>\mathrm{ft^{2}}\)

For more information

The package website has more examples and details about the new functions and arguments and their application. These recent improvements are largely due to user feedback, so feedback is welcome!

Software credits