查找重复行,删除和计数的有效方法

Tyl*_*ker 30 r

我有一个重复行的数据集.我想删除连续重复并计算它们,但前提是它们是连续的.我正在寻找一种有效的方法来做到这一点.想不出怎么在dplyrdata.table.

MWE

dat <- data.frame(
    x = c(6, 2, 3, 3, 3, 1, 1, 6, 5, 5, 6, 6, 5, 4),
    y = c(7, 5, 7, 7, 7, 5, 5, 7, 1, 2, 7, 7, 1, 7),
    z = c(rep(LETTERS[1:2], each=7))
)

##        x     y     z
## 1      6     7     A
## 2      2     5     A
## 3      3     7     A
## 4      3     7     A
## 5      3     7     A
## 6      1     5     A
## 7      1     5     A
## 8      6     7     B
## 9      5     1     B
## 10     5     2     B
## 11     6     7     B
## 12     6     7     B
## 13     5     1     B
## 14     4     7     B
Run Code Online (Sandbox Code Playgroud)

期望的输出

       x     y     z   n
1      6     7     A   1
2      2     5     A   1
3      3     7     A   3
4      1     5     A   2
5      6     7     B   1
6      5     1     B   1
7      5     2     B   1
8      6     7     B   2
9      5     1     B   1 
10     4     7     B   1
Run Code Online (Sandbox Code Playgroud)

Fra*_*ank 25

使用data.table:

library(data.table)
setDT(dat)

dat[, c(.SD[1L], .N), by=.(g = rleidv(dat))][, g := NULL]

    x y z N
 1: 6 7 A 1
 2: 2 5 A 1
 3: 3 7 A 3
 4: 1 5 A 2
 5: 6 7 B 1
 6: 5 1 B 1
 7: 5 2 B 1
 8: 6 7 B 2
 9: 5 1 B 1
10: 4 7 B 1
Run Code Online (Sandbox Code Playgroud)

  • @Tyler`.()`是`list()`的别名,所以它会给`list(.N,.SD)`,但`.SD`已经是一个列表,我们希望结果是一个 - 级别列表(而不是嵌套).我认为可能有一个功能请求`.(col,.SD)`并且我肯定错误地输入了很多次. (2认同)

Fra*_*ank 15

与Ricky的答案类似,这是另一个基本解决方案:

with(rle(do.call(paste, dat)), cbind(dat[ cumsum(lengths), ], lengths))
Run Code Online (Sandbox Code Playgroud)

如果paste没有为你的列类切割它,你可以做到

ud     = unique(dat)
ud$r   = seq_len(nrow(ud))
dat$r0 = seq_len(nrow(dat))
newdat = merge(dat, ud)

with(rle(newdat[order(newdat$r0), ]$r), cbind(dat[cumsum(lengths), ], lengths))
Run Code Online (Sandbox Code Playgroud)

......虽然我猜是有更好的方法.

  • *如果粘贴没有削减它*我看到你在那里做了什么. (8认同)

ali*_*ire 11

使用dplyr,你可以借用data.table::rleid来制作一个运行ID列,然后n用来计算行数并unique删除重复:

dat %>% group_by(run = data.table::rleid(x, y, z)) %>%  mutate(n = n()) %>% 
    distinct() %>% ungroup() %>% select(-run)
Run Code Online (Sandbox Code Playgroud)

rleid如果你愿意,你可以用基础R 替换它,但它不是那么漂亮:

dat %>% group_by(run = rep(seq_along(rle(paste(x, y, z))$len), 
                           times = rle(paste(x, y, z))$len)) %>%  
    mutate(n = n()) %>% distinct() %>% ungroup() %>% select(-run)
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,你得到:

Source: local data frame [10 x 4]

       x     y      z     n
   (dbl) (dbl) (fctr) (int)
1      6     7      A     1
2      2     5      A     1
3      3     7      A     3
4      1     5      A     2
5      6     7      B     1
6      5     1      B     1
7      5     2      B     1
8      6     7      B     2
9      5     1      B     1
10     4     7      B     1
Run Code Online (Sandbox Code Playgroud)

编辑

每@弗兰克的评论,你还可以使用summarise插入n和折叠,而不是mutateuniquegroup_by所有你想保持变量之前run,由于summarise坍塌的最后一组.一个优点这种方法是,你不必ungroup摆脱的run,因为summarise不适合你:

dat %>% group_by(x, y, z, run = data.table::rleid(x, y, z)) %>% 
    summarise(n = n()) %>% select(-run)
Run Code Online (Sandbox Code Playgroud)


Ric*_*cky 10

下面的基础解决方案

idx <- rle(with(dat, paste(x, y, z)))
d <- cbind(do.call(rbind, strsplit(idx$values, " ")), idx$lengths)
as.data.frame(d)  

   V1 V2 V3 V4
