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 set and modify the
unit of time difference.
This idea can be generalized to other physical units. The
units package, presented here, does this, and builds upon the
udunits2
R package, which in turn is build upon the
udunits2 C library.
The udunits2 library provides the following operations:
m/s is a valid physical unitm/s and km/h are convertableThis R package, called units, 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.
Numeric data with explicit physical units can be specified by as.units:
library(units)
(a = as.units(1:10, "m/s"))
## Units: m/s
## [1] 1 2 3 4 5 6 7 8 9 10
and converted by setting a new physical unit:
b = a
units(b) = "km/h"
b
## Units: km/h
## [1] 3.6 7.2 10.8 14.4 18.0 21.6 25.2 28.8 32.4 36.0
Impossible conversions lead to an error:
x = try(units(a) <- "secs")
x
## [1] "Error in `units<-.units`(`*tmp*`, value = \"secs\") : \n cannot convert m/s into secs\n"
## attr(,"class")
## [1] "try-error"
## attr(,"condition")
## <simpleError in `units<-.units`(`*tmp*`, value = "secs"): cannot convert m/s into secs>
Arithmetic operations verify units, and create new ones
a + a
## Units: m/s
## [1] 2 4 6 8 10 12 14 16 18 20
a * a
## Units: (m/s)*(m/s)
## [1] 1 4 9 16 25 36 49 64 81 100
a ^ 2.5
## Units: (m/s)^2.5
## [1] 1.000000 5.656854 15.588457 32.000000 55.901699 88.181631
## [7] 129.641814 181.019336 243.000000 316.227766
and convert if necessary:
a + b # m/s + km/h -> m/s
## Units: m/s
## [1] 2 4 6 8 10 12 14 16 18 20
but units are not simplified:
t = as.units(1, "s")
a * t
## Units: (m/s)*(s)
## [1] 1 2 3 4 5 6 7 8 9 10
Allowed operations that require convertable units are +, -, ==,
!=, <, >, <=, >=. Operations that lead to new units are
*, /, and the power operations ** and ^.
Mathematical operations allowed are: abs, sign, floor,
ceiling, trunc, round, signif, cumsum, cummax, cummin.
signif(a^2.5, 3)
## Units: (m/s)^2.5
## [1] 1.00 5.66 15.60 32.00 55.90 88.20 130.00 181.00 243.00 316.00
cumsum(a)
## [1] 1 3 6 10 15 21 28 36 45 55
(for reasons beyond my comprehension, here cumsum strips units,
but signif does not)
Summary functions sum, min, max, and range are allowed:
sum(a)
## 55 m/s
min(a)
## 1 m/s
max(a)
## 10 m/s
range(a)
## Units: m/s
## [1] 1 10
min(as.units(1, "m/s"), as.units(1, "km/h")) # converts to first unit:
## 0.2777778 m/s
Following difftime, printing behaves differently for length-one vectors:
a
## Units: m/s
## [1] 1 2 3 4 5 6 7 8 9 10
a[1]
## 1 m/s
The usual subsetting rules work:
a[2:5]
## Units: m/s
## [1] 2 3 4 5
a[-(1:9)]
## 10 m/s
c(a,a)
## Units: m/s
## [1] 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
concatenation converts to the units of the first argument, if necessary:
c(a,b) # m/s, km/h -> m/s
## Units: m/s
## [1] 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
c(b,a) # km/h, m/s -> km/h
## Units: km/h
## [1] 3.6 7.2 10.8 14.4 18.0 21.6 25.2 28.8 32.4 36.0 3.6 7.2 10.8 14.4
## [15] 18.0 21.6 25.2 28.8 32.4 36.0
difftimeFrom difftime to units:
t1 = Sys.time()
t2 = t1 + 3600
d = t2 - t1
as.units(d)
## 1 h
(du = as.units(d, "d"))
## 0.04166667 d
vice versa:
dt = as.dt(du)
class(dt)
## [1] "difftime"
dt
## Time difference of 0.04166667 days
matrix objectsas.units(matrix(1:4,2,2), "m/s")
## Units: m/s
## [,1] [,2]
## [1,] 1 3
## [2,] 2 4
as.units(matrix(1:4,2,2), "m/s") * as.units(4, "m/s")
## Units: (m/s)*(m/s)
## [,1] [,2]
## [1,] 4 12
## [2,] 8 16
but
as.units(matrix(1:4,2,2), "m/s") %*% as.units(4:3, "m/s")
## [,1]
## [1,] 13
## [2,] 20
strips units.
data.framesset.seed(131)
d = data.frame(x = runif(4), y = as.units(runif(4), "s"), z = as.units(1:4, "m/s"))
d
## x y z
## 1 0.2064370 0.8463468 1
## 2 0.1249422 0.5292048 2
## 3 0.2932732 0.5186254 3
## 4 0.3757797 0.2378545 4
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 1 0.8463468
## 2 0.1249422 0.5292048 2 1.0584095
## 3 0.2932732 0.5186254 3 1.5558761
## 4 0.3757797 0.2378545 4 0.9514180
d[1, "yz"]
## 0.8463468 (s)*(m/s)