【问题标题】:How to automate renaming of columns in wide data using R如何使用 R 自动重命名宽数据中的列
【发布时间】:2020-12-26 04:43:45
【问题描述】:

考虑以下宽格式数据

df<-data.frame("id"=c(1,2,3,4),
           "ex"=c(1,0,0,1),
           "aQL"=c(5,4,NA,6),
           "bQL"=c(5,7,NA,9),
           "cQL"=c(5,7,NA,9),
           "bST"=c(3,7,8,9),
           "cST"=c(8,7,5,3),
           "aXY"=c(1,9,4,4),
           "cXY"=c(5,3,1,4))

我想保留列(或变量)名称“id”和“ex”并重命名其余列,例如“aQL”、“bQL”和“cQL”分别为“QL.1”、“QL.2”和“QL.3”。名称以“ST”和“XY”结尾的其他列预计将以相同的方式重命名,顺序也为 .1、.2 和 .3。值得注意的是,数据集中缺少“aST”和“bXY”,但我希望将它们包括在内并重命名为 ST.1 和 XY.2,每个条目都有 NA。预期的输出看起来像

df
  id ex QL.1 QL.2 QL.3 ST.1 ST.2 ST.3 XY.1 XY.2 XY.3
1  1  1    5    5    5   NA    3    8    1   NA    5
2  2  0    4    7    7   NA    7    7    9   NA    3
3  3  0   NA   NA   NA   NA    8    5    4   NA    1
4  4  1    6    9    9   NA    9    3    4   NA    4

主数据集有很多变量,所以我希望重命名以自动方式完成。我尝试了以下代码

renameCol <- function(x) {
setNames(x, paste0("QL.", seq_len(ncol(x))))
}
renameCol(df)

但它没有按预期工作。因此,它重命名了我想要维护的“id”和“ex”,并且在重命名多个变量(即 QL、ST、XY)时不灵活。非常感谢任何帮助。

