首页 > 解决方案 > 为什么在将它们传递给 purrr 中的映射函数时需要引用-取消引用点?

问题描述

据我了解,通常在未修改点时(例如通过更改名称),您不需要引用或取消引用点。然而,这个例子让我看起来并不真正理解它是如何工作的。

这里我们有一个函数,它依赖于点来选择列进行嵌套。它所做的只是从foo参数中添加一列,然后嵌套点中未提及的所有列。

library(tidyverse)
dots_fun <- function(df, foo, ...) {
  df %>%
    mutate(foo = foo) %>%
    nest(data = -c(...))
}

dots_fun(mtcars, "a", cyl)
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>

我希望能够通过使用不同的参数调用它来映射这个函数。通过使用普通匿名函数语法来执行此操作的天真方法失败并出现令人困惑的错误:

list_of_foos <- c("a", "b")

mapping_fun1 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x, ...)
  )
}

mapping_fun1(mtcars, foos = list_of_foos, cyl)
#> Error: Can't subset columns that don't exist.
#> x The column `a` doesn't exist.

如果我只是将点移到匿名函数之外,也无济于事。它不再出错,但无法cyl按需要嵌套。

mapping_fun2 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x),
    ...
  )
}
mapping_fun2(mtcars, foos = list_of_foos, cyl)
#> [[1]]
#> # A tibble: 1 x 1
#>   data              
#>   <list>            
#> 1 <tibble [32 × 12]>
#> 
#> [[2]]
#> # A tibble: 1 x 1
#>   data              
#>   <list>            
#> 1 <tibble [32 × 12]>

我设法通过将点拼接到匿名函数中来使其工作,但我真的不明白为什么这是必要的。(您也可以通过颠倒映射函数的参数顺序并通过...of提供所有参数来使其工作map,但随后dots_fun具有“错误”的参数顺序。如果您使用function()样式匿名函数来颠倒参数顺序)

mapping_fun3 <- function(df, foos, ...) {
  dots <- enquos(...)
  map(
    .x = foos,
    .f = ~ dots_fun(df = df, foo = .x, !!!dots)
  )
}

mapping_fun3(mtcars, foos = list_of_foos, cyl)
#> [[1]]
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>
#> 
#> [[2]]
#> # A tibble: 3 x 2
#>     cyl data              
#>   <dbl> <list>            
#> 1     6 <tibble [7 × 11]> 
#> 2     4 <tibble [11 × 11]>
#> 3     8 <tibble [14 × 11]>

我的问题是:在什么条件/情况下,您需要引用和取消引用...才能安全地通过函数传递它们?该条件如何适用于此?

标签: rpurrrtidyeval

解决方案


我认为您的问题是您需要通过...每个级别的函数调用。所以...两者都必须通过map()以及你的内在功能。

我无法使用您的示例nest(),因此我制作了一个版本来select()代替

dots_fun <- function(df, foo, ...) {
  df %>%
    mutate(foo = foo) %>%
    select(...)
}

然后,您似乎无法通过此 github 问题实际使用带有非标准评估的as_mapper语法,因此您需要显式创建一个匿名函数,这样迭代值也不会在值中再次传递。Hadley 说该语法仅适用于“简单”功能,不适用于. 所以一个工作映射函数可能看起来像这样......~...

mapping_fun1 <- function(df, foos, ...) {
  map(
    .x = foos,
    .f = function(x, ...) dots_fun(df = df, foo = x, ...),
    ...
  )
}
mapping_fun1(mtcars, foos = list_of_foos, cyl, gear)

我们...通过map()我们的匿名函数,最后传入dots_fun. 如果你在任何时候打破这个链条,它就会分崩离析。


推荐阅读