Add multiple columns to R data.table in one function call?

To build on the previous answer, one can use lapply with a function that output more than one column. It's is then possible to use the function with more columns of the data.table.

 myfun <- function(a,b){
     res1 <- a+b
     res2 <- a-b
     list(res1,res2)
 }

 DT <- data.table(z=1:10,x=seq(3,30,3),t=seq(4,40,4))
 DT

 ## DT
 ##     z  x  t
 ## 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
 ##     z  x  t 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

The answer can not be used such as when the function is not vectorized.

For example in the following situation it will not work as intended:

myfun <- function (y, v, g) 
{
  ret1 = y + v + length(g)
  ret2 = y - v + length(g)
  return(list(r1 = ret1, r2 = ret2))
}
DT
#    v y                  g
# 1: 1 1                  1
# 2: 1 3                4,2
# 3: 1 6              9,8,6

DT[,c("new1","new2"):=myfun(y,v,g)]
DT
#    v y     g new1 new2
# 1: 1 1     1    5    3
# 2: 1 3   4,2    7    5
# 3: 1 6 9,8,6   10    8

It will always add the size of column g, not the size of each vector in g

A solution in such case is:

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

Since data.table v1.8.3, you can do this:

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

Another option is storing the output of the function and adding the columns one-by-one:

z <- myfun(DT$y,DT$v)
head(DT[,new1:=z$r1][,new2:=z$r2])
#      x y  v 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

Tags:

R

Data.Table