【问题标题】:R: remove duplicate rows with full overlap of non-missing variablesR:删除具有非缺失变量完全重叠的重复行
【发布时间】:2020-10-08 19:08:12
【问题描述】:

之前的许多问题都强调了删除具有缺失值的重复行的各种方法,但是没有一个问题涉及以下情况。起始数据示例:

df <- data.frame(x = c(1, NA, 1), y=c(NA, 1, 1), z=c(0, NA, NA))
print(df)

期望的输出:

df2 <- data.frame(x = c(1, 1), y=c(NA, 1), z=c(0, NA))
print(df2)

在这种情况下,第二行被删除,因为它是第 3 行的完美子集。在实际应用程序中,我想删除包含非缺失列中所有冗余信息的行,并保留整体缺失较少的行.

我认为这可以使用 dplyr 和 distinct() 的逐行应用来完成,但无济于事。我可以用一个非常慢的 for 循环来做到这一点,但是对于数百列和数千行,这是一个糟糕的选择。

【问题讨论】:

    标签: r dataframe dplyr na


    【解决方案1】:

    我不知道如何用 dplyr 来做,但这里是有循环的灵魂。另外我不确定 dplyr 解决方案能否比循环一更快(最后它必须使用一些循环),在这里您至少可以控制循环流。

    子集向量函数确定向量 a 是向量 b 的子集(返回 1)还是向量 b 是向量 a 的子集(返回 2),否则返回 0。然后我遍历 data.frame 的所有行并删除子集行。

    subsetVector <- function(a, b){
    
      na_a <- which(is.na(a))
      na_b <- which(is.na(b))
    
      if(all(na_a %in% na_b)){
        if(all(a[-na_b] == b[-na_b])) return(2)
      }else if(all(na_b %in% na_a)){
        if(all(b[-na_a] == a[-na_a])) return(1)
      }
    
      return(0)
    }
    
    i <- 1
    while(i < nrow(df)){
    
      remove_rows <- NULL
    
      for(j in (i+1):nrow(df)){
    
        p <- subsetVector(df[i,], df[j,])
    
        if(p == 1){
          remove_rows <- c(remove_rows, i)
          break()
        }else if(p == 2){
          remove_rows <- c(remove_rows, j)
        }
      }
    
      if(length(remove_rows) > 0)
        df <- df[-remove_rows,]
    
      if(!1 %in% remove_rows)
        i <- i + 1
    }
    

    【讨论】:

    • 这也可以很好地解决问题,而且我确实发现语法比 data.table 解决方案更容易理解。但它需要更多的代码行(而且我认为它没有理由使用 while 循环而不是 for 循环来节省一些复杂性)。
    • 因为行数不是恒定的(我正在删除进程中的“重复”行)。我取第一个不与任何其他行重复的可用行 - 行i 并且对于任何行j &gt; i 我正在检查j 是否与i 重复,或者i 是否与j 重复。在第一种情况下,我记得j 行是重复的,并继续检查下一行。在第二种情况下,我在重复行中包含行 i 并中断内部循环。之后,我删除了我找到的所有重复项并继续检查下一个可用行(如果 i 是第一行并且我删除了它,下一个可用的检查行再次是第一行)。
    【解决方案2】:

    这是另一个使用data.table的选项:

    library(data.table)
    #convert into long format and discard NAs
    mDT <- melt(setDT(df)[, rn := .I], id.var="rn", na.rm=TRUE)[, cnt := .N , rn]
    
    #self join and filter for rows that match to other rows
    merged <- mDT[mDT, on=.(variable, value), {
          diffrow <- i.rn!=x.rn
          .(irn=i.rn[diffrow], xrn=x.rn[diffrow], icnt=i.cnt[diffrow])
        }]
    
    #count the occurrence and delete rows where all values are matched to another row
    ix <- merged[, xcnt := .N, .(irn, xrn)][
        icnt==xcnt]$irn
    
    #delete dupe rows
    df[-ix]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-11-29
      • 1970-01-01
      • 2022-01-12
      • 2018-09-18
      • 2011-03-21
      • 2022-11-14
      • 1970-01-01
      • 2016-06-01
      相关资源
      最近更新 更多