如何在data.table R中对子组内的数据进行排序

15

考虑以下内容:

DT = data.table(a=sample(1:2), b=sample(1:1000,20))

如何显示每个a的前n个最大值?

我卡在了 DT[,b,by=a][order(a,-b)].

谢谢!

1个回答

24
最优雅的方式将是:
DT[order(-b),head(b,5),by=a]

就纯性能而言:

DT[order(-b), indx := seq_len(.N), "a"][indx <= 5][,indx:=NULL][]

或者是像 @Frank 建议的那样:

DT[DT[order(-b),.I[1:.N<=5],"a"]$V1]

低于以上三个基准水平:

# devtools::install_github("jangorecki/dwtools")
library(dwtools) # to populate complex dataset
N <- 5e6
DT <- dw.populate(N, scenario="fact")
str(DT)
#Classes ‘data.table’ and 'data.frame': 5000000 obs. of  8 variables:
# $ cust_code: chr  "id010" "id076" "id024" "id081" ...
# $ prod_code: int  8234 5689 31198 35479 39140 37589 8184 39489 35266 3596 ...
# $ geog_code: chr  "OH" "NH" "TN" "MI" ...
# $ time_code: Date, format: "2012-03-11" "2014-02-10" "2012-11-05" "2013-01-30" ...
# $ curr_code: chr  "XRP" "HRK" "CAD" "BRL" ...
# $ amount   : num  486 382 695 470 749 ...
# $ value    : num  193454 33694 351418 84888 20673 ...

通过 cust_code 列,当 uniqueN 等于 100 时:

system.time(DT[order(-time_code),head(.SD,5),"cust_code"])
#   user  system elapsed 
#  1.804   0.084   1.890 
system.time(DT[order(-time_code), indx := seq_len(.N),"cust_code"][indx <= 5][,indx:=NULL][])
#   user  system elapsed 
#  1.414   0.092   1.508 
system.time(DT[DT[order(-time_code),.I[1:.N<=5],"cust_code"]$V1])
#   user  system elapsed 
#  1.405   0.096   1.502 

如果有更多的组(prod_code列,uniqueN等于50000),那么我们可以看到对性能的影响:

system.time(DT[order(time_code),head(.SD,5),"prod_code"])
#   user  system elapsed 
# 10.177   0.109  10.322
system.time(DT[order(time_code), indx := seq_len(.N),"prod_code"][indx <= 5][,indx:=NULL][])
#   user  system elapsed 
#  1.555   0.099   1.665 
system.time(DT[DT[order(time_code),.I[1:.N<=5],"prod_code"]$V1])
#   user  system elapsed 
#  1.697   0.064   1.764

2015-11-09 更新:

根据今天的Arun提交e615532headtail应在内部得到优化。


3
我不知道为什么第二种方法应该更快。相对于制作“1:.N”,为每个“a”制作“b”的副本(以传递给“head”)是否代价高昂?我注意到,最终结果仍然不是按每个“a”分组的(根据 OP)。如果有加速可以实现,我打赌这也可以做到:DT[DT[order(-b),.I[1:.N<=5],"a"]$V1] ... 摘自 https://dev59.com/1WQn5IYBdhLWcg3wxZni#16574176 - Frank
使用@Frank的head替代品,我认为会显著加快第一个版本的速度,因为它将避免head进行的各种检查。 - eddi
1
@Frank,我已经添加了基准测试,似乎这三种解决方案之间没有太大的区别。不管怎样,很多取决于数据集。 - jangorecki
1
更新:刚刚添加了更多组的基准测试,现在可以清楚地看到第一种解决方案要慢得多。 - jangorecki

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接