对多个dplyr过滤条件使用整齐的eval

Mri*_*arg 5 r tidyverse tidyeval

我是整理eval并尝试编写泛型函数的新手 - 我现在正在努力的一件事就是为分类变量编写多个过滤条件.这就是我现在正在使用的 -

create_expr <- function(name, val){
   if(!is.null(val))
     val <- paste0("c('", paste0(val, collapse = "','"), "')")
   paste(name, "%in%", val)
}

my_filter <- function(df, cols, conds){
#   Args: 
#     df: dataframe which is to be filtered
#     cols: list of column names which are to be filtered
#     conds: corresponding values for each column which need to be filtered

cols <- as.list(cols)
conds <- as.list(conds)

args <- mapply(create_expr, cols, conds, SIMPLIFY = F)

if(!length(args))
  stop(cat("No filters provided"))

df <- df %>% filter_(paste(unlist(args), collapse = " & "))
return(df)
}

my_filter(gapminder, cols = list("continent", "country"), 
                     conds = list("Europe", c("Albania", "France")))
Run Code Online (Sandbox Code Playgroud)

我想知道如何使用整洁的eval实践重写它.我已经找到了使用quos()获取多个参数的材料,但是你可以看到我在这里有两个不同的参数列表需要相互映射.

任何帮助表示赞赏,谢谢!

MrF*_*ick 9

使用tidyverse,您可以将该函数重写为

library(dplyr)
library(purrr) # for map2()

my_filter <- function(df, cols, conds){     
  fp <- map2(cols, conds, function(x, y) quo((!!(as.name(x))) %in% !!y))
  filter(df, !!!fp)
}

my_filter(gapminder::gapminder, cols = list("continent", "country"), 
          conds = list("Europe", c("Albania", "France")))
Run Code Online (Sandbox Code Playgroud)

这称为相当于

filter(gapminder, continent %in% "Europe", country %in% c("Albania", "France"))
Run Code Online (Sandbox Code Playgroud)

这有效的主要原因是你可以传递多个参数,filter()并且它们是隐式组合的&.并且map2()只是mapply两个对象迭代的等价物.