R Rlang:在Quosure中使用.x在map()中?

时间:2019-05-07 15:39:46

标签: r rlang tidyeval

我正在尝试将data.frame中的一组变量/值传递给map函数,但不确定如何处理.x指的是需要要评估:mutate(df2 = map2(variable, value, ~filter(df1, .x==.y)))天真的!!.x将不起作用。

这是我的data.frame,其中一列是变量,一列是 value ,将被映射到过滤器调用中:

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) 
#> # A tibble: 2 x 2
#>   variable value
#>   <chr>    <chr>
#> 1 wool     A    
#> 2 tension  L

如何将它们传递给过滤器?我应该改为声明变量作为担保吗?我尝试了几种方法:

library(tidyverse)
data(warpbreaks)

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, ~filter(warpbreaks, .x==.y)))
#> # A tibble: 2 x 3
#>   variable value data_filtered       
#>   <chr>    <chr> <list>              
#> 1 wool     A     <data.frame [0 × 3]>
#> 2 tension  L     <data.frame [0 × 3]>

tibble(variable=c(quo(wool), quo(tension)), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, ~filter(warpbreaks, eval_tidy(.x)==.y)))
#> Error in eval_tidy(.x): object 'wool' not found

3 个答案:

答案 0 :(得分:1)

.x的匿名函数求值仍然很奇怪。老实说,我不确定是什么,但是在map2调用之外定义一个函数似乎可以正常工作(将~ filter(df1, !!sym(.x) == .y)位分配给@Lionel Henry:

library(tidyverse)

df <- tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) 

data(warpbreaks)

# doesn't work with anonymous function
tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, ~ filter(warpbreaks, !!sym(.x) == .y)))
#> Error in is_symbol(x): object '.x' not found

# works when you define function outside of map2
temp <- function(x, y, data){
  filter(data, !!sym(x) == y)
}

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, temp, warpbreaks))
#> # A tibble: 2 x 3
#>   variable value data_filtered        
#>   <chr>    <chr> <list>               
#> 1 wool     A     <data.frame [27 x 3]>
#> 2 tension  L     <data.frame [18 x 3]>

reprex package(v0.2.1)于2019-05-07创建

您也可以在没有外部定义函数的情况下执行以下操作:

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered = map2(variable, value, ~ filter(..3, ..3[[..1]] == ..2), warpbreaks))
#> # A tibble: 2 x 3
#>   variable value data_filtered        
#>   <chr>    <chr> <list>               
#> 1 wool     A     <data.frame [27 x 3]>
#> 2 tension  L     <data.frame [18 x 3]>

答案 1 :(得分:1)

在您的示例中,您尝试以嵌套方式使用dplyr动词:filter()中有一个mutate()。这对于正常使用来说效果很好,但是在使用整齐的评估功能时我们需要稍加小心,因为它们在调用外部函数时就很早就应用了。因此,如果您尝试在内部动词中使用!!.data,通常会出现时序问题。

@zack的答案显示了如何分两步分解问题以避免嵌套问题。在这种情况下,另一种可能性是通过直接映射到mutate()上来省去df步骤(该想法归功于@Spacedman)。在这里,我们将使用pmap()并行映射到列表或数据框:

# For pretty-printing
options(tibble.print_max = 5, tibble.print_min = 5)
warpbreaks <- as_tibble(warpbreaks)

pmap(df, ~ filter(warpbreaks, .data[[.x]] == .y))
#> [[1]]
#> # A tibble: 27 x 3
#>   breaks wool  tension
#>    <dbl> <fct> <fct>
#> 1     26 A     L
#> 2     30 A     L
#> 3     54 A     L
#> 4     25 A     L
#> 5     70 A     L
#> # … with 22 more rows
#>
#> [[2]]
#> # A tibble: 18 x 3
#>   breaks wool  tension
#>    <dbl> <fct> <fct>
#> 1     26 A     L
#> 2     30 A     L
#> 3     54 A     L
#> 4     25 A     L
#> 5     70 A     L
#> # … with 13 more rows

答案 2 :(得分:1)

您可以使用R的本机替换工具, rlang 在处理环境时更有价值,但对于更复杂的符号替换(例如嵌套),R基础更容易(对我来说)至少)。

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, ~eval(bquote(
    filter(warpbreaks, .(sym(.x)) ==.y)))))

tibble(variable=c("wool", "tension"), 
       value= c("A", "L")) %>% 
  mutate(data_filtered=map2(variable, value, ~eval(substitute(
    filter(warpbreaks, X ==.y), list(X = sym(.x))))))

# output for either
# # A tibble: 2 x 3
#       variable value data_filtered        
#          <chr>    <chr> <list>               
#   1 wool     A     <data.frame [27 x 3]>
#   2 tension  L     <data.frame [18 x 3]>