【问题标题】:String based filtering in dplyr - NSEdplyr 中基于字符串的过滤 - NSE
【发布时间】:2018-02-19 14:59:28
【问题描述】:

我想在我的数据上使用dplyr 的新 NSE 表示法(版本 >= 0.6)作为动态 filter。假设我有以下虚拟数据集:

df = data_frame(x = 1:10, y = 10:1, z = 10 * runif(10))

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

df %>% 
  filter((!!rlang::sym(tofilter)) >= 5)

问题 1

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

我想做的是:

op = ">="
val = 5
filt_expr = paste("x", op, val)
df %>% 
  filter(filt_expr)

显然,这不起作用,我已经使用了 rlang quosore/symbols 等,但没有找到正确的方法来“引用”我的输入。

问题 2

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

一个例子是一个闪亮的应用程序,用户可以输入他/她想要应用于数据的多个条件,以便我们有一个动态变化的格式列表:

filt_expr_list = list("x >= 5", "y <= 10", "z >= 2")

我们想动态地应用它们,这样输出就相当于:

df %>%
  filter(x >= 5, y <= 10, z >= 2)

我想这在某种意义上是问题 1 的一个子集,因为当我知道如何正确引用论点时,我认为我可以这样做:

filt_expr = paste0(unlist(filt_expr_list), collapse = ", ")
df %>%
  filter(filt_expr)

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

【问题讨论】:

    标签: r dplyr tidyverse nse tidyeval


    【解决方案1】:

    如果我也想动态改变过滤的操作符怎么办

    您可以通过取消引用表示运算符的符号来使用 tidy eval 来完成(注意我使用expr() 来说明取消引用的结果):

    lhs <- "foo"
    
    # Storing the symbol `<` in `op`
    op <- quote(`<`)
    
    expr(`!!`(op)(!!sym(lhs), 5))
    #> foo < 5
    

    但是,使用常规 R 代码在 tidy eval 之外执行此操作更简洁。仅当您取消引用的符号代表数据框中的一列时,才需要取消引用,即不在上下文中的内容。在这里,您可以将运算符存储在一个变量中,然后在您的过滤表达式中调用该变量:

    # Storing the function `<` in `op`
    op <- `<`
    
    expr(op(!!sym(lhs), 5))
    #> op(foo, 5)
    

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

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

    filters <- list(
      quote(x >= 5),
      quote(y <= 10),
      quote(z >= 2)
    )
    
    expr(df %>% filter(!!!filters))
    #> df %>% filter(x >= 5, y <= 10, z >= 2)`
    

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

    【讨论】:

    • 你完全正确!很好的解决方案,谢谢!!总结一下,可以运行多个动态过滤,创建一个类似filt_expr = list( quo(`&gt;=`(!!sym("x"), 5)), quo(`&lt;=`(!!sym("y"), 10)), quo(`&gt;=`(!!sym("z"), 2)) ) 的列表,然后应用!!! 运算符df %&gt;% filter(!!!filt_expr)
    • @lionel-henry 在您的列表示例中,似乎rlang::quoquote 的作用相同。在这种情况下,是否有任何区别或偏好?
    • 实际上,使用quo() 而不是quote()expr() 几乎没有用处。 Quosures 只对外国表达很重要,对本地表达不重要。这就是为什么您应该几乎总是使用enquo(),但不必使用quo()。您可以在自己的上下文中构建原始表达式并通过!! 传递它们。如果该函数使用enquo(),原始表达式将被适当地包裹在一个quosure中。
    【解决方案2】:

    您实际上可以这样做:

        df = data_frame(x = 1:10, y = 10:1, z = 10 * runif(10))
        op = ">="
        val = 5
        filt_expr = paste("x", op, val)
    
        df %>% filter(eval(parse(filt_expr)))
    

    【讨论】:

    • 你需要指定parse(text = filt_expr)然后
    猜你喜欢
    • 2020-06-19
    • 2020-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-22
    • 1970-01-01
    • 2019-08-16
    • 1970-01-01
    相关资源
    最近更新 更多