我正在尝试分析游戏数据,但我需要删除指定行之后的所有行。
在以下情况中,我想删除每个用户的EVENT“Die”之后的所有行。数据按UID,TIME.HOUR排序。
DF:
UID TIME.HOUR EVENT
1 5 Run
1 5 Run
1 6 Run
1 7 Die
1 8 Run
1 9 Run
2 14 Jump
2 15 Die
2 16 Run
2 17 Run
预期结果:
UID TIME.HOUR EVENT
1 5 Run
1 5 Run
1 6 Run
1 7 Die
2 14 Jump
2 15 Die
我认为我正在使用下面的代码走上正轨,但不要在下一步中挣扎。
args <- which(df$EVENT== "Die")
df[,c(sapply(args, function(x) ???), by = UID] #seq? range?
谢谢。
答案 0 :(得分:3)
我们可以使用data.table
。转换&#39; data.frame&#39;通过&#39; UID&#39;分组到&#39; data.table&#39;,得到逻辑向量的双cumsum
(EVENT == "Die"
),检查它是否小于2子集Data.table(.SD
)
library(data.table)
setDT(df)[, .SD[cumsum(cumsum(EVENT=="Die"))<2] , UID]
# UID TIME.HOUR EVENT
#1: 1 5 Run
#2: 1 5 Run
#3: 1 6 Run
#4: 1 7 Die
#5: 2 14 Jump
#6: 2 15 Die
Or a faster approach:获取行索引,提取该列($V1
)以对数据进行子集化
setDT(df)[df[, .I[cumsum(cumsum(EVENT=="Die"))<2] , UID]$V1]
或修改@ Psidom的方法
setDT(df)[df[, .I[seq(match("Die", EVENT, nomatch = .N))] , UID]$V1]
或使用dplyr
library(dplyr)
df %>%
group_by(UID) %>%
slice(seq(match("Die", EVENT, nomatch = n())))
# UID TIME.HOUR EVENT
# <int> <int> <chr>
#1 1 5 Run
#2 1 5 Run
#3 1 6 Run
#4 1 7 Die
#5 2 14 Jump
#6 2 15 Die
如果我们需要一个data.frame
输出,带%>% as.data.frame
的链(来自@ R.S。评论)
答案 1 :(得分:3)
这可能不是那么有效,但你可以做一个花哨的加入:
mdf = df[EVENT == "Die", head(.SD, 1L), by=UID, .SDcols = "TIME.HOUR"]
df[!mdf, on=.(UID, TIME.HOUR > TIME.HOUR)]
UID TIME.HOUR EVENT
1: 1 5 Run
2: 1 5 Run
3: 1 6 Run
4: 1 7 Die
5: 2 14 Jump
6: 2 15 Die
当然,您实际上不需要将mdf
表另存为单独的对象。
工作原理
x[!i]
,其中i
是另一个data.table或向量列表,是一个反连接,告诉R根据{{1}排除x
行类似于它如何与向量一起工作(其中i
必须是一个逻辑向量)。
i
选项告诉R我们正在进行“非等连接”。 on=.(ID, v >= v)
部分意味着来自v >= v
(左侧)的v
应该大于来自i
的{{1}}(右侧)手边)。
将这两者结合起来,我们排除的行符合v
中指定的条件。
附注。有几件我不确定的事情:我们有一个比非equi加入更好的名字吗?为什么x
左侧的on=
即使v
i
左侧有x[i]
?
我借用Psidom和akrun的答案,使用x
和不平等。这里的一个(可能是?)优势是i
is optimized而head
还没有。
答案 2 :(得分:2)
另一种选择,您可以将head()
与match()
一起使用(找到第一个Die
索引):
dt[, head(.SD, match("Die", EVENT, nomatch = .N)), UID] # if no match is found within the
# group, return the whole group
# UID TIME.HOUR EVENT
#1: 1 5 Run
#2: 1 5 Run
#3: 1 6 Run
#4: 1 7 Die
#5: 2 14 Jump
#6: 2 15 Die