【问题标题】:Filter data frame by character column name (in dplyr)按字符列名过滤数据框(在 dplyr 中)
【发布时间】:2015-01-27 15:24:15
【问题描述】:

我有一个数据框,并希望通过“this”列或“that”列以两种方式之一对其进行过滤。我希望能够将列名作为变量引用。我如何(在dplyr 中,如果有区别的话)我如何通过变量引用列名?

library(dplyr)
df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
df
#   this that
# 1    1    1
# 2    2    1
# 3    2    2
df %>% filter(this == 1)
#   this that
# 1    1    1

但是假设我想使用变量column 来保存“this”或“that”,并过滤column 的任何值。 as.symbolget 都可以在其他情况下工作,但不是这样:

column <- "this"
df %>% filter(as.symbol(column) == 1)
# [1] this that
# <0 rows> (or 0-length row.names)
df %>% filter(get(column) == 1)
# Error in get("this") : object 'this' not found

如何将column 的值转换为列名?

【问题讨论】:

  • 为什么要这样做?试图参数化变量的选择?
  • 我有一个数据集,里面有两种类型的东西,每种都有不同的列。我想以这种方式处理事情,因为我正在使用 Shiny 进行交互式可视化,并且我想让人们选择要查看的事物类型,然后使用相同的代码来提取数据,传入基于列名称根据他们的选择。
  • 几乎发布了我的问题并找到了这个:)

标签: r dplyr


【解决方案1】:

使用 rlang 的 injection 范式

来自current dplyr documentation(我强调):

dplyr 用于提供每个动词的双版本,后缀为下划线。这些版本具有标准评估 (SE) 语义:它们不像 NSE 动词那样通过代码获取参数,而是通过值获取参数。他们的目的是使使用 dplyr 编程成为可能。但是,dplyr 现在使用整洁的评估语义。 NSE 动词仍然捕获它们的论点,但 您现在可以取消引用这些论点的部分内容。这为 NSE 动词提供了完全的可编程性。因此,下划线版本现在是多余的。

所以,基本上我们需要执行两个步骤才能在dplyr::filter() 中引用变量column 的值"this"

  1. 我们需要将字符类型的变量column转为symbol类型。

    使用base R这可以通过函数as.symbol()来实现 这是as.name() 的别名。前者是preferred by the tidyverse developers,因为它

    遵循更现代的术语(R 类型而不是 S 模式)。

    或者,同样可以通过 tidyverse 中的rlang::sym() 来实现。

  2. 我们需要将 1) 中的符号注入到dplyr::filter() 表达式中。

    这是由所谓的injection operator !! 完成的,基本上是syntactic sugar 允许在 R 评估之前修改一段代码。

    (在早期版本的dplyr(或底层的rlang)曾经有过!!会与单个!发生冲突的情况(包括你的),但这不是一个问题了since !! gained the right operator precedence。)

应用于您的示例:

library(dplyr)
df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(!!as.symbol(column) == 1)
#   this that
# 1    1    1

使用替代解决方案

dplyr::filter()中引用变量column的值"this"的其他方法不依赖于rlang的注入范式包括:

【讨论】:

  • 对于后一种情况,您可以使用一些额外的圆括号df %&gt;% filter((!!as.name(column)) == 1)
  • 很好,但是“‘UQ()’和‘UQS()’在 rlang 0.2.0 中被软性弃用,以使准引用的语法更加一致。”
  • 感谢@ggll,我已经更新了答案以说明 dplyr/rlang 的最新改进。
【解决方案2】:

我会避免一起使用get()。在这种情况下似乎很危险,特别是如果您正在编程。您可以使用未评估的调用或粘贴的字符串,但您需要使用filter_() 而不是filter()

df <- data.frame(this = c(1, 2, 2), that = c(1, 1, 2))
column <- "this"

选项 1 - 使用未评估的调用:

您可以将y 硬编码为1,但这里我将其显示为y 以说明如何轻松更改表达式值。

expr <- lazyeval::interp(quote(x == y), x = as.name(column), y = 1)
## or 
## expr <- substitute(x == y, list(x = as.name(column), y = 1))
df %>% filter_(expr)
#   this that
# 1    1    1

选项 2 - 使用 paste()(显然更容易):

df %>% filter_(paste(column, "==", 1))
#   this that
# 1    1    1

这两个选项的主要内容是我们需要使用filter_() 而不是filter()。事实上,根据我的阅读,如果您使用 dplyr 进行编程,您应该始终使用 *_() 函数。

我将此帖子用作有用的参考:character string as function argument r,我使用的是 dplyr 版本 0.3.0.2。

【讨论】:

  • 选项 1 是@hadley 在 SO 和 vignette("nse") 的几篇文章中推荐的稳健解决方案。然而,它看起来与 hadley 优雅的函数调用风格背道而驰。不能用更简单的方式包装和呈现吗?
  • 为什么 get() 被认为是危险的?
  • 这对于旧版本的dpylr 来说是一个很好的答案,但下划线功能已被弃用,因此@Salim-B 下面的答案会更好。
【解决方案3】:

这是最新 dplyr 版本的另一个解决方案:

df <- data.frame(this = c(1, 2, 2),
                 that = c(1, 1, 2))
column <- "this"

df %>% filter(.[[column]] == 1)

#  this that
#1    1    1

【讨论】:

    【解决方案4】:

    关于理查德的解决方案,如果您的列是字符,只想添加。您可以添加shQuote 以按字符值进行过滤。

    例如,你可以使用

    df %>% filter_(paste(column, "==", shQuote("a")))
    

    如果您有多个过滤器,您可以在paste 中指定collapse = "&amp;"

    df %>$ filter_(paste(c("column1","column2"), "==", shQuote(c("a","b")), collapse = "&"))
    

    【讨论】:

      【解决方案5】:

      执行此操作的最新方法是使用my.data.frame %&gt;% filter(.data[[myName]] == 1),其中myName 是包含列名的环境变量。

      【讨论】:

        【解决方案6】:

        或使用filter_at

        library(dplyr)
        df %>% 
           filter_at(vars(column), any_vars(. == 1))
        

        【讨论】:

          【解决方案7】:

          与上面解释的 Salim B 一样,但有细微的变化:

          df %>% filter(1 == !!as.name(column))
          

          即只需反转条件,因为 !! 否则行为 喜欢

          !!(as.name(column)==1)
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-27
            • 2021-10-05
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-12-05
            相关资源
            最近更新 更多