# r - Custom round number function

I am trying to create a function (`my.func`) that could round a number/numeric vector/matrix/data.frame conditionaly to its value.

For example: `my.func(1630.123)` will give 1630 and `my.func(0.123)` will return 0.12

I have tried:

``my.func <- function(x0, ...) {unlist(lapply(x0, function(x) {if(x >= 100) return(round(x, 0))if(x >= 10 & x < 100) return(round(x, 1))if(x >= 0.1 & x < 10) return(round(x, 2))if(x >= 0.01 & x < 0.1) return(round(x, 3))if(x >= 0.001 & x < 0.01) return(round(x, 4))if(x >= 0.0001 & x < 0.001) return(round(x, 5))if(x >= 0.00001 & x < 0.0001) return(round(x, 6))if(x >= 0.000001 & x < 0.00001) return(round(x, 7))if(x >= 0.0000001 & x < 0.000001) return(round(x, 8))if(x < 0.0000001) return(x)}))}``

Works good for number and a vector but not for matrix/data.frame.

I could put `if`'s and loop for matrix/df but probably there is another way to do this, much faster and easier

Another example:

`my.func(c(1.314315, 125135.1234, 0.0124133, .00000234))` expect to return 1.31, 125135, 0.012, 0.0000023

You probably want `signif` which rounds off to a given number of significant digits.

``````x <- runif(16) * 10^(7*runif(16)-5)
cbind(x, mx = my.func(x), sx = signif(x, 3))
``````
``````                 x       mx       sx
[1,] 1.395044e-01 1.40e-01 1.40e-01
[2,] 9.751368e-06 9.80e-06 9.75e-06
[3,] 3.451619e-04 3.50e-04 3.45e-04
[4,] 2.203204e-03 2.20e-03 2.20e-03
[5,] 6.660684e-05 6.70e-05 6.66e-05
[6,] 3.143732e-02 3.10e-02 3.14e-02
[7,] 1.976514e-05 2.00e-05 1.98e-05
[8,] 3.747693e+00 3.75e+00 3.75e+00
[9,] 4.099091e-03 4.10e-03 4.10e-03
[10,] 3.124711e-02 3.10e-02 3.12e-02
[11,] 3.162478e-04 3.20e-04 3.16e-04
[12,] 1.029170e-05 1.00e-05 1.03e-05
[13,] 6.746715e-04 6.70e-04 6.75e-04
[14,] 7.667078e-03 7.70e-03 7.67e-03
[15,] 2.002506e-03 2.00e-03 2.00e-03
[16,] 2.164340e-03 2.20e-03 2.16e-03
``````

Or is there a special reason for wanting an extra digit above 0? I.e. why don't you want

``````if(x >= 10 & x < 100) return(round(x, 1))
if(x >= 1 & x < 10) return(round(x, 2))
if(x >= 0.1 & x < 1) return(round(x, ?))   # <----- This line
if(x >= 0.01 & x < 0.1) return(round(x, 3))
if(x >= 0.001 & x < 0.01) return(round(x, 4))
``````

### Update

If you just want a function that output numbers that are nice to read I suggest you round off the digits with `signif`, print with `sprintf`, and trim off tailing zeros and decimal separators with `sub`.

``````sub("\\.\$", "", sub(".0+\$", "", sprintf("%f", signif(x, 3))))
``````
``````  "0.000226" "3.8"      "363"      "1380"     "0.0016"   "0.000331"
 "0.000047" "0.20"     "0.047"    "105"      "22"       "0.000013"
 "0.000013" "0.054"    "2.6"      "0.31"
``````

Another function that might be of interest in `formatC`, but you'd have to bear with the scientific notation for very large or small values then.

``````formatC(x, digits=3)
``````
``````  "0.000226" "3.82"     " 363"     "1.38e+03" "0.00163"  "0.000331"
 "4.68e-05" "0.208"    "0.0479"   " 105"     "22.6"     "1.25e-05"
 "1.25e-05" "0.0542"   "2.69"     "0.311"
``````

it does not work on a data.frame because R cannot round different rows with different digits. You therefore have to select a number in the variable that you want to apply your function on, e.g. the maximum, like so:

``````myfun = function(val) {

cutvec = 10^(-7:2)
cutvec = cutvec[!cutvec == 1]
rounding = 8:0

find.round = rounding[max(which(val > cutvec))]

return(find.round)
}

data(mtcars)
digits = myfun(max(mtcars\$disp))
round(mtcars\$disp, digits)
``````