R - 删除连续的(仅)重复项

R - delete consecutive (ONLY) duplicates

我需要根据给定列中重复的值从数据框中删除行,但仅删除那些连续的行。 例如,对于以下数据框:

df = data.frame(x=c(1,1,1,2,2,4,2,2,1))
df$y <- c(10,11,30,12,49,13,12,49,30)
df$z <- c(1,2,3,4,5,6,7,8,9)

x  y z
1 10 1
1 11 2
1 30 3
2 12 4
2 49 5
4 13 6
2 12 7
2 49 8
1 30 9

我需要消除 x 列中具有连续重复值的行,保留最后重复的行,并保持数据框的结构:

x  y z
1 30 3
2 49 5
4 13 6
2 49 8
1 30 9

按照 help 和其他一些帖子的说明,我尝试使用 duplicated 函数:

df[ !duplicated(x,fromLast=TRUE), ] # which gives me this:
      x  y  z
1     1 10  1
6     4 13  6
7     2 12  7
9     1 30  9
NA   NA NA NA
NA.1 NA NA NA
NA.2 NA NA NA
NA.3 NA NA NA
NA.4 NA NA NA
NA.5 NA NA NA
NA.6 NA NA NA
NA.7 NA NA NA
NA.8 NA NA NA

不确定为什么我在最后得到 NA 行(我正在测试的类似 table 没有发生),但只对值起作用。

我也试过使用 data.table 包如下:

library(data.table)
dt <- as.data.table(df)           
setkey(dt, x)                    
dt[J(unique(x)), mult ='last'] 

效果很好,但它消除了数据框中的所有重复项,而不仅仅是那些连续的重复项,给出如下内容:

x  y z
1 30 9
2 49 8
4 13 6

如有转载请见谅。我尝试了一些建议,但 none 只消除了那些连续的建议。 如果有任何帮助,我将不胜感激。

谢谢

怎么样:

df[cumsum(rle(df$x)$lengths),]

解释:

rle(df$x)

为您提供 运行 长度和 连续 重复值 x 变量。那么:

rle(df$x)$lengths

提取长度。最后:

cumsum(rle(df$x)$lengths)

给出您可以 select 使用 [ 的行索引。

编辑 为了好玩,这里有 microbenchmark 到目前为止给出的答案,rle 是我的,consec 是我的想法最基本的直接答案,由@James 给出,我会 "accept",dp 是@Nik 给出的 dplyr 答案。

#> Unit: microseconds
#>    expr       min         lq       mean     median         uq        max
#>     rle   134.389   145.4220   162.6967   154.4180   172.8370    375.109
#>  consec   111.411   118.9235   136.1893   123.6285   145.5765    314.249
#>      dp 20478.898 20968.8010 23536.1306 21167.1200 22360.8605 179301.213

rle 比我想象的要好。

我能想到的 dplyr 的廉价解决方案:

方法:

library(dplyr)
df %>% 
  mutate(id = lag(x, 1), 
         decision = if_else(x != id, 1, 0), 
         final = lead(decision, 1, default = 1)) %>% 
  filter(final == 1) %>% 
  select(-id, -decision, -final)

输出:

  x  y z
1 1 30 3
2 2 49 5
3 4 13 6
4 2 49 8
5 1 30 9

如果您的数据在底部具有相同的 x 值,这甚至会起作用

新输入:

df2 <- df %>% add_row(x = 1, y = 10, z = 12)
df2

   x  y  z
1  1 10  1
2  1 11  2
3  1 30  3
4  2 12  4
5  2 49  5
6  4 13  6
7  2 12  7
8  2 49  8
9  1 30  9
10 1 10 12

使用相同的方法:

df2 %>% 
  mutate(id = lag(x, 1), 
         decision = if_else(x != id, 1, 0), 
         final = lead(decision, 1, default = 1)) %>% 
  filter(final == 1) %>% 
  select(-id, -decision, -final)

新输出:

  x  y  z
1 1 30  3
2 2 49  5
3 4 13  6
4 2 49  8
5 1 10 12

您只需检查数字后面没有重复项,即 x[i+1] != x[i] 并注意最后一个值将始终存在。

df[c(df$x[-1] != df$x[-nrow(df)],TRUE),]
  x  y z
3 1 30 3
5 2 49 5
6 4 13 6
8 2 49 8
9 1 30 9

这是一个data.table解决方案。诀窍是使用 shift 函数创建 x 的移位版本,并将其与 x

进行比较
library(data.table)
dattab <- as.data.table(df)
dattab[x != shift(x = x, n = 1, fill = -999, type = "lead")] # edited to add closing )

通过这种方式,您可以将 x 的每个值与其紧随其后的值进行比较,并在它们匹配的地方抛出。确保将 fill 设置为 x 中没有的内容,以便正确处理最后一个值。