Reputation: 21737
dt <- data.table(x=1:4, y=c(1,1,2,2), z=c(1,2,1,2))
I would likt to achieve this:
dt[,list(z, p=cumsum(x)), by=y]
y z p
1: 1 1 1
2: 1 2 3
3: 2 1 3
4: 2 2 7
But via a function call like test(dt, z, x, y)
None of the following 2 ways works. data.table 1.8.10
test1 <- function(dt, a, b, c){
dt[,list(eval(substitute(a), parent.frame()),
p=cumsum(eval(substitute(b), parent.frame()))),
by=eval(substitute(c)), verbose=TRUE]
}
test1(dt, z, x, y)
# Error in eval(expr, envir, enclos) : object 'a' not found
test2 <- function(dt, a, b, c){
dt[,list(eval(substitute(a)),
p=cumsum(eval(substitute(b)))),
by=eval(substitute(c)), verbose=TRUE]
}
test2(dt, z, x, y)
# Error in eval(expr, envir, enclos) : object 'z' not found
What is a correct way to make it work?
Upvotes: 2
Views: 160
Reputation: 7784
Here's a (IMO) cleaner version of @Chinmay Patil's second answer. It builds up the expression in a lispy way using R's backquote operator, and then evals the quoted expression.
test = function(dt, a, b, c) {
z = substitute(a)
x = substitute(b)
y = substitute(c)
expr = bquote(dt[, list(.(z), p=cumsum(.(x))), by=.(y)])
eval(expr)
}
> test(dt, z, x, y)
y z p
1: 1 1 1
2: 1 2 3
3: 2 1 3
4: 2 2 7
>
Upvotes: 1
Reputation: 17189
You can use deparse
, substitute
, eval
and parse
in following way. There maybe simpler solution, but following seem to work.
test1 <- function(dt, a, b, c){
jvar <- paste0('list(',deparse(substitute(a)),', p=cumsum(',deparse(substitute(b)),'))')
byvar <- paste0('list(', deparse(substitute(c)),')')
dt[, eval(parse(text=jvar)), by=eval(parse(text=byvar))]
}
test1(dt, z, x, y)
## y z p
## 1: 1 1 1
## 2: 1 2 3
## 3: 2 1 3
## 4: 2 2 7
or as @eddi sugguested
test2 <- function(dt, a, b, c){
eval(parse(text = paste0('dt[,', 'list(',deparse(substitute(a)),', p=cumsum(',deparse(substitute(b)),'))', ',by=', 'list(', deparse(substitute(c)),')', ']') ))
}
test2(dt, z, x, y)
## y z p
## 1: 1 1 1
## 2: 1 2 3
## 3: 2 1 3
## 4: 2 2 7
Upvotes: 3