r-quantities / units

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

Inconsistent comparison of plural and abbreviated "inch" and "foot" units #339

Closed elipousson closed 1 year ago

elipousson commented 1 year ago

I discovered what seem to be some inconsistencies in the handling of plural and abbreviated units "inch" and "foot" units. I initially thought this may just be how the pre-defined units from UDUNITS2 work but I noticed that "foot" and "feet" are listed as valid name_singular_aliases and name_plural_aliases in the data.frame returned by units::valid_udunits() (and plural forms that work like degrees are not) so I'm unsure why these comparisons work the way they do.

If there is another approach to verify that the strings "ft" and "foot" represent the same unit, I'd welcome the suggestion but I figured I'd open an issue just in case it is a bug (or at least a missing feature) that could be addressed.

# Make a helpful function to compare character strings as units
is_same_units <- function(x, y) {
  units::as_units(x) == units::as_units(y)
}

# These comparisons work as expected

# Plural form
is_same_units("kilometer", "kilometers")
#> [1] TRUE
is_same_units("mile", "miles")
#> [1] TRUE
is_same_units("degree", "degrees")
#> [1] TRUE

# Abbreviation
is_same_units("km", "kilometer")
#> [1] TRUE
is_same_units("mi", "mile")
#> [1] TRUE

# These comparisons do not work as expected

is_same_units("inch", "inches")
#> [1] FALSE
is_same_units("international_inch", "international_inches")
#> [1] FALSE
is_same_units("foot", "feet")
#> [1] FALSE

is_same_units("in", "inch")
#> [1] FALSE
is_same_units("in", "international_inch")
#> [1] FALSE
is_same_units("ft", "foot")
#> [1] FALSE

Created on 2022-12-18 with reprex v2.0.2

edzer commented 1 year ago

I think that this is an upstream issue, i.e. a property of the UDUNITS library; please note that this R package is only an interface to that library, the package description mentions Uses the UNIDATA udunits library and unit database for unit compatibility checking and conversion.

Enchufa2 commented 1 year ago

This definitely is an upstream issue:

library(units)
#> udunits database from /usr/share/udunits/udunits2.xml

x <- set_units(1, foot)
y <- set_units(1, feet)
print(x, nsmall=20)
#> 1.00000000000000000000 [foot]
print(y, nsmall=20)
#> 1.00000000000000000000 [feet]

units(y) <- units(x)
print(x, nsmall=20)
#> 1.00000000000000000000 [foot]
print(y, nsmall=20)
#> 0.99999999999999988898 [foot]

print(set_units(set_units(x, m), foot), nsmall=20)
#> 0.99999999999999988898 [foot]

From the last line, there is some error when converting from/to SI and imperial units (which probably cannot be avoided), but the issue is that conversion from/to plural and singular units (and symbols) seems to go through the conversion to SI.

elipousson commented 1 year ago

Thank you both for the quick and information replies! I went ahead and submitted an issue to the UDUNITS repository. I'm using units within a wrapper function so I can probably figure out a work around in the interim.

elipousson commented 1 year ago

I wanted to share my wrapper function here in case anyone else finds this issue and either finds it useful or has a suggestion for a better approach:

is_same_units <- function(x, y) {
  if (is.character(x)) {
    x <- units::as_units(x)
  }

  if (is.character(y)) {
    y <- units::as_units(y)
  }

  x <- units(x)
  y <- units(y)

  in_opts <- c("in", "inch", "inches", "international_inch", "international_inches")
  ft_opts <- c("ft", "foot", "feet", "international_foot", "international_feet")
  yd_opts <- c("yd", "yard", "yards", "international_yard", "international_yards")

  nums <- c(x[["numerator"]], y[["numerator"]])
  dens <- c(x[["denominator"]], y[["denominator"]])

  if (any(
    c(all(nums %in% in_opts), all(nums %in% ft_opts), all(nums %in% yd_opts))
  ) && all(dens == character(0))) {
    return(TRUE)
  }

  units::as_units(x) == units::as_units(y)
}

is_same_units("inch", "inches")
#> [1] TRUE
is_same_units("international_inch", "international_inches")
#> [1] TRUE
is_same_units("foot", "feet")
#> [1] TRUE

is_same_units("in", "inch")
#> [1] TRUE
is_same_units("in", "international_inch")
#> [1] TRUE
is_same_units("ft", "foot")
#> [1] TRUE

Created on 2022-12-19 with reprex v2.0.2

Enchufa2 commented 1 year ago

Thanks, closing here.

elipousson commented 1 year ago

I'll leave it to you, @Enchufa2 or @edzer to decide if it is appropriate to re-open this issue but it looks like the maintainers for UDUNITS do not believe this is an upstream issue and recommend using the ut_compare() method as a possible solution. I don't have any experience with R packages that build on C libraries but I'm happy to try to figure it out if you can point me in the direction of a place to start.

edzer commented 1 year ago

Fantastic!