根据命名列表对data.table进行子设置

jay*_*dee 5 r list subset data.table

我正在尝试子集给定的data.table

DT <- data.table(
  a = c(1:20),
  b = (3:4),
  c = (5:14),
  d = c(1:4)
)
Run Code Online (Sandbox Code Playgroud)

在函数中由参数命名列表

param <- list(a = 1:10,
              b = 2:3,
              c = c(5, 7, 10))
Run Code Online (Sandbox Code Playgroud)

我可能在这里有些卡住,但是我当然不想实现像这样的丑陋的东西。特别是因为它不是很动态。

DT[(if (!is.null(param$a))
  a %in% param$a
  else
    TRUE)
  &
    (if (!is.null(param$b))
      b %in% param$b
     else
       TRUE)
  &
    (if (!is.null(param$c))
      c %in%  param$c
     else
       TRUE)
  &
    (if (!is.null(param$d))
      d %in% param$d
     else
       TRUE)]
Run Code Online (Sandbox Code Playgroud)
   a b c d
1: 1 3 5 1
2: 3 3 7 3
Run Code Online (Sandbox Code Playgroud)

有什么想法如何使用命名列表的名称在data.table或base R中以优雅的方式实现这一点,以使用关联值对data.table中的对应列进行子集化?谢谢!

编辑

我用一些答案进行了微基准测试:

func_4 <- function(myp, DT) {
  myp    = Filter(Negate(is.null), param)

  exs = Map(function(var, val)
    call("%in%", var, val),
    var = sapply(names(myp), as.name),
    val = myp)
  exi = Reduce(function(x, y)
    call("&", x, y), exs)
  ex = call("[", x = as.name("DT"), i = exi)
  # eval(as.call(c(as.list(ex))))
  eval(ex)
}

microbenchmark(
  (DT[do.call(pmin, Map(`%in%`, DT[, names(param), with = FALSE], param)) == 1L]),
  (DT[rowSums(mapply(`%in%`, DT[, names(param), with = FALSE], param)) == length(param)]),
  (DT[do.call(CJ, param), on = names(param), nomatch = NULL]),
  (DT[expand.grid(param), on = names(param), nomatch = NULL]),
  (DT[DT[, all(mapply(`%in%`, .SD, param)), by = 1:nrow(DT), .SDcols = names(param)]$V1]),
  (func_4(myp = param, DT = DT)),
  times = 200)

   min        lq      mean   median        uq       max neval
  446.656  488.5365  565.5597  511.403  533.7785  7167.847   200
  454.120  516.3000  566.8617  538.146  561.8965  1840.982   200
 2433.450 2538.6075 2732.4749 2606.986 2704.5285 10302.085   200
 2478.595 2588.7240 2939.8625 2642.311 2743.9375 10722.578   200
 2648.707 2761.2475 3040.4926 2814.177 2903.8845 10334.822   200
 3243.040 3384.6220 3764.5087 3484.423 3596.9140 14873.898   200
Run Code Online (Sandbox Code Playgroud)

Nat*_*rth 5

您可以使用CJÇ罗斯Ĵ OIN)函数data.table从列表中进行过滤表。

lookup <- do.call(CJ, param)
head(lookup)
#    a b  c
# 1: 1 2  5
# 2: 1 2  7
# 3: 1 2 10
# 4: 1 3  5
# 5: 1 3  7
# 6: 1 3 10

DT[
    lookup,
    on = names(lookup),
    nomatch = NULL
]
#    a b c d
# 1: 1 3 5 1
# 2: 3 3 7 3
Run Code Online (Sandbox Code Playgroud)

请注意,这nomatch = 0意味着lookup其中不存在的任何组合DT都不会返回一行。


Ron*_*hah 2

我们可以在DTusing namesin中选择列param,应用于%in%每个包含 columns 的列表元素,并仅选择所有值为 的行TRUE

DT[which(rowSums(mapply(`%in%`, DT[, names(param), with = FALSE],
      param)) == length(param)), ]

#   a b c d
#1: 1 3 5 1
#2: 3 3 7 3
Run Code Online (Sandbox Code Playgroud)