How to round up to the nearest 10 (or 100 or X)?

RRounding

R Problem Overview


I am writing a function to plot data. I would like to specify a nice round number for the y-axis max that is greater than the max of the dataset.

Specifically, I would like a function foo that performs the following:

foo(4) == 5
foo(6.1) == 10 #maybe 7 would be better
foo(30.1) == 40
foo(100.1) == 110 

I have gotten as far as

foo <- function(x) ceiling(max(x)/10)*10

for rounding to the nearest 10, but this does not work for arbitrary rounding intervals.

Is there a better way to do this in R?

R Solutions


Solution 1 - R

The plyr library has a function round_any that is pretty generic to do all kinds of rounding. For example

library(plyr)
round_any(132.1, 10)               # returns 130
round_any(132.1, 10, f = ceiling)  # returns 140
round_any(132.1, 5, f = ceiling)   # returns 135

Solution 2 - R

If you just want to round up to the nearest power of 10, then just define:

roundUp <- function(x) 10^ceiling(log10(x))

This actually also works when x is a vector:

> roundUp(c(0.0023, 3.99, 10, 1003))
[1] 1e-02 1e+01 1e+01 1e+04

..but if you want to round to a "nice" number, you first need to define what a "nice" number is. The following lets us define "nice" as a vector with nice base values from 1 to 10. The default is set to the even numbers plus 5.

roundUpNice <- function(x, nice=c(1,2,4,5,6,8,10)) {
    if(length(x) != 1) stop("'x' must be of length 1")
    10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]
}

The above doesn't work when x is a vector - too late in the evening right now :)

> roundUpNice(0.0322)
[1] 0.04
> roundUpNice(3.22)
[1] 4
> roundUpNice(32.2)
[1] 40
> roundUpNice(42.2)
[1] 50
> roundUpNice(422.2)
[1] 500

[[EDIT]]

If the question is how to round to a specified nearest value (like 10 or 100), then James answer seems most appropriate. My version lets you take any value and automatically round it to a reasonably "nice" value. Some other good choices of the "nice" vector above are: 1:10, c(1,5,10), seq(1, 10, 0.1)

If you have a range of values in your plot, for example [3996.225, 40001.893] then the automatic way should take into account both the size of the range and the magnitude of the numbers. And as noted by Hadley, the pretty() function might be what you want.

Solution 3 - R

The round function in R assigns special meaning to the digits parameter if it is negative.

> round(x, digits = 0) > > Rounding to a negative number of digits means rounding to a power of ten, so for example round(x, digits = -2) rounds to the nearest hundred.

This means a function like the following gets pretty close to what you are asking for.

foo <- function(x)
{
    round(x+5,-1)
}

The output looks like the following

foo(4)
[1] 10
foo(6.1)
[1] 10
foo(30.1)
[1] 40
foo(100.1)
[1] 110

Solution 4 - R

If you add a negative number to the digits-argument of round(), R will round it to the multiples of 10, 100 etc.

    round(9, digits = -1) 
    [1] 10    
    round(89, digits = -1) 
    [1] 90
    round(89, digits = -2) 
    [1] 100

Solution 5 - R

How about:

roundUp <- function(x,to=10)
{
  to*(x%/%to + as.logical(x%%to))
}

Which gives:

> roundUp(c(4,6.1,30.1,100.1))
[1]  10  10  40 110
> roundUp(4,5)
[1] 5
> roundUp(12,7)
[1] 14

Solution 6 - R

Round ANY number Up/Down to ANY interval

You can easily round numbers to a specific interval using the modulo operator %%.

The function:

round.choose <- function(x, roundTo, dir = 1) {
  if(dir == 1) {  ##ROUND UP
    x + (roundTo - x %% roundTo)
  } else {
    if(dir == 0) {  ##ROUND DOWN
      x - (x %% roundTo)
    }
  }
}

Examples:

