Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Formatting all columns using mutate_all in dplyr

I'm looking at formatting a table using scales::dollar within mutate_all.

Desired results

The desired results can be obtained with use of the sapply:

>> sapply(mtcars, scales::dollar)
      mpg      cyl  disp      hp     drat    wt      qsec     vs   am   gear carb
 [1,] "$21.00" "$6" "$160.00" "$110" "$3.90" "$2.62" "$16.46" "$0" "$1" "$4" "$4"
 [2,] "$21.00" "$6" "$160.00" "$110" "$3.90" "$2.88" "$17.02" "$0" "$1" "$4" "$4"
 [3,] "$22.80" "$4" "$108.00" "$93"  "$3.85" "$2.32" "$18.61" "$1" "$1" "$4" "$1"
 [4,] "$21.40" "$6" "$258.00" "$110" "$3.08" "$3.22" "$19.44" "$1" "$0" "$3" "$1"

Challenge

Attempt to achieve the same results via dplyr pipeline and scales::dollar:

mtcars %>% mutate_all(funs(scales::dollar(.)))

fails:

Error in vapply(dots[missing_names], function(x) make_name(x$expr), character(1)) : 
  values must be length 1,
 but FUN(X[[1]]) result is length 3

Further exploration

A primitive workaround can be attempted:

mtcars %>% mutate_each(funs(as.character(paste0("$", .))))

results:

>> mtcars %>% mutate_each(funs(as.character(paste0("$", .))))
     mpg cyl   disp   hp  drat     wt   qsec vs am gear carb
1    $21  $6   $160 $110  $3.9  $2.62 $16.46 $0 $1   $4   $4
2    $21  $6   $160 $110  $3.9 $2.875 $17.02 $0 $1   $4   $4
3  $22.8  $4   $108  $93 $3.85  $2.32 $18.61 $1 $1   $4   $1

Following a similar discussion, the approach could be easily developed further to create the desired currency format, but this is not the point.

Question

  • Why scales::dollar(.) fails when applied within mutate_all (or mutate_each)? When applied to vector elements it works as expected, shouldn't this behaviour be replicated along observations available in a column when passed within mutate_all / mutate_each:

    >> scales::dollar(c(1, 1e4)) 
    [1] "$1"      "$10,000"
    
like image 585
Konrad Avatar asked Mar 31 '17 16:03

Konrad


1 Answers

We need to wrap with as.character as it seems to be a bug when we use package::function in the funs. It is documented here

mtcars %>% 
     mutate_each(funs(as.character(scales::dollar(.))))

Besides, the function mutate_each is getting deprecated based on the blog link @Frank shared, so we can use mutate_at

mtcars %>% 
       mutate_at(names(.), funs(as.character(scales::dollar(.))))

The remedy would be to load the package and then call without the ::

library(scales)
mtcars %>% 
    mutate_at(names(.), funs(dollar(.)))

Or

mtcars %>% 
        mutate_at(names(.), dollar)
like image 71
akrun Avatar answered Nov 19 '22 12:11

akrun