我正在使用由2个二进制索引创建的名为“ lines”的data.table
setkeyv(lines,c("start","end"))
我需要执行快速的二进制搜索以查找“开始”列或“结束”列中的哪些记录等于一个值(或多个),例如,在SQL中,将是这样的: / p>
select column1, column2, column3 from lines where start = 2 OR end = 2
在具有二进制索引的R data.tables中,我可以执行以下操作
lines[.(2,2)]
但这句话等于start = 2 AND end = 2,那不是我所需要的。
我知道可以用这样的方法来做到这一点,但它的速度还不够快,并且不使用二进制搜索。
line[(start == c(2,3) | end == c(2,3)];
如何执行符合此要求的快速搜索?
我需要做什么的简单示例。
lines <- data.table(id = c(1,2,3,4,5,6,7), start = c(901,902,903,904,905,906,907), end = c(101,102,103,104,105,106,107));
checklines <- data.table(id = c(1,2,3,4), startcheck = c(330,902,903,101), endcheck = c(106,400,907));
setkeyv(lines, c("start","end");
我需要在开始或结束的值是检查行startcheck或endcheck的值的行中搜索记录。
我现在正在做的是:
lines[start %in% c(checklines$startcheck,checklines$endcheck) | end %in% c(checklines$startcheck,checklines$endcheck)];
结果将是:
但是此搜索还不够快,如果我没记错的话,它不会使用二进制键。
答案 0 :(得分:2)
我们可以使用%in%
代替==
。 ==
用于只有一个要比较的元素或整个列用于元素比较的情况。如果有多个元素,请使用%in%
line[(start %in% c(2,3) | end %in% c(2,3))];
答案 1 :(得分:1)
在此示例中,您可以使用%in%子句进行检查,如果启用了索引,则性能显着提高(它使用二进制索引)
set.seed(108)
N = 1e8
DT = setDT(list(sample(N/10, N, TRUE), sample(letters, N, TRUE)))
setindexv(DT, c("V1","V2"))
options("datatable.use.index"=TRUE)
system.time(ans1<-DT[V1 %in% 1000:1002 & V2 %in% c("a","b","c")])
# user system elapsed
# 0.001 0.000 0.002
options("datatable.use.index"=FALSE)
system.time(ans2<-DT[V1 %in% 1000:1002 & V2 %in% c("a","b","c")])
# user system elapsed
# 4.051 0.848 4.899
但是,用 |
更改&system.time(ans1<-DT[V1 %in% 1000:1002 | V2 %in% c("a","b","c")])
索引是ON还是OFF都没有关系, 就像未激活索引一样。
关于如何优化此搜索效果的任何想法?
-编辑-
我找到了一种解决方案,可以通过melt函数将搜索列(开始和结束)转换为行:
channelids <<- melt(lines[,c("id","start","end")], id=c("id"));
结果是这样的结构:
现在,包括对象的二进制索引
setkey(channelids, value);
搜索速度更快,并且对象比我测试过的其他方法还小。
答案 2 :(得分:0)
其他一些方法和时间安排实际上取决于您的实际尺寸。如评论中所述,重复对象对1条记录多次运行查询,而这里需要在数据集中搜索多个记录。
数据:
#generate sample datasets
library(data.table)
set.seed(0L)
nr <- 1e6
lines <- data.table(start=sample(1:1e4, nr, TRUE), end=sample(1:1e4, nr, TRUE))[, id := .I]
checklines <- unique(data.table(start=sample(1:1000, 1000, TRUE), end=sample(1:1000, 1000, TRUE))[, id := .I])
checks <- c(checklines$start, checklines$end)
DT <- copy(lines)
sl <- copy(lines)
el <- copy(lines)
计时代码:
bench::mark(
mtd0={
setkey(lines, start, end)
lines[start %in% checks | end %in% checks]
},
mtd2={
setkey(DT, start)
ix1 <- DT[.(checks), id]
setkey(DT, end)
ix2 <- DT[.(checks), id]
DT[unique(c(ix1, ix2))]
},
mtd3={
setkey(sl, start)
setkey(el, end)
lines[unique(c(sl[.(checks), id], el[.(checks), id]))]
},
check=FALSE #ordering difference
)
时间:
# A tibble: 3 x 14
expression min mean median max `itr/sec` mem_alloc n_gc n_itr total_time result memory time gc
<chr> <bch:tm> <bch:tm> <bch:tm> <bch:tm> <dbl> <bch:byt> <dbl> <int> <bch:tm> <list> <list> <list> <list>
1 mtd0 36.8ms 37.2ms 37.2ms 37.3ms 26.9 36.9MB 8 5 186ms <data.table [165,061 x 3]> <Rprofmem [19 x 3]> <bch:t~ <tibble [13 x 3~
2 mtd2 49.4ms 51.1ms 50.4ms 57ms 19.6 20.5MB 3 7 358ms <data.table [165,061 x 3]> <Rprofmem [43 x 3]> <bch:t~ <tibble [10 x 3~
3 mtd3 20.8ms 21.7ms 21.4ms 22.8ms 46.1 20.5MB 4 14 303ms <data.table [165,061 x 3]> <Rprofmem [43 x 3]> <bch:t~ <tibble [18 x 3~