仅获取具有给定行数的data.table组

时间:2019-06-27 15:24:44

标签: r data.table

我有以下data.table:

dt = data.table(year=c(1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2),
                quar=c(1, 1, 1, 2, 2, 3, 4, 4, 4, 1, 1, 1),
                item=c(1, 2, 3, 1, 2, 1, 1, 2, 3, 1, 2, 3))

某些期间(由年份和季度定义)包含三个项目:

  • Y1Q1,Y1Q4,Y2Q1

其他时期不:

  • 第二季度第一季度有2个项目
  • Y1Q3有1个项目

我如何只获取包含所有三个项目的行?

我到目前为止所管理的是

dt[dt[, "i" := nrow(.SD) == 3, .(year, quar)]$i]
> year | quar | item | i
> -----+------+------+-----
> 1    | 1    | 1    | TRUE
> 1    | 1    | 2    | TRUE
> 1    | 1    | 3    | TRUE
> 1    | 4    | 1    | TRUE
> 1    | 4    | 2    | TRUE
> 1    | 4    | 3    | TRUE
> 2    | 1    | 1    | TRUE
> 2    | 1    | 2    | TRUE
> 2    | 1    | 3    | TRUE

yearquar进行分组,然后设置列i来说明该组是否有效。该组中的所有行均获得i的计算值。

哪个工作正常。但是,这样做的副作用是在表中添加实际的i列。

我尝试使用用.(i =...)声明的临时列,但是i列具有较短的分组表的长度,我们得到了

dt[dt[, .(i = nrow(.SD) == 3), .(year, quar)]$i]
> Error in `[.data.table`(dt, dt[, .(i = nrow(.SD) == 3), .(year, quar)]$i) :
> i evaluates to a logical vector length 5 but there are 12 rows. [...]

那么,有没有更优雅的解决方法?还是我应该只使用它然后放下i

3 个答案:

答案 0 :(得分:3)

如果需要子集,请使用.I获取行索引和子集

dt[dt[, .I[.N == 3], .(year, quar)]$V1]
#    year quar item
#1:    1    1    1
#2:    1    1    2
#3:    1    1    3
#4:    1    4    1
#5:    1    4    2
#6:    1    4    3
#7:    2    1    1
#8:    2    1    2
#9:    2    1    3

或使用.SD,但速度可能很慢

dt[, .SD[.N == 3], .(year, quar)]

或者另一个选择是if/else

dt[, if(.N == 3) .SD, .(year, quar)]

答案 1 :(得分:2)

使用连接的另一个选项:

.PBIX

编辑: 为了解决akrun注释,速度实际上取决于数据集的特征。以下是样本数据集的时间安排:

dt[dt[, .N, .(year, quar)][N==3], on=.(year, quar)]

时间:

set.seed(0L)
ngrp <- 1e6
x <- sample(1:3, ngrp, TRUE)
dt <- data.table(year=rep(1:ngrp, times=x))[,
    quar:=year]

microbenchmark::microbenchmark(
    mtd0=dt[dt[, .I[.N == 3], .(year, quar)]$V1],
    mtd1=dt[dt[, .N, .(year, quar)][N==3], on=.(year, quar)],
    times=3L
)

答案 2 :(得分:1)

当然这是一个data.table问题,@ akrun的回答涵盖了该问题,但是为了完整起见;

一种dplyr解决方案:

library(dplyr)
dt %>% group_by(year,quar) %>% filter(n()==3)

## A tibble: 9 x 3
## Groups:   year, quar [3]
#   year  quar  item
#  <dbl> <dbl> <dbl>
#1     1     1     1
#2     1     1     2
#3     1     1     3
#4     1     4     1
#5     1     4     2
#6     1     4     3
#7     2     1     1
#8     2     1     2
#9     2     1     3

Base中:

dt[ave(dt$item, dt[,c("year","quar")], FUN = length) == 3, ]

freqt <- as.data.frame(table(dt[,c("year", "quar")]))
subt <- freqt[freqt$Freq == 3,c("year", "quar")]

merge(x = subt, y = dt, by = c("year", "quar") , all.x = TRUE)