遍历过滤器表达式列表: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 的解决方案,我找不到其他解决方案。
我定义了一个表达式列表,其中包含要传递给 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 的解决方案,我找不到其他解决方案。