找到所有重复行,包括“下标较小的元素”

163

R的duplicated函数返回一个向量,显示向量或数据框中的每个元素是否是具有较小下标的元素的副本。因此,如果一个5行数据框的第3、4和5行相同,则duplicated将给我这个向量。

FALSE, FALSE, FALSE, TRUE, TRUE

但在这种情况下,我确实想要获取

FALSE, FALSE, TRUE, TRUE, TRUE

也就是说,我想知道一个行是否被具有更大下标的另一行所重复。

10个回答

191

duplicated有一个fromLast参数。在?duplicated的“示例”部分中,展示了如何使用它。只需两次调用duplicated,一次使用fromLast=FALSE,一次使用fromLast=TRUE,并获取其中任何一个为TRUE的行。


稍晚的编辑: 由于您没有提供可复制的示例,因此这里有一个由@jbaums提供的说明。

vec <- c("a", "b", "c","c","c") 
vec[duplicated(vec) | duplicated(vec, fromLast=TRUE)]
## [1] "c" "c" "c"

编辑:以下是数据框的示例:

df <- data.frame(rbind(c("a","a"),c("b","b"),c("c","c"),c("c","c")))
df[duplicated(df) | duplicated(df, fromLast=TRUE), ]
##   X1 X2
## 3  c  c
## 4  c  c

4
等一下,我刚刚进行了一项测试,发现我错了: x <- c(1:9, 7:10, 5:22); y <- c(letters, letters[1:5]); test <- data.frame(x, y); test[duplicated(test$x) | duplicated(test$x, fromLast=TRUE), ] 返回了三个7、8和9的副本。为什么会这样? - JoeM05
2
因为无论从前往后还是从后往前开始,中间的值都会被捕获。例如,duplicated(c(1,1,1))duplicated(c(1,1,1), fromLast=TRUE) 分别返回 c(FALSE,TRUE,TRUE)c(TRUE,TRUE,FALSE)。在两种情况下,中间的值都是 TRUE。 对这两个向量取 | 运算符可得 c(TRUE, TRUE, TRUE) - Brandon

42
你需要组装一组 duplicated 值,应用 unique,然后使用 %in% 进行测试。像往常一样,一个示例问题将使这个过程变得生动起来。
> vec <- c("a", "b", "c","c","c")
> vec[ duplicated(vec)]
[1] "c" "c"
> unique(vec[ duplicated(vec)])
[1] "c"
>  vec %in% unique(vec[ duplicated(vec)]) 
[1] FALSE FALSE  TRUE  TRUE  TRUE

同意。甚至可能会减慢处理速度,但不太可能减慢很多。 - IRTFM
非常正确。原帖没有提供一个数据示例,以测试数据框中是否存在“重复”的行。我认为,如果首先使用一个不同寻常的分隔符将每一行粘贴在一起,那么使用“duplicated”,“unique”和“%in%” 的建议可以很容易地推广到数据框中。(接受的答案更好。) - IRTFM

24

使用 dplyr,可以通过以下方式获得数据框中的重复行:

library(tidyverse)
df = bind_rows(iris, head(iris, 20)) # build some test data
df %>% group_by_all() %>% filter(n()>1) %>% ungroup()

为了排除特定的列,可以使用group_by_at(vars(-var1, -var2))代替对数据进行分组。

如果实际上需要行索引而不仅仅是数据,可以首先将它们添加进去,例如:

df %>% add_rownames %>% group_by_at(vars(-rowname)) %>% filter(n()>1) %>% pull(rowname)

2
很好地使用了 n()。别忘了取消分组后的数据框。 - qwr
@qwr 我已经调整了答案,以取消结果的分组。 - Holger Brandl
@HolgerBrandl, @qwr,通常的回答是有用的,但我不明白如何选择要排除的列。在 group_by_at(vars(-var1,-var2)) 中,“vars”是指什么?var1var2是datatable vars中的列名吗?我假设负号表示排除,那么进程的其余部分(filterungroup)作用于datatable vars中其余的列,但不包括var1var2,对吗?抱歉这么啰嗦,但我经常在快速简写方面遇到问题! - W Barker
“vars”是dplyr中的一个方法,请参见https://dplyr.tidyverse.org/reference/vars.html。var1、var2确实是要从重复检查中排除的列名。在建议的解决方案中,重复是根据分组变量进行评估的。确实,负数表示排除。 - Holger Brandl
2
group_by_all() and group_by_at() have been superseded in recent versions of dplyr. Now you can do this:
iris %>% group_by(across()) %>% filter(n() > 1) %>% ungroup()
- MCornejo

6

这是@Joshua Ulrich的解决方案,已转化为函数。这种格式允许您以与duplicated()相同的方式使用此代码:

allDuplicated <- function(vec){
  front <- duplicated(vec)
  back <- duplicated(vec, fromLast = TRUE)
  all_dup <- front + back > 0
  return(all_dup)
}

使用同一个例子:

vec <- c("a", "b", "c","c","c") 
allDuplicated(vec) 
[1] FALSE FALSE  TRUE  TRUE  TRUE


3

1
这个答案似乎同时将vec用作原子向量和数据框。我怀疑如果使用实际的数据框,它会失败。 - IRTFM

3
这是 vctrs::vec_duplicate_detect() 的工作原理。
# on a vector
vctrs::vec_duplicate_detect(c(1, 2, 1))
#> [1]  TRUE FALSE  TRUE
# on a data frame
vctrs::vec_duplicate_detect(mtcars[c(1, 2, 1),])
#> [1]  TRUE FALSE  TRUE

reprex package(v2.0.1)于2022年7月19日创建


2

我遇到了类似的问题,但是我需要通过特定列中的值来识别重复的行。我想出了以下 dplyr 解决方案:

df <- df %>% 
  group_by(Column1, Column2, Column3) %>% 
  mutate(Duplicated = case_when(length(Column1)>1 ~ "Yes",
                            TRUE ~ "No")) %>%
  ungroup()

代码按照特定列对行进行分组。如果一组的长度大于1,则代码将该组中的所有行标记为重复。完成后,您可以使用“Duplicated”列进行筛选等操作。

0
如果您对于某些列中的重复行感兴趣,可以使用plyr方法:
ddply(df, .(col1, col2), function(df) if(nrow(df) > 1) df else c())

使用 dplyr 添加计数变量:

df %>% add_count(col1, col2) %>% filter(n > 1)  # data frame
df %>% add_count(col1, col2) %>% select(n) > 1  # logical vector

对于重复行(考虑所有列):

df %>% group_by_all %>% add_tally %>% ungroup %>% filter(n > 1)
df %>% group_by_all %>% add_tally %>% ungroup %>% select(n) > 1

这些方法的好处是您可以指定重复项的截止数量。


0
如果你想创建一个新的列,对于任何值为“id”重复的行,列中的值为“TRUE”,我花了一些时间才弄明白这个问题: data %>% mutate(duplicate_id = if_else(id %in% id[duplicated(id)], TRUE, FALSE))

0

这篇文章更新了@Holger Brandl的答案,以反映dplyr的最新版本(例如1.0.5),其中group_by_all()group_by_at()已被取代。帮助文档建议使用across()代替。

因此,要获取所有存在重复的行,可以执行以下操作: iris %>% group_by(across()) %>% filter(n() > 1) %>% ungroup()

要包括这些行的索引,请添加一个“rowid”列,但将其排除在分组之外: iris %>% rowid_to_column() %>% group_by(across(!rowid)) %>% filter(n() > 1) %>% ungroup()

在上述操作后附加%>% pull(rowid),您将获得一个索引向量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接