2016-03-03 63 views
7

我有一個data.table,包含3列:id,時間和狀態。對於每個id,我想用最大時間查找記錄 - 然後如果對於該記錄,狀態爲true,如果時間> 7(例如),我想將其設置爲false。我按照以下方式進行。data.table根據條件更新組中的最後一個元素

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] 

我有很多我正在使用的數據,並希望加快此操作。任何建議,將不勝感激!

+0

是''內id' time'唯一的(所以就有了 「用最長時間紀錄」) ? – Frank

+0

個人而言,我更喜歡你的方法比答案更好。我將它改爲'y = x [,.SD [.N,。(time,status)],by = id] [time> 7&status];但是[x,y,status:= FALSE]'。 ('。(時間,狀態)'這個東西只有在你有其他的條件不需要的變量時纔有用。) – Frank

+1

是的,時間在ID內是唯一的,所以會有最長時間的記錄。 – user2506086

回答

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 

x[order(time), .I[.N], by=id]$V1給我們的最大time的行指數爲每個組(id

而且,從@ Floo0的答案借貸,我們可以稍微簡化它

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

速度比較

的各種答案的速度測試(並保持對數據的密鑰)

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 
+2

感謝您的比較,但我認爲需要兩項改進:第一次比較4x3 data.table是非常無聊的。請去一個1mio x 3表左右來真正比較加速。第二:你沒有鍵入數據表......爲什麼?在原來的問題中有鑰匙。大多數解決方案使用''''它可能會產生巨大的差異。 – Rentrop

+0

@ Floo0 - 1:好點,我會稍微運行一個更大的測試。2:我把原來的'setkey'作爲解決方案的一部分,而不是問題。但是,我同意,如果在所有解決方案上設置了密鑰,將會發生什麼情況。 – SymbolixAU

+0

我有興趣知道爲什麼這得到了一個投票...? – SymbolixAU

8
x[x[,.N, by=id][,cumsum(N)], status := status * time <=7] 

如果我沒有記錯,這是沒有加入爲x[,.N, by=id][,cumsum(N)]收益的行指數每組最後一個元素。

更新: 看到速度對比後,這一次似乎獲勝者,應先列出

這是我這原來是最慢的所有建議的解決方案的初步嘗試

x[,status := c(.SD[-.N, status], .SD[.N, status * time <=7]), by=id] 
+2

有一件事總讓我感到驚訝的是'data.table'有多靈活,可以實現多種解決方案! – SymbolixAU

+1

你有你的不平等表達方式錯誤嗎? – SymbolixAU

+0

這太好了,正是需要的。 – user2506086

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 
3

下面是另一種類似於OP' S:

y = unique(x[,c("id","time"), with=FALSE], by="id", fromLast=TRUE) 
x[y[time > 7], status := FALSE] 

下面是另一個風向標:

n_id = 1e3; n_col = 100; n_draw = 5 

set.seed(1) 
X = data.table(id = 1:n_id)[, .(
    time = sample(10,n_draw), 
    status = sample(c(T,F), n_draw, replace=TRUE) 
), by=id][, paste0("V",1:n_col) := 0] 
setkey(X,id,time) 

X1 = copy(X); X2 = copy(X); X3 = copy(X); X4 = copy(X) 
X5 = copy(X); X6 = copy(X); X7 = copy(X); X8 = copy(X) 

library(microbenchmark) 
library(multcomp) 

microbenchmark(
unique = { 
    Y = unique(X1[,c("id","time"), with=FALSE], by="id", fromLast=TRUE) 
    X1[Y[time > 7], status := FALSE] 
}, 
OP = { 
    y <- X2[,.SD[.N],by=id] 
    X2[y,status:=status & time > 7] 
}, 
Floo0a = X3[,status := c(.SD[-.N, status], .SD[.N, status * time >7]), by=id], 
Floo0b = X4[X4[,.N, by=id][,cumsum(N)], status := status * time >7], 
tlm = X5[ X5[,.I==.I[which.max(time)], by=id]$V1 & time > 7, status := FALSE], 
Symbolix=X6[ X6[order(time), .I[.N], by=id]$V1 , status := ifelse(time > 7, FALSE, TRUE)], 
Frank1 = { 
    y <- X7[, .SD[.N, .(time, status)], by=id][time > 7 & status] 
    X7[y, status := FALSE] 
}, 
Frank2 = { 
    y <- X8[, .SD[.N], by=id][time > 7 & status] 
    X8[y, status := FALSE] 
}, times = 1, unit = "relative") 

結果:

 expr  min   lq  mean  median   uq  max neval 
    unique 1.348592 1.348592 1.348592 1.348592 1.348592 1.348592  1 
     OP 35.048724 35.048724 35.048724 35.048724 35.048724 35.048724  1 
    Floo0a 416.175654 416.175654 416.175654 416.175654 416.175654 416.175654  1 
    Floo0b 1.000000 1.000000 1.000000 1.000000 1.000000 1.000000  1 
     tlm 2.151996 2.151996 2.151996 2.151996 2.151996 2.151996  1 
Symbolix 1.770835 1.770835 1.770835 1.770835 1.770835 1.770835  1 
    Frank1 404.045660 404.045660 404.045660 404.045660 404.045660 404.045660  1 
    Frank2 36.603303 36.603303 36.603303 36.603303 36.603303 36.603303  1