Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

number(accuracy = NULL) heuristic can fail #251

Closed
davidchall opened this issue Feb 3, 2020 · 1 comment
Closed

number(accuracy = NULL) heuristic can fail #251

davidchall opened this issue Feb 3, 2020 · 1 comment
Labels
bug an unexpected problem or unintended behavior

Comments

@davidchall
Copy link
Contributor

The label_number family of functions have a default value of accuracy = NULL. According to the documentation, this:

uses a heuristic that should ensure breaks have the minimum number of digits needed to show the difference between adjacent values.

Unfortunately, it seems this heuristic can fail under certain conditions. Here's an example:

> scales::comma(c(Inf, 1, Inf, 0))
Error in if (smallest_diff < sqrt(.Machine$double.eps)) { : 
  missing value where TRUE/FALSE needed

It looks like the problem stems from the underlying scales::number function:

> scales::number(c(Inf, 1, Inf, 0))
Error in if (smallest_diff < sqrt(.Machine$double.eps)) { : 
  missing value where TRUE/FALSE needed
@teunbrand
Copy link
Contributor

I think part of the problem might be duplicate values, which is not checked for in the scales:::precision() internal function.

library(scales)
#> Warning: package 'scales' was built under R version 3.6.2
number(c(1, 2, 3))
#> [1] "1.0" "2.0" "3.0"
number(c(1, 2, 2, 3))
#> [1] "1" "2" "2" "3"

Created on 2020-02-16 by the reprex package (v0.3.0)

It seems like deduplicating the x * scale input before passing it to the scales:::precision function alleviates some errors:

library(scales)
#> Warning: package 'scales' was built under R version 3.6.2
library(rlang) # for the %||% operator


number2 <- function (x, accuracy = NULL, scale = 1, prefix = "", suffix = "", 
                     big.mark = " ", decimal.mark = ".", trim = TRUE, 
                     ...) 
{
  if (length(x) == 0) 
    return(character())
  # Deduplicate x before passing to precision
  accuracy <- accuracy %||% scales:::precision((x * scale)[!duplicated(x)])
  x <- scales:::round_any(x, accuracy/scale)
  nsmall <- -floor(log10(accuracy))
  nsmall <- min(max(nsmall, 0), 20)
  ret <- format(scale * x, big.mark = big.mark, decimal.mark = decimal.mark, 
                trim = trim, nsmall = nsmall, scientific = FALSE, ...)
  ret <- paste0(prefix, ret, suffix)
  ret[is.infinite(x)] <- as.character(x[is.infinite(x)])
  ret[is.na(x)] <- NA
  names(ret) <- names(x)
  ret
}

# Some examples
ex1 <- c(1, 2, 3)
ex2 <- c(1, 2, 2, 3)
ex3 <- c(Inf, 1, Inf, 0)

number2(ex1)
#> [1] "1.0" "2.0" "3.0"
number2(ex2)
#> [1] "1.0" "2.0" "2.0" "3.0"
number2(ex3)
#> [1] "Inf" "1.0" "Inf" "0.0"

Created on 2020-02-16 by the reprex package (v0.3.0)

But I don't know whether you would want the -.0 if the input are rounded numbers, but that might a different issue altogether.

This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug an unexpected problem or unintended behavior
Projects
None yet
Development

No branches or pull requests

3 participants