【问题标题】:Remove rows which have all NAs in certain columns删除在某些列中具有所有 NA 的行
【发布时间】:2019-01-06 20:42:44
【问题描述】:

假设您有一个包含 9 列的数据框。您想要删除在 5:9 列中具有所有 NA 的案例。如果 1:4 列中有 NA,则根本不相关。

到目前为止,我已经找到了允许您删除在 5:9 的 any 列中具有 NA 的行的功能,但我特别需要仅删除那些具有 all 的行em> NA 在第 5:9 列中。

我编写了自己的函数来执行此操作,但由于我有 300k+ 行,所以速度很慢。我想知道有没有更有效的方法?这是我的代码:

remove.select.na<-function(x, cols){
  nrm<-vector("numeric")
  for (i in 1:nrow(x)){
    if (sum(is.na(x[i,cols]))<length(cols)){
      nrm<-c(nrm,i)
    }
    #Console output to track the progress
    cat('\r',paste0('Checking row ',i,' of ',nrow(x),' (', format(round(i/nrow(x)*100,2), nsmall = 2),'%).'))
    flush.console()
  }
  x<-x[nrm,]
  rm(nrm)
  return(x)
}

其中 x 是数据框,cols 是一个向量,其中包含应检查 NA 的列的名称。

【问题讨论】:

    标签: r dataframe na


    【解决方案1】:
    lines=
       'V1   V2   V3   V4  
        A    10   20   NA   
        B    NA   NA   NA   
        C     5   20   3     
        D    15   20   4    
        E    NA   10   5'
    
    df = read.table(textConnection(lines), header = T)
    
    df[is.na(df)] = 'X'
    
    
    attach(df)
    
    x = subset(df, V2 == 'X'   &   V3 == 'X'   &   V4 == 'X')
    df_new = df[-as.numeric(row.names(x)),]
    df_new
    
    #  V1 V2 V3 V4
    #1  A 10 20  X
    #3  C  5 20  3
    #4  D 15 20  4
    #5  E  X 10  5
    
    detach(df)
    

    【讨论】:

    • 但是这个函数将删除在第 3 列和第 4 列中具有任何缺失值的案例。如上所述,我只需要删除在第 3 列和第 4 列中具有所有缺失值的案例。换句话说: - 如果第 3 列有缺失值,但第 4 列没有,则不应删除该案例。 - 如果第 4 列有缺失值,但第 3 列没有,则不应删除该案例。 - 只有在第 3 列和第 4 列都存在缺失值时,才应删除该案例。 - 如果第 1 列和第 2 列中存在缺失值,则根本不重要。
    【解决方案2】:

    我不知道它比您的函数快,但也许您可以对数据框的每一行使用!anyis.na。使用此示例数据:

    set.seed(1234)
    x = do.call(cbind, lapply(1:9, function(x) runif(10)))
    x[sample(length(x), size = 70)] <- NA
    x <- data.frame(x)
    
    > x
         X1 X2   X3   X4   X5   X6   X7   X8  X9
    1  0.11 NA   NA 0.46 0.55 0.07   NA   NA  NA
    2  0.62 NA   NA   NA   NA   NA 0.04   NA  NA
    3    NA NA   NA 0.30   NA   NA   NA 0.01  NA
    4  0.62 NA 0.04 0.51   NA   NA   NA   NA  NA
    5  0.86 NA   NA 0.18   NA   NA   NA   NA 0.2
    6  0.64 NA   NA   NA   NA 0.50   NA 0.52  NA
    7    NA NA   NA   NA 0.68   NA   NA   NA  NA
    8    NA NA   NA   NA   NA   NA   NA   NA  NA
    9    NA NA   NA   NA   NA 0.17   NA   NA  NA
    10   NA NA 0.05   NA   NA   NA   NA   NA  NA
    

    看起来应该删除第 4、第 8 和第 10 行。因此,您可以使用 apply 遍历每一行以查看是否满足条件 - 第 5 到第 9 列中除 NA 以外的任何值的任何行都将返回 TRUE,因此您可以将其用作数据框的索引器。

    keep.rows <- apply(x[, 5:9], 1, FUN = function(row){
      any(!is.na(row))
    })
    
    > x[keep.rows, ]
        X1 X2 X3   X4   X5   X6   X7   X8  X9
    1 0.11 NA NA 0.46 0.55 0.07   NA   NA  NA
    2 0.62 NA NA   NA   NA   NA 0.04   NA  NA
    3   NA NA NA 0.30   NA   NA   NA 0.01  NA
    5 0.86 NA NA 0.18   NA   NA   NA   NA 0.2
    6 0.64 NA NA   NA   NA 0.50   NA 0.52  NA
    7   NA NA NA   NA 0.68   NA   NA   NA  NA
    9   NA NA NA   NA   NA 0.17   NA   NA  NA
    

    再次,不确定它是否比您的函数更快,但...也许?

    【讨论】:

      【解决方案3】:

      这是一个删除所有 5 到 9 列中带有 NA 的行的单行器。通过将rowSums()is.na() 结合起来,可以很容易地检查这 5 列中的所有条目是否都是NA

      x <- x[rowSums(is.na(x[,5:9]))!=5,]
      

      【讨论】:

        【解决方案4】:

        这里有两个dplyr 选项:

        library(dplyr)
        df <- data_frame(a = c(0, NA, 0, 4, NA, 0, 6), b = c(1, NA, 0, 4, NA, 0, NA), c = c(1, 0, 1, NA, NA, 0, NA))
        
        
        # columns b and c would be the columns you don't want all NAs
        
        df %>% 
          filter_at(vars(b, c), any_vars(!is.na(.)))
        
        df %>% 
          filter_at(vars(b, c), any_vars(complete.cases(.)))
        
        # A tibble: 5 x 3
              a     b     c
          <dbl> <dbl> <dbl>
        1     0     1     1
        2    NA    NA     6
        3     0     6     1
        4     4     4    NA
        5     0     0     0
        

        【讨论】:

        • 不错的解决方案!原来any_vars()across() 取代。但是,我无法将此处的解决方案翻译为依赖across()。有什么提示吗?
        【解决方案5】:

        您可以使用allapply 来查找所有值为NA 的行:

        x[!apply(is.na(x[,5:9]), 1, all),]
        

        或否定is.na 并测试any

        x[apply(!is.na(x[,5:9]), 1, any),]
        

        或者使用rowSums,比如@RHertel,你不需要计算选中的行数:

        x[rowSums(!is.na(x[,5:9])) > 0,]
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2015-10-19
          • 2020-07-22
          • 2019-11-14
          • 2022-01-16
          • 2012-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多