首页 > 解决方案 > 组为空时 data.table 中的子集

问题描述

对于这些数据

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>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 功能,这必须允许更优雅的解决方案。

标签: rdata.table

解决方案


使用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 为改进此答案提出的宝贵建议。


推荐阅读