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.
Specif
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:
=1e-09
, feel free to modify!) to the max(x)
if you want a bigger numberIf 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
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
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.
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
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