我一直在使用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
。
答案 0 :(得分:3)
您对j
组件的处理方式不是原生data.table
- 说
值得阅读关于do的data.table wiki而不是关于data.table语法(使用data.frame
可怕!,就性能而言)。
您也可以参考this question,也许您会开始了解使用j
和list
的工作原理。
[
依赖于观察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
答案 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