Rounding Numbers in R to Specified Number of Digits

Rounding numbers in R to specified number of digits

For fine control over formatting of numbers, try formatC()

res <- structure(c(9.50863385275955e-05, 4.05702267762077e-06), 
.Names = c("CC/CC", "TT/CC"))

formatC(res, format="f", digits=8)

`round()` to many digits in R

Actually, round IS working correctly:

a <- round(x = 234234.3456789, digits = 7)
paste(a)
[1] "234234.3456789"

In your example, print.default, which is what happens when you ask R to print something is cutting the number of digits.

print.default(a, digits = 15)
[1] 234234.3456789

You can control the behavoiour via options

options(digits = 15)
round(pi, digits = 15)
[1] 3.14159265358979

Different rounding rule based on number of digits before the decimal

It is not clear whether the question is about pretty printing (result is character) or rounding (result is numeric).

If the question is about conversion to character for pretty printing, the sprintf() function can be used.

The sprintf() function understands the conversion specifier g where the number of significant digits can be specified. However, it does not cover the whole range of values. Therefore, different conversion specifiers have to be used depending on the size of y:

y <- c(0.0111, 0.111, 1.11, 11.1, 111.1, 1111, 11111)
sprintf(c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))], y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"

If the question is about rounding numeric values:

round(y, c(2, 1, 0)[cut(y, c(0, 10, 100, Inf))])
[1]     0.01     0.11     1.11    11.10   111.00  1111.00 11111.00

Explanation

c("%3.2f", "%3.3g", "%3.0f")[cut(y, c(0, 1, 1000, Inf))]

is a replacement for nested ifelse() in order to pick the right conversion specifier for each y:

[1] "%3.2f" "%3.2f" "%3.3g" "%3.3g" "%3.3g" "%3.0f" "%3.0f"

cut() converts from numeric to factor. It divides the range of y into intervals and codes the values in y according to which interval they fall. The leftmost interval corresponds to level one, the next leftmost to level two and so on. (from help("cut")).

Then, the level number is used to subset the vector of conversion specifiers to pick the appropriate specifier according to the value of y.

While writing the explanation, there is a slightly different approach. Instead of using the level numbers to subset the vector of conversion specifiers, we can use the labels of the levels directly:

sprintf(as.character(cut(y, c(0, 1, 1000, Inf), labels = c("%3.2f", "%3.3g", "%3.0f"))), y)
[1] "0.01"  "0.11"  "1.11"  "11.1"  "111"   "1111"  "11111"

How to round to specific decimal numbers in R

In R there are two usefull operator for this %% and %/% which can be used to get the integer part and the decimal part of a divition. If you have this, and you divide by 1 yo can do it easy.

set.seed(123)

x <- rnorm(10, mean = 300)

ifelse(x %% 1 > 0.5,
x %/%1 + 0.75,
x %/%1 + 0.25)

the vector went from this

[1] 299.4395 299.7698 301.5587 300.0705 300.1293 301.7151 300.4609 298.7349 299.3131 299.5543

to this

 [1] 299.25 299.75 301.75 300.25 300.25 301.75 300.25 298.75 299.25 299.75

Formatting Decimal places in R

Background: Some answers suggested on this page (e.g., signif, options(digits=...)) do not guarantee that a certain number of decimals are displayed for an arbitrary number. I presume this is a design feature in R whereby good scientific practice involves showing a certain number of digits based on principles of "significant figures". However, in many domains (e.g., APA style, business reports) formatting requirements dictate that a certain number of decimal places are displayed. This is often done for consistency and standardisation purposes rather than being concerned with significant figures.

Solution:

The following code shows exactly two decimal places for the number x.

format(round(x, 2), nsmall = 2)

For example:

format(round(1.20, 2), nsmall = 2)
# [1] "1.20"
format(round(1, 2), nsmall = 2)
# [1] "1.00"
format(round(1.1234, 2), nsmall = 2)
# [1] "1.12"

A more general function is as follows where x is the number and k is the number of decimals to show. trimws removes any leading white space which can be useful if you have a vector of numbers.

specify_decimal <- function(x, k) trimws(format(round(x, k), nsmall=k))

E.g.,

specify_decimal(1234, 5)
# [1] "1234.00000"
specify_decimal(0.1234, 5)
# [1] "0.12340"

Discussion of alternatives:

The formatC answers and sprintf answers work fairly well. But they will show negative zeros in some cases which may be unwanted. I.e.,

formatC(c(-0.001), digits = 2, format = "f")
# [1] "-0.00"
sprintf(-0.001, fmt = '%#.2f')
# [1] "-0.00"

One possible workaround to this is as follows:

formatC(as.numeric(as.character(round(-.001, 2))), digits = 2, format = "f")
# [1] "0.00"

Always round down to specified number of significant digits in R

Here's a custom function to do it

down_signif <- function(x, digits = 0) {
m <- 10^(ceiling(log(x, 10)) - digits)
(x %/% m)*m
}
down_signif(3599, digits = 2)
#> [1] 3500
down_signif(7890349, digits = 2)
#> [1] 7800000

Round depending on number of decimal places

You can just count the digits after the dot and round to that value-1, i.e.

x <- c(4.5, 2.12, 3, 5.245)
i1 <- nchar(sub('.*\\.', '', x))

round(x, (i1-1))
#[1] 4.00 2.10 3.00 5.25


Related Topics



Leave a reply



Submit