【问题标题】:Viewing all column names with any NA in R在 R 中查看具有任何 NA 的所有列名
【发布时间】:2014-09-28 13:32:09
【问题描述】:

我需要获取至少有 1 个 NA 的列的名称。

df<-data.frame(a=1:3,b=c(NA,8,6), c=c('t',NA,7))

我需要得到“b,c”。

我找到this code:

sapply(df, function(x) any(is.na(x)))

但我只需要具有任何 NA 的变量。

我试过了:

sapply(df, function(x) colnames(df[,any(is.na(x))]))

但是我得到了所有的列名。

【问题讨论】:

    标签: r sapply


    【解决方案1】:

    另一个杂技解决方案(只是为了好玩):

    colnames(df)[!complete.cases(t(df))]
    [1] "b" "c"
    

    这个想法是:获取 A 的至少 1 个 NA 的列等效于获取 t(A) 至少具有 NA 的行。 complete.cases 根据定义(非常有效,因为它只是对 C 函数的调用)给出没有任何缺失值的行。

    【讨论】:

    • @Arun 当然..在 akrun 答案中查看我的编辑(性能解释)。
    • @Arun 因为表演是由 akrun 完成的,我以“杂技解决方案”开始我的回答,OP 接受了它我不知道为什么(声誉 baisis):) 但随时编辑它!
    【解决方案2】:

    你们很亲密。您的第一次尝试会产生一个boolean 向量,您可以使用它来索引dfnames

    contains_any_na = sapply(df, function(x) any(is.na(x)))
    names(df)[contains_any_na]
    # [1] "b" "c"
    

    2017 年 1 月 14 日更新: 从 R 版本 3.1.0 开始,anyNA() 可以用作 any(is.na(.)) 的替代品,上面的代码可以简化为

    names(df)[sapply(df, anyNA)]
    # [1] "b" "c"
    

    【讨论】:

      【解决方案3】:

      试试 data.table 版本:

      library(data.table)
      setDT(df)
      names(df)[df[,sapply(.SD, function(x) any(is.na(x))),]]
      [1] "b" "c"
      

      使用@akrun 的代码进行微基准测试:

      set.seed(49)
      df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))
      setDT(df1)
      
      
      f1 <- function() {contains_any_na = sapply(df1, function(x) any(is.na(x)))
                 names(df1)[contains_any_na]}
      
      f2 <- function() {colnames(df1)[!complete.cases(t(df1))] }
      f3 <- function() { names(df1)[!!colSums(is.na(df1))] }
      
      f4 <- function() { names(df1)[df1[,sapply(.SD, function(x) any(is.na(x))),]] }
      
      microbenchmark(f1(), f2(), f3(), f4(), unit="relative")   
      # Unit: relative
      #  expr       min        lq    median       uq      max neval
      #  f1()  1.000000  1.000000  1.000000 1.000000 1.000000   100
      #  f2() 10.459124 10.928821 10.955986 9.858967 7.069066   100
      #  f3()  3.323144  3.805183  4.159624 3.775549 2.797329   100
      #  f4() 10.108998 10.242207 10.121022 9.117067 6.576976   100
      

      @agstudy :此解决方案的速度类似于colnames(df1)[!complete.cases(t(df1))]

      【讨论】:

      • 这将是最慢的解决方案 :)
      • @rnso 我只是在开玩笑,我的评论只是说不是因为你使用 data.table 你会有最快的解决方案。 +1 为您和 akrun 进行基准测试。
      • 这里花费的时间是由于.SDdf1副本。在没有by 的情况下,可以避免这种情况以提高操作速度。见#838
      • 基准测试使这成为一个非凡的答案。
      【解决方案4】:
       names(df)[!!colSums(is.na(df))]
       #[1] "b" "c"
      

      说明

      colSums(is.na(df)) #gives you the number of missing value per each columns
      #a b c 
      #0 1 1 
      

      通过使用!,我们正在创建一个逻辑索引

      !colSums(is.na(df))   #here the value of `0` will be `TRUE` and all other values `>0` FALSE
       #   a     b     c 
       #TRUE FALSE FALSE 
      

      但是,我们需要选择那些至少有一个NA的列,所以!再次否定

      !!colSums(is.na(df))
      #   a     b     c 
      #FALSE  TRUE  TRUE 
      

      并使用这个逻辑索引来获取至少有一个NA的colnames

      基准测试

       set.seed(49)
       df1 <- as.data.frame(matrix(sample(c(NA,1:200), 1e4*5000, replace=TRUE), ncol=5000))
      
       library(microbenchmark)
      
       f1 <- function() {contains_any_na = sapply(df1, function(x) any(is.na(x)))
                  names(df1)[contains_any_na]}
      
       f2 <- function() {colnames(df1)[!complete.cases(t(df1))] }
       f3 <- function() { names(df1)[!!colSums(is.na(df1))] }
      
       microbenchmark(f1(), f2(), f3(), unit="relative")
       #Unit: relative
       #expr      min       lq   median       uq      max neval
       #f1() 1.000000 1.000000 1.000000 1.000000 1.000000   100
       #f2() 8.921109 7.289053 6.852122 6.210826 4.889684   100
       #f3() 3.248072 3.105798 2.984453 2.774513 2.599745   100
      

      EDIT性能说明:

      也许令人惊讶的是,基于sapply 的解决方案是这里的赢家,因为正如下面@flodel 评论中所述,其他两个解决方案在幕后创建了一个矩阵(t(df)is.na(df))创建矩阵。

      【讨论】:

      • 您能否提供更多详细信息,说明您的解决方案为何有效,例如,对逻辑向量求和得出 TRUE 值的数量,! 表示 NOT。这将使答案对 OP 和其他人更有用。
      • @Paul Hiemstra 感谢您的评论。我添加了一些解释。
      • 谢谢,但为什么我不能用 names(colSums(is.na(df))>0)?
      • 但我得到:> names(colSums(is.na(df))>0) [1] "a" "b" "c"
      • 您的两个解决方案较慢,因为 t(df)is.na(df) 创建矩阵。
      【解决方案5】:

      一个简单的衬里是:

      colnames(df[,sapply(df, function(x) any(is.na(x)))])
      

      解释:

      sapply(df, function(x) any(is.na(x)))
      

      为至少 1 个 NA 的列返回 True/False。 df[,sapply(df, function(x) any(is.na(x)))] 获取其所有列至少为 1 NA 的数据帧子集。 colnames 给出了这些列的名称。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-06-20
        • 2018-10-18
        • 2020-07-19
        • 2018-12-15
        • 1970-01-01
        • 2019-01-06
        • 1970-01-01
        • 2021-12-19
        相关资源
        最近更新 更多