【问题标题】:How to delete duplicate rows (the shorter ones) based on certain columns?如何根据某些列删除重复行(较短的行)?
【发布时间】:2018-02-14 01:03:24
【问题描述】:

假设我有以下df

df <- data.frame(col1 = c(1, 3, 1), col2 = c(2, 4, 2), col3 = c(NA, NA, "c"))
> df
  col1 col2 col3
1    1    2 <NA>
2    3    4 <NA>
3    1    2    c

我的目标是删除基于col1col2 的所有重复行,以便较长的行“存活”。在这种情况下,应该删除第一行。我试过了

df[duplicated(df[, 1:2]), ]

但这只给了我第三行(而不是第三行和第二行)。怎么做才合适?

编辑:真正的df 有15 列,其中前13 列用于识别重复项。在最后两列中,大约 2/3 的行填充了 NA(前 13 列不包含任何 NA)。因此,我的示例df 具有误导性,因为要排除两列来识别重复项。我很抱歉。

【问题讨论】:

  • 如果有两行具有相同的col1col2 并且都具有非NA col3 怎么办?你应该保留两者吗?删除第一个?
  • 我应该提到这个案例不存在。

标签: r dataframe duplicates


【解决方案1】:

你可以试试这个:

library(dplyr)
df %>% group_by(col1,col2) %>%
  slice(which.min(is.na(col3)))

或者这个:

df %>%
  group_by(col1,col2) %>%
  arrange(col3) %>%
  slice(1)

# # A tibble: 2 x 3
# # Groups:   col1, col2 [2]
#    col1  col2   col3
#   <dbl> <dbl> <fctr>
# 1     1     2      c
# 2     3     4     NA

通用解决方案

对于最通用的解决方案,col1 的每个值只能有一行,请参阅下面的注释以将 col2 添加到分组变量中。它假定所有NAs 都在右边。

df %>% mutate(nna = df %>% is.na  %>% rowSums) %>%
  group_by(col1) %>%         # or group_by(col1,col2)
  slice(which.min(nna)) %>%
  select(-nna)

【讨论】:

  • 次要点:如果“最长行”OP 的意思是“我已经将一个向量分布在右侧的许多列上,这些列都看起来像 col3”,那么这个答案将使用 df = data.frame(col1 = 1, col2 = 1, col3 = c(NA, "a", "a"), col4 = c(NA, NA, "b")) 选择错误的东西。但是,如果 OP 真的这样做,那么他们的数据格式就很糟糕。
  • 嗯,不一定。这个问题有多种解释,OP可能应该澄清。看起来他们已经迷路了,所以不要急于解决它。
【解决方案2】:
df <- data.frame(col1 = c(1, 3, 1), col2 = c(2, 4, 2), col3 = c(NA, NA, "c"))
df <- df[order(df$col3),] 

duplicates <- duplicated(df[,1:2])
duplicates_sub <- subset(df , duplicates == FALSE)  

> duplicates_sub
  col1 col2 col3
3    1    2    c
2    3    4 <NA>

编辑:保留所有非 NA 行

df <- data.frame(col1 = c(1, 3, 1,3, 1), col2 = c(2, 4, 2,4, 2), col3 = c("a", NA, "c",NA, "b"))
df <- df[order(df$col3),] 
duplicates <- duplicated(df[,1:2]) & is.na(df[,3])
duplicates_sub <- subset(df , duplicates == FALSE)  

> duplicates_sub
  col1 col2 col3
1    1    2    a
5    1    2    b
3    1    2    c
2    3    4 <NA>

【讨论】:

  • @Moody_Mudskipper 该数据帧不会失败?
  • 你是对的对不起,我执行错了。那我不明白反对票
  • 看弗兰克对我的回答的评论,我们不够笼统
  • 好吧,有一个平衡的赞成票。我的猜测是,反对者不喜欢您使用subset 而不是df[!duplicates, ] 或更简单的df[!duplicated(df[, 1:2]), ]
  • @user108363 很抱歉投反对票(现在已锁定:/)。 @Frank 这实际上是因为这将删除所有具有不同非 NA col3 的后续重复行。试试这个:df &lt;- data.frame(col1 = c(1, 3, 1,3, 1), col2 = c(2, 4, 2,4, 2), col3 = c("a", NA, "c",NA, "b"))。我同意 OP 没有说明这个要求,但他/她也没有说明如何处理它。如果 OP 表示他/她不关心使用不同的 col3 保留重复项,或者您已编辑您的答案以解决该问题,我将取消投票。
【解决方案3】:

您可以在删除欺骗之前将 NA 排序到顶部或底部:

# in base, which puts NAs last
odf = df[do.call(order, df), ]
odf[!duplicated(odf[, c("col1", "col2")]), ]

#   col1 col2 col3
# 3    1    2    c
# 2    3    4 <NA>

# or with data.table, which puts NAs first
library(data.table)
DF = setorder(data.table(df))
unique(DF, by=c("col1", "col2"), fromLast=TRUE)

#    col1 col2 col3
# 1:    1    2    c
# 2:    3    4   NA

这种方法不能用于 dplyr,它在 arrange 中不提供“按所有列排序”,在 distinct 中也不提供 fromLast

【讨论】:

  • 根据我的新解释,它以df &lt;- data.frame(col1 = c(1, 3, 1,3), col2 = c(2, NA, 2,4), col3 = c(NA, NA, "c",NA)) 失败,因为应该删除结果的第二行
  • @Moody 是的,这是一种可能的解释。我猜 col1, col2 是对永远不会丢失的变量进行分组;而 col3+ 是不同长度的向量,每个向量分流成一行(因此我们看到任何非最大长度向量都缺失)。不过,这完全是猜测。
  • @MM,您的df 由弗兰克的odf 处理,这正是我想要的方式。再次对问题的模棱两可的阐述感到抱歉。
  • 想想很有趣 :)
猜你喜欢
  • 1970-01-01
  • 2022-11-22
  • 2023-02-25
  • 1970-01-01
  • 2020-10-10
  • 1970-01-01
  • 2021-05-14
  • 1970-01-01
  • 2017-08-08
相关资源
最近更新 更多