2015-06-04 20 views
1

我有一个看起来像这样的数据表。R:有效地识别X组变量Z的最高N值

ID <- c(rep("ABC",4), rep("DEF",4), rep("GHI",5)) 
X <- c(rep(c(1,2,3,4),3),5) 
set.seed(1234) 
Z <- runif(13,min=0, max =1) 
a <- data.table(ID, X, Z) 
a 

    ID X   Z 
1: ABC 1 0.113703411 
2: ABC 2 0.622299405 
3: ABC 3 0.609274733 
4: ABC 4 0.623379442 
5: DEF 1 0.860915384 
6: DEF 2 0.640310605 
7: DEF 3 0.009495756 
8: DEF 4 0.232550506 
9: GHI 1 0.666083758 
10: GHI 2 0.514251141 
11: GHI 3 0.693591292 
12: GHI 4 0.544974836 
13: GHI 5 0.282733584 

我想生成一个数据帧,它在每个X子组中有N个最高Z值。因此,可以说N为2。我想用一个数据集,看起来像这样结束了:

X ID   Z 
1: 1 DEF 0.8609154 
2: 1 GHI 0.6660838 
3: 2 DEF 0.6403106 
4: 2 ABC 0.6222994 
5: 3 GHI 0.6935913 
6: 3 ABC 0.6092747 
7: 4 ABC 0.6233794 
8: 4 GHI 0.5449748 
9: 5 GHI 0.2827336 

我用这条线才达到了它,但我发现它是特别慢当数据表很大时(即超过1,500,000行或更多)。

top_n <- 2 
a <- a[order(a$X, -a$Z),] 
a_2 <- a[, head(.SD, top_n), by=X] 
a_2 

    X ID   Z 
1: 1 DEF 0.8609154 
2: 1 GHI 0.6660838 
3: 2 DEF 0.6403106 
4: 2 ABC 0.6222994 
5: 3 GHI 0.6935913 
6: 3 ABC 0.6092747 
7: 4 ABC 0.6233794 
8: 4 GHI 0.5449748 
9: 5 GHI 0.2827336 

任何帮助将不胜感激!

谢谢!

+0

一般情况下,没有必要在中间用'<-'分配步骤,因为'a [order(a $ X,-a $ Z),] [,head(.SD,top_n),by = X]'和'a [order(a $ X,-a $ Z), head(.SD,top_n),by = X]'做同样的事情。另外,当你在data.table括号中使用'a []'时,你不需要包含'$'。最后,没有必要提前命令'X'来按顺序查看结果('keyby = X'做同样的事情),虽然它可能会更快(正如David在下面的答案中用'setorder'建议的那样) 。只是关于一些陷阱。 – Frank

回答

5

这应该是快于.SD

n <- 2 
indx <- a[order(-Z), .I[seq_len(n)], by = X]$V1 
a[indx] 
#  ID X   Z 
# 1: DEF 1 0.8609154 
# 2: GHI 1 0.6660838 
# 3: GHI 3 0.6935913 
# 4: ABC 3 0.6092747 
# 5: DEF 2 0.6403106 
# 6: ABC 2 0.6222994 
# 7: ABC 4 0.6233794 
# 8: GHI 4 0.5449748 
# 9: GHI 5 0.2827336 
# 10: NA NA  NA 

如果你需要一个有序的结果,这也应该是快

setorder(a, X, -Z) 
indx <- a[, .I[seq_len(n)], by = X]$V1 
a[indx] 
#  ID X   Z 
# 1: DEF 1 0.8609154 
# 2: GHI 1 0.6660838 
# 3: DEF 2 0.6403106 
# 4: ABC 2 0.6222994 
# 5: GHI 3 0.6935913 
# 6: ABC 3 0.6092747 
# 7: ABC 4 0.6233794 
# 8: GHI 4 0.5449748 
# 9: GHI 5 0.2827336 
# 10: NA NA  NA 
+0

“NA”来自哪里? – MichaelChirico

+1

@MichaelChirico尽管可能只有一个obs,但它来自要求排名前2的obs。为了摆脱它,可以在'seq_len'中放置'min(.N,n)'''n' – Frank

+1

@Frank我认为最快的选择是将最终结果包装到'na.omit'中而不是每组运行“min”。 –