# Units of Measurement for R Vectors: an Introduction

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:

    t1 = Sys.time()
    t2 = t1 + 3600
    d = t2 - t1
    class(d)
    ## [1] "difftime"
    units(d)
    ## [1] "hours"
    d
    ## Time difference of 1 hours
    units(d) = "secs"
    d
    ## Time difference of 3600 secs

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 R package, which in turn is build upon the udunits2 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 R package `udunits2` 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`:

    library(units)
    (a <- set_units(runif(10),  m/s))
    ## Units: [m/s]
    ##  [1] 0.832006439 0.359083909 0.603057928 0.576472604 0.613411909 0.830128625
    ##  [7] 0.642478956 0.800919445 0.464359833 0.006960209

the result, e.g.

    set_units(10, m/s)
    ## 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

    b = a
    units(b) <- make_units(km/h)
    b
    ## Units: [km/h]
    ##  [1] 2.99522318 1.29270207 2.17100854 2.07530137 2.20828287 2.98846305
    ##  [7] 2.31292424 2.88331000 1.67169540 0.02505675

## Basic manipulations

### Arithmetic operations

Arithmetic operations verify units, and create new ones

    a + a
    ## Units: [m/s]
    ##  [1] 1.66401288 0.71816782 1.20611586 1.15294521 1.22682382 1.66025725
    ##  [7] 1.28495791 1.60183889 0.92871967 0.01392042
    a * a
    ## Units: [m^2/s^2]
    ##  [1] 6.922347e-01 1.289413e-01 3.636789e-01 3.323207e-01 3.762742e-01
    ##  [6] 6.891135e-01 4.127792e-01 6.414720e-01 2.156301e-01 4.844451e-05
    a ^ 2
    ## Units: [m^2/s^2]
    ##  [1] 6.922347e-01 1.289413e-01 3.636789e-01 3.323207e-01 3.762742e-01
    ##  [6] 6.891135e-01 4.127792e-01 6.414720e-01 2.156301e-01 4.844451e-05
    a ** -2
    ## Units: [s^2/m^2]
    ##  [1]     1.444597     7.755470     2.749679     3.009142     2.657637
    ##  [6]     1.451140     2.422603     1.558915     4.637572 20642.175610

and convert to the units of the first argument if necessary:

    a + b # m/s + km/h -> m/s
    ## Units: [m/s]
    ##  [1] 1.66401288 0.71816782 1.20611586 1.15294521 1.22682382 1.66025725
    ##  [7] 1.28495791 1.60183889 0.92871967 0.01392042

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:

    t <- make_units(s)
    a * t
    ## Units: [m]
    ##  [1] 0.832006439 0.359083909 0.603057928 0.576472604 0.613411909 0.830128625
    ##  [7] 0.642478956 0.800919445 0.464359833 0.006960209

which also work when units need to be converted before they can be simplified:

    t <- make_units(min)
    a * t
    ## Units: [m]
    ##  [1] 49.9203864 21.5450345 36.1834757 34.5883562 36.8047145 49.8077175
    ##  [7] 38.5487374 48.0551667 27.8615900  0.4176125

Simplification to unit-less values gives the “1” as unit:

    m <- make_units(m)
    a * t / m
    ## Units: [1]
    ##  [1] 49.9203864 21.5450345 36.1834757 34.5883562 36.8047145 49.8077175
    ##  [7] 38.5487374 48.0551667 27.8615900  0.4176125

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`.

    signif(a ** 2 / 3, 3)
    ## Units: [m^2/s^2]
    ##  [1] 2.31e-01 4.30e-02 1.21e-01 1.11e-01 1.25e-01 2.30e-01 1.38e-01 2.14e-01
    ##  [9] 7.19e-02 1.61e-05
    cumsum(a)
    ## Units: [m/s]
    ##  [1] 0.8320064 1.1910903 1.7941483 2.3706209 2.9840328 3.8141614 4.4566404
    ##  [8] 5.2575598 5.7219196 5.7288799
    log(a) # base defaults to exp(1)
    ## Units: [(ln(re 1 m.s-1))]
    ##  [1] -0.1839151 -1.0241992 -0.5057420 -0.5508275 -0.4887186 -0.1861746
    ##  [7] -0.4424212 -0.2219949 -0.7670955 -4.9675458
    log(a, base = 10)
    ## Units: [(lg(re 1 m.s-1))]
    ##  [1] -0.07987331 -0.44480406 -0.21964097 -0.23922133 -0.21224780 -0.08085461
    ##  [7] -0.19214109 -0.09641116 -0.33314535 -2.15737773
    log(a, base = 2)
    ## Units: [(lb(re 1 m.s-1))]
    ##  [1] -0.2653334 -1.4776071 -0.7296315 -0.7946760 -0.7050719 -0.2685932
    ##  [7] -0.6382789 -0.3202709 -1.1066849 -7.1666537

### Summary functions

Summary functions `sum`, `min`, `max`, and `range` are allowed:

    sum(a)
    ## 5.72888 [m/s]
    min(a)
    ## 0.006960209 [m/s]
    max(a)
    ## 0.8320064 [m/s]
    range(a)
    ## Units: [m/s]
    ## [1] 0.006960209 0.832006439
    make_units(min(m/s, km/h)) # converts to first unit:
    ## 0.2777778 [m/s]

### Printing

