r-quantities / units

Measurement units for R
https://r-quantities.github.io/units
173 stars 27 forks source link

[feature request] mixed units comparison, or at least the ability to turn mixed units back to normal untis objects #286

Closed vorpalvorpal closed 3 years ago

vorpalvorpal commented 3 years ago

I have a data frame with two columns. One represents a measured value and the other the detection level for the measured value. Both of these columns are mixed units objects, but both have the same units (since they represent two different attributes of the same actual thing), however I can't compare these columns to see if the measured value is above the detection limit or not.

As a minimal example:

> df <- data.frame(a = c(1,2), b = c(2,3))
> df$a <- mixed_units(df$a, c("mg", "L"))
> df$a
Mixed units: L (1), mg (1) 
1 [mg], 2 [L] 
> df$b <- mixed_units(df$b, c("mg", "L"))
> df$a < df$b
Error in Ops.mixed_units(df$a, df$b) : operation < not supported

I get that you might not want to introduce this, because you don't want people accidentally comparing things that aren't actually comparable, but surely that is unlikely if the units in each vector have matched types (eg. length, mass, etc).

Since this sort of comparison isn't currently supported, the obvious way to get the same functionality is to break the mixed unit down into component units, do the comparison, and then build them back up again. Something along the lines of:

df %>%
    mutate(c = case_when(units(a) == "mg" ~ set_units(a, mg) > set_units(b, mg)) 

However, as far as I can see, there is no nice way of turning a mixed units object into a normal units object to allow comparison for each unit separately.

> df$a[1]
Mixed units: mg (1) 
1 [mg] 
> set_units(df$a[1], "ng")
Mixed units: ng (1) 
1e+06 [ng] 
# still a mixed units object, not a units object.

Is it possible to turn a mixed unit object with just a single type of unit (eg. all length units) into a normal units object?

edzer commented 3 years ago

You could use mapply, as in

library(units)
# udunits database from /usr/share/xml/udunits/udunits2.xml
df <- data.frame(a = c(1,2), b = c(2,3))
df$a <- mixed_units(df$a, c("mg", "L"))
df$a
# Mixed units: L (1), mg (1) 
# 1 [mg], 2 [L] 
df$b <- mixed_units(df$b, c("mg", "L"))
mapply("<", df$a, df$b)
# [1] TRUE TRUE
vorpalvorpal commented 3 years ago

Thanks.