提问人:kuba 提问时间:7/4/2014 最后编辑:Jaapkuba 更新时间:10/22/2022 访问量:40014
在 dplyr 函数中使用变量名
Use variable names in functions of dplyr
问:
我想在函数中使用变量名作为字符串。请参阅以下示例: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
答:
经常被问到,但仍然没有轻松的支持afaik。但是,关于此帖子:
eval(substitute(filter(df, var == "blue"),
list(var = as.name(var))))
# color value
# 1 blue 1
# 2 blue 3
# 3 blue 4
在较新的版本中,我们可以创建带引号的变量,然后取消引号(或)进行评估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
编辑:重新排列了解决方案的顺序
评论
(!!"term")
Error in !"term" : invalid argument type
Update
term
filter(df, (!!rlang::sym("color")) == "blue")
!!
对于版本 [0.3 - 0.7) (? - 2017 年 6 月)dplyr
(有关更新的 dplyr
版本,请参阅此问题的其他答案)
由于每个使用非标准评估(NSE,请参阅发布帖子和小插图)的函数都有一个标准评估 (SE) 孪生体,下面划线结尾。这些可用于传递变量。因为它将是.使用 u 可以将逻辑条件作为字符串传递。dplyr 0.3
dplyr
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)
评论
从 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 编程小插曲中解释了更一般的情况。
评论
quo
enquo
quo
enquo
quo
以下是使用包中的函数执行此操作的一种方法: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")
评论
filter(UQ(col_name) == UQ(value))
rlang::sym(column_string)
==
filter()
版本 >= 0.4.0 的新功能rlang
.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
更多阅读和示例,请参阅使用 dplyr 编程文章/小插曲。
评论
.data[[var]]
df %>% filter(!.data[[var]] %in% df2[[var]])
.data
{{
上面的几个解决方案对我不起作用。现在有函数,我们把它包装在里面。似乎有点简单。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
评论
dplyr 1.0.1
更新。新功能具有一些出色的新功能,可以更轻松地解决此类问题。您可以在新软件包随附的“编程”小插曲中了解它。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
这个问题发布于 6 年前。 现在最高版本为 1.0.2。然而,这仍然是一个很棒的讨论,对我的问题有很大帮助。我希望能够从列、运算符和值中构造过滤器,这些变量都是由内存中的变量指定的。哦,还有不确定数量的过滤器!dplyr
请考虑以下列表,其中我指定了两个筛选器的列、运算符和值:
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)
该解决方案使用 s,它们是未计算的表达式。(参见 Hadley 的 Advanced R 书籍)基本上,从变量中列出对象,然后在调用时使用运算符强制评估调用。call
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)
评论