2

我正在解决从金融中心删除邮件的财务问题。我正在使用 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.frame 样式但是我想知道data.table是否有更优雅的方式)?
  3. 最后一个命令中的复制是什么?遵循通过引用删除行的线程,我认为复制是唯一的方法。如果我有多个任务,我可以以某种方式将它们组合起来并避免为每个任务复制吗?
4

2 回答 2

1

我很困惑为什么您将密钥设置为FLAG,这不是您想要的

setkey(DT, SYM, PRC, VOL)

DT[!DT[FLAG == "R"]]
于 2013-10-17T16:24:45.013 回答
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”)来完成类似的事情,这会更快一些。

于 2013-10-17T16:13:52.400 回答