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

106,499

Solution 1

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 2

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 3

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

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

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
Share:
106,499

Related videos on Youtube

Abe
Author by

Abe

Updated on July 08, 2022

Comments

  • Abe
    Abe almost 2 years

    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?

    • joran
      joran almost 13 years
      The R default behavior when plotting is to set the plot limits ~4% beyond the range of the data in each direction. If this isn't satisfying to you maybe just write something that goes out by a higher or lower %?
    • Abe
      Abe almost 13 years
      @joran thanks for the info, but I want multiple plots that all have the same axis limits and ticks and I am not sure how this helps.
    • joran
      joran almost 13 years
      Well, I'm sort of groping in the dark here, since I don't know all the background. Your foo will round up to the nearest X if you just add another parameter X and replace both 10's with X. Or you could use faceting.
    • hadley
      hadley almost 13 years
      Are you looking for ?pretty ?
    • James
      James almost 13 years
      Why is foo(4)==5 and not 10?
    • Abe
      Abe almost 13 years
      @james because I would not want to make xlim = c(0,10) if max(x) = 4.
    • Abe
      Abe almost 13 years
      @hadley yes, I was apparently looking for pretty and if you post that as an answer, I will accept it. Your plyr::round_any also provides a very nice implementation of the idea in my original post
    • daroczig
      daroczig almost 13 years
      @Abe: I might be wrong, but most of the answers do not add an extra value to rounded input numbers - see your example: foo(4) == 5. Please clarify if this is really needed, as I made up my answer concentrating on this requirement.
    • Abe
      Abe almost 13 years
      @daroczig your answer works well and it nicely solves the central algebraic question that I thought I was facing. Still, from Tommy's answer roundUpNice(x=4, nice = c(1,5,10)) == 5 or from Ramnath's answer round_any(4,5) also provide the desired functionality of foo(4)==5. I think that the use of the nice argument in the roundUpNice function makes Tommy's answer the best; I am sorry if my question was poorly stated.
  • mbq
    mbq almost 13 years
    Vectorize(roundUpNice) is quite fast =) +1 Anyway.
  • James
    James almost 13 years
    @daroczig The question is a little confusing, I wrote this focusing on the "arbitrary X" requirement, but clearly all the expected values could not be produce by "a single round up to the nearest X" solution. It seems that the OP wants to produce values for an axis, so pretty is probably the best option.
  • helen.h
    helen.h about 8 years
    I was wondering if there is a way to edit the roundUp function to half the end result if the original value is lower? for example a number between 101-500 would roundup to 500 and numbers 501-999 would roundUp to 1000? instead of all rounding to 1000?
  • Tommy
    Tommy almost 8 years
    Just change the nice vector: roundUpNice(501, nice=c(5, 10)) # 1000
  • Yohan Obadia
    Yohan Obadia over 7 years
    Just a warning since you work with logarithms in your function, I would had 2 cases, one where x < 0 and apply - x in the log before putting the - back. I would also add an exception for the situation where x = 0
  • jessi
    jessi about 7 years
    The default output of this is a round down: > round.up.nice(.01) [1] 0 > round.up.nice(4.5) [1] 0 > round.up.nice(56) [1] 50
  • jessi
    jessi about 7 years
    I think part of the problem is that nice_big and nice_small are defined backwards, (if we flip them in the function, round.up.nice(4.5) becomes 4) but it still rounds down.
  • Alessandro Jacopson
    Alessandro Jacopson almost 7 years
    Awesome! Your answer should be marked as the right one!
  • theforestecologist
    theforestecologist over 5 years
    I was asked how to convert Round to VBA in Excel: Function ROUND(x,y) 'Function that automatically rounds UP or DOWN based on standard rounding rules. 'Automatically rounds up if the "x" value is > halfway between subsequent instances of the rounding value "y": If (y - (Evaluate("Mod(" & x & "," & y & ")"))) <= (Evaluate("Mod(" & x & "," & y & ")")) Then Ans = x + (y - (Evaluate("Mod(" & x & "," & y & ")"))) Else Ans = x - (Evaluate("Mod(" & x & "," & y & ")")) End If ROUND = Ans End Function
  • theforestecologist
    theforestecologist over 5 years
    +1 . However, @Alessandro the functions in my answer are much more versatile: you can round ANY number up OR down to ANY interval.
  • Alessandro Jacopson
    Alessandro Jacopson over 5 years
    @theforestecologist thank you for the hint! As a personal taste, I usually prefer a language built-in solution rather than a custom one.
  • slhck
    slhck about 5 years
    For a dplyr replacement, see: stackoverflow.com/a/46489816/435093
  • jared
    jared over 4 years
    Obviously late to this party, but wouldn't to * ceiling(x / to) be cleaner?
  • Daniel Noworyta
    Daniel Noworyta about 4 years
    The pretty function worked really well for my needs (to set a pretty limit on a graph's X axis)
  • Bernardo
    Bernardo over 3 years
    Great approach. I'd suppress the length condition and wrap it up with sapply so you can actually pass a vector: roundUpNice <- function(x, nice = c(1,2,4,5,6,8,10)) { sapply(x, function (x) 10^floor(log10(x)) * nice[[which(x <= 10^floor(log10(x)) * nice)[[1]]]]) }
  • Pake
    Pake about 3 years
    Great answer here. I combined the functions here together and came up with: round_well <- function(x, roundTo, dir = NULL) { if(is.null(dir)){ if((roundTo - x %% roundTo) <= x %% roundTo) { x + (roundTo - x %% roundTo)} else { x - (x %% roundTo)}} else if(dir == "Up") { ##ROUND UP x + (roundTo - x %% roundTo) } else if (dir == "Down") { ##ROUND DOWN x - (x %% roundTo) } else {print("Options are Up, Down, and blank.")} } Defaults to normal rounding rules to whatever number to pick, but you can pass "Up" or "Down" to specify direction