在一个函数调用中将多个列添加到R data.table?

我有一个函数返回列表中的两个值。 这两个值都需要添加到一个data.table两列中。 function的评估是昂贵的,所以我想避免不得不两次计算function。 这是一个例子:

library(data.table) example(data.table) DT xyv 1: a 1 42 2: a 3 42 3: a 6 42 4: b 1 4 5: b 3 5 6: b 6 6 7: c 1 7 8: c 3 8 9: c 6 9 

这是我的function的一个例子。 请记住,我说这是昂贵的计算,最重要的是无法从其他给定值中推导出一个返回值(如下例所示):

 myfun <- function (y, v) { ret1 = y + v ret2 = y - v return(list(r1 = ret1, r2 = ret2)) } 

这是我在一个语句中添加两列的方法。 但是,这需要两次致电myfun:

 DT[,new1:=myfun(y,v)$r1][,new2:=myfun(y,v)$r2] xyv new1 new2 1: a 1 42 43 -41 2: a 3 42 45 -39 3: a 6 42 48 -36 4: b 1 4 5 -3 5: b 3 5 8 -2 6: b 6 6 12 0 7: c 1 7 8 -6 8: c 3 8 11 -5 9: c 6 9 15 -3 

任何build议如何做到这一点? 每次我打电话给myfun时,我都可以将r2保存在一个单独的环境中,我只需要一种方法来一次添加两个引用列。

你可以存储函数调用的输出:

 z <- myfun(DT$y,DT$v) head(DT[,new1:=z$r1][,new2:=z$r2]) # xyv new1 new2 # [1,] a 1 42 43 -41 # [2,] a 3 42 45 -39 # [3,] a 6 42 48 -36 # [4,] b 1 4 5 -3 # [5,] b 3 5 8 -2 # [6,] b 6 6 12 0 

但是这也似乎工作:

 DT[, c("new1","new2") := myfun(y,v), with = FALSE] 

为了data.table起见,在R-Forge的data.table v1.8.3中data.table with = FALSE

 DT[, c("new1","new2") := myfun(y,v)] 

最新的现场新闻就在这里 。

为了构build上一个答案,可以使用lapply来输出一个以上的列。 那么可以使用data.table中更多列的函数。

  myfun <- function(a,b){ res1 <- a+b res2 <- ab list(res1,res2) } DT <- data.table(z=1:10,x=seq(3,30,3),t=seq(4,40,4)) DT ## DT ## zxt ## 1: 1 3 4 ## 2: 2 6 8 ## 3: 3 9 12 ## 4: 4 12 16 ## 5: 5 15 20 ## 6: 6 18 24 ## 7: 7 21 28 ## 8: 8 24 32 ## 9: 9 27 36 ## 10: 10 30 40 col <- colnames(DT) DT[, paste0(c('r1','r2'),rep(col,each=2)):=unlist(lapply(.SD,myfun,z), recursive=FALSE),.SDcols=col] ## > DT ## zxt r1z r2z r1x r2x r1t r2t ## 1: 1 3 4 2 0 4 2 5 3 ## 2: 2 6 8 4 0 8 4 10 6 ## 3: 3 9 12 6 0 12 6 15 9 ## 4: 4 12 16 8 0 16 8 20 12 ## 5: 5 15 20 10 0 20 10 25 15 ## 6: 6 18 24 12 0 24 12 30 18 ## 7: 7 21 28 14 0 28 14 35 21 ## 8: 8 24 32 16 0 32 16 40 24 ## 9: 9 27 36 18 0 36 18 45 27 ## 10: 10 30 40 20 0 40 20 50 30 

答案不能使用,例如当函数没有被vector化时。

例如在下面的情况下,它不会按预期工作:

 myfun <- function (y, v, g) { ret1 = y + v + length(g) ret2 = y - v + length(g) return(list(r1 = ret1, r2 = ret2)) } DT # vyg # 1: 1 1 1 # 2: 1 3 4,2 # 3: 1 6 9,8,6 DT[,c("new1","new2"):=myfun(y,v,g)] DT # vyg new1 new2 # 1: 1 1 1 5 3 # 2: 1 3 4,2 7 5 # 3: 1 6 9,8,6 10 8 

它将总是添加g列的大小,而不是每个vector的大小

这种情况下的解决scheme是:

 DT[, c("new1","new2") := data.table(t(mapply(myfun,y,v,g)))] DT # vyg new1 new2 # 1: 1 1 1 3 1 # 2: 1 3 4,2 6 4 # 3: 1 6 9,8,6 10 8 

为什么不让你的函数直接返回一个dataframe?

 myfun <- function (DT) { DT$ret1 = with(DT, y + v) DT$ret2 = with(DT, y - v) return(DT) }