Pass variable name as argument inside data.table

Generally, quote and eval will work:

library(data.table)
plus <- function(x, y) {
   x + y
}

add_one <- function(data, col) {
   expr0 = quote(copy(data)[, z := plus(col, 1)][])

   expr  = do.call(substitute, list(expr0, list(col = substitute(col))))
   cat("Evaluated expression:\n"); print(expr); cat("\n")

   eval(expr)
}

set.seed(1)
library(magrittr)
data.table(x = 1:10, y = rnorm(10)) %>% 
   add_one(y)

which gives

Evaluated expression:
copy(data)[, `:=`(z, plus(y, 1))][]

     x          y         z
 1:  1 -0.6264538 0.3735462
 2:  2  0.1836433 1.1836433
 3:  3 -0.8356286 0.1643714
 4:  4  1.5952808 2.5952808
 5:  5  0.3295078 1.3295078
 6:  6 -0.8204684 0.1795316
 7:  7  0.4874291 1.4874291
 8:  8  0.7383247 1.7383247
 9:  9  0.5757814 1.5757814
10: 10 -0.3053884 0.6946116

Another option, quoting the column name and using get:

add_one <- function(data, col) {
  copy(data)[, z := plus(get(col), 1)][]
}

add_one(data, "y")

An option would be to extract the unquoted argument as a string with deparse(substitute and specify that in the .SDcols

add_one <- function(data, col) {
   copy(data)[, z := plus(.SD[[1]], 1), .SDcols = deparse(substitute(col))][]
 }

add_one(data, y)
#     x           y          z
# 1:  1  0.50269855  1.5026986
# 2:  2 -0.33022414  0.6697759
# 3:  3  0.57517246  1.5751725
# 4:  4  1.09928586  2.0992859
# 5:  5  0.84683311  1.8468331
# 6:  6 -1.42023443 -0.4202344
# 7:  7  0.04539331  1.0453933
# 8:  8  0.11870596  1.1187060
# 9:  9 -1.11735007 -0.1173501
#10: 10 -1.94834136 -0.9483414

or using get

add_one <- function(data, col) {
   copy(data)[, z := plus(get(deparse(substitute(col)))][]
 }

Or using tidyverse

library(tidyverse)
add_one <- function(data, col, col2) {
   data %>%
         dplyr::mutate(z =plus({{col}}, {{col2}}))
  }

add_one(data, x, y)
#    x           y         z
#1   1 -0.53389875 0.4661013
#2   2  1.28743777 3.2874378
#3   3 -1.26674091 1.7332591
#4   4  0.95017120 4.9501712
#5   5  0.06741833 5.0674183
#6   6 -0.70212949 5.2978705
#7   7 -0.38003803 6.6199620
#8   8 -0.50941072 7.4905893
#9   9  0.54055720 9.5405572
#10 10 -0.87486953 9.1251305

While potentially more error prone, you could rely on ... arguments.

data <- data.table(x = 1:10, y = rnorm(10))

plus <- function(x, y) {
  x + y
}

add_one <- function(data, ...) {
  copy(data)[, z:= plus(data[, ...], 1)][]
}

add_one(data, y)

#or
library(dplyr)
data.table(x = 1:10, y = rnorm(10))%>%
  add_one(y)

     x           y          z
 1:  1 -1.29851891 -0.2985189
 2:  2 -1.36494928 -0.3649493
 3:  3  0.38282492  1.3828249
 4:  4  1.24578886  2.2457889
 5:  5  1.12897695  2.1289770
 6:  6 -0.80122005  0.1987800
 7:  7  1.89093661  2.8909366
 8:  8 -0.34525212  0.6547479
 9:  9 -0.07070159  0.9292984
10: 10 -1.94145962 -0.9414596

Unfortunately, expanding this to multiple variables would lead to failure. Still, you may be able to use the ... to your advantage.

add_one2 <- function(data, ...){
  copy(data)[...][]
}
add_one2(data, , z:=plus(y, 1))

     x          y          z
 1:  1 -0.1565010  0.8434990
 2:  2  0.6516824  1.6516824
 3:  3  0.5355833  1.5355833
 4:  4  0.1941661  1.1941661
 5:  5  0.2994167  1.2994167
 6:  6 -2.5681215 -1.5681215
 7:  7 -1.4587147 -0.4587147
 8:  8  0.9375132  1.9375132
 9:  9  1.3984343  2.3984343
10: 10 -0.6498709  0.3501291

Tags:

R

Data.Table