遍历过滤器表达式列表:mutate 中 map2 调用中的 NSE 问题

looping over a list of filter expressions: problem with NSE in map2 call within mutate

我定义了一个表达式列表,其中包含要传递给 dplyr::filter 调用的参数。

library(tidyverse) # using tidyr 1.0.0

cond_filter <- list(expr(1 > 0), # condition to select all rows
                    expr(Species == "setosa"),
                    expr(Species != "virginica")) 

我还有一个数据框,我将其放入列表列中,然后根据所述列表中的过滤器表达式的数量进行扩展。

iris_nest <- iris %>% 
               nest(data = everything()) %>% 
               expand_grid(., filters = cond_filter) 

在最后一步中,我想根据列表的过滤表达式过滤每个嵌套数据集。通常我会在嵌套的 tibble 上使用 mutate 在管道内执行此操作,然后使用 map2 循环遍历数据集和过滤条件。但是,不知何故,下面的调用不起作用。看来我没有让非标准评估正常工作:

# not working: map2 inside mutate with mapper function 
iris_nest %>% 
  mutate(data = map2(data, filters, ~ filter(.x, !! .y))) 
> Error in splice(dot_call(capture_dots, frame_env = frame_env, named = named,  : 
> object '.y' not found

# not working: map2 inside mutate with anonymous function
iris_nest %>% 
  mutate(data = map2(data, filters, function(x,y) filter(x, !! y)))
> Error in splice(dot_call(capture_dots, frame_env = frame_env, named = named,  : 
> object '.y' not found

如果我在 mutate 调用之外执行此操作,一切正常:

# working: map2 not nested in mutate  
iris_nest$data <- map2(iris_nest$data, iris_nest$filters, ~ filter(.x, !! .y))

我更喜欢可以在 mutate 中使用 map2 的解决方案。我需要更改什么才能获得此 运行?也许定义一个辅助函数可以让它工作,但我对这样的辅助函数需要是什么样子缺乏想象力。


总结

smingerson 在 mutate 中评估 !! 的方式确定了问题的原因。似乎在一个简单的 map2 调用中 !! 可以用来评估过滤条件。但是,当在 mutate 中使用 map2 时,必须使用 eval 显式评估过滤条件 .y

当使用已经包含 !!:

的辅助函数时,可以演示 !! 取决于其环境的不同效果
my_filter <- function(df, x) {
  filter(df, !! x)
}

在这种情况下,嵌套的 map2 调用中不需要进一步评估(!!eval)。

iris_nest %>% 
  mutate(data = map2(data, filters, ~ my_filter(.x, .y))) 

我也不确定为什么它不起作用,但使用 eval 可以让它在 mutate

中起作用
library(tidyverse)
iris_nest %>% mutate(filters = map2(data, filters, ~filter(.x, eval(.y))))

# A tibble: 3 x 2
#            data filters           
#  <list<df[,5]>> <list>            
#1      [150 × 5] <tibble [150 × 5]>
#2      [150 × 5] <tibble [50 × 5]> 
#3      [150 × 5] <tibble [100 × 5]>

表达式不会存储为小标题中的表达式,而是存储为调用 objects。这意味着您需要先使用 eval ,就像 Ronak 的解决方案一样。我通过 运行 debugonce(dplyr:::tbl_df) 和 运行 class(eval_tidy(quo)) 发现了这一点,它们的计算结果为 "call".

至于为什么它在 !! 的 mutate 中不起作用,这是因为外部 mutate() 调用试图在 .y 实际存在之前评估 .y .在 Ronak 的示例中,mutate() 不会触发 eval(.y)。当 eval(.y) 被触发时,它在传入的 data.frame 的上下文中,调用被正确评估为长度等于行数的逻辑向量。

除了 Ronak 的解决方案,我找不到其他解决方案。