在data.table中动态创建一个过滤表达式(i)

4

使用data.table

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

我可以使用过滤器来筛选它。
dd[x %in% c(1, 3) & z %in% c(12, 20)]
   x  y  z
1: 1 10 20
2: 3  8 20

现在我想动态创建相同的过滤器。目前为止,我尝试了以下方法:

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=" & ") ] 

但我仍然收到一个错误提示:
 Error in match(x, table, nomatch = 0L) : object 'x' not found

即使表达式的结果看起来不错:
expression(x %in% c(1, 3) & z %in% c(12, 20))

有没有办法修复这个问题?

3
你可能在寻找一个通用解决方案,但针对你的具体任务,我认为你可以像这样做:EVAL = function(x, vars, cond){ setkeyv(x, vars) ; x[do.call(CJ, cond), nomatch = 0L] } ; EVAL(dd, vars, cond) - David Arenburg
@DavidArenburg 是的,我正在寻找一般解决方案。但我喜欢你的解决方案。我认为它比我的尝试评估/解析表达式更有效率... 你能否将你的评论改为带有一些解释的答案? - agstudy
在基础语言中,将 dd[rowSums(mapply(\%in%`, dd[names(cond)], cond)) == length(cond), ]` 进行翻译。 - rawr
1
代价较高的方法是:对于 cond 的每个元素取笛卡尔积,然后连接结果:dd[do.call(CJ,cond), on=names(cond), nomatch=0](哦,我看到 David 在上面发表了类似的内容)。 - Frank
1个回答

5
构建表达式而不是解析它。
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

调用call("(",x)call("(",y)可能是不必要的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接