Pass multiple functions to purrr:map

You want to apply multiple functions to a dataframe with map(), but (apparently) there is no map() variation that does exactly this, only parts of it. For the multiple function part we have invoke_map() and for the multiple argument part over a dataframe we have pmap().

invoke_map() allows the use of multiple functions at once. For example, if we want to generate 5 random variates for a uniform and normal distributions, the code is:

func <- list(runif, rnorm) 
invoke_map(func, n = 5)

pmap() is just like map, but it allows to pass multiple arguments to a single function. For example, if we want to generate 10 random variates from a normal distribution with mean = 0 and sd = 1, but also 100 random variates from a normal distribution with mean = 100 and sd = 20, the code looks like this:

args <- list(mean = c(0, 100), sd = c(1, 20), n = c(10, 100))
pmap(args, rnorm)

To solve your question, we have to combine both functions in the following way:

fun <- function(f) pmap(list(x = mtcars, na.rm = TRUE), f)
param <- list(list(mean), list(median))

invoke_map(.f = fun, .x = param)

How does this work?

  1. At the invoke_map() level, fun takes as arguments param, which are the functions we want to apply to mtcars.

  2. Next, at the fun level, these functions stored in param are applied by pmap(), one at a time, to each column in mtcars.

Note: For the solution to really make sense, keep in mind the arguments invoke_map() and pmap() take.

More info about how invoke_map() and pmap() work.


invoke() and its map variants have been retired in favour of rlang::exec(). From the documentation:

These functions are retired in favour of exec(). They are no longer under active development but we will maintain them in the package indefinitely.

invoke() is retired in favour of the simpler exec() function reexported from rlang. exec() evaluates a function call built from its inputs and supports tidy dots

invoke_map() is retired without replacement because it is more complex to understand than the corresponding code using map(), map2() and exec()

So the equivalent method now is:

library(dplyr)
library(purrr)

funs <- c(mean = mean, median = median)
args <- list(na.rm = TRUE, trim = .1) # trim argument non-matching and ignored for median

mtcars %>%
  map_df(~ funs %>%
           map(exec, .x, !!!args), .id = "var")

# A tibble: 11 x 3
   var      mean median
   <chr>   <dbl>  <dbl>
 1 mpg    19.7    19.2 
 2 cyl     6.23    6   
 3 disp  223.    196.  
 4 hp    141.    123   
 5 drat    3.58    3.70
 6 wt      3.15    3.32
 7 qsec   17.8    17.7 
 8 vs      0.423   0   
 9 am      0.385   0   
10 gear    3.62    4   
11 carb    2.65    2  

Here is my baby-steps solution (depends what you mean by "at once"):

mtcars %>% 
  map_dbl(~{mean(.x, na.rm = TRUE)}) %>% 
  enframe() %>%
  rename(mean = value) %>%
  as_tibble %>%
  left_join(mtcars %>% 
              map_dbl(~{median(.x, na.rm = TRUE)}) %>% 
              enframe() %>% 
              as_tibble %>%
              rename(median = value))

Tags:

R

Tidyverse

Purrr