有效地匹配几个变量上的行 - 排除列

时间:2013-10-17 08:17:47

标签: r data.table

我正在处理从金融中心删除邮件的财务问题。我正在使用data.table,我对它的性能和易于处理非常满意。

尽管如此,我总是问自己如何改进和使用data.table的全部功能。

以下是我的任务示例:

set.seed(1)
DT <- data.table(SYM = c(rep("A", 10), rep("B", 12)), PRC = format(rlnorm(22, 2), digits = 2), VOL = rpois(22, 312), ID = c(seq(1000, 1009), seq(1004, 1015)), FLAG = c(rep("", 8), "R", "A", rep("", 4), "R", rep("", 7)))
DT$PRC[9] <- DT$PRC[6]
DT$PRC[7] <- DT$PRC[6] 
DT$VOL[9] <- DT$VOL[6]
DT$VOL[7] <- DT$VOL[6]
DT$PRC[15] <- DT$PRC[13]
DT$VOL[15] <- DT$VOL[13]
## See the original dataset
DT
## Set the key
setkey(DT, "SYM", "PRC", "VOL", "FLAG")
## Get all rows, that match a row with FLAG == "R" on the given variables in the list
DT[DT[FLAG == "R"][,list(SYM, PRC, VOL)]]
## Remove these rows from the dataset
DT <- DT[!DT[FLAG == "R"][,list(SYM, PRC, VOL)]]
## See the modified data.table
DT

我现在的问题是:

  1. 这是执行任务的有效方式还是存在更多'data.table'样式?关键设置是否有效?
  2. 如果我不仅要有三个变量匹配(这里:SYM,PRC,VOL),还有更多,是否存在类似排除的东西(我知道我可以使用它的数据),如何执行我的任务。框架样式,但我想知道data.table是否有更优雅的方式??
  3. 最后一个命令中的复制是什么?在remove row by reference上的帖子之后,我认为复制是唯一的方法。如果我有几个任务,我可以以某种方式复合它们并避免为每个任务复制吗?

2 个答案:

答案 0 :(得分:1)

如果您只是设置密钥来执行此操作,@ eddi的答案是最好和最容易阅读的。

setkey(DT, SYM, PRC, VOL)
# ^ as in @eddi's answer, since you are not using the rest of the key
microbenchmark(
    notjoin=DT[!DT[FLAG == "R"][,list(SYM, PRC, VOL)]],
    logi_not=DT[!DT[,rep(any(FLAG=='R'),.N),by='SYM,PRC,VOL']$V1],
    idx_not=DT[!DT[,if(any(FLAG=='R')){.I}else{NULL},by='SYM,PRC,VOL']$V1],
    SD=DT[,if(!any(FLAG=='R')){.SD}else{NULL},by='SYM,PRC,VOL'],
    eddi=DT[!DT[FLAG == "R"]],
    times=1000L
)   

结果:

Unit: milliseconds
     expr      min       lq   median       uq       max neval
  notjoin 4.983404 5.577309 5.715527 5.903417 66.468771  1000
 logi_not 4.393278 4.960187 5.097595 5.273607 66.429358  1000
  idx_not 4.523397 5.139439 5.287645 5.453129 15.068991  1000
       SD 3.670874 4.180012 4.308781 4.463737  9.429053  1000
     eddi 2.767599 3.047273 3.137979 3.255680 11.970966  1000

另一方面,上面的几个选项不要求您的操作涉及按键分组。假设你要么......

  • 使用除键以外的组(您不想更改)或
  • 执行此操作
  • 希望在执行复制操作之前使用不同的分组执行这样的多个操作,以删除行newDT <- DT[...](如OP的第3点所述)。

setkey(DT,NULL)
shuffDT <- DT[sample(1:nrow(DT))] # not realistic, of course
# same benchmark with shuffDT, but without methods that require a key
# Unit: milliseconds
#      expr      min       lq   median       uq      max neval
#  logi_not 4.466166 5.120273 5.298174 5.562732 64.30966  1000
#   idx_not 4.623821 5.319501 5.517378 5.799484 15.57165  1000
#        SD 4.053672 4.448080 4.612213 4.849505 66.76140  1000

在这些情况下,OP和eddi的方法不可用(因为加入需要密钥)。对于一次性操作,使用.SD似乎更快。对于按多个条件进行子集化,您需要在复制newDT <- DT[!union(badrows1,badrows2,...)]之前跟踪要保留/删除的行。

DT[,rn:=1:.N] # same as .I
badflagrows <- DT[,if(any(FLAG=='R')){rn}else{NULL},by='SYM,PRC,VOL']$V1
# fill in next_cond, next_grp
badnextrows <- DT[!badflagrows][,
    if(any(next_cond)){rn}else{NULL},by='next_grp']$V1

也许类似的事情可以通过逻辑子集(基准测试中的“logi_not”)来完成,这样会快一些。

答案 1 :(得分:1)

我很困惑为什么你要把钥匙设置为FLAG,不是你想要的只是

setkey(DT, SYM, PRC, VOL)

DT[!DT[FLAG == "R"]]