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