你可以在R中创建一个函数从文本字符串通过使用substitute()?



我正在尝试编写一个函数,允许我输入field,operatorvalue的文本字符串,并返回一个简单的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"方法是使用formalsbody

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""

最新更新