43

我想在dplyr. 请参见下面的示例:

df <- data.frame( 
      color = c("blue", "black", "blue", "blue", "black"), 
      value = 1:5)
filter(df, color == "blue")

它工作得很好,但我想color通过字符串来引用,如下所示:

var <- "color"
filter(df, this_probably_should_be_a_function(var) == "blue").

我很乐意以任何方式做到这一点,并且非常乐意使用易于阅读的dplyr语法。

4

9 回答 9

36

在较新的版本中,我们可以使用我们可以创建引用的变量,然后取消引用(UQ!!)进行评估

var <- quo(color)
filter(df, UQ(var) == "blue")
#   color value
#1  blue     1
#2  blue     3
#3  blue     4

由于运算符优先级,我们可能需要()环绕!!

filter(df, (!!var) == "blue")
#   color value
#1  blue     1
#2  blue     3
#3  blue     4

有了新版本,||优先级更高,所以

filter(df, !! var == "blue")

应该可以工作(正如@Moody_Mudskipper 评论的那样)

较旧的选项

我们还可以使用:

 filter(df, get(var, envir=as.environment(df))=="blue")
 #color value
 #1  blue     1
 #2  blue     3
 #3  blue     4

编辑:重新排列解决方案的顺序

于 2014-07-04T08:10:29.593 回答
27

对于dplyr版本 [0.3 - 0.7) (? - 2017 年 6 月)

(有关更新的dplyr版本,请参阅此问题的其他答案)

对于使用非标准评估(NSE,请参阅 发布帖子小插图dplyr 0.3)的每个函数,都有一个以下划线结尾的标准评估 (SE) 双胞胎。这些可用于传递变量。因为它会。使用您可以将逻辑条件作为字符串传递。dplyrfilterfilter_filter_

filter_(df, "color=='blue'")

#   color value
# 1  blue     1
# 2  blue     3
# 3  blue     4

用逻辑条件构造字符串当然是直截了当的

l <- paste(var, "==",  "'blue'")
filter_(df, l)
于 2015-10-31T10:31:39.573 回答
17

从 dplyr 0.7 开始,有些事情又发生了变化。

library(dplyr)
df <- data.frame( 
  color = c("blue", "black", "blue", "blue", "black"), 
  value = 1:5)
filter(df, color == "blue")

# it was already possible to use a variable for the value
val <- 'blue'
filter(df, color == val)

# As of dplyr 0.7, new functions were introduced to simplify the situation
col_name <- quo(color) # captures the current environment
df %>% filter((!!col_name) == val)

# Remember to use enquo within a function
filter_col <- function(df, col_name, val){
  col_name <- enquo(col_name) # captures the environment in which the function was called
  df %>% filter((!!col_name) == val)
}
filter_col(df, color, 'blue')

dplyr 编程小插图中解释了更一般的情况。

于 2017-07-07T10:11:11.067 回答
12

rlang版本 >= 0.4.0

.data现在被认为是引用父数据框的一种方式,因此通过字符串引用的工作方式如下:

var <- "color"
filter(df, .data[[var]] == "blue")

如果变量已经是一个符号,那么{{}}将正确地取消引用它

示例 1:

var <- quo(color)
filter(df, {{var}} == "blue")

或更现实地

f <- function(v) {
    filter(df, {{v}} == "blue")
}
f(color) # Curly-curly provides automatic NSE support
于 2019-07-01T06:22:27.580 回答
7

经常被问到,但仍然不容易支持 afaik。但是,关于这个帖子

eval(substitute(filter(df, var == "blue"), 
                list(var = as.name(var))))
#   color value
# 1  blue     1
# 2  blue     3
# 3  blue     4
于 2014-07-04T07:54:24.533 回答
5

上面的几个解决方案对我不起作用。现在有as.symbol函数,我们将其包装在!!. 好像有点简单,有点。

set.seed(123)

df <- data.frame( 
  color = c("blue", "black", "blue", "blue", "black"), 
  shape = c("round", "round", "square", "round", "square"),
  value = 1:5)

