使用`data.table`来获取基于变量的第一个子组

时间:2014-11-06 10:42:21

标签: r data.table

考虑一个由分组变量(此处为id)和一个有序变量(此处为date)组成的数据集

(df <- data.frame(
  id = rep(1:2,2),
  date = 4:1
))
#   id date
# 1  1    4
# 2  2    3
# 3  1    2
# 4  2    1

我想知道data.table中最简单的方法是做同等级的dplyr代码:

library(dplyr)
df %>%
  group_by(id) %>%
  filter(min_rank(date)==1)
# Source: local data frame [2 x 2]
# Groups: id
# 
#   id date
# 1  1    2
# 2  2    1

即。每个id根据date得到第一个。

基于类似的stackoverflow问题(Create an "index" for each element of a group with data.table),我提出了这个

library(data.table)
dt <- data.table(df)
setkey(dt, id, date)
for(k in unique(dt$id)){
  dt[id==k, index := 1:.N]
}
dt[index==1,]

但似乎应该有一个单行。不熟悉data.table我认为这样的事情

dt[,,mult="first", by=id]

应该有用,但唉!最后一段代码看起来应该按id进行分组然后取第一位(id内的date将由data.table确定,因为我已经以这种方式设置了密钥。)


修改

感谢Ananda Mahto,这个单行班车现在将在我的dt[,.SD[1], by=id] # id date # 1: 1 2 # 2: 2 1 曲目中

{{1}}

2 个答案:

答案 0 :(得分:5)

直接使用您的来源data.frame,您可以尝试:

setkey(as.data.table(df), id, date)[, .SD[1], by = id]
#    id date
# 1:  1    2
# 2:  2    1

扩展您最初的想法,您可以这样做:

dt <- data.table(df)
setkey(dt, id, date)
dt[, index := sequence(.N), by = id][index == 1]
#    id date index
# 1:  1    2     1
# 2:  2    1     1

可能在某种程度上,大卫对head[1]的关联是正确的,但我不确定会有多大的规模。

set.seed(1)
nrow <- 10000
ncol <- 20

df <- data.frame(matrix(sample(10, nrow * ncol, TRUE), nrow = nrow, ncol = ncol))

fun1 <- function() setkey(as.data.table(df), X1, X2)[, head(.SD, 1), by = X1]
fun2 <- function() setkey(as.data.table(df), X1, X2)[, .SD[1], by = X1]

library(microbenchmark)
microbenchmark(fun1(), fun2())
# Unit: milliseconds
#    expr       min        lq      mean    median        uq      max neval
#  fun1() 12.178189 12.496777 13.400905 12.808523 13.483545 30.28425   100
#  fun2()  4.474345  4.554527  4.948255  4.620596  4.965912  8.17852   100

答案 1 :(得分:2)

这是使用data.table二进制搜索的另一个选项

setkey(dt[, indx := seq_len(.N), by = id], indx)[J(1)]
#    id date indx
# 1:  1    2    1
# 2:  2    1    1

一些基准: 似乎所有方法的执行大致相同,但是在庞大的数据集(1e+06*1e+2)上,二进制搜索获胜

set.seed(1)
nrow <- 1e6
ncol <- 1e2

df <- data.frame(matrix(sample(10, nrow * ncol, TRUE), nrow = nrow, ncol = ncol))
library(data.table)

funAM1 <- function() setkey(as.data.table(df), X1, X2)[, .SD[1], by = X1]
funAM2 <- function() setkey(as.data.table(df), X1, X2)[, index := sequence(.N), by = X1][index == 1]
funDA1 <- function() setkey(as.data.table(df), X1, X2)[, head(.SD, 1), by = X1]
funDA2 <- function() setkey(as.data.table(df)[, indx := seq_len(.N), by = X1], X1)[J(1)]

library(microbenchmark)
Res <- microbenchmark(funAM1(), funAM2(), funDA1(), funDA2())
Res
# Unit: milliseconds
#     expr      min       lq   median       uq      max neval
# funAM1() 737.5690 758.3015 771.9344 794.1417 910.1019   100
# funAM2() 631.7822 693.8286 704.6912 729.6960 806.5556   100
# funDA1() 757.0327 772.4353 784.3107 810.0759 938.6344   100
# funDA2() 564.7291 578.1089 587.6470 611.7269 740.4077   100
boxplot(Res)

enter image description here