R语言 从上到下删除两个未分组列中的重复项

gcxthw6b  于 12个月前  发布在  其他
关注(0)|答案(4)|浏览(109)

下面是一个示例数据框,显示了我的问题和我想要实现的目标。这里我有两个列xy,我想要从中删除重复项。我还有一个列z,它包含行的排序秩。

x   y   z
1 A   BB  8
2 B   BB  7.5
3 B   AA  6.2
4 B   CC  5
5 C   DD  4
6 D   CC  3

字符串
我试图同时查看xy,每次在任一列中有重复时,删除该行并继续。
我想要的最终结果是

x   y   z
1 A   BB  8
3 B   AA  6.2
5 C   DD  4
6 D   CC  3


y列中的第二个BB被删除。然后B - AA行没有被删除,因为逐行向下,它现在是x列中的第一个B。这是一个大数据集,所以不幸的是我不能手工完成。
我不想把这两个列组合在一起,也不想一次删除一个重复的列,因为这样做会删除太多的观察结果。
如何才能做到这一点?

pgky5nke

pgky5nke1#

由于我们需要同时检查两列,我怀疑我们是否可以使用duplicated()顺序检查x和y列,所以这里是我的报价,速度较慢,但可能更可靠:

i <- 2
  repeat {
    row_removed <- F
    if(df[i,]$x %in% df[1:(i-1),"x"]) {
      df <- df[-i,]
      row_removed <- T
    }
    if (i>nrow(df)) break
    if(df[i,]$y %in% df[1:(i-1),"y"]) {
      df <- df[-i,]
      row_removed <- T
    }
    if (!row_removed) i <- i + 1
    if (i>nrow(df)) break
  }

字符串
测试结果:

x  y   z
1 A BB 8.0
3 B AA 6.2
5 C DD 4.0


对于编辑的数据集,它也返回OP所期望的结果:

x  y   z
1 A BB 8.0
3 B AA 6.2
5 C DD 4.0
6 D CC 3.0

v64noz0r

v64noz0r2#

看起来你需要迭代地检查最新的一行,并决定是否应该保留或删除它。在这种情况下,你可以像下面这样尝试Reduce,其中行索引迭代地更新:

df[!duplicated(
  Reduce(\(p, q) ifelse(any(df[p, c(1, 2)] == df[q, c(1, 2)]), p, q),
    seq_len(nrow(df)),
    accumulate = TRUE
  )
), ]

字符串

df[!duplicated(
  Reduce(\(...) rev(c(...))[1 + any(sapply(df[c(...), c(1, 2)], duplicated))],
    seq_len(nrow(df)),
    accumulate = TRUE
  )
), ]


这给

x  y   z
1 A BB 8.0
3 B AA 6.2
5 C DD 4.0
6 D CC 3.0

数据

structure(list(x = c("A", "B", "B", "B", "C", "D"), y = c("BB",
"BB", "AA", "CC", "DD", "CC"), z = c(8, 7.5, 6.2, 5, 4, 3)), class = "data.frame", row.names = c("1",
"2", "3", "4", "5", "6"))

mxg2im7a

mxg2im7a3#

显然,你想从第二列开始,迭代地删除前两列中连续的重复项。我们可以尝试使用repeat循环,在第二列和第一列之间迭代,直到没有更多的重复项:

> cs <- 1:2
> j <- 2
> repeat {
+   dat <- dat[-which.max(cumsum(duplicated(dat[, j])) == 1), ]
+   if (!any(sapply(dat[cs], duplicated))) break
+   if (!j %% 2) j <- 1 else j <- 2
+ }
> dat
  x  y   z
1 A BB 8.0
3 B AA 6.2
5 C DD 4.0
6 D CC 3.0

字符串
注意,我已经添加了一个E-CC行,但它已被成功删除。您可以进一步测试。

  • 数据:*
> dput(dat)
structure(list(x = c("A", "B", "B", "B", "C", "D", "E"), y = c("BB", 
"BB", "AA", "CC", "DD", "CC", "CC"), z = c(8, 7.5, 6.2, 5, 4, 
3, 3)), class = "data.frame", row.names = c(NA, -7L))

i2byvkas

i2byvkas4#

这解决了我的问题。

remove_top_duplicate <- function(tempdf) {
  tempdf <- tempdf %>% 
    group_by(x) %>% 
    mutate(xrep = row_number()) %>% 
    ungroup() %>% 
    group_by(y) %>% 
    mutate(yrep = row_number()) %>% 
    ungroup()

  if(!(any(tempdf$xrep > 1)) & !(any(tempdf$yrep > 1))){
    break
  }

  tempdf <- slice(tempdf, -which(xrep > 1 | yrep > 1)[1])
  return(tempdf)
}

while(TRUE){
  df <- remove_top_duplicate(df)
  if(!(any(df$xrep > 1)) & !(any(df$yrep > 1))){
    print("Finished")
    break
  }
}

字符串
我创建了一个函数,它接受一个日期帧,并将首先为x和y改变row_numbers()列。这样我们就有了两个名为xrepyrep的列,我们可以在其中找到最顶部的重复。然后我只需切片找到的第一行,大于1,然后我返回 Dataframe 。由于这是一个函数,我只需在一个while(TRUE)的情况,只要等到重复列中没有2's,这是当我们知道没有更多的重复。
下面是第一次运行后的一个例子。然后,由于yrep列中的2,它将对第二行进行切片。之后,它返回 Dataframe ,然后重复重新计算 Dataframe 的过程。然后再次切片。

x     y       z     xrep  yrep
1 A     BB      8       1     1
2 B     BB      7.5     1     2
3 B     AA      6.2     2     1
4 B     CC      5       3     1
5 C     DD      4       1     1
6 D     CC      3       1     2


这不是一个干净的解决方案,但它完成了工作。

相关问题