2

I am trying to wrap up a function from an R package. From the source codes, it appears that it has non-standard evaluations for some arguments. How can I write my function to pass the value to the argument that has non-standard evaluation implemented?

Here is a toy example

data <- data.frame(name = 1:10)
#Suppose the one below is the function from that package
toy.fun <- function(dat, var) {
    eval(substitute(var), dat)
}
> toy.fun(data, name)
 [1]  1  2  3  4  5  6  7  8  9 10

Here is what I try to wrap it

toy.fun2 <- function(dat, var2) {
    var_name <- deparse(substitute(var2))
    #example, but for similar purpose. 
    data_subset <- dat[var_name] 
    toy.fun(data_subset, var2)
}
> toy.fun2(data, name)
 Error in eval(substitute(var), dat) : object 'var2' not found

Edit

I should make the question more clear, in which I want to pass different variable name to the function argument in the wrapper for var2. So that when there are different variable names in the data, it could use that name for both data selection, and pass to the function I try to wrap. The source codes for exceedance function from heatwaveR has this ts_y <- eval(substitute(y), data) to capture the input variable y already. This is equivalent to my toy.fun.

I have modified the toy.fun2 for clarity.

Edit 2

It turns out the solution is quite easy, just by substituting the entire function including arguments, and evaluating it.

toy.fun2 <- function(dat, var2) {
  var_name <- deparse(substitute(var2))
  #example, but for similar purpose.
  data_subset <- dat[var_name]
  exprs <- substitute(toy.fun(data_subset, var2))
  eval(exprs, parent.frame()) 
  #include `envir` argument if this is to be worked in data.table
}
Fred
  • 579
  • 2
  • 4
  • 13
  • 1
    Non-standard evaluation is non-standard. What works for `toy.fun` is not necessarily going to work for some other non-standard evaluation. You should specify which function you're trying to wrap. – user2554330 Nov 28 '21 at 13:13
  • For example, @DonaldSeinen's answer below works fine for your example, but might not work in tidyverse-style NSE, which doesn't fully support dots arguments. E.g. see https://github.com/tidyverse/glue/issues/231 . – user2554330 Nov 28 '21 at 13:18
  • @user2554330 Thanks for the suggestion. I have modified the question, and hope it provides more clarity this time. – Fred Nov 28 '21 at 14:10

1 Answers1

0

Grab the call using match.call, replace the function name and evaluate it. It would also be possible to modify the other arguments as needed. Look at the source of lm to see another example of this approach.

toy.fun2 <- function(dat, var) {
  cl <- match.call()
  cl[[1L]] <- quote(toy.fun)
  eval.parent(cl)
}
toy.fun2(data, name)
##  [1]  1  2  3  4  5  6  7  8  9 10

Added

The question was revised after this answer was already posted. This addresses the new question. The line setting cl[[2]] could alternately be written as cl[[2]] <- dat[deparse(cl[[3]])] .

toy.fun2 <- function(dat, var2) {
  cl <- match.call()
  cl[[1]] <- quote(toy.fun)
  cl[[2]] <- dat[deparse(substitute(var2))]
  names(cl)[3] <- "var"
  eval.parent(cl)
}
toy.fun2(data, name)
##  [1]  1  2  3  4  5  6  7  8  9 10
G. Grothendieck
  • 254,981
  • 17
  • 203
  • 341
  • It's not exactly the answer for my actual problem, but did give me an idea. I could `substitute` the entire `toy.fun` function call, and the variables inside will automatically be substituted. Evaluating that expression will give me the result. – Fred Nov 30 '21 at 03:42
  • The eval.parent(substitute(...)) paradigm basically defines a macro and an interface is given by the defmacro function in the gtools package. – G. Grothendieck Nov 30 '21 at 11:03