使用整洁评估(tidy eval)进行多个dplyr筛选条件

8

我是一个新手,正在尝试编写通用函数,其中一个我目前遇到的问题是为分类变量编写多个筛选条件。这是我目前正在使用的代码:

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")))

我想知道如何使用整洁评估实践来重新编写这个内容。我找到了关于使用quos()处理多个参数的材料,但是正如您所看到的,我在这里有两个不同的参数列表需要映射到一起。

任何帮助都会受到赞赏,谢谢!

1个回答

14

使用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")))

这是调用相当于

filter(gapminder, continent %in% "Europe", country %in% c("Albania", "France"))

这个方法能够奏效的主要原因是你可以通过 filter() 传递多个参数,并且它们会隐式地与 & 结合。而 map2() 则是 tidyverse 中相当于使用两个对象进行迭代的 mapply 的函数。


非常感谢您的帮助,@MrFlick。那么map2()是不是类似于quos()的扩展版本,可以帮助将两个不同的quos()映射到彼此? - Mridul Garg
1
“map2” 一般与 “quos” 没有关系。它只是在“cols”和“conds”上进行迭代,并应用函数。在这种情况下,该函数恰好返回一个“quo”,因此我们最终得到了一个“quo”列表。 - MrFlick
这是非常有帮助的答案,@MrFlick。我想知道如何将这种方法扩展到多个和任意运算符(%in%==>,...)??我刚刚在这里发布了一个新问题here - Till

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接