dplyr中基于字符串的过滤-NSE

Lor*_*ssi 1 r dplyr nse tidyverse tidyeval

我想使用dplyr的新NSE表示法(版本> = 0.6)对filter数据进行动态处理。假设我有以下虚拟数据集:

df = data_frame(x = 1:10, y = 10:1, z = 10 * runif(10))
Run Code Online (Sandbox Code Playgroud)

如果现在我想过滤列tofilter = "x"中大于5的值,我知道可以这样做:

df %>% 
  filter((!!rlang::sym(tofilter)) >= 5)
Run Code Online (Sandbox Code Playgroud)

问题1

如果我也想动态更改过滤器的运算符(假设我有一个Shiny App,用户可以在其中动态selectInput过滤数据是否大于5,等于5或小于5的值)怎么办?

我想做的是以下事情:

op = ">="
val = 5
filt_expr = paste("x", op, val)
df %>% 
  filter(filt_expr)
Run Code Online (Sandbox Code Playgroud)

显然,这是行不通的,并且我在使用rlang定额/符号等方面有些过,但是没有找到正确的方法来“引用”我的输入。

问题2

额外的问题是,如果我想应用多个过滤器怎么办?我是否需要循环,还是可以创建一系列过滤表达式并将其一次性应用?

例如Shiny App,用户可以在其中键入他/她想要应用于数据的多个条件,以便我们可以动态更改格式列表:

filt_expr_list = list("x >= 5", "y <= 10", "z >= 2")
Run Code Online (Sandbox Code Playgroud)

并且我们要动态地全部应用它们,以便输出等效于:

df %>%
  filter(x >= 5, y <= 10, z >= 2)
Run Code Online (Sandbox Code Playgroud)

我想从某种意义上讲,这是问题1的子集,因为当我知道如何正确引用参数时,我认为我可以执行以下操作:

filt_expr = paste0(unlist(filt_expr_list), collapse = ", ")
df %>%
  filter(filt_expr)
Run Code Online (Sandbox Code Playgroud)

但很高兴看看是否有更好的清洁方法

Lio*_*nry 5

如果我也想动态更改过滤器的运算符怎么办

您可以通过对表示操作符的符号取消引号来进行整洁的评估(请注意,我expr()用来说明取消引号的结果):

lhs <- "foo"

# Storing the symbol `<` in `op`
op <- quote(`<`)

expr(`!!`(op)(!!sym(lhs), 5))
#> foo < 5
Run Code Online (Sandbox Code Playgroud)

但是,在常规E代码之外进行整洁的评估是比较干净的。仅当取消引用的符号代表数据框中的一列(即不在上下文中的内容)时,才需要取消引用。在这里,您可以将运算符存储在一个变量中,然后在过滤表达式中调用该变量:

# Storing the function `<` in `op`
op <- `<`

expr(op(!!sym(lhs), 5))
#> op(foo, 5)
Run Code Online (Sandbox Code Playgroud)

如果我想应用多个过滤器怎么办?

您将表达式保存在列表中,然后在调用中将它们拼接为!!!

filters <- list(
  quote(x >= 5),
  quote(y <= 10),
  quote(z >= 2)
)

expr(df %>% filter(!!!filters))
#> df %>% filter(x >= 5, y <= 10, z >= 2)`
Run Code Online (Sandbox Code Playgroud)

注意:上面我说过,不必从上下文中取消对变量的引用,但是如果您正在编写将数据框作为输入的函数,那么这样做通常仍然是一个好主意。由于数据框是可变的,因此您事先不知道它包含哪些列。这些列将始终优先于您在环境中定义的对象。在这种情况下,这不是问题,因为我们正在谈论一个函数,如果R在数据帧中找到一个类似命名的对象,R将继续寻找该函数。