【问题标题】:How to replace blanks for groups of columns based on column group prefix?如何根据列组前缀替换列组的空白?
【发布时间】:2017-02-13 19:51:01
【问题描述】:

如何根据是否存在以相同前缀开头的其他列中的值来将列中的 NA 替换为全 0?例如,对于 A1 列,我只想将 NA 替换为 0,其中 A2 或 A3 列是 NONBLANK。我的真实数据有数百组列。

我的数据:

ID<-c(1,2,3,4,5,6,7,8)
A1<-c(1,NA,1,NA,1,1,1,NA)
A2<-c(1,NA,NA,1,NA,1,NA,NA)
A3<-c(1,NA,NA,NA,1,NA,NA,NA)
B1<-c(1,1,1,1,1,1,NA,1)
B2<-c(1,1,1,1,NA,1,NA,NA)
B3<-c(1,1,NA,NA,1,NA,NA,NA)

mydata<-cbind.data.frame(ID,A1,A2,A3,B1,B2,B3)

拥有:

通缉:

如果 A2 或 A3 列有 1,则 A1 列中的 0 应替换 NA。如果 A1 或 A3 列中有 1,则 A2 列中的 0 应替换 NA,依此类推,如下所示:

【问题讨论】:

  • 部分答案,因为我必须跑去开会,但是您可以使用以下命令索引 A1 和 A2 中的非缺失值: mydata[!is.na(mydata[c('A1',' A2')])

标签: r replace na prefix


【解决方案1】:

另一种方法是

mydata[, 2:4][is.na(mydata[, 2:4])] <- rep(NA^(rowSums(is.na(mydata[2:4])) == 3) - 1,
                                           length(2:4))[is.na(mydata[, 2:4])]
mydata[, 5:7][is.na(mydata[, 5:7])] <- rep(NA^(rowSums(is.na(mydata[5:7])) == 3) - 1,
                                           length(5:7))[is.na(mydata[, 5:7])]

mydata
  ID A1 A2 A3 B1 B2 B3
1  1  1  1  1  1  1  1
2  2 NA NA NA  1  1  1
3  3  1  0  0  1  1  0
4  4  0  1  0  1  1  0
5  5  1  0  1  1  0  1
6  6  1  1  0  1  1  0
7  7  1  0  0 NA NA NA
8  8 NA NA NA  1  0  0

列值是硬编码的,这对许多组没有帮助,所以按照@haboryme 的技术,你可以这样做

# group columns into list elements with lapply and grep
myCols <- lapply(c("A", "B"), function(i) grep(i, colnames(mydata)))

# loop through and make changes
for(i in myCols) {
  mydata[, i][is.na(mydata[, i])] <- rep(NA^(rowSums(is.na(mydata[i])) == 3) - 1,
                                         length(i))[is.na(mydata[, i])]
}

【讨论】:

    【解决方案2】:

    使用lapply() 的自定义函数:泛化为具有任意数量的列,只要它们遵循这种具有单个字母的模式

    func <- function(x){
      df <- mydata[grepl(x, colnames(mydata))] # extract only the same letter columns
      m <- !is.na(df)          # create a logical matrix to know which all are NA's
      i = which(rowSums(m)!=0) # if all had NA's then summ will be 0. so avoid that
      df[i,][is.na(df[i,])] <- 0 # insert wherever NA's to be 0( but only in those rows decided above)
      return(df)
      }
    
    data.frame(ID = mydata$ID,lapply(LETTERS[1:2], func))
    #  ID A1 A2 A3 B1 B2 B3
    #1  1  1  1  1  1  1  1
    #2  2 NA NA NA  1  1  1
    #3  3  1  0  0  1  1  0
    #4  4  0  1  0  1  1  0
    #5  5  1  0  1  1  0  1
    #6  6  1  1  0  1  1  0
    #7  7  1  0  0 NA NA NA
    #8  8 NA NA NA  1  0  0
    

    【讨论】:

      【解决方案3】:

      基础R 中的未完善答案,但似乎有效:

      for(i in unique(gsub("\\d","",colnames(mydata)[-1]))){
        mydata[apply(mydata[,grepl(i,colnames(mydata))],1,function(x) any(!is.na(x))),grepl(i,colnames(mydata))][is.na(mydata[apply(mydata[,grepl(i,colnames(mydata))],1,function(x) any(!is.na(x))),grepl(i,colnames(mydata))])]<-0
      }
      

      这给出了:

        ID A1 A2 A3 B1 B2 B3
      1  1  1  1  1  1  1  1
      2  2 NA NA NA  1  1  1
      3  3  1  0  0  1  1  0
      4  4  0  1  0  1  1  0
      5  5  1  0  1  1  0  1
      6  6  1  1  0  1  1  0
      7  7  1  0  0 NA NA NA
      8  8 NA NA NA  1  0  0
      

      编辑:
      这个想法是从示例中的colnames(mydata)AB 中提取unique 字母,方法是将数字(\\d)替换为空白""
      然后循环遍历这些字母以选择以它开头的列。这就是 grepl(i,colnames(mydata)) 所做的。
      apply 用于获取至少有 (any()) 一个非 NA 值 (!is.na()) 的行的向量:apply(mydata[,grepl(i,colnames(mydata))],1,function(x) any(!is.na(x)))
      然后将所有内容组合成它本质上的df[is.na(df))]&lt;-0,但df 对应于以给定字母分层的列,并且应该替换NA 的行。
      df 将是:@987654339 @

      【讨论】:

      • 您应该在一些水平和垂直空白处进行编辑以使其更具可读性。
      【解决方案4】:

      两个 tidyverse 选项;哪个更实用取决于您的实际数据的维度。两者都有条件地利用coalesce

      手动:

      library(tidyverse)
      
      mydata %>% rowwise() %>%    # group by row
          mutate_at(vars(starts_with('A')),    # for A prefixes, coalesce if not all NA
                    funs(ifelse(all(is.na(c(A1, A2, A3))), ., coalesce(., 0)))) %>% 
          mutate_at(vars(starts_with('B')),    # likewise for B
                    funs(ifelse(all(is.na(c(B1, B2, B3))), ., coalesce(., 0))))
      
      ## Source: local data frame [8 x 7]
      ## Groups: <by row>
      ## 
      ## # A tibble: 8 × 7
      ##      ID    A1    A2    A3    B1    B2    B3
      ##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
      ## 1     1     1     1     1     1     1     1
      ## 2     2    NA    NA    NA     1     1     1
      ## 3     3     1     0     0     1     1     0
      ## 4     4     0     1     0     1     1     0
      ## 5     5     1     0     1     1     0     1
      ## 6     6     1     1     0     1     1     0
      ## 7     7     1     0     0    NA    NA    NA
      ## 8     8    NA    NA    NA     1     0     0
      

      或以编程方式进行整形:

      mydata %>% gather(var, val, -ID) %>%    # reshape to long
          group_by(ID, letter = substr(var, 1, 1)) %>%    # group by ID and prefix
          mutate(val = if(all(is.na(val))) val else coalesce(val, 0)) %>% 
          ungroup() %>% select(-letter) %>% spread(var, val)    # clean up
      
      ## # A tibble: 8 × 7
      ##      ID    A1    A2    A3    B1    B2    B3
      ## * <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
      ## 1     1     1     1     1     1     1     1
      ## 2     2    NA    NA    NA     1     1     1
      ## 3     3     1     0     0     1     1     0
      ## 4     4     0     1     0     1     1     0
      ## 5     5     1     0     1     1     0     1
      ## 6     6     1     1     0     1     1     0
      ## 7     7     1     0     0    NA    NA    NA
      ## 8     8    NA    NA    NA     1     0     0
      

      如果前缀可以超出单个字母,请将substr 替换为合适的正则表达式,例如sub('\\d+$', '', var).

      【讨论】:

        猜你喜欢
        • 2017-11-05
        • 2020-09-28
        • 2023-01-26
        • 2021-08-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多