我尝试使用dplyr
按组取出重复值的第一行和最后一行。我出于效率原因这样做,尤其是图表更快。
这不是Select first and last row from grouped data的重复,因为我没有要求组中的严格的第一行和最后一行;我要求分组按级别(在我的情况下为1&0; s和0' s)中的第一行和最后一行可能出现在多个块中。
这是一个例子。假设我想从C列中删除所有冗余的1和0,同时保持A和B不变。
df = data.frame(
A = rep(c("a", "b"), each = 10),
B = rep(c(1:10), 2),
C = c(1,0,0,0,0,0,1,1,1,1,0,0,0,1,0,0,0,0,0,1))
A B C
a 1 1
a 2 0
a 3 0
a 4 0
a 5 0
a 6 0
a 7 1
a 8 1
a 9 1
a 10 1
b 1 0
b 2 0
b 3 0
b 4 1
b 5 0
b 6 0
b 7 0
b 8 0
b 9 0
b 10 1
最终结果应如下所示:
A B C
a 1 1
a 2 0
a 6 0
a 7 1
a 10 1
b 1 0
b 3 0
b 4 1
b 5 0
b 9 0
b 10 1
使用unique
将不会删除任何内容,或者只是选择1或0中的一个,而不保留我尝试实现的开始和结束质量。有没有办法在没有循环的情况下执行此操作,可能使用dplyr
或forcats
?
答案 0 :(得分:2)
我认为slice
应该让你接近:
df %>%
group_by(A,C) %>%
slice(c(1, n()))
给出
A B C
<chr> <int> <dbl>
1 a 2 0
2 a 6 0
3 a 1 1
4 a 10 1
5 b 1 0
6 b 9 0
7 b 4 1
8 b 10 1
虽然这与您的预期结果并不完全一致。 n()给出组中的最后一行。
在您编辑之后,很明显您没有在任何已建立的组中查找值(这是我以前的版本所做的)。您希望按1或0的运行进行分组。为此,您需要创建一个列来检查1&#39; s / 0的运行是否已更改,然后一个用于标识组。然后,slice
将如前所述工作。但是,因为你的一些运行只有1行,所以如果它超过1,我们只需要包含n()
(否则1行显示两次)。
df %>%
mutate(groupChanged = (C != lag(C, default = C[1]))
, toCutBy = cumsum(groupChanged)
) %>%
group_by(toCutBy) %>%
slice(c(1, ifelse(n() == 1, NA, n())))
给出
A B C groupChanged toCutBy
<chr> <int> <dbl> <lgl> <int>
1 a 1 1 FALSE 0
2 a 2 0 TRUE 1
3 a 6 0 FALSE 1
4 a 7 1 TRUE 2
5 a 10 1 FALSE 2
6 b 1 0 TRUE 3
7 b 3 0 FALSE 3
8 b 4 1 TRUE 4
9 b 5 0 TRUE 5
10 b 9 0 FALSE 5
11 b 10 1 TRUE 6
如果1或0的运行必须保持在A
列的级别内,则还需要检查列A
中的更改是否为该调用。在此示例中,它没有效果(因此返回完全相同的值),但在其他情况下可能需要。
df %>%
mutate(groupChanged = (C != lag(C, default = C[1]) |
A != lag(A, default = A[1]))
, toCutBy = cumsum(groupChanged)
) %>%
group_by(toCutBy) %>%
slice(c(1, ifelse(n() == 1, NA, n())))
答案 1 :(得分:0)
一个解决方案:
C_filter <- function(x) {
!sapply(1:length(x), function(i) {
identical(x[i], x[i-1])
}) | !sapply(1:length(x), function(i) {
identical(x[i], x[i+1])
})
}
df %>% group_by(A) %>% filter(C_filter(C))
A B C
1 a 1 1
2 a 2 0
3 a 6 0
4 a 7 1
5 a 10 1
6 b 1 0
7 b 3 0
8 b 4 1
9 b 5 0
10 b 9 0
11 b 10 1