9

Consider the following column selection in a data.table:

library(data.table) # using 1.8.7 from r-forge
dt <- data.table(a = 1:5, b = i <- rnorm(5), c = pnorm(i))
dt[, list(a,b)]  #ok

To streamline my code in certain computations with many and variable columns I want to replace list(a,b) with a function. Here is a first try:

.ab <- function()  quote(list(a, b))
dt[, eval(.ab())] #ok - same as above

Ideally, I would like to get rid of eval() from the [.data.table call and confine it to the definition of .ab while at the same time avoid passing the data table dt to the function .ab.

.eab <- function()  eval(quote(list(a, b)))
dt[, .eab()] 
# Error in eval(expr, envir, enclos) : object 'b' not found

What's happening? How can this be fixed?

I suspect what's biting me is R's lexical scoping and the fact that the correct evaluation of list(a,b) relies on it being within the J environment of the data table dt. Alas, I don't know how to fetch a reference to the correct environment and use it as an envir or enclos argument in dt.

# .eab <- function()  eval(quote(list(a, b)), envir = ?, enclos = ?)

EDIT

This approach almost works:

.eab <- function(e)  eval(quote(list(a, b)), envir = e)
dt[, .eab(dt)]

There are two shortcomings: (1) column names are not returned, (2) dt has to be passed explicitly (which i'd rather avoid). I would also rather avoid hardcoding dt as the choice environment. These consideration lead an alternative way of asking the above question: is there a programmatic way to get the environment dt from within .eab?

4

2 に答える 2

5

意図は、関数ではなく式を作成することです。

DT[, list(a,b), by=...]  # ok

.ab = quote(list(a, b))    # simpler here, no need for function()

DT[, eval(.ab), by=...]  # same

このアプローチは、data.table でのグループ化が高速である理由の 1 つです。jすべてのグループに対して静的環境で評価されるため、各関数呼び出しの (わずかな) オーバーヘッドを回避できます。

しかし、.ab何らかの理由で本当に関数である必要がある場合は、さらに検討することができます。

于 2013-02-01T22:12:29.280 に答える
2

警告、これは堅牢でない、遅い、および/または内部機構が変更された場合に壊れる可能性があります[.data.tableが、何らかの理由でそれを回避する方法がない場合は、要件を満たすと思われる関数を次に示します。byのような他のオプションを使用し始めると、機能しないことも想像できます[.data.table

.eab <- function() {
  foo <- quote(list(a,b))
  ans <- eval(foo, envir = parent.frame(3)$x)
  names(ans) <- vapply(as.list(foo)[-1], deparse, character(1))
  ans
}

identical(dt[, .eab()], dt[, list(a,b)])
# TRUE

繰り返しますが、これは正当な理由で存在する多くのコードを破壊/削減しています。

于 2013-02-02T02:59:34.027 に答える