4

Having a data.table

library(data.table)
dd <- data.table(x=1:10,y=10:1,z=20:20)

I can filter it using

dd[x %in% c(1, 3) & z %in% c(12, 20)]
   x  y  z
1: 1 10 20
2: 3  8 20

Now I would like to create the same filter dynamically. This what I have tried so far:

cond <- list(x=c(1,3),z=c(12,20))
vars <- names(cond)
## dd[get(vars[[1]]) %in% cond[[1]] & get(vars[[2]]) %in% cond[[2]]]

EVAL = function(...){
  expr <- parse(text=paste0(...))
  print(expr)
  eval(expr)
  }

dd[ EVAL(vars, " %in% ", cond, collapse=" & ") ] 

But I still get an error:

 Error in match(x, table, nomatch = 0L) : object 'x' not found

even if the expression evalutaed looks good:

expression(x %in% c(1, 3) & z %in% c(12, 20))

Is there a way to fix this?

4
  • 3
    You are probably looking for a general solution, but for your specific task, it seems to me that you could just do something like EVAL = function(x, vars, cond){ setkeyv(x, vars) ; x[do.call(CJ, cond), nomatch = 0L] } ; EVAL(dd, vars, cond) Commented Jan 24, 2016 at 0:01
  • @DavidArenburg yes I am looking for a general solution. But I like your solution. I think it is more efficient than my attempt to eavl/parse expressions.. can you please change your comment to an answer with some explanations? Commented Jan 24, 2016 at 0:13
  • dd[rowSums(mapply(`%in%`, dd[names(cond)], cond)) == length(cond), ] in base Commented Jan 24, 2016 at 1:22
  • 1
    The costly way is: take the Cartesian product of the elements of cond and then join the result: dd[do.call(CJ,cond), on=names(cond), nomatch=0] (Oh, I see David posted something similar above.) Commented Jan 24, 2016 at 20:24

1 Answer 1

5

Building expression instead of parsing it.

library(data.table)
dd = data.table(x=1:10,y=10:1,z=20:20)
AndIN = function(cond){
    Reduce(
        function(x, y) call("&", call("(",x), call("(",y)),
        lapply(names(cond), function(var) call("%in%", as.name(var), cond[[var]]))
    )
}
cond = list(x=c(1,3),z=c(12,20))
AndIN(cond)
#(x %in% c(1, 3)) & (z %in% c(12, 20))
dd[eval(AndIN(cond))]
#   x  y  z
#1: 1 10 20
#2: 3  8 20

Calls call("(",x) and call("(",y) may not be necessary.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.