Vitalijs Jascisens Vitalijs Jascisens - 3 months ago 12
R Question

functions with data.table variables which names are stored in a character vector

I am not a big data.table expert but I am somehow puzzled by some things. Here is my simple example:

test<-data.table(x= 1:10,y= 1:10,z= 1:10, l = 11:20,d= 21:30)
test<-test[,..I:=.I]
vec_of_names = c("z","l","d")

function_test<-function(x,y){
sum(x)+y
}

vec_of_final_names<-c("sum_z","sum_l","sum_d")


When I then attempt do to something like this:

for (i in 1:length(vec_of_names)){
test<-test[,vec_of_final_names[i]:=function_test(x=.SD,y=eval(parse(text=vec_of_names[i]))),.SDcols=c("x","y"),by=..I]
}


I get an error:

Error in eval(expr, envir, enclos) : object 'z' not found


Whereas code below works perfectly fine but is a little bit ugly and also slow. Maybe somebody can suggest better alternatives.

for (i in 1:length(vec_of_names)){
test<-test[,vec_of_final_names[i]:=function_test(x=eval(parse(text=paste("c(",paste(c("x","y"),collapse=","),")",sep=""))),y=eval(parse(text=vec_of_names[i]))),by=..I]
}

Answer

After specifying the .SDcols and grouped by = ..I (the ..I is a strange name for a column name), we unlist the .SD, get the sum, get the values of 'vec_of_names' in a list with mget, do the + of corresponding elements of this with the sum(unlist(.SD)) and assign (:=) it to 'vec_of_final_names' to create new columns

test[, (vec_of_final_names) := Map(`+`, sum(unlist(.SD)), 
                    mget(vec_of_names)), by = ..I, .SDcols = x:y]

Based on the example, this can also be done without the grouping variable

test[, (vec_of_final_names) := Map(`+`, list(x+y), mget(vec_of_names))]

Or by specifying the .SDcols

test[, (vec_of_final_names) := Map(`+`, list(Reduce(`+`, .SD)), 
                            mget(vec_of_names)), .SDcols = x:y]

Or using the OP's function

test[, (vec_of_final_names) := Map(function_test, list(unlist(.SD)), 
                mget(vec_of_names)), ..I, .SDcols = x:y]
test
#      x  y  z  l  d ..I sum_z sum_l sum_d
# 1:  1  1  1 11 21   1     3    13    23
# 2:  2  2  2 12 22   2     6    16    26
# 3:  3  3  3 13 23   3     9    19    29
# 4:  4  4  4 14 24   4    12    22    32
# 5:  5  5  5 15 25   5    15    25    35
# 6:  6  6  6 16 26   6    18    28    38
# 7:  7  7  7 17 27   7    21    31    41
# 8:  8  8  8 18 28   8    24    34    44
# 9:  9  9  9 19 29   9    27    37    47
#10: 10 10 10 20 30  10    30    40    50