---
title: "Format powers of ten"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Format powers of ten}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

library("formatdown")

options(
  datatable.print.nrows = 15,
  datatable.print.topn = 3,
  datatable.print.class = TRUE
)
```

![](../man/figures/powers-of-ten-chart-2.png){width=70%}    
<small>Chart by Jeff Cruzan is licensed under CC BY-NC-SA 3.0</small>

<br>

The notation we use to represent large and small numbers depends on the context of our communications. In a computer script, for example, we might encode Avogadro's number as `6.0221*10^23`. A computer printout of this number would typically use E-notation, as in `6.0221E+23`.

In professional technical communications, however, Avogadro's number would be typeset using power of ten notation, $\small 6.0221 \times 10^{23}$. Computer syntax is generally avoided---the asterisk (`*`) and carat (`^`) in `6.0221*10^23` communicate   *instructions to a computer*, not *syntactical mathematics*. And while scientific E-notation (`6.0221E+23`) has currency in some discourse communities, the general convention in technical communications is to format large and small numbers using powers-of-ten notation of the form, 

$$
\small a \times 10^n, 
$$

The $\times$ multiplication symbol, often avoided in other contexts, is conventional syntax in powers-of-ten notation. Also, the notation has two forms in general use: *scientific* and *engineering* (Chase 2021, 63–67).

- *Scientific.* &nbsp; $\small n$ is an integer and $\small a \in Re: 1\leq{|a|}<10$. For example, `r format_power(6.0221E+23, 4, format = "sci")`

- *Engineering.* &nbsp; $\small n$ is a multiple of 3 and $\small a \in Re: 1\leq{|a|}<1000$. For example, `r format_power(6.0221E+23, 4)`

*Exceptions.* &nbsp; When exponents are in the neighborhood of zero, for example, $\small n \in \{-1, 0, 1, 2\}$, decimal notation may be preferred to power of ten notation. Decimal values such as $\small 0.1234$, $\small 1.234$, $\small 12.34$, and $\small 123.4$ might be printed as-is. The range of exponents to include in this set is discretionary. 

*Notes on syntax.* &nbsp; We have to distinguish between computer syntax, math syntax, and markup syntax. 

- Anyone who has written a line of code involving multiplication is familiar with the asterisk as the multiplication operator, e.g., `x = a * b` or `y = a * (b + c)`. In written mathematics, however, these expressions are written without multiplication symbols, that is, $\small x=ab$ and $\small y=a(b+c)$. Moreover, the asterisk has a completely different meaning in mathematics, representing the *convolution* operation between two functions, as in $\small \left(f * g \right)(t)$. 

- The carat symbol (`^`), to the best of my knowledge, is a programming operator only; mathematically it has no meaning. 

- In our markup language, `\times` is the product symbol and `^` indicates a superscript or exponent. 








## Markup

In R Markdown and Quarto Markdown, we use an inline equation markup delimited by `$ ... $` to create a math expression in the output document (`\( ... \)` is also an option). For example, the markup for Avogadro's number is given by, 

        $6.0221 \times 10^{23}$
        
rendered in an R markdown document as $\small 6.0221 \times 10^{23}$. To *program* the markup, however, we enclose the markup as a character string, that is, 

        "$6.0221 \\times 10^{23}$"

which requires the backward slash `\` to be "escaped", hence the product symbol is `\\times`. This is the form of the output produced by `format_power()`. 




## `format_power()`

Given a number, a numerical vector, or a numerical column from a data frame, `format_power()` converts the numbers to character strings of the form, 

        "$a \\times 10^{n}$" 
 
where `a` is the coefficient and `n` is the exponent. The user can specify the number of significant digits and scientific or engineering format. Unless otherwise specified, numbers with exponents in the neighborhood of zero are excluded from power of ten notation and are converted to character strings of the form, 

        "$a$" 

where `a` is the number in decimal notation to the specified number of significant digits.
 
*Arguments.*

- **x** &nbsp; Numerical vector to be formatted. Can be a scalar, a vector, or a column from a data frame. 

- **digits** &nbsp; Positive nonzero integer to specify the number of significant digits. Default is 4. 

- **format** &nbsp; Possible values are "engr" (default) for engineering notation and "sci" for scientific notation. Can also be set as a global option, for example, `options(formatdown.power.format = "sci")`, that can be overwritten in an individual function call.

- **size** &nbsp; Font size. Possible values are "scriptsize", "small" (default), "normalsize", "large", or "huge". Can also be set as a global option, for example, `options(formatdown.font.size = "normalsize")`, that can be overwritten in an individual function call.

- **omit_power** &nbsp; Numeric vector of length two (or NULL). Determines the range of exponents between which power-of-ten notation is excluded. Default is `c(-1, 2)`.

- **set_power** &nbsp; Integer assigned as the fixed power-of-ten, overriding scientific or engineering formatting. Default NULL. 

- **delim** &nbsp; Character vector of length one or two defining the math markup delimiters. Possible values include `"$"` or `"\\("`, both of which create appropriate left and right delimiters. Custom delimiters can be assigned if required by one's TeX processor. 

If you are writing your own script to follow along, we use these packages in this vignette:

```{r}
library("formatdown")
library("data.table")
library("knitr")
```

*Equivalent usage.*  &nbsp;  The first two arguments do not have to be named if the argument order is maintained. 

```{r}
# Numerical value
avogadro <- 6.0221E+23

