--- title: "Units of Measurement for R Vectors: an Introduction" output: rmarkdown::html_vignette: toc: yes vignette: > %\VignetteIndexEntry{Units of Measurement for R Vectors: an Introduction} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r echo=FALSE} knitr::opts_chunk$set(collapse = TRUE, fig.asp = 0.7, fig.width = 7) ``` ```{r echo=FALSE} units:::units_options(negative_power = FALSE) ``` R has little support for physical measurement units. The exception is formed by time differences: time differences objects of class `difftime` have a `units` attribute that can be modified: ```{r} t1 = Sys.time() t2 = t1 + 3600 d = t2 - t1 class(d) units(d) d units(d) = "secs" d ``` We see here that the `units` method is used to retrieve and modify the unit of time differences. The `units` package generalizes this idea to other physical units, building upon the [udunits2](https://www.unidata.ucar.edu/software/udunits/) C library. The `udunits2` library provides the following operations: * validating whether an expression, such as `m/s` is a valid physical unit * verifying whether two units such as `m/s` and `km/h` are convertible * converting values between two convertible units * providing names and symbols for specific units * handle different character encodings (utf8, ascii, iso-8859-1 and latin1) The `units` R package uses the [udunits2](https://www.unidata.ucar.edu/software/udunits/) C library to extend R with functionality for manipulating numeric vectors that have physical measurement units associated with them, in a similar way as `difftime` objects behave. ## Setting units, unit conversion We can set units to numerical values by `set_units`: ```{r} library(units) (a <- set_units(runif(10), m/s)) ``` the result, e.g. ```{r} set_units(10, m/s) ``` literally means "10 times 1 m divided by 1 s". In writing, the "1" values are omitted, and the multiplication is implicit. ### Unit conversion When conversion is meaningful, such as hours to seconds or meters to kilometers, conversion can be done explicitly by setting the units of a vector ```{r} b = a units(b) <- make_units(km/h) b ``` ## Basic manipulations ### Arithmetic operations Arithmetic operations verify units, and create new ones ```{r} a + a a * a a ^ 2 a ** -2 ``` and convert to the units of the first argument if necessary: ```{r} a + b # m/s + km/h -> m/s ``` Currently, powers are only supported for integer powers, so using `a ** 2.5` would result in an error. ### Unit simplification There are some basic simplification of units: ```{r} t <- make_units(s) a * t ``` which also work when units need to be converted before they can be simplified: ```{r} t <- make_units(min) a * t ``` Simplification to unit-less values gives the "1" as unit: ```{r} m <- make_units(m) a * t / m ``` Allowed operations that require convertible units are `+`, `-`, `==`, `!=`, `<`, `>`, `<=`, `>=`. Operations that lead to new units are `*`, `/`, and the power operations `**` and `^`. ### Mathematical functions Mathematical operations allowed are: `abs`, `sign`, `floor`, `ceiling`, `trunc`, `round`, `signif`, `log`, `cumsum`, `cummax`, `cummin`. ```{r} signif(a ** 2 / 3, 3) cumsum(a) log(a) # base defaults to exp(1) log(a, base = 10) log(a, base = 2) ``` ### Summary functions Summary functions `sum`, `min`, `max`, and `range` are allowed: ```{r} sum(a) min(a) max(a) range(a) make_units(min(m/s, km/h)) # converts to first unit: ``` ### Printing Following `difftime`, printing behaves differently for length-one vectors: ```{r} a a[1] ``` ### Subsetting The usual subsetting rules work: ```{r} a[2:5] a[-(1:9)] ``` ### Concatenation ```{r} c(a,a) ``` concatenation converts to the units of the first argument, if necessary: ```{r} c(a,b) # m/s, km/h -> m/s c(b,a) # km/h, m/s -> km/h ``` ## Conversion to/from `difftime` From `difftime` to `units`: ```{r} t1 = Sys.time() t2 = t1 + 3600 d = t2 - t1 (du = as_units(d)) ``` vice versa: ```{r} (dt = as_difftime(du)) class(dt) ``` ## units in `matrix` objects ```{r} set_units(matrix(1:4,2,2), m/s) set_units(matrix(1:4,2,2), m/s * m/s) ``` but ```{r} set_units(matrix(1:4,2,2), m/s) %*% set_units(4:3, m/s) ``` strips units. ## units objects in `data.frame`s units in `data.frame` objects are printed, but do not appear in `summary`:. ```{r} set.seed(131) d <- data.frame(x = runif(4), y = set_units(runif(4), s), z = set_units(1:4, m/s)) d summary(d) d$yz = with(d, y * z) d d[1, "yz"] ``` ## Formatting Units are often written in the form `m2 s-1`, for square meter per second. This can be defined as unit, and also parsed by `as_units`: ```{r} (x = 1:10 * as_units("m2 s-1")) ``` udunits understands such string, and can convert them ```{r} y = 1:10 * make_units(m^2/s) x + y ``` Printing units in this form is done by ```{r} deparse_unit(x) ``` ## Plotting Base scatter plots and histograms support automatic unit placement in axis labels. In the following example we first convert to SI units. (Unit `in` needs a bit special treatment, because `in` is a reserved word in R.) ```{r} mar = par("mar") + c(0, .3, 0, 0) displacement = mtcars$disp * as_units("in")^3 units(displacement) = make_units(cm^3) weight = mtcars$wt * 1000 * make_units(lb) units(weight) = make_units(kg) par(mar = mar) plot(weight, displacement) ``` We can change grouping symbols from `[ ]` into `( )`: ```{r} units_options(group = c("(", ")") ) # parenthesis instead of square brackets par(mar = mar) plot(weight, displacement) ``` We can also remove grouping symbols, increase space between variable name and unit by: ```{r} units_options(sep = c("~~~", "~"), group = c("", "")) # no brackets; extra space par(mar = mar) plot(weight, displacement) ``` More complex units can be plotted either with negative powers, or as divisions, by modifying one of `units`'s global options using `units_options`: ```{r} gallon = as_units("gallon") consumption = mtcars$mpg * make_units(mi/gallon) units(consumption) = make_units(km/l) par(mar = mar) plot(displacement, consumption) # division in consumption units_options(negative_power = TRUE) # division becomes ^-1 plot(displacement, consumption) # division in consumption ``` As usual, units modify automatically in expressions: ```{r} units_options(negative_power = TRUE) # division becomes ^-1 par(mar = mar) plot(displacement, consumption) plot(1/displacement, 1/consumption) ``` ```{r echo=FALSE} units_options(negative_power = FALSE) # division becomes / ```