R:以编程方式创建函数调用

时间:2016-03-22 17:52:34

标签: r dplyr lazy-evaluation

我经常需要在另一个函数内创建一个函数调用,然后对其进行求值。我倾向于使用eval(parse(text = "what_needs_to_be_done")) 为此,使用paste0()构建文本。但是,这并不是一种好方法。这是一个例子:

select_data <- function(x, A = NULL, B = NULL, C = NULL) {
  kall <- as.list(match.call())
  vars <- names(kall)[names(kall) %in% c("A", "B", "C")]
  selection_criteria <- paste0(vars,  " == ", kall[vars], collapse = ", ")
  txt <- paste0("dplyr::filter(x, ", selection_criteria, ")")
  res <- eval(parse(text = txt))
  return(res)
}   

DF <- data.frame(A = c(1,1,2,2,3,3), B = c(1,2,1,2,1,2), C = c(1,1,1,2,2,2))
select_data(DF, A = 2, C = 2)

这只是一个例子,在大多数情况下,要构建的功能更加复杂和广泛。但是,该示例显示了一般问题。我现在做的是首先paste0函数调用,我在控制台输入它然后评估它的方式。

我已经篡改了substitutelazyevalbquote的替代方法,但我不太明白他们真正做了什么,因此无法让他们工作。

你能帮助我找到一种更好的方法来构建呼叫并随后对其进行评估吗?

1 个答案:

答案 0 :(得分:10)

更新4.29.17 - 即将发布的dplyr 0.6.0将解决这些问题。以下问题增加了新的答案。有关使用dplyr进行编程的更多信息,see this vignette

你有正确的想法。您可以使用?filter_和点参数...稍微缩短代码:

select_data <- function(x, ...) {
  kall <- list(...)
  filter_(.data=x, paste0(names(kall), "==", unlist(kall), collapse="&"))
}
select_data(DF, A = 2, C = 2)
#   A B C
# 1 2 2 2

<强>更新

使用dplyr进行编程即使对于中间编码人员也非常具有挑战性。作者承认,非标准评估的优势伴随着功能编程的难度。有许多SO用户遇到同样的问题:

standard evaluation in dplyr

dplyr function does not work

Using dplyr functions within another function

Major dplyr functions in a function Pass arguments to dplyr functions

dplyr: filter where two columns in data.frame are equal

已采取措施解决这些问题。有一个vignette to outline the basic fixes。然而,在我的拙见中,小插图缺乏对函数式编程的解释。没有一个函数作为示例编写。它也没有解决通常出现的任何混淆的例子。希望随着修复NSE的呼声增加,我们最终可能得到足够的响应。

作为非标准评估在编程中可能造成的混乱的最后一个例子,我试图为这个用户解决一段时间无法解决的问题。它只是要求以编程方式使用summarise

Sub-function in grouping function using dplyr