【问题标题】:identifying location of NA values in a data frame by ID (not row number) and column name通过 ID(不是行号)和列名识别数据框中 NA 值的位置
【发布时间】:2018-06-15 03:16:23
【问题描述】:

我进行了一项调查,其中一些问题没有得到一些参与者的回答。这是我的数据的简化版本

df <- data.frame(ID = c(12:16), Q1 = c("a","b","a","a",NA), 
      Q2 = c("a","a",NA,"b",NA), Q3 = c(NA,"a","a","a","b"))
df

我想看看哪些身份证号码没有回答哪些问题。以下代码非常接近我想要的输出,但通过行号标识主题 - 我希望通过 ID 号标识主题

table(data.frame(which(is.na(df), arr.ind=TRUE)))

现在输出显示第 1、3、5 行至少没有回答一个问题,并且它标识了具有缺失值的列。我希望它显示相同的东西,但 ID 号为 12、14、16。如果您可以在输出中包含列名(例如 Q1、Q2、Q3)而不是列号,那将是一个额外的好处。

【问题讨论】:

    标签: r dataframe na


    【解决方案1】:

    我们可以使用apply 逐行获取NANA,并将其转换为逗号分隔的字符串,并将其与ID 一起附加到新的数据框。

    new_df <- data.frame(ID =df$ID, ques = apply(df, 1, function(x) 
                   paste0(names(which(is.na(x))), collapse = ",")))
    
    new_df
    
    #  ID  ques
    #1 12    Q3
    #2 13      
    #3 14    Q2
    #4 15      
    #5 16 Q1,Q2
    

    类似的等价物是

    new_df <- data.frame(ID = df$ID, ques = apply(is.na(df), 1, function(x) 
                 paste0(names(which(x)), collapse = ",")))
    

    【讨论】:

      【解决方案2】:

      在基地R:

      res <- df[!complete.cases(df),]
      res[-1] <- as.numeric(is.na(res[-1]))
      res
      #    ID Q1 Q2 Q3
      # 12 12  0  0  1
      # 14 14  0  1  0
      # 16 16  1  1  0
      

      【讨论】:

      • 谢谢,这正是我想要的,而且很简单,我很容易理解。
      【解决方案3】:

      如果您希望避免apply 键入操作并从which(..., T) 继续,您可以执行以下操作:

      tmp <- data.frame(which(is.na(df[, 2:4]), T))
      # change to character
      tmp[, 2] <- paste0('Q', tmp[, 2])
      # gather column numbers together for each row number
      tmp_split <- split(tmp[, 2], tmp[, 1])
      
      # preallocate new column in df
      df$missing <- vector('list', 5)
      df$missing[as.numeric(names(tmp_split))] <- tmp_split
      

      这会产生

      > df
        ID   Q1   Q2   Q3 missing
      1 12    a    a <NA>      Q3
      2 13    b    a    a    NULL
      3 14    a <NA>    a      Q2
      4 15    a    b    a    NULL
      5 16 <NA> <NA>    b  Q1, Q2
      

      【讨论】:

      • 我也很喜欢这个答案。我不会将它用于我当前的数据集,因为我有太多的受试者确实回答了所有问题(我目前对它们不感兴趣)——但这肯定会在未来派上用场。
      【解决方案4】:

      您可以使用tidyr::gather 转换长格式数据。过滤 Answer 不可用。最后,您可以使用toString 将您的数据汇总为:

      library(tidyverse)
      
      df %>% gather(Question, Ans, -ID) %>%
        filter(is.na(Ans)) %>%
        group_by(ID) %>%
        summarise(NotAnswered = toString(Question))
      # # A tibble: 3 x 2
      #      ID NotAnswered
      #   <int> <chr>     
      # 1    12 Q3        
      # 2    14 Q2        
      # 3    16 Q1, Q2
      

      如果 OP 想要在结果中包含所有 IDs,那么解决方案可以是:

      df %>% gather(Question, Ans, -ID) %>%
        group_by(ID) %>%
        summarise(NoAnswered = toString(Question[is.na(Ans)])) %>%
        as.data.frame()
      
      #   ID NoAnswered
      # 1 12         Q3
      # 2 13           
      # 3 14         Q2
      # 4 15           
      # 5 16     Q1, Q2
      

      【讨论】:

        【解决方案5】:

        tidyverse 怎么样:

        数据:

        library(tidyverse)
        df <- data.frame(ID = c(12:16), Q1 = c("a","b","a","a",NA), Q2 = c("a","a",NA,"b",NA), Q3 = c(NA,"a","a","a","b"))
        

        代码:

        x <- df %>% filter(is.na(Q1) | is.na(Q2) | is.na(Q3)) # filter out NAs
        
        y <- cbind(x %>% select(ID),
              x %>% select(Q1, Q2, Q3) %>% sapply(., function(x) ifelse(is.na(x), 1, 0))
        ) # in 1/0 format
        

        输出: x:

          ID   Q1   Q2   Q3
        1 12    a    a <NA>
        2 14    a <NA>    a
        3 16 <NA> <NA>    b
        

        y:

          ID Q1 Q2 Q3
        1 12  0  0  1
        2 14  0  1  0
        3 16  1  1  0
        

        【讨论】:

          【解决方案6】:

          我的尝试并不比任何已经提供的更好,但这是一个有趣的问题,所以这是我的。因为为什么不呢?:

          library( magrittr )
          
          df$ques <- df %>%
              is.na() %>%
              apply( 1, function(x) {
                  x %>%
                      which() %>%
                      names() %>%
                      paste0( collapse = "," )
              } )
          
          df
          
          #   ID   Q1   Q2   Q3  ques
          # 1 12    a    a <NA>    Q3
          # 2 13    b    a    a      
          # 3 14    a <NA>    a    Q2
          # 4 15    a    b    a      
          # 5 16 <NA> <NA>    b Q1,Q2
          

          【讨论】:

            【解决方案7】:

            大部分答案来自您的问题:

            df[which(is.na(df), arr.ind=TRUE)[,1],]
            #     ID   Q1   Q2   Q3
            # 5   16 <NA> <NA>    b
            # 3   14    a <NA>    a
            # 5.1 16 <NA> <NA>    b
            # 1   12    a    a <NA>
            

            【讨论】:

            • 这基本上是我想要的,但我对第 5.1 列以及它为什么存在感到困惑
            • 对,接受的答案更简单,所以我建议使用它。但重复行的原因是which(..., arr.ind=TRUE) 为每个缺失值返回行和列索引。第 5 行有两个 NA,所以它是重复的。您可以使用 df[unique(which(is.na(df), arr.ind=TRUE=[,1]),] 绕过此问题,但公认的 asnwer 提供了更简单的解决方案。
            猜你喜欢
            • 2022-11-02
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-02
            相关资源
            最近更新 更多