以下是我的示例数据:
df = data.frame(id=rep(c(123,456,789),each=5),day=rep(c(1:5),3),measure=c(2.2,3.4,2.1,-0.2,-1.2,3.4,2.4,-2.2,-3.1,-1.7,3.9,5.4,-1,3.2,4.2))
id day measure
1 123 1 2.2
2 123 2 3.4
3 123 3 2.1
4 123 4 -0.2
5 123 5 -1.2
6 456 1 3.4
7 456 2 2.4
8 456 3 -2.2
9 456 4 -3.1
10 456 5 -1.7
11 789 1 3.9
12 789 2 5.4
13 789 3 -1.0
14 789 4 3.2
15 789 5 4.2
每个人都有五天的数据。
我想在每个人中找到df $ measure中的位置,其中有三个或更多连续负值在每个单独的内容中,并删除这些行。如果有两个或更少的连续负值,只需将值设置为0。
个人123最后有两个负值,因此将值更改为0 个别456最后有三个负值,所以删除这些行 单个789在第3天有一个负值,因此将值更改为0
结果:
id day measure
1 123 1 2.2
2 123 2 3.4
3 123 3 2.1
4 123 4 0
5 123 5 0
6 456 1 3.4
7 456 2 2.4
8 789 1 3.9
9 789 2 5.4
10 789 3 0
11 789 4 3.2
12 789 5 4.2
到目前为止我所拥有的:
如果我首先将df $ measure中的所有负值都转换为0 ..
df$measure[df$measure < 0] <- 0
然后以某种方式使用rle:
m = rle(df$measure)
Run Length Encoding
lengths: int [1:12] 1 1 1 2 1 1 3 1 1 1 ...
values : num [1:12] 2.2 3.4 2.1 0 3.4 2.4 0 3.9 5.4 0 ...
从m $ length和m $值计算得出连续3个或更多的0的索引 - 需要删除的那些。
但需要为每个ID单独检查吗?
实现这一目标的最有效方法是什么?
答案 0 :(得分:4)
我们从&#39;衡量&#39;得到逻辑向量的rle
(!df$measure
- 0为0,其他所有为FALSE为TRUE)。 ,分配&#39;值&#39;具有&#39;长度的矢量(来自rle
)&#39;小于3到FALSE,否定它(!
)并对数据集进行子集化。
df[!inverse.rle(within.list(rle(!df$measure), values[lengths<3] <- FALSE)),]
# id day measure
#1 123 1 2.2
#2 123 2 3.4
#3 123 3 2.1
#4 123 4 0.0
#5 123 5 0.0
#6 456 1 3.4
#7 456 2 2.4
#11 789 1 3.9
#12 789 2 5.4
#13 789 3 0.0
#14 789 4 3.2
#15 789 5 4.2
注意:上述结果与OP的预期输出相匹配,因为0值在相邻的id&#39;之间不连续。如果我们需要在每个ID中执行此操作,请按技术使用任何组。在base R
中,我们可以使用ave
indx <- with(df, !ave(!measure, id, FUN = function(x) {
inverse.rle(within.list(rle(x), values[lengths<3] <- FALSE))
}))
df[indx,]
# id day measure
#1 123 1 2.2
#2 123 2 3.4
#3 123 3 2.1
#4 123 4 0.0
#5 123 5 0.0
#6 456 1 3.4
#7 456 2 2.4
#11 789 1 3.9
#12 789 2 5.4
#13 789 3 0.0
#14 789 4 3.2
#15 789 5 4.2
或者我们可以使用rleid
中的data.table
。转换&#39; data.frame&#39;到&#39; data.table&#39; (setDT(df)
),按&#39; id&#39;分组和否定的&#39;度量&#39;的run-length-id,得到一个逻辑索引列(!(!measure & .N >2)
)来对数据集中的行进行子集化。
library(data.table)
setDT(df)[df[, !(!measure & .N >2), .(id, rleid(!measure))]$V1]
# id day measure
# 1: 123 1 2.2
# 2: 123 2 3.4
# 3: 123 3 2.1
# 4: 123 4 0.0
# 5: 123 5 0.0
# 6: 456 1 3.4
# 7: 456 2 2.4
# 8: 789 1 3.9
# 9: 789 2 5.4
#10: 789 3 0.0
#11: 789 4 3.2
#12: 789 5 4.2
或者我们可以使用dplyr
library(dplyr)
df %>%
group_by(id, gr = cumsum(c(0,abs(diff(!measure))))) %>%
filter(!(all(!measure) & n() >2)) %>%
ungroup() %>%
select(-gr)
# id day measure
# (dbl) (int) (dbl)
#1 123 1 2.2
#2 123 2 3.4
#3 123 3 2.1
#4 123 4 0.0
#5 123 5 0.0
#6 456 1 3.4
#7 456 2 2.4
#8 789 1 3.9
#9 789 2 5.4
#10 789 3 0.0
#11 789 4 3.2
#12 789 5 4.2
注2:用0替换负值后使用数据。
答案 1 :(得分:4)
另一个基础R版本ave
使用旧的&#34;反向否定逻辑检查&#34;获得适当的反击的技巧。
自:
with(df, rev(cumsum(!(rev(measure) < 0))) )
#[1] 9 8 7 6 6 6 5 4 4 4 4 3 2 2 1
# compare the equivalent of df$id groups
#[1] 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
与id
结合使用时,您只需检查长度:
df[with(df, ave(measure, list(id, rev(cumsum(!(rev(measure) < 0)))), FUN=length) < 3 ),]
# id day measure
#1 123 1 2.2
#2 123 2 3.4
#3 123 3 2.1
#4 123 4 -0.2
#5 123 5 -1.2
#6 456 1 3.4
#7 456 2 2.4
#11 789 1 3.9
#12 789 2 5.4
#13 789 3 -1.0
#14 789 4 3.2
#15 789 5 4.2