排除后续重复行
Exclude subsequent duplicated rows
我想排除所有重复的行。但是,只有当它们是后续行时才必须为真。下面是一个代表性的例子:
我的输入df
:
df <- "NAME VALUE
Prb1 0.05
Prb2 0.05
Prb3 0.05
Prb4 0.06
Prb5 0.06
Prb6 0.01
Prb7 0.10
Prb8 0.05"
df <- read.table(text=df, header=T)
我的预期outdf
:
outdf <- "NAME VALUE
Prb1 0.05
Prb4 0.06
Prb6 0.01
Prb7 0.10
Prb8 0.05"
outdf <- read.table(text=df, header=T)
可能有很多方法可以解决这个问题,我会尝试 data.table
devel version
中的 rleid/unique
组合
library(data.table) ## v >= 1.9.5
unique(setDT(df)[, indx := rleid(VALUE)], by = "indx")
# NAME VALUE indx
# 1: Prb1 0.05 1
# 2: Prb4 0.06 2
# 3: Prb6 0.01 3
# 4: Prb7 0.10 4
# 5: Prb8 0.05 5
或来自评论的一些很好的建议:
仅使用新的 shift
函数
setDT(df)[VALUE != shift(VALUE, fill = TRUE)]
或使用 duplicated
结合 rleid
setDT(df)[!duplicated(rleid(VALUE)), ]
这个怎么样:
> df[c(T, df[-nrow(df),-1] != df[-1,-1]), ]
NAME VALUE
1 Prb1 0.05
4 Prb4 0.06
6 Prb6 0.01
7 Prb7 0.10
8 Prb8 0.05
在这里,df[-nrow(df),-1] != df[-1,-1]
找到包含不同值的连续行对,其余代码从数据帧中提取它们。
rle()
是一个很好的函数,它可以识别相同值的运行,但将其输出转换为可用形式可能会有点痛苦。这是一个 相对 无痛的咒语,适用于你的情况。
df[sequence(rle(df$VALUE)$lengths) == 1, ]
# NAME VALUE
# 1 Prb1 0.05
# 4 Prb4 0.06
# 6 Prb6 0.01
# 7 Prb7 0.10
# 8 Prb8 0.05
我刚才遇到了这个很好的函数,它根据指定的变量将行标记为第一个:
isFirst <- function(x,...) {
lengthX <- length(x)
if (lengthX == 0) return(logical(0))
retVal <- c(TRUE, x[-1]!=x[-lengthX])
for(arg in list(...)) {
stopifnot(lengthX == length(arg))
retVal <- retVal | c(TRUE, arg[-1]!=arg[-lengthX])
}
if (any(missing<-is.na(retVal))) # match rle: NA!=NA
retVal[missing] <- TRUE
retVal
}
将其应用于您的数据得到:
> df$first <- isFirst(df$VALUE)
> df
NAME VALUE first
1 Prb1 0.05 TRUE
2 Prb2 0.05 FALSE
3 Prb3 0.05 FALSE
4 Prb4 0.06 TRUE
5 Prb5 0.06 FALSE
6 Prb6 0.01 TRUE
7 Prb7 0.10 TRUE
8 Prb8 0.05 TRUE
然后您可以对第一列进行去重以获得预期的输出。
我过去发现这非常有用,尤其是在 SAS 背景下,这很容易做到。
我会使用类似于@NPE 的解决方案
df[c(TRUE,abs(diff(df$VALUE))>1e-6),]
当然,您可以使用任何其他容差级别(1e-6
除外)。
已经有很多好的答案,这里是 dplyr
版本:
filter(df,VALUE!=lag(VALUE,default=df$VALUE[1]+1))
我想排除所有重复的行。但是,只有当它们是后续行时才必须为真。下面是一个代表性的例子:
我的输入df
:
df <- "NAME VALUE
Prb1 0.05
Prb2 0.05
Prb3 0.05
Prb4 0.06
Prb5 0.06
Prb6 0.01
Prb7 0.10
Prb8 0.05"
df <- read.table(text=df, header=T)
我的预期outdf
:
outdf <- "NAME VALUE
Prb1 0.05
Prb4 0.06
Prb6 0.01
Prb7 0.10
Prb8 0.05"
outdf <- read.table(text=df, header=T)
可能有很多方法可以解决这个问题,我会尝试 data.table
devel version
rleid/unique
组合
library(data.table) ## v >= 1.9.5
unique(setDT(df)[, indx := rleid(VALUE)], by = "indx")
# NAME VALUE indx
# 1: Prb1 0.05 1
# 2: Prb4 0.06 2
# 3: Prb6 0.01 3
# 4: Prb7 0.10 4
# 5: Prb8 0.05 5
或来自评论的一些很好的建议:
仅使用新的 shift
函数
setDT(df)[VALUE != shift(VALUE, fill = TRUE)]
或使用 duplicated
结合 rleid
setDT(df)[!duplicated(rleid(VALUE)), ]
这个怎么样:
> df[c(T, df[-nrow(df),-1] != df[-1,-1]), ]
NAME VALUE
1 Prb1 0.05
4 Prb4 0.06
6 Prb6 0.01
7 Prb7 0.10
8 Prb8 0.05
在这里,df[-nrow(df),-1] != df[-1,-1]
找到包含不同值的连续行对,其余代码从数据帧中提取它们。
rle()
是一个很好的函数,它可以识别相同值的运行,但将其输出转换为可用形式可能会有点痛苦。这是一个 相对 无痛的咒语,适用于你的情况。
df[sequence(rle(df$VALUE)$lengths) == 1, ]
# NAME VALUE
# 1 Prb1 0.05
# 4 Prb4 0.06
# 6 Prb6 0.01
# 7 Prb7 0.10
# 8 Prb8 0.05
我刚才遇到了这个很好的函数,它根据指定的变量将行标记为第一个:
isFirst <- function(x,...) {
lengthX <- length(x)
if (lengthX == 0) return(logical(0))
retVal <- c(TRUE, x[-1]!=x[-lengthX])
for(arg in list(...)) {
stopifnot(lengthX == length(arg))
retVal <- retVal | c(TRUE, arg[-1]!=arg[-lengthX])
}
if (any(missing<-is.na(retVal))) # match rle: NA!=NA
retVal[missing] <- TRUE
retVal
}
将其应用于您的数据得到:
> df$first <- isFirst(df$VALUE)
> df
NAME VALUE first
1 Prb1 0.05 TRUE
2 Prb2 0.05 FALSE
3 Prb3 0.05 FALSE
4 Prb4 0.06 TRUE
5 Prb5 0.06 FALSE
6 Prb6 0.01 TRUE
7 Prb7 0.10 TRUE
8 Prb8 0.05 TRUE
然后您可以对第一列进行去重以获得预期的输出。
我过去发现这非常有用,尤其是在 SAS 背景下,这很容易做到。
我会使用类似于@NPE 的解决方案
df[c(TRUE,abs(diff(df$VALUE))>1e-6),]
当然,您可以使用任何其他容差级别(1e-6
除外)。
已经有很多好的答案,这里是 dplyr
版本:
filter(df,VALUE!=lag(VALUE,default=df$VALUE[1]+1))