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)
您可以使用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都不会返回一行。
我们可以在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)