【问题标题】:Select one row from each group in a large data.table based on a condition [duplicate]根据条件从大型data.table中的每个组中选择一行[重复]
【发布时间】:2015-03-07 21:58:14
【问题描述】:

我有一个表,其中键被重复了多次,并且使用另一列的最大值为每个键只选择一行。

这个例子演示了我目前的解决方案:

N = 10
k = 2
DT = data.table(X = rep(1:N, each = k), Y = rnorm(k*N))
     X           Y
 1:  1 -1.37925206
 2:  1 -0.53837461
 3:  2  0.26516340
 4:  2 -0.04643483
 5:  3  0.40331424
 6:  3  0.28667275
 7:  4 -0.30342327
 8:  4 -2.13143267
 9:  5  2.11178673
10:  5 -0.98047230
11:  6 -0.27230783
12:  6 -0.79540934
13:  7  1.54264549
14:  7  0.40079650
15:  8 -0.98474297
16:  8  0.73179201
17:  9 -0.34590491
18:  9 -0.55897393
19: 10  0.97523187
20: 10  1.16924293
> DT[, .SD[Y == max(Y)], by = X]
     X          Y
 1:  1 -0.5383746
 2:  2  0.2651634
 3:  3  0.4033142
 4:  4 -0.3034233
 5:  5  2.1117867
 6:  6 -0.2723078
 7:  7  1.5426455
 8:  8  0.7317920
 9:  9 -0.3459049
10: 10  1.1692429

问题在于,对于较大的 data.table,这需要很长时间:

N = 10000
k = 25
DT = data.table(X = rep(1:N, each = k), Y = rnorm(k*N))
system.time(DT[, .SD[Y == max(Y)], by = X])
   user  system elapsed 
   9.69    0.00    9.69 

我的实际表大约有 1 亿行...

谁能提出更有效的解决方案?


编辑 - 设置键的重要性

建议的解决方案效果很好,但您必须使用 setkey,或订购 DT 以使其工作:

请参阅代表中没有“每个”的示例:

N = 10
k = 2
DT = data.table(X = rep(1:N, k), Y = rnorm(k*N))
DT[DT[, Y == max(Y), by = X]$V1,]
     X           Y
 1:  1  1.26925708
 2:  4 -0.66625732
 3:  5  0.41498548
 4:  8  0.03531185
 5:  9  0.30608380
 6:  1  0.50308578
 7:  4  0.19848227
 8:  6  0.86458423
 9:  8  0.69825500
10: 10 -0.38160503

【问题讨论】:

  • 这是因为.SD[..] 为每个组调用[.data.table。我们知道这一点,很可能会在 1.9.8 中进行优化。 SO上有一些解决方案可以解决这个问题(通常使用.I),您可以通过搜索找到。优化后我会更新这篇文章。
  • @Arun 与devel 版本的.SD 解决方案(由Khashaa 和AnandaMahto 测试)相比,cran 版本给出了不同的结果/错误。我使用的是开发版。
  • @akrun,corone,如果您使用的是 1.9.4,请执行以下操作:options(datatable.auto.index=FALSE),一切正常。这是 1.9.5 中修复的新功能中的一个错误。
  • @Corone,不确定您是否知道,但如果给定 X 的 max(Y) 重复,您的代码将返回每个唯一 X 的多行。
  • @docendodiscimus 是的,好点。在我的实际情况下,数据不会有联系(或者在示例中不太可能出现平局),但是是的,我认为每个组都需要唯一地满足“条件”。

标签: r data.table


【解决方案1】:

.SD相比,这会更快

 system.time({setkey(DT, X)
    DT[DT[,Y==max(Y), by=X]$V1,]})
  # user  system elapsed 
  #0.016   0.000   0.016 

或者

system.time(DT[DT[, .I[Y==max(Y)], by=X]$V1])
#  user  system elapsed 
# 0.023   0.000   0.023 

如果只有两列,

system.time(DT[,list(Y=max(Y)), by=X])
#   user  system elapsed 
#  0.006   0.000   0.007 

相比,

system.time(DT[, .SD[Y == max(Y)], by = X] )
#  user  system elapsed 
# 2.946   0.006   2.962 

基于来自@Khashaa、@AnandaMahto 的 cmets,CRAN 版本 (1.9.4) 与开发版本 (1.9.5)(我使用的)相比,.SD 方法给出了不同的结果。通过设置options

,您可以获得相同的“CRAN”版本(来自@Arun 的 cmets)的结果
 options(datatable.auto.index=FALSE)

注意:在“tie”的情况下,此处描述的解决方案将为每个组返回多行(如 @docendo discimus 所述)。我的解决方案基于 OP 发布的“代码”。

如果有“关系”,那么您可以使用uniqueby 选项(如果列数是> 2)

 setkey(DT,X)
 unique(DT[DT[,Y==max(Y), by=X]$V1,], by=c("X", "Y"))

微基准

library(microbenchmark)
f1 <- function(){setkey(DT,X)[DT[, Y==max(Y), by=X]$V1,]}
f2 <- function(){DT[DT[, .I[Y==max(Y)], by=X]$V1]}
f3 <- function(){DT[, list(Y=max(Y)), by=X]}
f4 <- function(){DT[, .SD[Y==max(Y)], by=X]}
microbenchmark(f1(), f2(), f3(), f4(), unit='relative', times=20L)
#Unit: relative
# expr        min         lq       mean     median         uq        max neval
# f1()   2.794435   2.733706   3.024097   2.756398   2.832654   6.697893    20
# f2()   4.302534   4.291715   4.535051   4.271834   4.342437   8.114811    20
# f3()   1.000000   1.000000   1.000000   1.000000   1.000000   1.000000    20
# f4() 533.119480 522.069189 504.739719 507.494095 493.641512 466.862691    20
# cld
#  a 
#  a 
#  a 
#  b

数据

N = 10000
k = 25
set.seed(25)
DT = data.table(X = rep(1:N, each = k), Y = rnorm(k*N))

【讨论】:

  • 我能问一下为什么DT[, .SD[Y == max(Y)], by = X] 会返回 2010 年的观测值,而 DT[,list(Y=max(Y)), by=X] 会返回 10000(应该如此)吗?
  • @Khashaa 我得到DT1 &lt;- DT[, .SD[Y == max(Y)], by = X];dim(DT1) #[1] 10000 2。我正在使用data.table_1.9.5
  • 错误消失了,devel 版本:)
  • 但我认为我们不应该依赖开发版本作为一般手段。我们是否清楚什么定义了预期的行为?
  • @Corone 如果你使用.I,那不会有问题。
猜你喜欢
  • 2019-01-15
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 2017-01-18
  • 1970-01-01
  • 1970-01-01
  • 2016-05-30
  • 2022-01-08
相关资源
最近更新 更多