我正在尝试编写一个函数,允许我输入field
,operator
和value
的文本字符串,并返回一个简单的dplyr::filter
函数,然后我可以应用于数据集。
的例子:
library(dplyr)
field <- "Species"
operator <- "=="
value <- "virginica"
myfun <- substitute(
function(x) filter(x, EXPR(FIELD, VALUE)),
list(
FIELD = as.symbol(field),
EXPR = as.symbol(operator),
VALUE = value
)
)
myfun
function(x) filter(x, Species == "virginica")
到目前为止,一切顺利,对吧?看来我们都准备好出发了。但不要这么快:
> myfun(iris)
Error in myfun(iris) (from foo.R!10Zf0E#19) : could not find function "myfun"
如果我输入class(myfun)
,结果是我创建了一个叫做call
的东西。但我想要一个函数。是否有一种方法可以将调用转换为函数,或者重写上面的代码,以便我实际上最终得到一个工作函数?
这很简单。解决方案是用eval()
包装substitute()
,如下所示:
> myfun <- eval(
substitute(
function(x) filter(x, EXPR(FIELD, VALUE)),
list(
FIELD = as.symbol(field),
EXPR = as.symbol(operator),
VALUE = value
)
)
)
则myfun(iris)
按预期工作。
更多"R"方法是使用formals
和body
f <- function() {}
formals(f) <- alist(x = )
body(f) <- substitute(
filter(x, EXPR(FIELD, VALUE)),
list(FIELD = as.symbol(field), EXPR = as.symbol(operator), VALUE = value)
)
f
# function (x)
# filter(x, Species == "virginica")
f(iris)
另外,你可以在函数中添加形式和行:
formals(f) <- c(formals(f), alist(y = 120))
f
# function (x, y = 120)
# filter(x, Species == "virginica")
body(f) <- as.call(c(as.name('{'), quote(x <- head(x, y)), body(f)))
f
# function (x, y = 120)
# {
# x <- head(x, y)
# filter(x, Species == "virginica")
# }
f(iris)
并编辑函数的行:
body(f)[[2]] <- quote(x <- tail(x, y))
# function (x, y = 120)
# {
# x <- tail(x, y)
# filter(x, Species == "virginica")
# }
f(iris)
我们可以动态创建过滤器表达式,并使用eval(parse())
或eval(parse_expr())
。
myfun <- function(x, field, operator, value) {
dplyr::filter(x, eval(rlang::parse_expr(sprintf('%s %s "%s"',
field, operator, value))))
}
field <- "Species"
operator <- "=="
value <- "virginica"
myfun(iris, field, operator, value)
# Sepal.Length Sepal.Width Petal.Length Petal.Width Species
#1 6.3 3.3 6.0 2.5 virginica
#2 5.8 2.7 5.1 1.9 virginica
#3 7.1 3.0 5.9 2.1 virginica
#4 6.3 2.9 5.6 1.8 virginica
#5 6.5 3.0 5.8 2.2 virginica
#6 7.6 3.0 6.6 2.1 virginica
#7 4.9 2.5 4.5 1.7 virginica
#8 7.3 2.9 6.3 1.8 virginica
#9 6.7 2.5 5.8 1.8 virginica
#10 7.2 3.6 6.1 2.5 virginica
#...
#...
,
sprintf('%s %s "%s"', field, operator, value)
#[1] "Species == "virginica""