0

R Code

library(data.table)
x <- 4
f1 <- function(){
    x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==get("x", envir=parent.env(environment()))]
}
f1()

I got this:

   x y
1: 1 1
2: 1 5
3: 1 9

f2 is a new function that remove x <- 1 in the function.

f2 <- function(){
    #x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==get("x", envir=parent.env(environment()))]
}
f2()

I got this:

x  y
1: 4  4
2: 4  8
3: 4 12

that's right, my question is how to write a function to replace the get("x", envir=parent.env(environment()))?

Thanks!

liqg3
  • 47
  • 4

3 Answers3

1

I only just realized that the OP is grabbing x from the function's enclosing environment instead of passing it as an argument. I consider this bad practice and don't really have a recommendation for that case. I might delete this answer (which only covers passing x to the function) if it's too much of a distraction.

library(data.table)
dt <- data.table(x=1:4,y=1:12)

ff = function(x, ...){
  mDT = data.table(x)
  dt[mDT, on=.(x), ...]
}

ff(4L, verbose = TRUE)

# Calculated ad hoc index in 0 secs
# Starting bmerge ...done in 0 secs
#    x  y
# 1: 4  4
# 2: 4  8
# 3: 4 12

This only addresses the OP's specific example, of DT[x == get("x", ...)], and not broader expressions. For those, constructing and evaluating an expression should work:

fs = function(x, ...){
  e = substitute(x == ..x, list(..x = x))
  dt[eval(e), ...]
}

fs(4L, verbose = TRUE)

# Creating new index 'x'
# Starting bmerge ...done in 0 secs
#    x  y
# 1: 4  4
# 2: 4  8
# 3: 4 12

fs(3L, verbose = TRUE)

# Using existing index 'x'
# Starting bmerge ...done in 0 secs
#    x  y
# 1: 3  3
# 2: 3  7
# 3: 3 11

The verbose output indicates that fs creates indices, which can be helpful for speed. See vignette("datatable-secondary-indices-and-auto-indexing").


Eventually, there might be syntax so we can simply write ...

dt[..x == x]

perhaps using the proposed inherits = TRUE argument from the link for safety (so that x must be a column and either (i) x must exist in the parent environment or ..x must be a column name).

Frank
  • 66,179
  • 8
  • 96
  • 180
  • I thought `..x` syntax is not a good idea because `..x` can be a legal variable. we may can not understand the meaning of `....a` or `...a`. – liqg3 Jul 29 '17 at 09:46
  • @liqg3 Yeah, that's a good point. If/when `inherits = TRUE` is an option, then the only requirement will be that `..x` not be a column name (so that it will search "one level up" for `x`), I think. Your edit into your question should/could instead be posted by you as an answer down here. – Frank Jul 29 '17 at 14:29
  • 1
    Thanks for your answer, this is the first time for me to see the secondary key of data.table, which is very helpful in big data manipulation. – liqg3 Jul 30 '17 at 06:07
1

@Frank, Thanks! Based on this post variable usage in data.table, I wrote a function:

`..` <- function(x){
    stopifnot(inherits(x, "character"))
    stopifnot(length(x)==1)
    get(x, parent.frame(4))
}
x <- 4
f1 <- function(){
    x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==..("x")]
}
f1()

f2 <- function(){
    #x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==..("x")]
}
f2()

Both f1 and f2 got the correct results!

Why parent.frame(4)?

We see the code first:

current_frame <- sys.nframe()
dt <- data.table()
dt[, sys.nframe() - current_frame]

We got 4, this should be the reason.

liqg3
  • 47
  • 4
0

I found the old solution does not work for data.table 1.11.4, and I wrote a new one:

.. <- function (x, env = parent.frame()) 
{
    stopifnot(inherits(x, "character"))
    stopifnot(length(x) == 1)
    get(x, envir = parent.env(env))
}

x <- 4
f1 <- function(){
    x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==..("x")]
}
f1j <- function(){
    x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[, ..("x")]
}


f2 <- function(){
    #x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[x==..("x")]
}
f2j <- function(){
    #x <- 1
    dt <- data.table(x=1:4,y=1:12)
    dt[,..("x")]
}

stopifnot(all(f1()$y==c(1,5,9)))
stopifnot(all(f1j()==c(1)))

stopifnot(all(f2()$y==c(4,8,12)))
stopifnot(all(f2j()==c(4)))

I have tested that this worked for data.table_1.10.4-3 and data.table_1.11.4.

Actually, I am confused about R's parent.frame and how to find the right variables in R, I just tested some possible ways until I got the expected results.

liqg3
  • 47
  • 4