从data.table到eval的函数创建表达式

Jus*_*tin 23 r data.table

鉴于data.tabledat:

dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10) 
Run Code Online (Sandbox Code Playgroud)

我想要一个函数,它在两个相似的行之间创建一个表达式,给出它们的"根"名称,例如x_one - x_two.

myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')

  parse(text=paste(one, '-', two))
}
Run Code Online (Sandbox Code Playgroud)

现在,只使用一个根名称可以按预期工作,并生成一个向量.

dat[, eval(myfun('x')),]

[1] 0 0 0 0 0 0 0 0 0 0
Run Code Online (Sandbox Code Playgroud)

但是,尝试使用该list技术为该输出分配名称失败:

dat[, list(x_out = eval(myfun('x'))),]

Error in eval(expr, envir, enclos) : object 'x_one' not found
Run Code Online (Sandbox Code Playgroud)

我可以通过添加一个"解决"这个with(dat, ...),但是这几乎是data.table十岁上下

dat[, list(x_out = with(dat, eval(myfun('x'))),
           y_out = with(dat, eval(myfun('y')))),]

    x_out y_out
 1:     0     0
 2:     0     0
 3:     0     0
 4:     0     0
 5:     0     0
 6:     0     0
 7:     0     0
 8:     0     0
 9:     0     0
10:     0     0
Run Code Online (Sandbox Code Playgroud)

如果我想要一个像上面那样的输出,那么生成和评估这些表达式的正确方法是什么?

如果它有帮助,sessionInfo()输出低于.我记得能够做到这一点,或接近它的东西,但它已经有一段时间了,并且data.table自......

R version 2.15.1 (2012-06-22)

Platform: x86_64-pc-linux-gnu (64-bit)

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C               LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8    LC_PAPER=C                 LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C             LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

attached base packages:
[1] graphics  grDevices utils     datasets  stats     grid      methods   base     

other attached packages:
 [1] Cairo_1.5-1      zoo_1.7-7        stringr_0.6.1    doMC_1.2.5       multicore_0.1-7  iterators_1.0.6  foreach_1.4.0   
 [8] data.table_1.8.2 circular_0.4-3   boot_1.3-5       ggplot2_0.9.1    reshape2_1.2.1   plyr_1.7.1      

loaded via a namespace (and not attached):
 [1] codetools_0.2-8    colorspace_1.1-1   dichromat_1.2-4    digest_0.5.2       labeling_0.1       lattice_0.20-6    
 [7] MASS_7.3-20        memoise_0.1        munsell_0.3        proto_0.3-9.2      RColorBrewer_1.0-5 scales_0.2.1      
[13] tools_2.15.1      
Run Code Online (Sandbox Code Playgroud)

mne*_*nel 19

一种解决方案是将list(...)函数输出.

我倾向于使用as.quoted,从@hadley .()plyr包中实现的方式窃取.

library(data.table)
library(plyr)
dat <- data.table(x_one=1:10, x_two=1:10, y_one=1:10, y_two=1:10) 
myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')
  out <- paste0(name,'_out')
 as.quoted(paste('list(',out, '=',one, '-', two,')'))[[1]]
}


dat[, eval(myfun('x')),]

#    x_out
# 1:     0
# 2:     0
# 3:     0
# 4:     0
# 5:     0
# 6:     0
# 7:     0
# 8:     0
# 9:     0
#10:     0
Run Code Online (Sandbox Code Playgroud)

要一次执行两列,您可以调整通话

myfun <- function(name) {
  one <- paste0(name, '_one')
  two <- paste0(name, '_two')
  out <- paste0(name,'_out')
  calls <- paste(paste(out, '=', one, '-',two), collapse = ',')


 as.quoted(paste('list(', calls,')'))[[1]]
}


dat[, eval(myfun(c('x','y'))),]

#   x_out y_out
# 1:     0     0
# 2:     0     0
# 3:     0     0
# 4:     0     0
# 5:     0     0
# 6:     0     0
# 7:     0     0
# 8:     0     0
# 9:     0     0
# 0:     0     0
Run Code Online (Sandbox Code Playgroud)

至于原因.....

在这个解决方案中,整个调用' list(..)在parent.frame中被评估为data.table.

