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

时间:2016-04-18 01:20:06

标签: r

我有一个重复行的数据集。我想删除连续重复并计算它们,但前提是它们是连续的。我正在寻找一种有效的方法来做到这一点。无法考虑如何使用 dplyr data.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

期望的输出

       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

7 个答案:

答案 0 :(得分: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

答案 1 :(得分:15)

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

with(rle(do.call(paste, dat)), cbind(dat[ cumsum(lengths), ], lengths))

如果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))

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

答案 2 :(得分: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)

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

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)

不管怎样,你得到:

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

修改

Per @ Frank的评论,如果您summarise所有变量,您还可以使用n插入mutate并折叠而不是uniquegroup_by希望在run 之前保留,因为summarise会折叠 last 组。这种方法的一个优点是,您不必ungroup就可以摆脱run,因为summarise会为您做到:

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

答案 3 :(得分: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

答案 4 :(得分:7)

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

dat[, g := rleidv(dat)][, N := .N, keyby = g
   ][J(unique(g)), mult = "first"
   ][, g := NULL
   ][]

它的可读性较差,事实证明它也较慢。弗兰克的解决方案更快,更易读。

# 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)

使用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 

答案 5 :(得分:7)

与其他答案没有太大差别,但(1)有序数据和(2)寻找连续运行似乎是一个很好的候选者,只是OR x[-1L] != x[-length(x)]跨越列而不是{ {1}}或其他复杂的操作。我想这不过是等同于paste

data.table::rleid

答案 6 :(得分: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

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)]