# Arguments named
(x <- format_power(x = avogadro, digits = 4))

# Arguments unnamed
y <- format_power(avogadro, 4)

# Implicit use of default argument
z <- format_power(avogadro)

# Demonstrate equivalence
all.equal(x, y)
all.equal(x, z)
```






## Scalar

Use with inline R code.  

```{r}
format_power(avogadro)
```

which, in an `.Rmd` or `.qmd` document, is rendered as `r format_power(avogadro)`. 


## Vector 

```{r}
x <- c(
  1.2222e-6, 2.3333e-5, 3.4444e-4, 4.1111e-3, 5.2222e-2, 6.3333e-1,
  7.4444e+0, 8.1111e+1, 9.2222e+2, 1.3333e+3, 2.4444e+4, 3.1111e+5, 4.2222e+6
)
format_power(x)
```

is rendered as `r format_power(x)[1:4]`, etc.  






## Significant digits

Argument does not have to be named. 

```{r}
format_power(x[1], 3)
format_power(x[1], 4)
```

are rendered as 

- `r format_power(x[1], 3)`
- `r format_power(x[1], 4)`








## Format

`format` argument default value is "engr". If included, argument must be named. 

```{r}
format_power(x[3])
format_power(x[3], format = "sci")
```

are rendered as 

- `r format_power(x[3])`
- `r format_power(x[3], format = "sci")`

To compare the effects across many orders of magnitude, we format the example vector twice, placing the results side by side in a data frame for comparison, rendered using `knitr::kable()`, 

```{r}
# Compare two formats
DT <- data.table(
  scientific  = format_power(x, 3, format = "sci"),
  engineering = format_power(x, 3)
)
knitr::kable(DT, align = "r", col.names = c("scientific notation", "engineering notation"))
```

The values displayed without powers-of-ten notation are in the range set by the `omit_power` argument, described in the next section. 





## Font size

Format the same column of text using each of the five possible `size` arguments for comparison. 

```{r}
x <- c(1.2E-6, 3.4E+0, 5.6E+13)

