我正在对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
任何想法如何做到正确?
答案 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