> round.choose(17,5,1)   #round 17 UP to the next 5th
[1] 20
> round.choose(17,5,0)   #round 17 DOWN to the next 5th
[1] 15
> round.choose(17,2,1)   #round 17 UP to the next even number
[1] 18
> round.choose(17,2,0)   #round 17 DOWN to the next even number
[1] 16

How it works:

The modulo operator %% determines the remainder of dividing the first number by the 2nd. Adding or subtracting this interval to your number of interest can essentially 'round' the number to an interval of your choosing.

> 7 + (5 - 7 %% 5)       #round UP to the nearest 5
[1] 10
> 7 + (10 - 7 %% 10)     #round UP to the nearest 10
[1] 10
> 7 + (2 - 7 %% 2)       #round UP to the nearest even number
[1] 8
> 7 + (100 - 7 %% 100)   #round UP to the nearest 100
[1] 100
> 7 + (4 - 7 %% 4)       #round UP to the nearest interval of 4
[1] 8
> 7 + (4.5 - 7 %% 4.5)   #round UP to the nearest interval of 4.5
[1] 9

> 7 - (7 %% 5)           #round DOWN to the nearest 5
[1] 5
> 7 - (7 %% 10)          #round DOWN to the nearest 10
[1] 0
> 7 - (7 %% 2)           #round DOWN to the nearest even number
[1] 6

Update:

The convenient 2-argument version:

rounder <- function(x,y) {
  if(y >= 0) { x + (y - x %% y)}
  else { x - (x %% abs(y))}
}

Positive y values roundUp, while negative y values roundDown:

 # rounder(7, -4.5) = 4.5, while rounder(7, 4.5) = 9.

Or....

Function that automatically rounds UP or DOWN based on standard rounding rules:

Round <- function(x,y) {
  if((y - x %% y) <= x %% y) { x + (y - x %% y)}
  else { x - (x %% y)}
}

Automatically rounds up if the x value is > halfway between subsequent instances of the rounding value y:

# Round(1.3,1) = 1 while Round(1.6,1) = 2
# Round(1.024,0.05) = 1 while Round(1.03,0.05) = 1.05

Solution 7 - R

Regarding the rounding up to the multiplicity of an arbitrary number, e.g. 10, here is a simple alternative to James's answer.

It works for any real number being rounded up (from) and any real positive number rounded up to (to):

> RoundUp <- function(from,to) ceiling(from/to)*to

Example:

> RoundUp(-11,10)
[1] -10
> RoundUp(-0.1,10)
[1] 0
> RoundUp(0,10)
[1] 0
> RoundUp(8.9,10)
[1] 10
> RoundUp(135,10)
[1] 140

> RoundUp(from=c(1.3,2.4,5.6),to=1.1)  
[1] 2.2 3.3 6.6

Solution 8 - R

If you always want to round a number up to the nearest X, you can use the ceiling function:

#Round 354 up to the nearest 100:
> X=100
> ceiling(354/X)*X
[1] 400

#Round 47 up to the nearest 30:
> Y=30
> ceiling(47/Y)*Y
[1] 60

Similarly, if you always want to round down, use the floor function. If you want to simply round up or down to the nearest Z, use round instead.

> Z=5
> round(367.8/Z)*Z
[1] 370
> round(367.2/Z)*Z
[1] 365

Solution 9 - R

I think your code just works great with a small modification:

foo <- function(x, round=10) ceiling(max(x+10^-9)/round + 1/round)*round

And your examples run:

> foo(4, round=1) == 5
[1] TRUE
> foo(6.1) == 10 			#maybe 7 would be better
[1] TRUE
> foo(6.1, round=1) == 7 	# you got 7
[1] TRUE
> foo(30.1) == 40
[1] TRUE
> foo(100.1) == 110
[1] TRUE
> # ALL in one:
> foo(c(4, 6.1, 30.1, 100))
[1] 110
> foo(c(4, 6.1, 30.1, 100), round=10)
[1] 110
> foo(c(4, 6.1, 30.1, 100), round=2.3)
[1] 101.2

