【问题标题】:subset in data.table when groups are empty组为空时 data.table 中的子集
【发布时间】:2019-09-16 13:51:01
【问题描述】:

对于这些数据

library(data.table)
set.seed(42)
dat <- data.table(id=1:12, group=rep(1:3, each=4), x=rnorm(12))

> dat
    id group           x
 1:  1     1  1.37095845
 2:  2     1 -0.56469817
 3:  3     1  0.36312841
 4:  4     1  0.63286260
 5:  5     2  0.40426832
 6:  6     2 -0.10612452
 7:  7     2  1.51152200
 8:  8     2 -0.09465904
 9:  9     3  2.01842371
10: 10     3 -0.06271410
11: 11     3  1.30486965
12: 12     3  2.28664539

我的目标是从每个组中获取x 大于某个阈值的第一个ID,例如x&gt;1.5

> dat[x>1.5, .SD[1], by=group]
   group id        x
1:     2  7 1.511522
2:     3  9 2.018424

确实是正确的,但我对它默默地为第 1 组不产生任何结果这一事实感到不满。相反,我希望它产生每个组的最后一个 id,其中没有 id 满足条件。我发现我可以分两步实现这一目标

> tmp <- dat[x>1.5, .SD[1], by=group]
> rbind(tmp,dat[!group%in%tmp$group,.SD[.N], by=group])
   group id         x
1:     2  7 1.5115220
2:     3  9 2.0184237
3:     1  4 0.6328626

但我确信我没有充分利用这里的 data.table 功能,这必须允许更优雅的解决方案。

【问题讨论】:

  • 关于信息,它没有产生 1 的结果的原因是因为第一部分 (x&gt;1.5) 过滤了 data.table,因此数据中不存在组 1。表了。

标签: r data.table


【解决方案1】:

使用data.table,我们可以逐组检查条件和子集。

library(data.table)
dat[dat[, if(any(x>1.5)) .I[which.max(x > 1.5)] else .I[.N], by=group]$V1]

#   id group         x
#1:  4     1 0.6328626
#2:  7     2 1.5115220
#3:  9     3 2.0184237

dplyr,翻译过来就是

library(dplyr)
dat %>%
  group_by(group) %>%
  slice(if(any(x > 1.5)) which.max(x > 1.5) else n())

或者更高效

dat[, .SD[{temp = x > 1.5; if (any(temp)) which.max(temp) else .N}], by = group]

感谢@IceCreamTouCan、@sindri_baldur 和@jangorecki 为改进此答案提出的宝贵建议。

【讨论】:

  • 或简化为:dat[, .SD[if (any(x &gt; 1.5)) which.max(x &gt; 1.5) else .N], by=group]
  • 不要多次使用x &gt; 1.5,效率低下,在j, compute x > 1.5`中使用一次{,赋值给temp var再使用
  • 我同意没有连接的方法似乎更直观。您能否将其添加到解决方案中,包括 Jan 的建议(我不明白)?
  • @bumblebee 好吧..我已经根据建议更新了答案。
【解决方案2】:

另一种选择是:

dat[x>1.5 | group!=shift(group, -1L), .SD[1L], .(group)]

【讨论】:

    【解决方案3】:

    您可以对这两种方式进行子集化(由 GForce 优化),然后将它们组合起来:

    D1 = dat[x>1.5, lapply(.SD, first), by=group]
    D2 = dat[, lapply(.SD, last), by=group]
    rbind(D1, D2[!D1, on=.(group)])
    
       group id         x
    1:     2  7 1.5115220
    2:     3  9 2.0184237
    3:     1  4 0.6328626
    

    这里有一些效率低下,因为我们按group 分组了三次。我不确定这是否会被j 中更有效的计算所抵消,这要归功于 GForce。 @jangorecki 指出,可以通过先设置密钥来缓解分组三次的低效率。

    评论:我使用了 last(.SD),因为 .SD[.N] 尚未优化并且 last(.SD) 会引发错误。为了对称,我将 OP 的代码更改为先使用 lapply。

    【讨论】:

    • setkey on group 如果我们想按该列分组 3 次,可能会加快速度
    猜你喜欢
    • 2011-07-26
    • 1970-01-01
    • 2013-06-25
    • 2020-07-20
    • 2013-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-11-08
    相关资源
    最近更新 更多