在这个简单的例子中,如何使函数传递到data.table中的j?

时间:2013-11-21 13:14:51

标签: r data.table

dt <- data.table(x=1:4, y=c(1,1,2,2), z=c(1,2,1,2))

我想实现这个目标:

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

但是通过像test(dt, z, x, y)

这样的函数调用

以下两种方式均无效。 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

使它运作的正确方法是什么?

2 个答案:

答案 0 :(得分:3)

您可以通过以下方式使用deparsesubstituteevalparse。可能有更简单的解决方案,但以下似乎有效。

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

或@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

答案 1 :(得分:1)

这是@Chinmay Patil第二个答案的(IMO)清洁版。它使用R的反引号运算符以lispy方式构建表达式,然后逐出引用的表达式。

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
>