# Compare formats
DT <- data.table(
  scriptsize = format_power(x, 2, size = "scriptsize"),
  small      = format_power(x, 2, size = "small"),
  normalsize = format_power(x, 2, size = "normalsize"),
  large      = format_power(x, 2, size = "large"),
  huge       = format_power(x, 2, size = "huge")
)
knitr::kable(DT, align = "r")
```






## Omit power notation

`omit_power` argument, if included, must be named. 

Suppose, for example, we want the value 0.0678 to be rendered without powers-of-ten notation. The exponent for this number in scientific notation is $-2$, as in `r format_power(0.0678, format = "sci")`. If this exponent is within the range set by `omit_power`, the number is rendered without power-of-ten notation (overriding the `format` argument). 

```{r}
format_power(0.0678, 3, format = "sci", omit_power = c(-2, 2))
format_power(0.0678, 3, format = "sci", omit_power = c(-1, 2))
```

are rendered as 

- `r format_power(0.0678, 3, format = "sci", omit_power = c(-2, 2))`
- `r format_power(0.0678, 3, format = "sci", omit_power = c(-1, 2))`

At the upper limit, 

```{r}
format_power(6789, 3, format = "sci", omit_power = c(-1, 2))
format_power(6789, 3, format = "sci", omit_power = c(-1, 3))
```


are rendered as

- `r format_power(6789, 3, format = "sci", omit_power = c(-1, 2))`    
- `r format_power(6789, 3, format = "sci", omit_power = c(-1, 3))` 



`omit_power = NULL` removes the exceptions and formats all numbers in  power-of-ten notation.  

```{r}
# Omit no values from power-of-ten notation
DT <- data.table(
  scientific  = format_power(x, 3, format = "sci", omit_power = NULL),
  engineering = format_power(x, 3, omit_power = NULL)
)
knitr::kable(DT, align = "r", col.names = c("scientific notation", "engineering notation"))
```

Suppose you wish to omit one specific power of ten only, for example, let `omit_power = c(-5, -5)`. 

```{r}
x <- c(3.2e-7, 4.5e-6, 4.5e-5, 6.7e-4, 3, 37800)
format_power(x, 3, format = "sci", omit_power = c(-5, -5))
format_power(x, 3, format = "engr", omit_power = c(-5, -5))
```

All the values except `4.5e-5` are formatted as powers of ten. 


## Set power notation

`set_power` argument, if included, must be named. 

When values in a table column span a few orders of magnitude, an audience is often better served by setting the notation to a constant power of ten. In the example below, we show the tabulated values of the viscosity of water as a function of temperature (from the `water` data set included with `formatdown`). 

```{r}
# Copy to avoid by-reference changes
DT <- copy(water)

# Convert temperature from K to C
DT <- DT[, .(visc)]

# Create two columns to compare
DT[, ver1 := format_power(visc, 3)]
DT[, ver2 := format_power(visc, 3, set_power = -3)]
```

The first column is unformatted. The second column, in engineering format, includes exponents of $\small -3$ and $\small -6$. In the third column, we set the exponents to $\small -3$ throughout. 

```{r}
knitr::kable(DT, align = "r", col.names = c(
  "$\\small\\mu$ [Pa s]", "engr notation", "set power"
))
```

In the next example, we have density (that decreases with altitude) with additional columns of density values formatted three ways. 

```{r}
DT <- copy(atmos)
DT <- DT[, .(dens)]
DT[, ver1 := format_power(dens, 3)]
DT[, ver2 := format_power(dens, 3, set_power = -3)]
DT[, ver3 := format_power(dens, 3, set_power = -3, omit_power = NULL)]

kable(DT, align = "r", col.names = c(
  "$\\small\\rho$ [kg/m$^3$]",
  "engr notation",
  "set power",
  "set power, omit none"
))
```

- **engr notation** formatted in engineering notation except for values with exponents near zero. 
- **set power** formatted with a constant exponent except for values with exponents near zero.  
- **set power, omit none** formatted with a constant exponent for all values, no exceptions. 









## Delimiters

`delim` argument, if included, must be named. 

```{r}
# Equivalent usage
w <- format_power(x[1])
y <- format_power(x[1], delim = "$")
z <- format_power(x[1], delim = c("$", "$"))

