LennyB51
LennyB51

Reputation: 21

How to format from 7 to 2 decimal places

I am exploring the tidyverse package.

I am okay with the below code:

library(dslabs)
data("murders")

murders <- mutate(murders, rate = total / population * 100000)
murders

filter(murders, rate <= 0.71)

The pipe - Using the pipe the code is as below:

murders %>% select(state, region, rate) %>% filter(rate <= 0.71)

How can I format the rate from the current 7 decimal places to 2?

Upvotes: 0

Views: 273

Answers (1)

Ben Norris
Ben Norris

Reputation: 5747

I presume that your desire is to just print these data with two decimal places and not to actually round them? If you do wish to round the data, that is simple:

Rounding

library(dplyr)
library(dslabs)
data("murders")

murders %>% 
  select(state, region, rate) %>% 
  filter(rate <= 0.71) %>% 
  mutate(rate = round(rate, 2))

          state        region rate
1        Hawaii          West 0.51
2          Iowa North Central 0.69
3 New Hampshire     Northeast 0.38
4  North Dakota North Central 0.59
5       Vermont     Northeast 0.32

tibble workaround

If you do not wish to round the data, but you wish to only print two digits, you can do this by converting your data to a tibble and setting the pillar.sigfig option. Note that options(pillar.sigfig = 2) prints two significant digits, not decimal places explicitly. So if your data had a value of 1.23345 it would print 1.2. However, your rates are all less than one, so this should function as a workaround. You can control decimal places precisely using the vctrs package, and that approach is below.

library(dplyr)
library(tibble)
library(dslabs)
data("murders")
options(pillar.sigfig = 2)

murders2 <- murders %>% 
  select(state, region, rate) %>% 
  filter(rate <= 0.71) %>% 
  tibble()
murders2

# A tibble: 5 x 3
  state         region         rate
  <chr>         <fct>         <dbl>
1 Hawaii        West           0.51
2 Iowa          North Central  0.69
3 New Hampshire Northeast      0.38
4 North Dakota  North Central  0.59
5 Vermont       Northeast      0.32

An important distinction about the second approach is that the underlying values are not changed. If you grab just on value from the tibble, you get the full number of digits back:

murders2$rate[3]
[1] 0.3798036

Setting options(pillar.sigfig = ) changes a global option for the printing of all tibbles. To resume normal behavior, set options(pillar.sigfig = NULL).

Note, setting a similar option for decimal places and not significant figures has been a feature request for the tibble package (https://github.com/tidyverse/tibble/issues/772), but seems unlikely to be implemented at this time.

A better solution using vctrs

There is hope if your data is more complex! The vctrs package allows you do define new vector classes with various behaviors (including printing a certain number of decimal places). In fact, setting decimal places is one of the two use case examples in vignette("s3-vector"). Run the code from the vignette (reproduced below) to create the new class, then mutate your column.

library(vctrs)
new_decimal <- function(x = double(), digits = 2L) {
  vec_assert(x, ptype = double())
  vec_assert(digits, ptype = integer(), size = 1)

  new_vctr(x, digits = digits, class = "vctrs_decimal")
}

decimal <- function(x = double(), digits = 2L) {
  x <- vec_cast(x, double())
  digits <- vec_recycle(vec_cast(digits, integer()), 1L)

  new_decimal(x, digits = digits)
}

digits <- function(x) attr(x, "digits")

format.vctrs_decimal <- function(x, ...) {
  sprintf(paste0("%-0.", digits(x), "f"), x)
}

vec_ptype_abbr.vctrs_decimal <- function(x, ...) {
  "dec"
}

murders3 <- murders %>% 
  select(state, region, rate) %>% 
  filter(rate <= 0.71) %>% 
  mutate(rate = decimal(rate, 2))
murders3

          state        region rate
1        Hawaii          West 0.51
2          Iowa North Central 0.69
3 New Hampshire     Northeast 0.38
4  North Dakota North Central 0.59
5       Vermont     Northeast 0.32

This benefit of this vctrs approach is that it handles any value and can be done without converting to a tibble if you do not want to.

decimal(1.2345, 2)

<vctrs_decimal[1]>
[1] 1.23

Again, the full value is preserved, though you need to remove the class or change the digits attribute to see more decimal places:

unclass(num) 
  
[1] 1.2345
attr(,"digits")
[1] 2

attributes(num)$digits <- 4
num

<vctrs_decimal[1]>
[1] 1.2345

Upvotes: 1

Related Questions