1   6  7  A  1
2   2  5  A  1
3   3  7  A  3
4   1  5  A  2
5   6  7  B  1
6   5  1  B  1
7   5  2  B  1
8   6  7  B  2
9   5  1  B  1
10  4  7  B  1
Run Code Online (Sandbox Code Playgroud)

  • 或者`with(rle(do.call(paste,dat)),cbind(dat [cumsum(length),],lengths))`.另外strsplit会给你字符串或因子,而你可能想要一些cols中的数字. (2认同)
  • 那是辉煌的弗兰克,如果你把它作为一个答案,我会投票支持. (2认同)

Jot*_*ota 7

如果你有一个大型数据集,你可以使用与Frank的data.table解决方案类似的想法,但避免.SD像这样使用:

dat[, g := rleidv(dat)][, N := .N, keyby = g
   ][J(unique(g)), mult = "first"
   ][, g := NULL
   ][]
Run Code Online (Sandbox Code Playgroud)

它的可读性较差,事实证明它也较慢.Frank的解决方案更快,更易读.

# benchmark on 14 million rows
dat <- data.frame(
    x = rep(c(6, 2, 3, 3, 3, 1, 1, 6, 5, 5, 6, 6, 5, 4), 1e6),
    y = rep(c(7, 5, 7, 7, 7, 5, 5, 7, 1, 2, 7, 7, 1, 7), 1e6),
    z = rep(c(rep(LETTERS[1:2], each=7)), 1e6)
)

setDT(dat)
d1 <- copy(dat)
d2 <- copy(dat)
Run Code Online (Sandbox Code Playgroud)

使用R 3.2.4和data.table 1.9.7(在Frank的计算机上):

system.time(d1[, c(.SD[1L], .N), by=.(g = rleidv(d1))][, g := NULL])
#    user  system elapsed 
#    0.42    0.10    0.52 
system.time(d2[, g := rleidv(d2)][, N := .N, keyby = g][J(unique(g)), mult = "first"][, g := NULL][])
#    user  system elapsed 
#    2.48    0.25    2.74 
Run Code Online (Sandbox Code Playgroud)

  • 3.2.3和1.9.7在这里.如果我把它写成`system.time(d2 [,g:= rleidv(.SD)] [,c(.N,.SD [1L]),则= g] [,g:= NULL] [])`.我认为差异可能是`.SD [1L]`最近被优化了.https://github.com/Rdatatable/data.table/issues/735 (2认同)
  • @Frank我的data.table安装和datatable.dll出现问题.修好之后,我得到的结果与你的时间相似. (2认同)
  • @Frank,马上.它被优化了.测试它的一种简单方法是将`verbose = TRUE`添加到`[]`调用,例如`dat [,c(.SD [1L],.N),by =.(g = rleidv(dat) ),verbose = TRUE]` (2认同)

ale*_*laz 7

与其他答案没有太大的不同,但是(1)有序数据和(2)寻找连续运行似乎是一个很好的候选者,只是OR通过x[-1L] != x[-length(x)]列而不是paste或其他复杂的操作.我猜这是,不知何故,相当于data.table::rleid.

ans = logical(nrow(dat) - 1L)
for(j in seq_along(dat)) ans[dat[[j]][-1L] != dat[[j]][-nrow(dat)]] = TRUE    
ans = c(TRUE, ans)
#or, the two-pass, `c(TRUE, Reduce("|", lapply(dat, function(x) x[-1L] != x[-length(x)])))`

cbind(dat[ans, ], n = tabulate(cumsum(ans)))
#   x y z n
#1  6 7 A 1
#2  2 5 A 1
#3  3 7 A 3
#6  1 5 A 2
#8  6 7 B 1
#9  5 1 B 1
#10 5 2 B 1
#11 6 7 B 2
#13 5 1 B 1
#14 4 7 B 1
Run Code Online (Sandbox Code Playgroud)


the*_*ail 6

另一个基础尝试使用ave,只是因为:

dat$grp <- ave(
  seq_len(nrow(dat)),
  dat[c("x","y","z")],
  FUN=function(x) cumsum(c(1,diff(x))!=1)
)

dat$count <- ave(dat$grp, dat, FUN=length)

dat[!duplicated(dat[1:4]),]


#   x y z grp count
#1  6 7 A   0     1
#2  2 5 A   0     1
#3  3 7 A   0     3
#6  1 5 A   0     2
#8  6 7 B   0     1
#9  5 1 B   0     1
#10 5 2 B   0     1
#11 6 7 B   1     2
#13 5 1 B   1     1
#14 4 7 B   0     1
Run Code Online (Sandbox Code Playgroud)

data.table转换的尝试:

d1[, .(sq=.I, grp=cumsum(c(1, diff(.I)) != 1)), by=list(x,y,z)][(sq), .N, by=list(x,y,z,grp)]
Run Code Online (Sandbox Code Playgroud)