其中的相关代码[.data.table

if (missing(j)) stop("logical error, j missing")
jsub = substitute(j)
if (is.null(jsub)) return(NULL)
jsubl = as.list.default(jsub)
if (identical(jsubl[[1L]],quote(eval))) {
    jsub = eval(jsubl[[2L]],parent.frame())
    if (is.expression(jsub)) jsub = jsub[[1L]]
}
Run Code Online (Sandbox Code Playgroud)

如果(在你的情况下)

j = list(xout = eval(myfun('x'))) 

##then

jsub <- substitute(j) 
Run Code Online (Sandbox Code Playgroud)

 #  list(xout = eval(myfun("x")))
Run Code Online (Sandbox Code Playgroud)

as.list.default(jsub)
## [[1]]
## list
## 
## $xout
## eval(myfun("x"))
Run Code Online (Sandbox Code Playgroud)

所以jsubl[[1L]]就是list,jsubl[[2L]]eval(myfun("x"))

所以data.table没有找到一个电话eval,也不会适当地处理它.

这将起作用,迫使第二次评估在正确的data.table内

# using OP myfun
dat[,list(xout =eval(myfun('x'), dat))]
Run Code Online (Sandbox Code Playgroud)

一样的方法

eval(parse(text = 'x_one'),dat)
# [1]  1  2  3  4  5  6  7  8  9 10
Run Code Online (Sandbox Code Playgroud)

工作但是

 eval(eval(parse(text = 'x_one')), dat)
Run Code Online (Sandbox Code Playgroud)

才不是

编辑10/4/13

尽管使用它.SD作为环境可能更安全(但更慢),因为它对于环境来说是健壮的,i或者by也是如此

dat[,list(xout =eval(myfun('x'), .SD))]
Run Code Online (Sandbox Code Playgroud)

马修编辑:

+10到以上.我自己无法解释得更好.更进一步,我有时会做的是构建整个 data.table查询,然后再进行构建eval.有时,它可能会更加健壮.我认为它像SQL; 即,我们经常构造一个动态SQL语句,该语句被发送到要执行的SQL服务器.在进行调试时,有时也可以更容易地查看构造的查询并在浏览器提示符下运行它.但是,有时这样的查询会很长,所以传递evali,j或者by可以按不重新计算其他组件更有效.像往常一样,很多方法可以给猫皮肤.

考虑eval整个查询的微妙原因包括:

  1. 分组快速的一个原因是它j首先检查表达式.如果它是a list,它会删除名称,但会记住它们.然后它是每个组eval未命名列表,然后在最终结果的最后恢复名称一次.其他方法可能很慢的一个原因是一遍又一遍地为每个组重新创建相同的列名矢量.j虽然定义的越复杂(例如,如果表达式没有精确地开始list),内部编码检查逻辑就越难.这个领域有很多测试; 例如,eval如果名称丢失不起作用,则与详细报告相结合.但是,eval由于这个原因,构建一个"简单"查询(完整查询)和更快更强大的查询.

  2. 随着v1.8.2,现在优化j:options(datatable.optimize=Inf).到目前为止,这将检查j和修改它以优化meanlapply(.SD,...)成语.这使得数量级差异并且意味着用户需要知道的更少(例如,现在一些维基点已经消失).我们可以做更多的事情; 例如,如果[2014年9月更新 - 现在在v1.9.3中实施] ,DT[a==10]可以DT[J(10)]自动优化key(DT)[1]=="a".但同样,内部优化在内部进行编码变得更加困难,而不是例如包含调用DT[,mean(a),by=b]DT[,list(x=eval(expr)),by=b]地方.因此整个查询可能会发挥得更好.在报告中详细说明它正在做什么,并且如果需要可以关闭优化; 例如,测试它所产生的速度差异.exprmeanevaldatatable.optimize

根据评论,FR#2183已添加:"将j = list(xout = eval(...))的eval更改为在DT范围内的eval".感谢您的重点.这就是j我所说的复合体,eval它嵌套在表达式中.但是,如果j 开始eval,它更简单并且已经编码(如上所示)并经过测试,应该进行优化.

如果有一个外卖,那么它是:当用于动态查询时,使用DT[...,verbose=TRUE]options(datatable.verbose=TRUE)检查data.table仍然有效eval.