我有一个重复行的数据集.我想删除连续重复并计算它们,但前提是它们是连续的.我正在寻找一种有效的方法来做到这一点.想不出怎么在dplyr或data.table.
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)
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)
......虽然我猜是有更好的方法.
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和折叠,而不是mutate和unique你group_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)
如果你有一个大型数据集,你可以使用与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)
与其他答案没有太大的不同,但是(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)
另一个基础尝试使用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)