data.table中的j参数的返回类型

时间:2012-08-19 03:56:42

标签: r data.table apply

我一直在使用data.table进行一些计算,并且想知道j参数的可能返回类型是什么,以便它正确地堆叠我的输出?我知道data.frame是可以接受的,所以list也必须如此?我的函数为每个id返回多行和多列。想象一下:

dtb <- data.table(id=rep(1:5,20), a=1:100, b=sample(1:100, 100), c=sample(1:100, 100))
f <- function(dt) { return(c(dt$a+1, dt$b+1, dt$c+1))}
dtb[,f(.SD), by=id]

这显然不能正常工作。这样做:

dtb <- data.table(id=rep(1:5,20), a=1:100, b=sample(1:100, 100), c=sample(1:100, 100))
f <- function(dt) { return(data.frame(a=dt$a+1, b=dt$b+1, c=dt$c+1))}
dtb[,f(.SD), by=id]

构建这些data.frame似乎是一种非常低效的做事方式。有什么建议吗?必须使用by

2 个答案:

答案 0 :(得分:3)

您对j组件的处理方式不是原生data.table - 说

值得阅读关于do的data.table wiki而不是关于data.table语法(使用data.frame 可怕!,就性能而言)。

您也可以参考this question,也许您会开始了解使用jlist的工作原理。

  • 您正在传递将在data.table(或其分组子集)中评估的表达式列表
  • 这些是 unvaulated 表达式,而(currentl)函数[依赖于观察list以在正确的环境(data.table.SD,群组子集)

此通话将有效

    dtb[,list(a = a+1, b = b + 1, c = c+1), by = id]

就像这样(传递一个无关紧要的表达,恰好是对list(...)的调用

library(plyr) # for as.quoted
my_list <- as.quoted(paste('list(',paste(letters[1:3], '=', letters[1:3], '+1',collapse= ','),')'))[[1]]
my_list
## list(a = a + 1, b = b + 1, c = c + 1)
dtb[,eval(my_list), by = id]

还可以将lapply(.SD, a_function)的调用与.SDcols结合使用。 .SDcols参数允许您传递要在其上评估函数的列名称字符串,因此这将起作用

dtb[, lapply(.SD,base::'+',1),by= id, .SDcols = c('a','b','c')]

dtb[,lapply(.SD, .Primitive('+'),1), by= id, .SDcols = c('a','b','c')]

请注意,我调用了base::'+'.Primitive('+')而不是'+',因为data.table无法找到'+'作为函数

基准

对这些解决方案进行基准测试

benchmark(
  lstdt=dtb[ , flst(.SD), by=id], 
  dfdt=dtb[ , fdf(.SD), by=id], 
  lapplySD = dtb[, lapply(.SD,base::'+',1),by= id, .SDcols = c('a','b','c')],
  lapplySD2 = dtb[, lapply(.SD,.Primitive('+'),1),by= id, .SDcols = c('a','b','c')]
  just_list = dtb[,list(a = a+1,b=b+1,c=c+1),b=id],
  eval_mylist = dtb[,eval(my_list),b=id],
  replications=10^2

##             test replications elapsed relative user.self 
##  2        dfdt          100    0.36 4.000000      0.34       
##  6 eval_mylist          100    0.09 1.000000      0.10       
##  5   just_list          100    0.11 1.222222      0.10        
##  3    lapplySD          100    0.14 1.555556      0.14  
##  4   lapplySD2          100    0.11      1.1      0.11  
##  1       lstdt          100    0.18 2.000000      0.17
  • 未评估的表达式(传递表达式列表)是禁食的,这与Matthew Dowle在this previous question中的分数一致

答案 1 :(得分:2)

当你写下这个c(dt$a+1, dt$b+1, dt$c+1)时,你 应该期望一个向量(加上组ID列。请尝试这样做:

dtb <- data.table(id=rep(1:5,20), a=1:100, b=sample(1:100, 100), c=sample(1:100, 100))
f <- function(dt) { return(list(dt$a+1, dt$b+1, dt$c+1))}
dtb[,f(.SD), by=id]

EDIT2(我之前的编辑中出现错误,我在发布完整代码时才注意到)。关于“更便宜”的问题:这是一个基准测试,显示列表构造“更便宜”:

flst <- function(dt) { return(list(dt$a+1, dt$b+1, dt$c+1))}
fdf <- function(dt) { return(data.frame(dt$a+1, dt$b+1, dt$c+1))}
require(rbenchmark)
 benchmark(
    lstdt=dtb[ , flst(.SD), by=id], 
    dfdt=dtb[ , fdf(.SD), by=id], 
    replications=10^2
    )
   test replications elapsed relative user.self sys.self user.child sys.child
2  dfdt          100   0.466  2.89441     0.457    0.010          0         0
1 lstdt          100   0.161  1.00000     0.159    0.003          0         0