data.table根据条件更新组中的最后一个元素

use*_*086 7 r data.table

我有一个包含3列的data.table:id,time和status.对于每个id,我想找到具有最大时间的记录 - 如果对于该记录,状态为true,我想在时间> 7(例如)时将其设置为false.我是按照以下方式进行的.

x <- data.table(id=c(1,1,2,2),time=c(5,6,7,8),status=c(FALSE,TRUE,FALSE,TRUE))
setkey(x,id,time)
y <- x[,.SD[.N],by=id]
x[y,status:=status & time > 7]
Run Code Online (Sandbox Code Playgroud)

我有很多我正在使用的数据,并希望加快这项操作.任何建议,将不胜感激!

Ren*_*rop 8

x[x[,.N, by=id][,cumsum(N)], status := status * time <=7]
Run Code Online (Sandbox Code Playgroud)

如果我没有弄错的话,这不是连接,因为x[,.N, by=id][,cumsum(N)]返回每组最后一个元素的行索引.

更新: 看到速度比较后,这个似乎是赢家,应该先列出

这是我最初的尝试,结果证明是所有建议解决方案中最慢的

x[,status := c(.SD[-.N, status], .SD[.N, status * time <=7]), by=id]
Run Code Online (Sandbox Code Playgroud)

  • 让我感到惊讶的一件事是`data.table`是多么灵活,允许多种解决方案! (2认同)

Sym*_*xAU 7

一种data.table方法是

x[ x[order(time), .I[.N], by=id]$V1 , status := ifelse(time > 7, FALSE, TRUE)]

> x
#   id time status
#1:  1    5  FALSE
#2:  1    6   TRUE
#3:  2    7  FALSE
#4:  2    8  FALSE
Run Code Online (Sandbox Code Playgroud)

as x[order(time), .I[.N], by=id]$V1给出time了每个组的最大行索引(id)

借用@ Floo0的答案,我们可以稍微简化一下

x[ x[order(time), .I[.N], by=id]$V1 , status := status * time <= 7]
Run Code Online (Sandbox Code Playgroud)

速度比较

各种答案的速度测试(并保持数据上的键)

set.seed(123)
x <- data.table(id=c(rep(seq(1:10000), each=10)),
                time=c(rep(seq(1:10000), 10)),
                status=c(sample(c(TRUE, FALSE), 10000*10, replace=T)))
setkey(x,id,time)
x1 <- copy(x); x2 <- copy(x); x3 <- copy(x); x4 <- copy(x); x5 <- copy(x); x6 <- copy(x)

library(microbenchmark)

microbenchmark(

    Symbolix = {x1[ x1[order(time), .I[.N], by=id]$V1 , status := status * time < 7 ] },

    Floo0_1 = {x2[,status := c(.SD[-.N, status], .SD[.N, status * time > 7]), by=id]},

    Floo0_2 = {x3[x3[,.N, by=id][,cumsum(N)], status := status * time > 7]},

    Original = { 
                y <- x4[,.SD[.N],by=id]
                x4[y,status:=status & time > 7]
               },

    Frank = {
             y <- x5[, .SD[.N, .(time, status)], by=id][time > 7 & status]
             x5[y, status := FALSE]
             },

    thelatemail = {x6[ x6[,.I==.I[which.max(time)], by=id]$V1 & time > 7, status := FALSE]}
)

Unit: milliseconds
        expr         min          lq        mean      median          uq         max neval cld
    Symbolix    5.419768    5.857477    6.514111    6.222118    6.936000   11.284580   100 a  
     Floo0_1 4550.314775 4710.679867 4787.086279 4776.794072 4850.334011 5097.136148   100   c
     Floo0_2    1.653419    1.792378    1.945203    1.881609    2.014325    4.096006   100 a  
    Original   10.052947   10.986294   12.541595   11.431182   12.391287   89.494783   100 a  
       Frank 4609.115061 4697.687642 4743.886186 4735.086113 4785.212543 4932.270602   100  b 
 thelatemail   10.300864   11.594972   12.421889   12.315852   12.984146   17.630736   100 a  
Run Code Online (Sandbox Code Playgroud)

  • 感谢您的比较,但我认为它需要两个改进:第一次比较4x3 data.table非常无聊.请大概使用1mio x 3表来真正比较加速.第二:你没有关键数据表......为什么?在最初的问题中有关键.大多数解决方案使用`by` o它可以产生巨大的差异. (2认同)

the*_*ail 5

另一种尝试:

x[ x[,.I==.I[which.max(time)], by=id]$V1 & time > 7, status := FALSE]
x

#   id time status
#1:  1    5  FALSE
#2:  1    6   TRUE
#3:  2    7  FALSE
#4:  2    8  FALSE
Run Code Online (Sandbox Code Playgroud)