Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rounding a vector so that all resulting elements are different

Tags:

rounding

r

vector

I'm looking for a routine that would round a vector by the "necessary" number of digits so that all elements are still distinguishable. My first attempt looks like this:

discr.round <- function(x) {
  digits <- ceiling(-min(log10(diff(sort(x)))))
  round(x, digits)
}

discr.round(c(12.336, 12.344))
# [1] 12.336 12.344
discr.round(c(12.336, 12.347))
# [1] 12.34 12.35
discr.round(c(10, 25, 39))
# [1] 10 20 40
discr.round(c(1.2345679, 1.2345681))
# [1] 1.234568 1.234568
discr.round(c(1.23456789, 1.23456791))
# [1] 1.234568 1.234568

I need this to format a vector of floating-point numbers using "few" decimal places.

How would this kind of rounding be called? Is there a "standard" implementation of this or a similar logic, in base R or a package?

EDIT: I need this for pairs, but the code should generalize to vectors of arbitrary length. If the numbers are exactly equal, they do not need to be discriminated.

like image 962
krlmlr Avatar asked Nov 11 '22 12:11

krlmlr


1 Answers

options(digits=12) # Part of the problem is that your default digits = 7,
                   #   so you won't see any digits beyond that.
                   #  If you put too many (e.g, 22), you'll get floating point imprecision

#  Note the behavior for rounding off a 5 under ?round:
#  "IEC 60559 standard is expected to be used, ‘go to the even digit’."

# digits needed to distinguish between two nonequal elements
diff.dec.places <- function(x, y){
  delta <- abs(x - y)
  dec.place <- -ceiling(log10(abs(delta))) # your idea here was correct
  print(paste0("The elements (",x," & ",y,") differ in the ",
               10^-dec.place,"'s place."))
  if(round(x, dec.place)==round(y, dec.place)){
    print("But we add another sig. figure so they do not round to the same number.")
    dec.place <- dec.place + 1
  } 
  print(paste("The elements will round to",round(x, dec.place),'&',round(y, dec.place)))
  dec.place
}


# formula for arbitrary number of elements:
discr.round <- function(x){

  #- Find minimum-magnitude difference and which elements possess it: 
  #-   Create upper triangle of difference matrix of elements of vector with itself
  d <- abs(outer(x,x,"-"))
  d[lower.tri(d, diag=T)] <- NA
  #-   Return the smallest-magnitude difference and indices of elements forming it
  m <- min(d, na.rm=T)     
  if(m != 0){
    #- Round to number of dec places required to distinguish the closest elements
    e <- x[which(d==m, arr.ind=T)]
    round(x, diff.dec.places(e[1],e[2]))
  }
  else{
    print("Closest elements are equal.") 
    x
  }
}


discr.round(c(12.336, 12.344))
# [1] 12.336 12.344
discr.round(c(12.336, 12.347))
# [1] 12.34 12.35
discr.round(c(10, 25, 39))
# [1] 10 20 40
discr.round(c(1.2345679, 1.2345681))
# [1] 1.2345679 1.2345681
discr.round(c(1.23456789, 1.23456791))
# [1] 1.23456789 1.23456791
like image 136
C8H10N4O2 Avatar answered Nov 15 '22 07:11

C8H10N4O2