# 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

Existing units are resolved from a database in the `units` package, called `ud_units`. We can see the first three elements of it by

    library(units)
    ud_units[1:3]
    ## $m
    ## 1 m
    ##
    ## $kg
    ## 1 kg
    ##
    ## $s
    ## 1 s

We can set units to numerical values by `set_units`:

    (a <- set_units(runif(10),  m/s))
    ## Units: m/s
    ##  [1] 0.04174716 0.88530030 0.66073352 0.59667242 0.89301793 0.06805642
    ##  [7] 0.29603229 0.25016793 0.32991055 0.48558397

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.

The `units` package comes with a list of over 3000 predefined units,

    length(ud_units)
    ## [1] 3225

We can retrieve a single unit from the `ud_units` database by

    with(ud_units, km/h)
    ## 1 km/h

### 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) <- with(ud_units, km/h)
    b
    ## Units: km/h
    ##  [1] 0.1502898 3.1870811 2.3786407 2.1480207 3.2148645 0.2450031 1.0657162
    ##  [8] 0.9006045 1.1876780 1.7481023

## Basic manipulations

### Arithmetic operations

Arithmetic operations verify units, and create new ones

    a + a
    ## Units: m/s
    ##  [1] 0.08349432 1.77060060 1.32146704 1.19334483 1.78603586 0.13611285
    ##  [7] 0.59206457 0.50033585 0.65982109 0.97116793
    a * a
    ## Units: m^2/s^2
    ##  [1] 0.001742826 0.783756620 0.436568784 0.356017973 0.797481023
    ##  [6] 0.004631677 0.087635115 0.062583991 0.108840969 0.235791789
    a ^ 2
    ## Units: m^2/s^2
    ##  [1] 0.001742826 0.783756620 0.436568784 0.356017973 0.797481023
    ##  [6] 0.004631677 0.087635115 0.062583991 0.108840969 0.235791789
    a ** -2
    ## Units: s^2/m^2
    ##  [1] 573.780888   1.275906   2.290590   2.808847   1.253948 215.904531
    ##  [7]  11.410951  15.978527   9.187717   4.241030

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

    a + b # m/s + km/h -> m/s
    ## Units: m/s
    ##  [1] 0.08349432 1.77060060 1.32146704 1.19334483 1.78603586 0.13611285
    ##  [7] 0.59206457 0.50033585 0.65982109 0.97116793

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 <- with(ud_units, s)
    a * t
    ## Units: m
    ##  [1] 0.04174716 0.88530030 0.66073352 0.59667242 0.89301793 0.06805642
    ##  [7] 0.29603229 0.25016793 0.32991055 0.48558397

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

    t <- with(ud_units, min)
    a * t
    ## Units: m
    ##  [1]  2.504830 53.118018 39.644011 35.800345 53.581076  4.083385 17.761937
    ##  [8] 15.010076 19.794633 29.135038

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

    m <- with(ud_units, m)
    a * t / m
    ## Units: 1
    ##  [1]  2.504830 53.118018 39.644011 35.800345 53.581076  4.083385 17.761937
    ##  [8] 15.010076 19.794633 29.135038

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] 0.000581 0.261000 0.146000 0.119000 0.266000 0.001540 0.029200
    ##  [8] 0.020900 0.036300 0.078600
    cumsum(a)
    ## Units: m/s
    ##  [1] 0.04174716 0.92704746 1.58778098 2.18445340 3.07747133 3.14552775
    ##  [7] 3.44156004 3.69172796 4.02163851 4.50722248
    log(a) # base defaults to exp(1)
    ## Units: (ln(m/s))
    ##  [1] -3.1761238 -0.1218284 -0.4144047 -0.5163870 -0.1131486 -2.6874182
    ##  [7] -1.2172868 -1.3856229 -1.1089337 -0.7224031
    log(a, base = 10)
    ## Units: (lg(m/s))
    ##  [1] -1.37937304 -0.05290939 -0.17997366 -0.22426404 -0.04913982
    ##  [6] -1.16713088 -0.52866092 -0.60176837 -0.48160380 -0.31373566
    log(a, base = 2)
    ## Units: (lb(m/s))
    ##  [1] -4.5821781 -0.1757612 -0.5978596 -0.7449890 -0.1632390 -3.8771249
    ##  [7] -1.7561736 -1.9990313 -1.5998532 -1.0422073

### Summary functions

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

    sum(a)
    ## 4.507222 m/s
    min(a)
    ## 0.04174716 m/s
    max(a)
    ## 0.8930179 m/s
    range(a)
    ## Units: m/s
    ## [1] 0.04174716 0.89301793
    with(ud_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.04174716 0.88530030 0.66073352 0.59667242 0.89301793 0.06805642
    ##  [7] 0.29603229 0.25016793 0.32991055 0.48558397
    a[1]
    ## 0.04174716 m/s

### Subsetting

The usual subsetting rules work:

    a[2:5]
    ## Units: m/s
    ## [1] 0.8853003 0.6607335 0.5966724 0.8930179
    a[-(1:9)]
    ## 0.485584 m/s

### Concatenation

    c(a,a)
    ## Units: m/s
    ##  [1] 0.04174716 0.88530030 0.66073352 0.59667242 0.89301793 0.06805642
    ##  [7] 0.29603229 0.25016793 0.32991055 0.48558397 0.04174716 0.88530030
    ## [13] 0.66073352 0.59667242 0.89301793 0.06805642 0.29603229 0.25016793
    ## [19] 0.32991055 0.48558397

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.04174716 0.88530030 0.66073352 0.59667242 0.89301793 0.06805642
    ##  [7] 0.29603229 0.25016793 0.32991055 0.48558397 0.04174716 0.88530030
    ## [13] 0.66073352 0.59667242 0.89301793 0.06805642 0.29603229 0.25016793
    ## [19] 0.32991055 0.48558397
    c(b,a) # km/h, m/s -> km/h
    ## Units: km/h
    ##  [1] 0.1502898 3.1870811 2.3786407 2.1480207 3.2148645 0.2450031 1.0657162
    ##  [8] 0.9006045 1.1876780 1.7481023 0.1502898 3.1870811 2.3786407 2.1480207
    ## [15] 3.2148645 0.2450031 1.0657162 0.9006045 1.1876780 1.7481023

## 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)
    ## Units: m^2/s^2
    ##      [,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 * with(ud_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 * ud_units[["in"]]^3
    units(displacement) = with(ud_units, cm^3)
    weight = mtcars$wt * 1000 * with(ud_units, lb)
    units(weight) = with(ud_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 * with(ud_units, mi/gallon)
    units(consumption) = with(ud_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)