I frequently have to create a function call inside another function, which is then to be evaluated. I tend to use eval(parse(text = "what_needs_to_be_done"))
for that, with the text constructed by using paste0()
. However, this does not feel like a good approach. Here is an example:
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)
This is only an example, in most cases the function to be constructed is more complex and extensive. However, the example shows the general problem. What I do now is first paste0
together the function call, the way I would enter it at the console and then evaluate it.
I have tampered with alternative approaches with substitute
, lazyeval
, bquote
, but I dont't quite understand what they really do and therefore can not get them to work.
Can you help me with finding a better way to contruct the call and subsequently evaluate it?
Update 4.29.17 -- Soon to be released dplyr 0.6.0 will address these issues. New answers have been added to questions below. For more on programming with dplyr, see this vignette.
You have the right idea. You may be able to shorten the code a bit with ?filter_
and the dots argument ...
:
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
update
Programming with dplyr
can be very challenging even for intermediate coders. The author has confessed that the advantages of non-standard evaluation come with the cost of difficulty with respect to functional programming. There are a number of SO users running into the same problem:
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
Steps have been taken to solve these problems. There is a vignette to outline the basic fixes. In my humble opinion though, the vignette is deficient of explanation of functional programming. There isn't one function written as an example. Nor does it address any confounding examples that typically show up. Hopefully, with the increase in calls to fix NSE, we may eventually get a sufficient response.
As a final example of the mess that non-standard evaluation can cause in programming, I tried to work on a solution for this user for awhile to no avail. It is simply asking to use summarise
programmatically:
Sub-function in grouping function using dplyr
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With