【问题标题】:Why do I need to quote-unquote dots when passing them to a mapped function in purrr?为什么在将它们传递给 purrr 中的映射函数时需要引用-取消引用点?
【发布时间】:2020-03-04 01:19:42
【问题描述】:

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

这里我们有一个函数,它依靠点来选择列进行嵌套。它所做的只是从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]>

我设法通过将点拼接到匿名函数中来使其工作,但我真的不明白为什么这是必要的。 (您也可以通过反转映射函数的参数顺序并通过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]>

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

【问题讨论】:

  • 您使用的是哪个版本的tidyr?调用dots_fun(mtcars, "a", cyl) 时出现错误(cyl 未找到)。我用tidyr_1.0.2
  • 我认为问题与您需要通过 map 和内部函数传递省略号的事实有关:stackoverflow.com/questions/48215325/… 但我很难复制。

标签: r purrr tidyeval


【解决方案1】:

我认为您的问题是您需要通过每个级别的函数调用来传递...。所以... 都必须通过map() 以及你的内部函数。

我无法让您的示例与 nest() 一起使用,因此我制作了一个使用 select() 的版本

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

然后,您似乎实际上无法将as_mapper 语法与... 一起使用,并通过this github issue 进行非标准评估,因此您需要显式创建一个匿名函数,以便不传递迭代值第二次在... 值中也是如此。 Hadley said ~ 语法仅适用于“简单”函数,不适用于 ...。所以一个有效的映射函数可能看起来像这样

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。如果你在任何时候打破这个链条,它就会分崩离析。

【讨论】:

  • 不错的答案。 ... 在 lambda 公式中不能以这种方式工作的技术原因是它们将点用于其他目的,因此您可以将参数称为 ..1..2 等。
  • 感谢您解决这个问题!现在更有意义了
猜你喜欢
  • 1970-01-01
  • 2022-08-23
  • 2011-12-07
  • 2015-10-15
  • 1970-01-01
  • 2018-06-21
  • 2012-06-22
  • 2022-10-01
  • 2012-08-22
相关资源
最近更新 更多