对多个任意过滤条件使用整齐求值 [英] Using tidy eval for multiple, arbitrary filter conditions

查看:11
本文介绍了对多个任意过滤条件使用整齐求值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想用整齐的评估编写多个完全灵活的过滤条件。解决了一个相关但不太复杂的问题in this Stackoverflow Question。下面的代码(改编自前面提到的另一个问题)正在运行。它将两个过滤条件应用于Gapminder数据集,并返回筛选的数据。

library(tidyverse)
library(gapminder)

my_filter <- function(df, cols, vals){    
  paste_filter <- function(x, y) quo(!!sym(x) %in% {{y}})
  fp <- pmap(list(cols, vals), paste_filter)
  filter(df, !!!fp)
}

cols <- list("country", "year")
vals = list(c("Albania", "France"), c(2002, 2007))
gapminder %>% my_filter(cols, vals) 

问题:到目前为止,该解决方案仅限于一种类型的过滤运营商(%in%)。我想将此方法扩展到接受任意类型的运算符(==%in%>、.)。预期函数my_filter应处理以下内容:

cols <- list("country", "year")
ops <- list("%in%", ">=")
vals = list(c("Albania", "France"), 2007))
gapminder %>% my_filter(cols, ops, vals)

我脑海中浮现的用例是闪亮的应用程序。使用这样的功能,我们可以更容易地让用户对数据集的变量设置任意的过滤条件。

推荐答案

创建调用列表并将其拼接:

library(dplyr)
library(gapminder)

cols <- list("country", "year")
ops <- list("%in%", ">=")
vals <- list(c("Albania", "France"), 2007)

# Assumes LHS is the name of a variable and OP is
# the name of a function
op_call <- function(op, lhs, rhs) {
  call(op, sym(lhs), rhs)
}

my_filter <- function(data, cols, ops, vals) {
  exprs <- purrr::pmap(list(ops, cols, vals), op_call)
  data %>% dplyr::filter(!!!exprs)
}

gapminder %>% my_filter(cols, ops, vals)
#> # A tibble: 2 × 6
#>   country continent  year lifeExp      pop gdpPercap
#>   <fct>   <fct>     <int>   <dbl>    <int>     <dbl>
#> 1 Albania Europe     2007    76.4  3600523     5937.
#> 2 France  Europe     2007    80.7 61083916    30470.

在这里,我们不必担心作用域问题,因为(A)假定列名是在数据掩码中定义的,(B)值是按值传递的,并在创建的调用中内联,以及(C)假定函数是二元运算符,并且很少重新定义这些函数。

要允许自定义用户功能,有两种方法可以实现。首先,我们可以使用new_quosure()

获取一个环境并手动创建查询
op_call <- function(op, lhs, rhs, env = caller_env()) {
  new_quosure(call(op, sym(lhs), rhs), env)
}

my_filter <- function(data, cols, ops, vals, env = caller_env()) {
  exprs <- purrr::pmap(list(ops, cols, vals), op_call, env)
  data %>% dplyr::filter(!!!exprs)
}

gapminder %>% my_filter(cols, ops, vals)

local({
  my_op <- `%in%`
  gapminder %>% my_filter(cols, list("my_op", ">="), vals)
})
#> # A tibble: 2 × 6
#>   country continent  year lifeExp      pop gdpPercap
#>   <fct>   <fct>     <int>   <dbl>    <int>     <dbl>
#> 1 Albania Europe     2007    76.4  3600523     5937.
#> 2 France  Europe     2007    80.7 61083916    30470.

另一种可能更简单的方法是允许调用包含内联函数。为此,请使用rlang::call2()而不是base::call()

op_call <- function(op, lhs, rhs) {
  call2(op, sym(lhs), rhs)
}

my_filter <- function(data, cols, ops, vals) {
  exprs <- purrr::pmap(list(ops, cols, vals), op_call)
  data %>% dplyr::filter(!!!exprs)
}

local({
  my_op <- `%in%`
  gapminder %>% my_filter(cols, list(my_op, ">="), vals)
})
#> # A tibble: 2 × 6
#>   country continent  year lifeExp      pop gdpPercap
#>   <fct>   <fct>     <int>   <dbl>    <int>     <dbl>
#> 1 Albania Europe     2007    76.4  3600523     5937.
#> 2 France  Europe     2007    80.7 61083916    30470.

内联函数的缺点是这将阻止优化和传输到其他dplyr后端。

这篇关于对多个任意过滤条件使用整齐求值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