Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

R: programmatically create a function call

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?

like image 368
Peter Verbeet Avatar asked Mar 22 '16 17:03

Peter Verbeet


Video Answer


1 Answers

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

like image 57
Pierre L Avatar answered Oct 28 '22 14:10

Pierre L