R:pems.utils Data Unit Handling

Karl Ropkins

2021-04-19

Background

The R package pems.utils uses two main data types: pems.elements, data-series with assigned units, and pems, datasets of simultaneously logged pems.elements.

This document provides an overview of unit handling in pems.utils.

For a quick and more general introduction to pems.utils, see [>pems.utils introduction]

If you have any suggestions how to make either pems.utils or this document better or you have any problems using either, please let me know [>email me].

Unless you have setup R to automatically load pems.utils, you will need to load it at the start of each R session e.g. using library(pems.utils).

Unit Structure

If assigned, units are associated with pems.elements at the vector-level. So, all entries in a pems.element vector are assumed to have the same common unit assignment. For example, velocity in pems.1 is vehicle speed in kilometres per hour:

pems.1$velocity
## pems.element [n=1000]
##    [1]  0.1  0.1  0.3  0.3  0.2  0.4  0.3  0.7  0.1  0.2  0.2  0.2  0.1  0.2
##   [15]  0.1  0.3  0.2  0.3  0.2  0.4  0.1  0.2  0.2  0.1  0.1  0.1  0.3  0.2
##   [29]  0.1  0.1  0.2  0.3  0.4  0.1  0.3  0.4  0.1  0.4  0.2  0.2  0.1  0.2
##    ... not showing: 69 rows
##    ... <numeric> velocity [km/h]

You can access these directory with units, e.g:

#for an individual pems.element
units(pems.1$velocity)
## [1] "km/h"
#or for all pems.elements in a pems dataset
units(pems.1)
##        time.stamp local.time conc.co conc.co2 conc.hc conc.nox afr
## 1 Y-M-D H:M:S GMT          s    vol%     vol%   ppmC6      ppm    
##   exh.flow.rate exh.temp exh.press amb.temp amb.press amb.humidity velocity
## 1         L/min     degC       kPa     degC       kPa            %     km/h
##   revolution option.1 option2 option.3 latitude longitude altitude gps.velocity
## 1        rpm        V       V        V d.degLat  d.degLon        m         km/h
##   satellite n.s w.e
## 1

Handling Units

pems.utils includes convertUnits() to handle common unit conversions. For example, to convert velocity to units of metres per second:

pems.1$velocity.1 <- convertUnits(pems.1$velocity, to="m/s")
pems.1$velocity.1
## pems.element [n=1000]
##    [1]  0.02777778  0.02777778  0.08333333  0.08333333  0.05555556  0.11111111
##    [7]  0.08333333  0.19444444  0.02777778  0.05555556  0.05555556  0.05555556
##   [13]  0.02777778  0.05555556  0.02777778  0.08333333  0.05555556  0.08333333
##    ... not showing: 164 rows
##    ... <numeric> velocity.1 [m/s]

listUnitConversions() lists available unit conversions.

If the unit conversion you require is not available, you can manually calculate the conversion or add a conversion method.

Consider, for example, the hypothetical conversation:

my.unit = 12 + (21 * velocity[in km/h])

Manually, in R:

pems.1$velocity.2 <- 21 + (21 * pems.1$velocity) 

But if you do this, you also need to reset the units because R does not know this is a unit conversation, so:

units(pems.1$velocity.2) <- "my.unit"

Alternatively, you can make your own unit conversion method:

#add a new conversion
ref.list <- addUnitConversion(to = "my.unit", from = "km/h", 
                              conversion = function(x) 12 + (21 * x), 
                              tag = "kilometers/hour to my.unit")
#use it
pems.1$velocity.2 <- convertUnits(pems.1$velocity, to = "my.unit",
                                  unit.conversions = ref.list)

You can also add extra unit aliases to conversions in your reference list with addUnitAlias(). If, for example, you work with velocity units of miles/hour rather than the pems.utils default of mi/h:

ref.list <- addUnitAlias("mi/h", "miles/hour", ref.list)

The above might seem more long-winded but the approach:

  1. Adds an automatic check that inputs are in the expected units (something the manual R methods assumes the user does themselves every time they do the conversion), and
  2. Provides the structure for automatic method reporting.

If you are regularly using unit conversations not available as default pems.utils conversions and would like to propose them for packaging, please let me know. [>email me].

Unit Handling in Calculations

You can manually calculate other terms using standard R operators and manual unit reassigned, as above. However, pems.utils also includes functions to calculate common PEMS parameters that automatically track and handle pems.element units.

For example, to calculate acceleration using velocity and local.time, both in pems.1:

calcAccel(pems.1$velocity, pems.1$local.time)

Or using the argument data to assign pems.1 as the source of both:

calcAccel(velocity, local.time, data=pems.1)
## pems.element [n=1000]
##    [1]          NA  0.00000000  0.05555556  0.00000000 -0.02777778  0.05555556
##    [7] -0.02777778  0.11111111 -0.16666667  0.02777778  0.00000000  0.00000000
##   [13] -0.02777778  0.02777778 -0.02777778  0.05555556 -0.02777778  0.02777778
##    ... not showing: 164 rows
##    ... <numeric> accel [m/s/s]

Note: pems.utils calc() functions use convertUnits() to reset input units if they can. So, they only calculate their intended outputs if the inputs are already in the expected units or they can be converted to the expected units. This saves users a job and significantly reduces the chances of unit mismatching, a common source of errors of PEMS data analysis.

These can be used to quickly calculate PEMS data statistics, e.g.:

calcEm(conc.co2, ...) calculates CO2 emissions in grams per second and calcDistance(velocity, local.time, ...) calculates distance in metres.

So, for a 1Hz dataset like pems.1, total dataset CO2 g/km emissions can be calculated using:

sum(calcEm(conc.co2, data=pems.1), na.rm=TRUE) / 
  sum(convertUnits(calcDistance(velocity, local.time, data=pems.1), to="km"), na.rm=TRUE)
## [1] 310.2482

Functions like summaryReport() use this approach to generate tables of common PEMS data statistics, e.g.:

summaryReport(velocity, local.time, data=pems.1)
##   distance.travelled.km time.total.s avg.speed.km.h avg.running.speed.km.h
## 1              6.186056         1000        22.2698               28.78538
##   time.idle.s time.idle.pc avg.accel.m.s.s time.accel.s time.accel.pc
## 1          40            4       0.7921279          271          27.1
##   avg.decel.m.s.s time.decel.s time.decel.pc
## 1      -0.9039449          238          23.8

If you have any suggestions how to make either pems.utils or this document better or you have any problems using either, please let me know. [>email me].