RDiesel
RDiesel

Reputation: 21

R: Round to varying thresholds

I have a vector of numbers that I need to round according to the rules in the image below:

enter image description here

Consider the following examples:

0.5 -> 0.5 (no rounding)
1.2 -> 1.0
3.7 -> 4.0
18.9 -> 20.0
28.1 -> 30.0
110 -> 120

I could in theory write a series of conditional statements to achieve this task; however, it will be a tedious and inefficient thing to do. Is there a way to achieve the desired outcome in an efficient manner?

Thank you

Upvotes: 0

Views: 113

Answers (2)

Rui Barradas
Rui Barradas

Reputation: 76402

This solution uses findInterval to get which of the rounding functions is to be applied to the vector's elements.

roundSpecial <- function(x){
  round_funs <- list(
    no_round <- function(x) x,
    round_by_1 <- function(x) round(x),
    round_to_20 <- function(x) 20,
    round_by_10 <- function(x) 10*round(x / 10),
    round_by_15 <- function(x) 15*round(x / 15),
    round_by_30 <- function(x) 30*round(x / 30)
  )
  lims <- c(0, 1, 17, 20, 30, 90, Inf)
  which_fun <- findInterval(x, lims)
  sapply(seq_along(which_fun), function(i) {
    round_funs[[ which_fun[i] ]](x[i])
  })
}

roundSpecial(x)
#[1]   0.5   1.0   4.0  20.0  30.0 120.0

Data

x <- c(0.5, 1.2, 3.7, 18.9, 28.1, 110)

Upvotes: 1

jay.sf
jay.sf

Reputation: 72683

You could use the floor of base 10 logarithm to calculate powers of 10. Then divide the vector by that, round it and multiply with the powers of 10 again.

tens <- 10^floor(log10(abs(x)))
round(x/tens)*tens
# [1]   NaN   0.5   1.0   4.0  -4.0  20.0  30.0 100.0

Note, that this won't work for zero and you therefore should use case-handling.

(However, 110 -> 120 is not obvious to me.)


Data:

x <- c(0, .5, 1.2, 3.7, -3.7, 18.9, 28.1, 110)

Upvotes: 1

Related Questions