按顺序删除/折叠连续的重复值
Posted
技术标签:
【中文标题】按顺序删除/折叠连续的重复值【英文标题】:Remove/collapse consecutive duplicate values in sequence 【发布时间】:2015-02-13 11:17:19 【问题描述】:我有以下数据框:
a a a b c c d e a a b b b e e d d
要求的结果应该是
a b c d e a b e d
这意味着没有两个连续的行应该具有相同的值。不使用循环如何完成。
由于我的数据集非常庞大,循环执行需要花费大量时间。
dataframe结构如下
a 1
a 2
a 3
b 2
c 4
c 1
d 3
e 9
a 4
a 8
b 10
b 199
e 2
e 5
d 4
d 10
结果:
a 1
b 2
c 4
d 3
e 9
a 4
b 10
e 2
d 4
它应该删除整行。
【问题讨论】:
【参考方案1】:一种简单的方法是使用rle
:
这是您的示例数据:
x <- scan(what = character(), text = "a a a b c c d e a a b b b e e d d")
# Read 17 items
rle
返回具有两个值的 list
:运行长度 ("lengths
") 和为该运行重复的值 ("values
")。
rle(x)$values
# [1] "a" "b" "c" "d" "e" "a" "b" "e" "d"
更新:对于data.frame
如果您使用的是data.frame
,请尝试以下操作:
## Sample data
mydf <- data.frame(
V1 = c("a", "a", "a", "b", "c", "c", "d", "e",
"a", "a", "b", "b", "e", "e", "d", "d"),
V2 = c(1, 2, 3, 2, 4, 1, 3, 9,
4, 8, 10, 199, 2, 5, 4, 10)
)
## Use rle, as before
X <- rle(mydf$V1)
## Identify the rows you want to keep
Y <- cumsum(c(1, X$lengths[-length(X$lengths)]))
Y
# [1] 1 4 5 7 8 9 11 13 15
mydf[Y, ]
# V1 V2
# 1 a 1
# 4 b 2
# 5 c 4
# 7 d 3
# 8 e 9
# 9 a 4
# 11 b 10
# 13 e 2
# 15 d 4
更新 2
“data.table”包有一个函数rleid
,可以让你很容易地做到这一点。使用上面的mydf
,尝试:
library(data.table)
as.data.table(mydf)[, .SD[1], by = rleid(V1)]
# rleid V2
# 1: 1 1
# 2: 2 2
# 3: 3 4
# 4: 4 3
# 5: 5 9
# 6: 6 4
# 7: 7 10
# 8: 8 2
# 9: 9 4
【讨论】:
如何在 data.frame 中使用它?如果我要在列表中使用它,那么我必须再次将这个唯一值与旧的 data.frame 映射,但由于长度较短,因此无法映射。我的目的是每当我在特定列的连续行中获得相同的值时删除数据帧的整行。 不错的答案!对于您的第一个data.frame
解决方案,我发现我需要X <- rle(as.numeric(mydf$V1))
,因为V1
是一个因素。其他评论:我发现在某些情况下cumsum(X$lengths)
会完成这项工作,具体取决于您希望保留哪些重复行(从上到下 v. 从下到上),您确认吗?【参考方案2】:
library(dplyr)
x <- c("a", "a", "a", "b", "c", "c", "d", "e", "a", "a", "b", "b", "b", "e", "e", "d", "d")
x[x!=lag(x, default=1)]
#[1] "a" "b" "c" "d" "e" "a" "b" "e" "d"
编辑:对于data.frame
mydf <- data.frame(
V1 = c("a", "a", "a", "b", "c", "c", "d", "e",
"a", "a", "b", "b", "e", "e", "d", "d"),
V2 = c(1, 2, 3, 2, 4, 1, 3, 9,
4, 8, 10, 199, 2, 5, 4, 10),
stringsAsFactors=FALSE)
dplyr 解决方案是一个班轮:
mydf %>% filter(V1!= lag(V1, default="1"))
# V1 V2
#1 a 1
#2 b 2
#3 c 4
#4 d 3
#5 e 9
#6 a 4
#7 b 10
#8 e 2
#9 d 4
发布脚本
@Carl Witthoft 建议的lead(x,1)
以相反的顺序迭代。
leadit<-function(x) x!=lead(x, default="what")
rows <- leadit(mydf[ ,1])
mydf[rows, ]
# V1 V2
#3 a 3
#4 b 2
#6 c 1
#7 d 3
#8 e 9
#10 a 8
#12 b 199
#14 e 5
#16 d 10
【讨论】:
难道你在这里使用dplyr::lag
?我用干净的会话尝试了你的代码,它不能与stats::lag
【参考方案3】:
使用 base R,我喜欢有趣的算法:
x <- c("a", "a", "a", "b", "c", "c", "d", "e", "a", "a", "b", "b", "b", "e", "e", "d", "d")
x[x!=c(x[-1], FALSE)]
#[1] "a" "b" "c" "d" "e" "a" "b" "e" "d"
【讨论】:
类似地可以使用索引而不是tail
,类似于x[x != c(x[-1], FALSE)]
我努力适应 data.frame
中的因素。在数据帧内,右轴,FALSE
将向量限制为integer
类型,因此左轴x
必须受到如此限制,以便与as.integer()
进行比较。如果我错了,请纠正我!
很抱歉,但不清楚...如果您遇到有关此类主题的问题,您可以提出关于 SO 的问题,包括您的输入、输出和到目前为止所做的工作。【参考方案4】:
尽管我很喜欢,... errr,love rle
,这是一场枪战:
编辑:无法弄清楚 dplyr
到底是怎么回事,所以我使用了 dplyr::lead
。我在 OSX、R3.1.2 和来自 CRAN 的最新 dplyr
上。
xlet<-sample(letters,1e5,rep=T)
rleit<-function(x) rle(x)$values
lagit<-function(x) x[x!=lead(x, default=1)]
tailit<-function(x) x[x!=c(tail(x,-1), tail(x,1))]
microbenchmark(rleit(xlet),lagit(xlet),tailit(xlet),times=20)
Unit: milliseconds
expr min lq median uq max neval
rleit(xlet) 27.43996 30.02569 30.20385 30.92817 37.10657 20
lagit(xlet) 12.44794 15.00687 15.14051 15.80254 46.66940 20
tailit(xlet) 12.48968 14.66588 14.78383 15.32276 55.59840 20
【讨论】:
lagit
为您工作了吗?它没有给我任何回报。也许这就是它最快的原因......
我认为你需要在加载 dplyr
后重新运行它,因为我怀疑 lagit
目前没有做任何事情,因此速度非常快
糟糕,是的——我没有检查输出。但是怎么了?我在“leadlag”下看到“lag”,但没有实际功能dplyr::lag
基准测试中的tailit
函数缺少向量中的最后一个“d”.. 可能更新为x[x!=c(x[-1], FALSE)]
,如答案中所示
似乎lead
比lag
慢一点。为什么呢? github.com/hadley/dplyr/blob/master/R/lead-lag.R以上是关于按顺序删除/折叠连续的重复值的主要内容,如果未能解决你的问题,请参考以下文章
折叠 Pandas 数据框中的行,每列具有不同的逻辑 [重复]