How to round up to the nearest 10 (or 100 or X)?
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
Related videos on Youtube
Abe
Updated on July 08, 2022Comments
-
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 almost 13 yearsThe 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 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 almost 13 yearsWell, 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 almost 13 yearsAre you looking for
?pretty
? -
James almost 13 yearsWhy is
foo(4)==5
and not10
? -
Abe almost 13 years@james because I would not want to make xlim = c(0,10) if max(x) = 4.
-
Abe almost 13 years@hadley yes, I was apparently looking for
pretty
and if you post that as an answer, I will accept it. Yourplyr::round_any
also provides a very nice implementation of the idea in my original post -
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 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 answerround_any(4,5)
also provide the desired functionality offoo(4)==5
. I think that the use of thenice
argument in theroundUpNice
function makes Tommy's answer the best; I am sorry if my question was poorly stated.
-
-
mbq almost 13 years
Vectorize(roundUpNice)
is quite fast =) +1 Anyway. -
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 about 8 yearsI 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 almost 8 yearsJust change the nice vector:
roundUpNice(501, nice=c(5, 10)) # 1000
-
Yohan Obadia over 7 yearsJust 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 wherex = 0
-
jessi about 7 yearsThe 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 about 7 yearsI think part of the problem is that
nice_big
andnice_small
are defined backwards, (if we flip them in the function,round.up.nice(4.5)
becomes4
) but it still rounds down. -
Alessandro Jacopson almost 7 yearsAwesome! Your answer should be marked as the right one!
-
theforestecologist over 5 yearsI 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 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 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 about 5 yearsFor a
dplyr
replacement, see: stackoverflow.com/a/46489816/435093 -
jared over 4 yearsObviously late to this party, but wouldn't
to * ceiling(x / to)
be cleaner? -
Daniel Noworyta about 4 yearsThe pretty function worked really well for my needs (to set a pretty limit on a graph's X axis)
-
Bernardo over 3 yearsGreat approach. I'd suppress the
length
condition and wrap it up withsapply
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 about 3 yearsGreat 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