将多个函数传递给purrr:map

16

我想一次将多个需要参数的函数传递给purrr::map调用。伪代码如下:

funs <- c(median, mean)

mtcars %>% 
  purrr::map(funs, na.rm = TRUE) 

这段代码无法运行,但是其目的是展示我所需要的:将多个函数传递给map并搭配一些参数。

我看过compose,但那个函数做了不同的事情。

3个回答

18

您想使用map()函数对数据框应用多个函数,但是(显然)没有一个能够完全实现这个功能的map()变体,只有部分变体。对于多个函数的部分,我们有invoke_map(),对于在数据框上使用多个参数的部分,我们有pmap()。

invoke_map()允许一次使用多个函数。例如,如果我们要为均匀分布和正态分布生成5个随机变量,则代码如下:

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

pmap()与 map 类似,但它允许将多个参数传递给单个函数。例如,如果我们要从平均值为 0、标准差为 1 的正态分布中生成 10 个随机变量,以及从平均值为 100、标准差为 20 的正态分布中生成 100 个随机变量,则代码如下:

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

为了解决您的问题,我们必须以以下方式结合这两个函数:

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

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

这个怎么用?

  1. 在 invoke_map() 层面,fun 的参数是 param,它们是我们想要应用于 mtcars 的函数。

  2. 接下来,在 fun 层面,这些存储在 param 中的函数会被一个接一个地应用到 mtcars 中的每一列。

注意:要让解决方案真正有意义,请记住 invoke_map() 和 pmap() 接受的参数。

有关invoke_map()pmap()如何工作的更多信息。


1
谢谢,这很有帮助。虽然我希望解决方案更加简单明了一些。这段代码并不容易阅读。 - Sebastian Sauer
也许这段代码更清晰: mtcars %>% purrr::map_dfr(mosaic::favstats) - Sebastian Sauer

18

invoke()及其map变体已被弃用,建议使用rlang::exec()代替。根据文档:

这些函数已被弃用,建议使用exec()。它们不再处于活跃开发中,但我们将无限期地维护它们。

推荐使用简单的reexported rlang中的exec()函数来代替invoke()函数。exec()函数从输入构建一个函数调用并支持整洁点

因为理解起来比使用map()、map2()和exec()相应代码更复杂,所以invoke_map()已被废止且没有替代品

因此,现在的等效方法是:

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  

在参数列表中(args <- list(na.rm...)),所有这些参数都传递给所有的函数吗?换句话说,您的函数调用将如下所示:mean(mpg, na.rm = TRUE, trim = .1)median(mpg, na.rm = TRUE, trim = .1)median(cyl, na.rm = TRUE, trim = .1) ... - setty
是的,所有参数都传递给所有函数 - 可能不是最好的例子,因为 trim 不是 median() 的参数,但在这种情况下它被忽略了。 - Ritchie Sacramento
谢谢!有办法指定每个函数传递的参数吗? - setty
使用map2()代替map(),并同时迭代函数列表和参数列表。如果需要帮助,请随时发布新问题。 - Ritchie Sacramento

2
这是我的初步解决方案(具体取决于您对“同时”一词的理解):

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))

那是一种有效的方式,虽然我希望有更直接的方法。谢谢! - Sebastian Sauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接