The package data.table has some special syntax that requires one to use expressions as the i and j arguments.
This has some implications for how one write functions that accept and pass arguments to data tables, as is explained really well in section 1.16 of the FAQs.
But I can’t figure out how to take this one additional level.
Here is an example. Say I want to write a wrapper function foo() that makes a specific summary of my data, and then a second wrapper plotfoo() that calls foo() and plots the result:
library(data.table)
foo <- function(data, by){
by <- substitute(by)
data[, .N, by=list(eval(by))]
}
DT <- data.table(mtcars)
foo(DT, gear)
OK, this works, because I get my tabulated results:
by N
1: 4 12
2: 3 15
3: 5 5
Now, I try to just the same when writing plotfoo() but I fail miserably:
plotfoo <- function(data, by){
by <- substitute(by)
foo(data, eval(by))
}
plotfoo(DT, gear)
But this time I get an error message:
Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
OK, so the eval() is causing a problem. Let’s remove it:
plotfoo <- function(data, by){
by <- substitute(by)
foo(data, by)
}
plotfoo(DT, gear)
Oh no, I get a new error message:
Error in `[.data.table`(data, , .N, by = list(eval(by))) :
column or expression 1 of 'by' or 'keyby' is type symbol. Do not quote column names. Useage: DT[,sum(colC),by=list(colA,month(colB))]
And here is where I remain stuck.
Question: How to write a function that calls a function that calls data.table?
This will work:
Explanation:
The problem is that your call to
foo()inplotfoo()looks like one of the following:When
fooprocesses those calls, it dutifullysubstitutes for the second formal argument (by) getting asby‘s value the symbolseval(by)orby. But you wantby‘s value to begear, as in the callfoo(data, gear).do.call()solves this problem by evaluating the elements of its second argument before constructing the call that it then evaluates. As a result, when you pass itby, it evaluates it to its value (the symbolgear) before constructing a call that looks (essentially) like this: