Rounding significant figures in R

Nick Siemons picture Nick Siemons · Mar 22, 2017 · Viewed 9.1k times · Source

So basically I am doing a physics experiment, and in my table I want to have my data rounded to the same accuracy as the error is, which is rounded to 1 sig fig.

So for example if I have the following:

angle <- c(4, 4.1, 4.2)
angle.error <- c(0.024, 0.3, 0.113)
data <- data.frame(angle,angle.error)

want I want to end up with is the angle error rounded to 1 sig fig, and the angle rounded to the corresponding number of decimal places as the angle error, to give

angle <- c(4.00, 4.1, 4.2)
angle.error <- c(0.02,0.3,0.1)
data <- data.frame(angle, angle.error)

Hope this makes sense!! This is the standard way we are taught to present data, and as such I am surprised how difficult I am finding it to find a proper way of doing this. Any contributions would be great!!

EDIT:

If I want to turn this into a UDR where say the data is in column 5 and the errors in column 6 of my data frame, I write:

Conv2 <- function(x) {
x[6] <- signif(x[6],1)
exp <- floor(log10(x[6]))
man <- x[6]/10^exp         
x[5] <- round(x[5], -exp)   # Error associated with this line I think
sapply(seq_along(x[5]), function(i) format(x[i,5], nsmall=-exp[i]))

return(x)
}

When I implement it I get the error 'Error in FUN(X[[i]], ...) : non-numeric argument to mathematical function'

Answer

takje picture takje · Mar 22, 2017

This can be done with signif(). In contrast to round(), which rounds to a number of decimal places, signif() rounds to a specific number of significant places.

x <- c(1,1.0001,0.00001)
round(x,1) # rounds to the specified number of decimal places
signif(x,1) # rounds to the specified number of significant digits

Let's apply this to your case. Note that signif is vectorized over both arguments.

> signif(c(1.111,2.222,3.333),c(1,2,3))
[1] 1.00 2.20 3.33

This would mean the following for your case:

angle.error <- c(0.024, 0.3, 0.113)
cor <- signif(angle.error,1)

Rounding the angles is slightly more difficult. We need to round angle to the same number of decimal places as angle.error_corr. Therefore we will extract the mantissa and exponent of this number.

exp <- floor(log10(cor)) 
man <- cor/10^exp

Now it is possible to use the exponents to round your angles.

angle <- c(4, 4.1, 4.2)
angle_cor <- round(angle, -exp)

However, this does not give the intended result since R shows the number of digits of the most precise double and drops trailing zeros. We can solve this with the nsmall argument of format, which is unfortunately not vectorized.

sapply(seq_along(angle), function(i) format(angle[i], nsmall = -exp[i]))

Which should give you the answer you were looking for.