1

我一直在使用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.frames 似乎是一种非常低效的做事方式。有什么建议?by必须使用。

4

2 回答 2

3

您对j组件的处理方法不是data.table母语

值得一读data.table wiki on do's and don't about data.table 语法(使用data.frame可怕的!,就性能而言)。

您也可以参考 这个问题,也许您会开始了解使用jlist工作原理。

  • 您正在传递将在 data.table (或其分组子集)中评估的表达式列表
  • 这些是未评估的表达式,并且(当前)该函数[依赖于观察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::'+'or.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
于 2012-08-19T23:42:11.920 回答
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
于 2012-08-19T06:20:40.660 回答