使用二进制search而不是vector扫描,仅对2列密钥的第二列对data.table进行子集化

我最近在data.table发现了二进制search。 如果表格按多个键sorting,则可以仅在第二个键上search?

 DT = data.table(x=sample(letters,1e7,T),y=sample(1:25,1e7,T),rnorm(1e7)) setkey(DT,x,y) #R> DT[J('x')] # xy V3 # 1: x 1 0.89109 # 2: x 1 -2.01457 # --- #384922: x 25 0.09676 #384923: x 25 0.25168 #R> DT[J('x',3)] # xy V3 # 1: x 3 -0.88165 # 2: x 3 1.51028 # --- #15383: x 3 -1.62218 #15384: x 3 -0.63601 

编辑:感谢@阿伦

 R> system.time(DT[J(unique(x), 25)]) user system elapsed 0.220 0.068 0.288 R> system.time(DT[y==25]) user system elapsed 0.268 0.092 0.359 

是的,您可以将所有值传递给第一个键值和第二个键的具体值的子集。

 DT[J(unique(x), 25), nomatch=0] 

如果你需要在第二个关键字(如DT[y %in% 25:24] )中的一个以上的值上进行子集分类,则更一般的解决scheme是使用CJ

 DT[CJ(unique(x), 25:24), nomatch=0] 

请注意 ,默认情况下, CJ对列进行sorting并将键设置为所有列,这意味着结果也将被sorting。 如果这是不可取的,你应该使用sorted=FALSE

 DT[CJ(unique(x), 25:24, sorted=FALSE), nomatch=0] 

还有一个function请求,将来添加辅助键到data.table 。 我相信这个计划是增加一个新的函数set2key

FR#1007构build在辅助键中

还有merge ,它有一个data.table的方法。 它为你构build了它的二级密钥,所以应该比base merge更快。 请参阅?merge.data.table

基于这个电子邮件线程,我写了以下function:

 create_index = function(dt, ..., verbose = getOption("datatable.verbose")) { cols = data.table:::getdots() res = dt[, cols, with=FALSE] res[, i:=1:nrow(dt)] setkeyv(res, cols, verbose = verbose) } JI = function(index, ...) { index[J(...),i]$i } 

下面是我的系统上有更大的DT(1e8行)的结果:

 > system.time(DT[J("c")]) user system elapsed 0.168 0.136 0.306 > system.time(DT[J(unique(x), 25)]) user system elapsed 2.472 1.508 3.980 > system.time(DT[y==25]) user system elapsed 4.532 2.149 6.674 > system.time(IDX_y <- create_index(DT, y)) user system elapsed 3.076 2.428 5.503 > system.time(DT[JI(IDX_y, 25)]) user system elapsed 0.512 0.320 0.831 

如果你多次使用索引,这是值得的。