在dplyr的函数中使用变量名

时间:2014-07-04 07:44:53

标签: r dplyr r-faq rlang

我想在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语法。

9 个答案:

答案 0 :(得分:26)

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

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

dplyr 0.3开始,每个使用非标准评估的dplyr函数(NSE,请参阅release postvignette)都有一个以下划线结尾的标准评估(SE)。这些可用于传递变量。对于filter,它将是filter_。使用filter_,您可以将逻辑条件作为字符串传递。

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

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

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

l <- paste(var, "==",  "'blue'")
filter_(df, l)

答案 1 :(得分:25)

在较新的版本中,我们可以使用我们可以创建引用的变量,然后取消引用(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, get(var, envir=as.environment(df))=="blue")
 #color value
 #1  blue     1
 #2  blue     3
 #3  blue     4

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

答案 2 :(得分:15)

从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 programming vignette中解释了更多一般情况。

答案 3 :(得分:6)

经常被问到,但仍然没有轻松的支持。但是,关于this posting

eval(substitute(filter(df, var == "blue"), 
                list(var = as.name(var))))
#   color value
# 1  blue     1
# 2  blue     3
# 3  blue     4

答案 4 :(得分:3)

以下是使用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")

答案 5 :(得分:1)

新的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)

答案 6 :(得分:1)

上面的几种解决方案对我来说不起作用。现在有!!函数,我们将其包装在set.seed(123) df <- data.frame( color = c("blue", "black", "blue", "blue", "black"), shape = c("round", "round", "square", "round", "square"), value = 1:5) 中。似乎更简单一些。

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 传递给dplyr函数,以字符串形式输入

setState

答案 7 :(得分:0)

更新。新的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

答案 8 :(得分:0)

这个问题是 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 来创建 list 对象的 call,使用 !!! 运算符强制评估调用,以及将其传递给 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)

该解决方案使用 call,它们是未计算的表达式。 (参见 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)