3

Let's say I have the following data.table and would like to get the output below by referring to variables stored in a vector:

dt <- data.table(a = rep(1, 3),
                 b = rep(2, 3))

x <- 'a'
y <- 'b'

dt[, .(sum(get(x)), mean(get(y)))]

   V1 V2
1:  3  2

Cool, it works. But now I'd like to make a function, and then do something like:

foo <- function(arg1, arg2) {

  dt[, .(sum(get(arg1)), mean(get(arg2)))]

}

foo(x, y)

Realizing it works, I'd like to avoid calling all those gets, and do something like:

foo <- function(arg1, arg2) {

  eval(substitute(dt[, .(sum(arg1), mean(arg2))]))

}

foo(x, y) # or foo('x', 'y')

But this fails. Any idea on how to evaluate all the arguments at once in a way similar to calling get multiple times?

MarcW
  • 108
  • 8

2 Answers2

4

We can convert to symbol with as.symbol or as.nameand evaluate

foo <- function(arg1, arg2) {

    dt[, .(sum(eval(as.name(arg1))), mean(eval(as.name(arg2))))]

 }

foo(x, y)
#   V1 V2
#1:  3  2

Or use [[ to subset the columns of the data.table

foo <- function(arg1, arg2) {

  dt[, .(sum(.SD[[arg1]]), mean(.SD[[arg2]]))]

 }

foo(x, y)
#   V1 V2
#1:  3  2

Or another option is to paste and eval/parse

 foo <- function(arg1, arg2) {

     eval(parse(text = paste0("dt[, .(sum(", arg1, "), mean(", arg2, "))]")))

   }

foo(x, y)
#   V1 V2
#1:  3  2
akrun
  • 874,273
  • 37
  • 540
  • 662
  • Thanks! This works, but still I need to repeat the `eval` or `.SD`. I will see if there is another answer out there, and if not I accept. – MarcW Feb 23 '20 at 21:30
3

Not sure if this is what you are looking for, another option is to force the as.name conversion before calling substitute:

foo <- function(arg1, arg2) {
    A1 <- as.name(arg1);
    A2 <- as.name(arg2);
    eval(substitute(dt[, .(sum(A1), mean(A2))]))
}

dt <- data.table(a = rep(1, 3), b = rep(2, 3))
foo('a' 'b')

output:

   V1 V2
1:  3  2
chinsoon12
  • 25,005
  • 4
  • 25
  • 35