使用purrr将函数映射到嵌套列表的第二级

twg*_*er2 7 functional-programming nested r purrr

我正在努力更好地理解R中的函数式编程.我想坚持下去purrr,但我将用它rapply来演示我正在寻找的内容.首先,我想要了解的一个简单示例:

您可以使用map获取mtcars数据集的每列的平均值:

library(tidyverse)
mtcars %>% map_dbl(mean)

   mpg        cyl       disp         hp       drat         wt       qsec  
 20.090625   6.187500 230.721875 146.687500   3.596563   3.217250  17.848750 
    vs         am       gear       carb 
 0.437500   0.406250   3.687500   2.812500 
Run Code Online (Sandbox Code Playgroud)

但是,我怎么会用purrr映射meanmtcars被拆分cyl

library(tidyverse)
mtcars_split <- mtcars %>% split(.$cyl) 
mtcars_split %>% map(mean)
$`4`
[1] NA

$`6`
[1] NA

$`8`
[1] NA

Warning messages:
1: In mean.default(.x[[i]], ...) :
  argument is not numeric or logical: returning NA
2: In mean.default(.x[[i]], ...) :
  argument is not numeric or logical: returning NA
3: In mean.default(.x[[i]], ...) :
  argument is not numeric or logical: returning NA
Run Code Online (Sandbox Code Playgroud)

我理解为什么这不起作用:split创建一个列表,现在我正在尝试map mean新列表的每个元素,即data.frames.这种mapping操作等同于(必要时纠正我):

mean(mtcars_split[1])
mean(mtcars_split[2])
mean(mtcars_split[3])
Run Code Online (Sandbox Code Playgroud)

这显然不起作用 - 你不能只是采取mean一个data.frame.我真正想要的是做到这一点:

mtcars_split[[1]] %>% map(mean)
mtcars_split[[2]] %>% map(mean)
mtcars_split[[3]] %>% map(mean)
Run Code Online (Sandbox Code Playgroud)

问题是,我无法绕过如何做到这一点purrr.在寻找这个(看似非常基本的)问题的解决方案的时候,我发现rapply,这似乎做了我想要的,但在外面purrr(并且输出不完全是我想要的格式,但那不是重点) :

rapply(mtcars_split, mean, how = "unlist")
      4.mpg       4.cyl      4.disp        4.hp      4.drat        4.wt 
 26.6636364   4.0000000 105.1363636  82.6363636   4.0709091   2.2857273 
     4.qsec        4.vs        4.am      4.gear      4.carb       6.mpg 
 19.1372727   0.9090909   0.7272727   4.0909091   1.5454545  19.7428571 
  6.cyl      6.disp        6.hp      6.drat        6.wt      6.qsec 
  6.0000000 183.3142857 122.2857143   3.5857143   3.1171429  17.9771429 
       6.vs        6.am      6.gear      6.carb       8.mpg       8.cyl 
  0.5714286   0.4285714   3.8571429   3.4285714  15.1000000   8.0000000 
     8.disp        8.hp      8.drat        8.wt      8.qsec        8.vs 
353.1000000 209.2142857   3.2292857   3.9992143  16.7721429   0.0000000 
       8.am      8.gear      8.carb 
  0.1428571   3.2857143   3.5000000 
Run Code Online (Sandbox Code Playgroud)

rapply递归apply显然是我的答案的关键 - 我相信我需要嵌套的maps - 一个提取data.frame我的三个s中的每一列mtcars_split,然后一个mean在每个提取的列上运行.但是,我无法做到这一点.

我认为这是由Jenny Bryan在她的purrr教程中解决的,她使用map()内部a map(),但我不能按照她正在做的事情.她注意到这个例子可能在本教程前面没有充分解释,我已经在这里要求她详细说明,但还没有回答(我知道她很忙!).

Kon*_*lph 9

这种问题的答案总是相同的:

分解问题,针对个别情况解决,然后再由内而外放回去。

如您mtcars %>% split(.$cyl)所见,为您提供了一个列表列表(data.frames列表)。要映射mean内部列表

因此,让我们首先针对一个列表进行操作:

mtcars_split[[1]] %>% map_dbl(mean)
# Or, equivalently:
map_dbl(mtcars_split[[1]], mean)
Run Code Online (Sandbox Code Playgroud)

这可行。我们已经分解了问题并成功解决了个别情况:换句话说,给定一个列表x和一个转换f,我们已经x[[1]]通过执行f(x[[1]])(相当于x[[1]] %>% f())解决了问题。

是时候将其推广到所有情况了。而且我们已经知道如何将元素x[[1]]转换为整个列表xmap在该列表上使用:

x %>% map(~ .x %>% f())
# or, equivalently:
x %>% map(~ f(.x))
# or, equivalently:
map(x, ~ f(.x))
# or, finally:
map(x, f)
Run Code Online (Sandbox Code Playgroud)

让我们做完全相同的事情,分别用xf替换为mtcars_splitmap_dbl(mean)

mtcars_split %>% map(~ .x %>% map_dbl(mean))
# or, equivalently:
mtcars_split %>% map(~ map_dbl(.x, mean))
Run Code Online (Sandbox Code Playgroud)

可以像上面的示例一样简化此操作:

mtcars_split %>% map(map_dbl, mean)
Run Code Online (Sandbox Code Playgroud)

  • @user51462 它是等效的,只是因为 purrr 包函数明确支持两者。它们在其他方面并不等同。purrr 的“map”函数检查第一个参数类型以确定要做什么。如果它是一个函数(如第二个示例中所示),则将其定义为“map(xs, f, args)”与“map(xs, function (x) f(x, args))”相同。如果第一个参数是公式,则它将公式转换为等效的函数调用,即“map(xs, ~ f(.x, args))”再次相同。 (3认同)