【问题讨论】:

    标签: r dataframe rename dplyr


    【解决方案1】:

    在基础 R 中你可以这样做:

    names(df) <- sub("(\\d)([A-Z]{2})$","\\2.\\1", chartr("abc","123",names(df)))
     df
      id ex QL.1 QL.2 QL.3 ST.2 ST.3 XY.1 XY.3
    1  1  1    5    5    5    3    8    1    5
    2  2  0    4    7    7    7    7    9    3
    3  3  0   NA   NA   NA    8    5    4    1
    4  4  1    6    9    9    9    3    4    4
    

    如果您需要 NA 列:

    names(df) <- sub("(\\d)([A-Z]{2})$","\\2.\\1", chartr("abc","123",names(df)))
    a <- read.table(text=grep("\\.\\d",names(df),value = TRUE), sep=".")
    b <- subset(aggregate(.~V1, a, function(x) setdiff(1:3,x)), V2>0)
    df[do.call(paste, c(sep = ".", b))] <- NA
    (df1 <- df[c(1, 2, order(names(df)[-(1:2)]) + 2)])
    
      id ex QL.1 QL.2 QL.3 ST.1 ST.2 ST.3 XY.1 XY.2 XY.3
    1  1  1    5    5    5   NA    3    8    1   NA    5
    2  2  0    4    7    7   NA    7    7    9   NA    3
    3  3  0   NA   NA   NA   NA    8    5    4   NA    1
    4  4  1    6    9    9   NA    9    3    4   NA    4
    

    【讨论】:

    • 谢谢!你能解释一下重命名是如何工作的吗?例如如果您正在考虑重命名为 a.QL、b.QL、c.QL、a.ST、e.t.c.
    • @TRichard 这里发生的是chartrabc 更改为123 等尝试执行chartr("abcde", "12345", "you are my king") 您会注意到aeiou 已分别更改为12345。然后使用sub 捕获\\d(数字)后跟[A-Z]{2} 2 个大写字母,并在它们之间放置一个点时切换捕获组的顺序。即尝试执行sub("(\\d)([A-Z]{2})", "\\2-\\1", "2DR")
    【解决方案2】:

    这是一个通过stringr 包使用正则表达式的解决方案:

    library(stringr)
    
    df<-data.frame("id"=c(1,2,3,4),
                   "ex"=c(1,0,0,1),
                   "aQL"=c(5,4,NA,6),
                   "bQL"=c(5,7,NA,9),
                   "cQL"=c(5,7,NA,9),
                   "bST"=c(3,7,8,9),
                   "cST"=c(8,7,5,3),
                   "aXY"=c(1,9,4,4),
                   "cXY"=c(5,3,1,4))
    
    renameCol <- function(x) {
      col_names <- colnames(x)
      index_ql <- str_detect(col_names,
                             "^[a-z]{1}QL")
      index_st <- str_detect(col_names,
                             "^[a-z]{1}ST")
      index_xy <- str_detect(col_names,
                             "^[a-z]{1}XY")
      
      replace_fun <- function(x) {which(letters %in% x)}
      
      col_names[index_ql] <- paste0("QL.", str_replace(substr(col_names[index_ql], 1, 1),
                                                      "[a-z]", replace_fun))
      col_names[index_st] <- paste0("ST.", str_replace(substr(col_names[index_st], 1, 1),
                                                       "[a-z]", replace_fun))
      col_names[index_xy] <- paste0("XY.", str_replace(substr(col_names[index_xy], 1, 1),
                                                       "[a-z]", replace_fun))
      
      col_names
      
    }
    
    colnames(df) <- renameCol(df)
    
    df
    #>   id ex QL.1 QL.2 QL.3 ST.2 ST.3 XY.1 XY.3
    #> 1  1  1    5    5    5    3    8    1    5
    #> 2  2  0    4    7    7    7    7    9    3
    #> 3  3  0   NA   NA   NA    8    5    4    1
    #> 4  4  1    6    9    9    9    3    4    4
    

    reprex package (v0.3.0) 于 2020 年 9 月 7 日创建

    编辑

    修改了上面的函数,以便将订单考虑在内。

    【讨论】:

    • 谢谢!有一些秩序。变量的前缀 a、b、c 采用 .1、.2、.3 顺序。是否可以将“bST”和“cXY”重新编码为 ST.2 和 XY.3?
    【解决方案3】:

    使用基本模式匹配:

    您需要定义一个函数,在单个列名上执行您想要的操作:

    f = function(x){
      beg <- str_extract(x,"[a-z](?=[A-Z]{2})")
      num <- which(letters == beg)
      output <- paste0(str_extract(x,"(?<=[a-z])[A-Z]{2}"),".",num)
      return(output)
    }
    

    如果后面有两个大写字母,这里提取小写字母,找到它在字母表中的位置,然后将找到的数字粘贴回大写字母。

    > f("cQL")
    [1] "QL.3"
    

    然后您可以直接在数据框的名称上使用regmatches 和正则表达式:

    m <- gregexpr("[a-z][A-Z]{2}", names(df),perl = T)
    regmatches(names(df), m) <- lapply(regmatches(names(df), m), f)
    names(df)
    
    > names(df)
    [1] "id"   "ex"   "QL.1" "QL.2" "QL.3" "ST.2" "ST.3" "XY.1" "XY.3"
    

    它只解决了重命名部分,而不是您问题的“包括缺少的列号”部分

    【讨论】:

      【解决方案4】:

      你可以尝试另一种方式

      colnames(df)[grepl("QL", colnames(df))] <- str_c("QL.", 1:3)
      
      colnames(df)[grepl("ST", colnames(df))] <- str_c("ST.", 2:3)
      
      colnames(df)[grepl("XY", colnames(df))] <- str_c("XY.", c(1,3))
      
      #   id ex QL.1 QL.2 QL.3 ST.2 ST.3 XY.1 XY.3
      # 1  1  1    5    5    5    3    8    1    5
      # 2  2  0    4    7    7    7    7    9    3
      # 3  3  0   NA   NA   NA    8    5    4    1
      # 4  4  1    6    9    9    9    3    4    4
      

      【讨论】:

        【解决方案5】:

        我建议在不需要函数的情况下使用tidyverse 方法。在此解决方案中,您可以提取每个变量名称的第一个字母作为 id,然后使用 cur_group_id 分配一个数字,以便保持顺序。最后,使用这个新数字转换包含名称的变量,然后将其格式化为宽以获得预期的输出:

        library(tidyverse)
        #Data
        df<-data.frame("id"=c(1,2,3,4),
                       "ex"=c(1,0,0,1),
                       "aQL"=c(5,4,NA,6),
                       "bQL"=c(5,7,NA,9),
                       "cQL"=c(5,7,NA,9),
                       "bST"=c(3,7,8,9),
                       "cST"=c(8,7,5,3),
                       "aXY"=c(1,9,4,4),
                       "cXY"=c(5,3,1,4))
        #Reshape
        df %>% pivot_longer(cols = -c(1,2)) %>%
          #Extract first letter as id
          mutate(id2=substring(name,1,1)) %>%
          #Create the number id
          group_by(id2) %>%
          mutate(id3=cur_group_id()) %>%
          #Clean name
          mutate(name=substring(name,2,nchar(name))) %>%
          #Create final var
          mutate(name2=paste0(name,'.',id3)) %>% ungroup() %>%
          dplyr::select(-c(name,id2,id3)) %>%
          #Format to wide
          pivot_wider(names_from = name2,values_from=value)
        

        输出:

        # A tibble: 4 x 9
             id    ex  QL.1  QL.2  QL.3  ST.2  ST.3  XY.1  XY.3
          <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
        1     1     1     5     5     5     3     8     1     5
        2     2     0     4     7     7     7     7     9     3
        3     3     0    NA    NA    NA     8     5     4     1
        4     4     1     6     9     9     9     3     4     4
        

        【讨论】:

          猜你喜欢
          • 2019-09-05
          • 1970-01-01
          • 1970-01-01
          • 2020-04-25
          • 2021-04-18
          • 2013-07-11
          • 1970-01-01
          • 2020-05-19
          • 2018-07-16
          相关资源
          最近更新 更多