现在将变量作为字符串输入到 dplyr 函数中,方法是将其传递as.symbol()!!

var <- "color"
filter(df, !!as.symbol(var) == "blue")

#   color  shape value
# 1  blue  round     1
# 2  blue square     3
# 3  blue  round     4

var <- "shape"
df %>% group_by(!!as.symbol(var)) %>% summarise(m = mean(value))

#   shape      m
#   <fct>  <dbl>
# 1 round   2.33
# 2 square  4
于 2020-02-04T00:41:45.537 回答
5

这是使用包sym()中的函数执行此操作的一种方法rlang

library(dplyr)

df <- data.frame( 
  main_color = c("blue", "black", "blue", "blue", "black"), 
  secondary_color = c("red", "green", "black", "black", "red"),
  value = 1:5, 
  stringsAsFactors=FALSE
)

filter_with_quoted_text <- function(column_string, value) {
    col_name <- rlang::sym(column_string)
    df1 <- df %>% 
      filter(UQ(col_name) == UQ(value))
    df1
}

filter_with_quoted_text("main_color", "blue")
filter_with_quoted_text("secondary_color", "red")
于 2017-09-27T02:06:36.843 回答
5

更新。新的dplyr1.0.0有一些奇妙的新功能,使解决这些类型的问题变得更加容易。您可以在新软件包随附的“编程”小插曲中了解它。

基本上,该.data[[foo]]函数允许您更轻松地将字符串传递给函数。

所以你可以这样做

filtFunct <- function(d, var, crit) {
filter(d, .data[[var]] %in% crit)
}

filtFunct(df, "value", c(2,4))

#   color value
# 1 black     2
# 2  blue     4

filtFunct(df, "color", "blue")

#   color value
# 1  blue     1
# 2  blue     3
# 3  blue     4
于 2020-09-06T11:59:30.043 回答
1

这个问题是6年前发布的。 dplyr现在升级到 1.0.2 版。然而,这仍然是一个很好的讨论,并极大地帮助了我解决我的问题。我希望能够从所有由内存中的变量指定的列、运算符和值构造过滤器。哦,还有不确定数量的过滤器!

请考虑以下列表,其中我指定了两个过滤器的列、运算符和值:

myFilters = 
  list(
    list(var = "color", op = "%in%", val = "blue"),
    list(var = "value", op = "<=", val = 3)
  )

从这个列表中,我想运行:

dplyr::filter(color %in% "blue", value <= 3)

我们可以lapply在上面使用来list创建对象,使用运算符强制评估调用,并将其传递给:listcall!!!filter

library(dplyr)

df <- data.frame( 
  color = c("blue", "black", "blue", "blue", "black"), 
  value = 1:5)

result = 
  lapply(myFilters, function(x) call(x$op, as.name(x$var), x$val)) %>%
  {filter(df, !!!.)}

...和沙赞!

> result
  color value
1  blue     1
2  blue     3

这需要吸收很多东西,所以如果不能立即看出正在发生的事情,让我把它拆开一点。考虑:

var = "color"
op = "%in%"
val = "blue"

我希望能够运行:

filter(df, color %in% "blue")

如果我也有:

var2 = "value"
op2 = "<="
val2 = 3

我可能希望能够得到:

filter(df, color %in% "blue", value <= 3)

该解决方案使用calls,它们是未计算的表达式。(请参阅 Hadley 的Advanced R book)基本上,call从变量中创建一个对象列表,然后在调用!!!时使用运算符强制评估调用dplyr::filter

call1 = call(op, as.name(var), val)

这是 的值call1

> call1
color %in% "blue"

让我们创建另一个call

call2 = call(op2, as.name(var2), val2)

将它们放在列表中:

calls = list(call1, call2)

并用于!!!在将呼叫发送到之前评估呼叫列表filter

result = filter(df, !!!calls)
于 2020-12-20T15:26:21.260 回答