all.equal(w, y)
all.equal(w, z)
```

all of which render as 

- `r format_power(x[1])`

I encountered one instance in which the `$ ... $` delimiters did not produce the desired output---using `kableExtra::kbl()` to create a table in a `.qmd` output document. Switching the delimiters to `\\( ... \\)` fixed the problem, per the recommendation of the MathJax consortium  (Cervone, 2018). 

```{r}
format_power(x[1], delim = "\\(")
format_power(x[1], delim = c("\\(", "\\)"))
```

are both rendered as 

- `r format_power(x[1], delim = "\\(")`





## Data frame

Using `air_meas`, a data frame included with `formatdown` that contains columns of class Date, character, factor, numeric, and integer.

```{r}
# Included with formatdown
air_meas

# Render in document
knitr::kable(air_meas, align = "r")
```

Treating a column as a vector, 

```{r}
# Copy to avoid "by reference" changes to air_meas
DT <- copy(air_meas)

# Format as a vector
format_power(DT$pres, digits = 4)
```

is rendered as `r format_power(DT$pres, digits = 4)`. 

Treating a column within a data frame, 

```{r}
# Copy to avoid "by reference" changes to air_meas
DT <- copy(air_meas)

# Format one column, retain all columns
DT$pres <- format_power(DT$pres, digits = 4)
DT[]

# Render in document
knitr::kable(DT, align = "r")
```

Using `lapply()` to select and treat multiple columns from a data frame, 

```{r}
# Copy to avoid "by reference" changes to air_meas
DT <- copy(air_meas)

# Identify columns to format
cols_we_want <- c("temp", "pres", "dens")

# Select and format.
DT <- DT[, lapply(.SD, function(x) format_power(x, 4)), .SDcols = cols_we_want]
DT[]

# Render in document
knitr::kable(DT, align = "r")
```

Repeat, but retain all columns,  

```{r}
# Copy to avoid "by reference" changes to air_meas
DT <- copy(air_meas)

# Identify columns to format
cols_we_want <- c("temp", "pres", "dens")

# Format selected columns, retain all columns
DT <- DT[, (cols_we_want) := lapply(.SD, function(x) format_power(x, 4)), .SDcols = cols_we_want]

# Treat the gas constant with 3 digits
DT$sp_gas <- format_power(DT$sp_gas, digits = 3)
DT[]

# Render in document
knitr::kable(DT, align = "r")
```



## Options

The `options()` function can be used to set global values for two argument in `format_power()`:

- `format` (default is "engr")
- `size` (default is "small")

For example, 

```{r}
# Assign arguments to be used from this point forward in the script
options(formatdown.power.format = "sci")
options(formatdown.font.size = "scriptsize")

# Copy to avoid "by reference" changes to air_meas
DT <- copy(air_meas)

# Format selected columns to 4 digits
cols_we_want <- c("temp", "pres", "dens")
DT <- DT[, (cols_we_want) := lapply(.SD, function(x) format_power(x, 4)), .SDcols = cols_we_want]

# Treat the gas constant with 3 digits
DT$sp_gas <- format_power(DT$sp_gas, digits = 3)

# Format selected columns as text
cols_we_want <- c("date", "trial", "humid")
DT <- DT[, (cols_we_want) := lapply(.SD, function(x) format_text(x)), .SDcols = cols_we_want]

# Render in document
knitr::kable(DT,
  align = "r",
  caption = "Table 1: Multiple readings for calculating air density",
  col.names = c(
    "Date",
    "Trial",
    "Humidity",
    "$\\small\\theta$ [K]",
    "$\\small P$ [Pa]",
    "$\\small R_{sp}$ [J kg$^{-1}$K$^{-1}$]",
    "$\\small\\rho$ [kg/m$^3$]"
  )
)
```

You can overwrite your global assignment using the `format` or `size` argument in any individual function call.  

- The `format` option affects arguments of `format_power()` only. 
- The `size` option affects arguments of both `format_text()` and `format_power()`. 






## References

Davide Cervone (2018) MathJax: TeX and LaTeX math delimiters,   https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-math-delimiters. 

Morgan Chase (2021) *Technical Mathematics,*  https://openoregon.pressbooks.pub/techmath/chapter/module-11-scientific-notation/.

