1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
|
---
title: "Units of Measurement for R Vectors: an Introduction"
output:
html_document:
toc: true
theme: united
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)
```
```{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 fig=TRUE}
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 /
```
|