I altered your function in two way:

  • added second argument (for your specified X )
  • added a small value (=1e-09, feel free to modify!) to the max(x) if you want a bigger number

Solution 10 - R

You will find an upgraded version of Tommy's answer that takes into account several cases:

  • Choosing between lower or higher bound
  • Taking into account negative and zero values
  • two different nice scale in case you want the function to round differently small and big numbers. Example: 4 would be rounded at 0 while 400 would be rounded at 400.

Below the code :

round.up.nice <- function(x, lower_bound = TRUE, nice_small=c(0,5,10), nice_big=c(1,2,3,4,5,6,7,8,9,10)) {
  if (abs(x) > 100) {
    nice = nice_big
  } else {
    nice = nice_small
  }
  if (lower_bound == TRUE) {
    if (x > 0) {
      return(10^floor(log10(x)) * nice[[max(which(x >= 10^floor(log10(x)) * nice))[[1]]]])
    } else if (x < 0) {
      return(- 10^floor(log10(-x)) * nice[[min(which(-x <= 10^floor(log10(-x)) * nice))[[1]]]])
    } else {
      return(0)
    }
  } else {
    if (x > 0) {
      return(10^floor(log10(x)) * nice[[min(which(x <= 10^floor(log10(x)) * nice))[[1]]]])
    } else if (x < 0) {
      return(- 10^floor(log10(-x)) * nice[[max(which(-x >= 10^floor(log10(-x)) * nice))[[1]]]])
    } else {
      return(0)
    }
  }
}

Solution 11 - R

I tried this without using any external library or cryptic features and it works!

Hope it helps someone.

ceil <- function(val, multiple){
  div = val/multiple
  int_div = as.integer(div)
  return (int_div * multiple + ceiling(div - int_div) * multiple)
}

> ceil(2.1, 2.2)
[1] 2.2
> ceil(3, 2.2)
[1] 4.4
> ceil(5, 10)
[1] 10
> ceil(0, 10)
[1] 0

Solution 12 - R

This function rounds the number specified as the first argument up to the nearest integer multiple of the number specified as the second argument when the second argument is positive and down when the second argument is negative:

rom=function(x,y)x+(y-x%%y)%%y
rom(8.69,.1) # 8.7
rom(8.69,-.1) # 8.6
rom(8.69,.25) # 8.75
rom(8.69,-.25) # 8.5
rom(-8.69,.25) # -8.5

This rounds to the nearest multiple like round_any in plyr (<https://github.com/hadley/plyr/blob/34188a04f0e33c4115304cbcf40e5b1c7b85fedf/R/round-any.r>;):

rnm=function(x,y)round(x/y)*y
rnm(8.69,.25) # 8.75
plyr::round_any(8.69,.25) # 8.75

round_any can also be given ceiling as the third argument to always round numbers up or floor to always round numbers down:

plyr::round_any(8.51,.25,ceiling) # 8.75
plyr::round_any(8.69,.25,floor) # 8.5

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAbeView Question on Stackoverflow
Solution 1 - RRamnathView Answer on Stackoverflow
Solution 2 - RTommyView Answer on Stackoverflow
Solution 3 - RZeroView Answer on Stackoverflow
Solution 4 - RAnastasia PupyninaView Answer on Stackoverflow
Solution 5 - RJamesView Answer on Stackoverflow
Solution 6 - RtheforestecologistView Answer on Stackoverflow
Solution 7 - RhannaView Answer on Stackoverflow
Solution 8 - RSteve WalshView Answer on Stackoverflow
Solution 9 - RdaroczigView Answer on Stackoverflow
Solution 10 - RYohan ObadiaView Answer on Stackoverflow
Solution 11 - RRahul ChawlaView Answer on Stackoverflow
Solution 12 - RnisetamaView Answer on Stackoverflow