3

While working with dplyr pipeline, I would like to pass a function to mutate using NSE with the function name being passed from a vector.

Example

Given vector of two function names:

funs <- c("sum", "mean")

I would like to use first value to obtain sum:

require(dplyr)
mtcars %>% 
  group_by(cyl) %>% 
  mutate_(res = funs[1](hp))

This results in an error:

Error in as.lazy_dots(list(...)) : attempt to apply non-function

do.call

do.call based solution seems to generate some results for sum:

mtcars %>% 
  group_by(cyl) %>% 
  mutate_(res = do.call(funs[1], .))

but it fails when trying to use mean:

>> mtcars %>% 
+   group_by(cyl) %>% 
+   mutate_(res = do.call(funs[2], .))
Error in mean.default(mpg = c(21, 21, 22.8, 21.4, 18.7, 18.1, 14.3, 24.4,  : 
  argument "x" is missing, with no default

I'm guessing that it simply makes no sense in the way it is applied here. Hence my question: how to use in dplyr so the function can be passed as a string from a vector?

Konrad
  • 17,740
  • 16
  • 106
  • 167

2 Answers2

3

We can use get and get extracts the values of a single string. Here, it is a function, so it returns the function itself.

mtcars %>% 
     group_by(cyl) %>% 
     mutate(res= get(funs[1])(hp))

For passing additional arguments

mtcars$hp[1] <- NA
mtcars %>%
      group_by(cyl) %>% 
      mutate(res= get(funs[1])(hp, na.rm = TRUE))
akrun
  • 874,273
  • 37
  • 540
  • 662
  • 1
    As always, this is super useful. Thanks; would you care to add a brief explanation? Also, how would I pass arguments to this function, like `na.rm = TRUE`? – Konrad Dec 02 '16 at 15:34
  • @Konrad I updated the post. I didn't see your edit in your comment earlier – akrun Dec 02 '16 at 15:59
  • 1
    Just used `get()` to get around nse to pass a function name to `by_row` using a string in `purrr` (actually `purrrlyr`). This is an under-documented solution and VERY helpful!! (Somebody, e.g., me, ought to submit a push request/documentation update). – D. Woods Feb 18 '18 at 15:47
  • @D.Woods Another option would be `mtcars %>% group_by(cyl) %>% mutate(res = eval(call(funs[1], hp)))` – akrun Feb 18 '18 at 16:10
2

These both work using mutate rather than mutate_

mtcars %>% 
  group_by(cyl) %>% 
  mutate(res = do.call(funs[2], list(hp)))

mtcars %>% 
  group_by(cyl) %>% 
  mutate(res = match.fun(funs[2])(hp))

Also note that if we use [[2]] in place of [2] then these will work with both the character vector, funs, that appears in the question as well as funs <- c(sum, mean) .

G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341