当将点传递给purrr中的映射函数时,为什么需要取消引用的引用?

问题描述 投票:2回答:1

据我所知,当您不修改点时(例如,通过更改其名称),通常无需引用或取消引用点。但是,此示例使我似乎不太了解其工作原理。

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

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

r purrr tidyeval
1个回答
0
投票

我认为您的问题是,您需要通过各个级别的函数调用来传递...。因此,...和内部函数都必须通过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。如果您随时中断该链,它就会崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.