Following `difftime`, printing behaves differently for length-one vectors:

    a
    ## Units: [m/s]
    ##  [1] 0.832006439 0.359083909 0.603057928 0.576472604 0.613411909 0.830128625
    ##  [7] 0.642478956 0.800919445 0.464359833 0.006960209
    a[1]
    ## 0.8320064 [m/s]

### Subsetting

The usual subsetting rules work:

    a[2:5]
    ## Units: [m/s]
    ## [1] 0.3590839 0.6030579 0.5764726 0.6134119
    a[-(1:9)]
    ## 0.006960209 [m/s]

### Concatenation

    c(a,a)
    ## Units: [m/s]
    ##  [1] 0.832006439 0.359083909 0.603057928 0.576472604 0.613411909 0.830128625
    ##  [7] 0.642478956 0.800919445 0.464359833 0.006960209 0.832006439 0.359083909
    ## [13] 0.603057928 0.576472604 0.613411909 0.830128625 0.642478956 0.800919445
    ## [19] 0.464359833 0.006960209

concatenation converts to the units of the first argument, if necessary:

    c(a,b) # m/s, km/h -> m/s
    ## Units: [m/s]
    ##  [1] 0.832006439 0.359083909 0.603057928 0.576472604 0.613411909 0.830128625
    ##  [7] 0.642478956 0.800919445 0.464359833 0.006960209 0.832006439 0.359083909
    ## [13] 0.603057928 0.576472604 0.613411909 0.830128625 0.642478956 0.800919445
    ## [19] 0.464359833 0.006960209
    c(b,a) # km/h, m/s -> km/h
    ## Units: [km/h]
    ##  [1] 2.99522318 1.29270207 2.17100854 2.07530137 2.20828287 2.98846305
    ##  [7] 2.31292424 2.88331000 1.67169540 0.02505675 2.99522318 1.29270207
    ## [13] 2.17100854 2.07530137 2.20828287 2.98846305 2.31292424 2.88331000
    ## [19] 1.67169540 0.02505675

## Conversion to/from `difftime`

From `difftime` to `units`:

    t1 = Sys.time()
    t2 = t1 + 3600
    d = t2 - t1
    (du = as_units(d))
    ## 1 [h]

vice versa:

    (dt = as_difftime(du))
    ## Time difference of 1 hours
    class(dt)
    ## [1] "difftime"

## units in `matrix` objects

    set_units(matrix(1:4,2,2), m/s)
    ## Units: [m/s]
    ##      [,1] [,2]
    ## [1,]    1    3
    ## [2,]    2    4
    set_units(matrix(1:4,2,2), m/s * m/s)
    ## Units: [m^2/s^2]
    ##      [,1] [,2]
    ## [1,]    1    3
    ## [2,]    2    4

but

    set_units(matrix(1:4,2,2), m/s) %*% set_units(4:3, m/s)
    ##      [,1]
    ## [1,]   13
    ## [2,]   20

strips units.

## units objects in `data.frame`s

units in `data.frame` objects are printed, but do not appear in `summary`:.

    set.seed(131)
    d <- data.frame(x = runif(4),
                        y = set_units(runif(4), s),
                        z = set_units(1:4, m/s))
    d
    ##           x             y       z
    ## 1 0.2064370 0.8463468 [s] 1 [m/s]
    ## 2 0.1249422 0.5292048 [s] 2 [m/s]
    ## 3 0.2932732 0.5186254 [s] 3 [m/s]
    ## 4 0.3757797 0.2378545 [s] 4 [m/s]
    summary(d)
    ##        x                y                z
    ##  Min.   :0.1249   Min.   :0.2379   Min.   :1.00
    ##  1st Qu.:0.1861   1st Qu.:0.4484   1st Qu.:1.75
    ##  Median :0.2499   Median :0.5239   Median :2.50
    ##  Mean   :0.2501   Mean   :0.5330   Mean   :2.50
    ##  3rd Qu.:0.3139   3rd Qu.:0.6085   3rd Qu.:3.25
    ##  Max.   :0.3758   Max.   :0.8463   Max.   :4.00
    d$yz = with(d, y * z)
    d
    ##           x             y       z            yz
    ## 1 0.2064370 0.8463468 [s] 1 [m/s] 0.8463468 [m]
    ## 2 0.1249422 0.5292048 [s] 2 [m/s] 1.0584095 [m]
    ## 3 0.2932732 0.5186254 [s] 3 [m/s] 1.5558761 [m]
    ## 4 0.3757797 0.2378545 [s] 4 [m/s] 0.9514180 [m]
    d[1, "yz"]
    ## 0.8463468 [m]

## 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`:

    (x = 1:10 * as_units("m2 s-1"))
    ## Units: [m^2/s]
    ##  [1]  1  2  3  4  5  6  7  8  9 10

udunits understands such string, and can convert them

    y = 1:10 * make_units(m^2/s)
    x + y
    ## Units: [m^2/s]
    ##  [1]  2  4  6  8 10 12 14 16 18 20

Printing units in this form is done by

    deparse_unit(x)
    ## [1] "m2 s-1"

## 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.)

    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 `( )`:

    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:

    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`:

    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:

    units_options(negative_power = TRUE) # division becomes ^-1
    par(mar = mar)
    plot(displacement, consumption)

    plot(1/displacement, 1/consumption)