在data.table

时间:2016-08-25 19:55:00

标签: r data.table

我正在对data.table进行分组,并希望从每个组中选择x == 1的第一行,或者如果这样的行不存在,那么第一行中包含x中的任何值

d <- data.table(
           a = c(1,1,1,  2,2,  3,3), 
           x = c(0,1,0,  0,0,  1,1), 
           y = c(1,2,3,  1,2,  1,2)
)

这种尝试

d[, ifelse(any(.SD[,x] == 1),.SD[x == 1][1], .SD[1]), by = a]

返回

   a V1
1: 1  1
2: 2  0
3: 3  1

但我期待

   a  x  y
1: 1  1  2
2: 2  0  1
3: 3  1  1

任何想法如何做到正确?

3 个答案:

答案 0 :(得分:15)

我认为match nomatch参数

是一个很好的用例
d[, .SD[match(1L, x, nomatch = 1L)], by = a]
#    a x y
# 1: 1 1 2
# 2: 2 0 1
# 3: 3 1 1

基本上,如果不匹配,则返回1,结果为您提供组中的第一行。如果存在多重匹配,那么它将根据您的愿望返回第一个

答案 1 :(得分:15)

另一个选项(which.max基本上是为了完全按照你的意愿设计的):

d[, .SD[which.max(x == 1)], by = a]
#   a x y
#1: 1 1 2
#2: 2 0 1
#3: 3 1 1

答案 2 :(得分:6)

我们也可以使用.I执行此操作以返回行索引,并将其用于对行进行子集化。

d[d[, .I[which.max(x==1)], by = a]$V1]
#   a x y
#1: 1 1 2
#2: 2 0 1
#3: 3 1 1

data.table的当前版本中,.I方法与用于子集化行的.SD相比更有效(但是,它可能在将来发生变化)。这也是similar post

以下是order的另一个选项(setkey也可以用来提高效率)数据集按“&#39; a&#39;和&#39; x&#39;在按&#39; a&#39;分组后,然后获得head的第一行

d[order(a ,-x), head(.SD, 1) ,by = a]
#   a x y
#1: 1 1 2
#2: 2 0 1
#3: 3 1 1

基准

最初,我们正在考虑对&gt;进行基准测试。 1e6,但.SD方法花费时间,因此使用3e5

data.table_1.9.7行进行比较
set.seed(24)
d1 <- data.table(a = rep(1:1e5, 3), x = sample(0:1, 1e5*3, 
           replace=TRUE), y = rnorm(1e5*3))

system.time(d1[, .SD[which.max(x == 1)], by = a])
#   user  system elapsed 
#  56.21   30.64   86.42 

system.time(d1[, .SD[match(1L, x, nomatch = 1L)], by = a])
# user  system elapsed 
#  55.27   30.07   83.75 

system.time(d1[d1[, .I[which.max(x==1)], by = a]$V1])
#  user  system elapsed 
#   0.19    0.00    0.19 


system.time(d1[order(a ,-x), head(.SD, 1) ,by = a])
# user  system elapsed 
#   0.03    0.00    0.04