'How do I correctly use the env variable for data.tables within a function

Let us take a simple example

data <- data.table::data.table(a = 1:10, b = 2:11)
j <- quote(c("c") := list(a + 1))
data[, j, env = list(j = j)][]
#        a     b     c
#    <int> <int> <num>
# 1:     1     2     2
# 2:     2     3     3
# 3:     3     4     4
# 4:     4     5     5
# 5:     5     6     6

The above works and produces the correct output. However if I place this inside a function I get a very different output.

data <- data.table::data.table(a = 1:5, b = 2:6)
test <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  j <- call(":=", call("c", names(dots)), unname(dots))
  print(j)
  data[, j, env = list(j = j)][]
}
test(data = data, c = a + 1)
# `:=`(c("c"), list(a + 1))
#        a     b         c
#    <int> <int>    <list>
# 1:     1     2 <call[3]>
# 2:     2     3 <call[3]>
# 3:     3     4 <call[3]>
# 4:     4     5 <call[3]>
# 5:     5     6 <call[3]>

I assume that the c = a + 1 is just not being evaluated in the correct environment (i.e. the data.table itself).

EDIT: I am using data.table 1.14.3



Solution 1:[1]

The difference between

j_quote <- quote(c("c") := list(a + 1))

and

j_call <- call(":=", call("c", names(dots)), unname(dots))

is very subtle:

j_call
#`:=`(c("c"), list(a + 1))
j_quote
#`:=`(c("c"), list(a + 1))

all.equal(j_call,j_quote)
[1] TRUE

identical(j_call,j_quote)
[1] FALSE

This difference lies in the list structure:

# j_quote
as.list(j_quote)
[[1]]
`:=`

[[2]]
c("c")

[[3]]
list(a + 1)

# j_call
as.list(j_call)
[[1]]
`:=`

[[2]]
c("c")

[[3]]
[[3]][[1]]
a + 1

Try:

library(data.table)

data <- data.table::data.table(a = 1:5, b = 2:6)
test <- function(data, ...) {
  dots <- eval(substitute(alist(...)))
  j_call <- call(":=", call("c", names(dots)), call("list",unname(dots)[[1]]))
  j_quote <- quote(c("c") := list(a + 1))
  cat("j_quote",deparse(j_quote),"\n")
  cat("j_call ",deparse(j_call),"\n")
  cat("identical:",identical(j_quote,j_call),"\n")
  data[, j, env = list(j = j_call)][]
}
test(data = data, c = a + 1)
#> j_quote `:=`(c("c"), list(a + 1)) 
#> j_call  `:=`(c("c"), list(a + 1)) 
#> identical: TRUE 
#>        a     b     c
#>    <int> <int> <num>
#> 1:     1     2     2
#> 2:     2     3     3
#> 3:     3     4     4
#> 4:     4     5     5
#> 5:     5     6     6

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1