【问题标题】:Create a unique name from non-unique names within id groups从 id 组中的非唯一名称创建唯一名称
【发布时间】:2019-02-07 19:27:43
【问题描述】:

id 内,我的数据有多个非唯一的 name 标签。我想创建第三列,以便在非唯一名称的末尾粘贴一个字母以创建唯一名称。

dat <- structure(list(id = c("172262", "172262", "172262", "172262", 
"172504", "172504", "172504", "172507", "172507", "172507"), 
    name = c("Fam", "Fam", "Fam", "CM_fam", "CBT_Fam", "CBT_Fam", 
    "CBT_Fam", "TAU", "CBT_Educ", "CBT_MI")), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -10L))

想要的输出是:

id     name     unique_name    
172262 Fam      Fam_a      
172262 Fam      Fam_b      
172262 Fam      Fam_c      
172262 CM_fam   CM_fam    
172504 CBT_Fam  CBT_Fam_a  
172504 CBT_Fam  CBT_Fam_b  
172504 CBT_Fam  CBT_Fam_c  
172507 TAU      TAU       
172507 CBT_Educ CBT_Educ  
172507 CBT_MI   CBT_MI  

【问题讨论】:

    标签: r dplyr


    【解决方案1】:

    您可以将pastesprintfletters 一起使用...

    dat %>% group_by(id, name) %>% mutate(uname = 
      if (n() > 1) sprintf("%s_%s", name, letters[row_number()]) 
      else name
    )
    
    # A tibble: 10 x 3
    # Groups:   id, name [6]
       id     name     uname    
       <chr>  <chr>    <chr>    
     1 172262 Fam      Fam_a    
     2 172262 Fam      Fam_b    
     3 172262 Fam      Fam_c    
     4 172262 CM_fam   CM_fam   
     5 172504 CBT_Fam  CBT_Fam_a
     6 172504 CBT_Fam  CBT_Fam_b
     7 172504 CBT_Fam  CBT_Fam_c
     8 172507 TAU      TAU      
     9 172507 CBT_Educ CBT_Educ 
    10 172507 CBT_MI   CBT_MI   
    

    关于tidyverse(dplyr所属)中的其他字符串操作,可能见stringr和glue。

    【讨论】:

      【解决方案2】:

      你可以使用ave(基础R

      transform(dat, unique_name = 
                  ave(name,name, FUN = function(x) {
                    if((l <- length(x)) == 1) x
                    else paste0(x,"_",letters[seq(l)])
                  }))
      #        id     name unique_name
      # 1  172262      Fam       Fam_a
      # 2  172262      Fam       Fam_b
      # 3  172262      Fam       Fam_c
      # 4  172262   CM_fam      CM_fam
      # 5  172504  CBT_Fam   CBT_Fam_a
      # 6  172504  CBT_Fam   CBT_Fam_b
      # 7  172504  CBT_Fam   CBT_Fam_c
      # 8  172507      TAU         TAU
      # 9  172507 CBT_Educ    CBT_Educ
      # 10 172507   CBT_MI      CBT_MI
      

      还有一个代码高尔夫,虽然不是通用的:

      dat$unique_name <- chartr(
        make.unique(dat$name,sep="_"),old="123456789",new="abcdefghi")
      dat
      # # A tibble: 10 x 3
      #        id     name unique_name
      #     <chr>    <chr>       <chr>
      #  1 172262      Fam         Fam
      #  2 172262      Fam       Fam_a
      #  3 172262      Fam       Fam_b
      #  4 172262   CM_fam      CM_fam
      #  5 172504  CBT_Fam     CBT_Fam
      #  6 172504  CBT_Fam   CBT_Fam_a
      #  7 172504  CBT_Fam   CBT_Fam_b
      #  8 172507      TAU         TAU
      #  9 172507 CBT_Educ    CBT_Educ
      # 10 172507   CBT_MI      CBT_MI
      

      【讨论】:

        【解决方案3】:

        只是在这里带来一些 data.table 的味道:

        library(data.table)
        DT <- as.data.table(dat)
        DT[, unique_name := if (.N > 1) paste(name, letters[1:.N], sep = "_") 
                            else name, 
           by = .(id, name)]
        

        尽管正如其他人指出的那样,您可能希望将 letters[1:.N] 替换为 1:.N,以防您的重复名称超过 26 个。

        【讨论】:

          【解决方案4】:

          这里有很多选项可供选择,但如果您正在寻找一个相当简单的管道,并且可以接受格式稍有不同的输出,则可以使用 base R 中的make.unique

          library(dplyr)
          
          dat %>% 
            group_by(id, name) %>% 
            mutate(unique_name = make.unique(name))
          

          产量:

             id     name     unique_name
             <chr>  <chr>    <chr>      
           1 172262 Fam      Fam        
           2 172262 Fam      Fam.1      
           3 172262 Fam      Fam.2      
           4 172262 CM_fam   CM_fam     
           5 172504 CBT_Fam  CBT_Fam    
           6 172504 CBT_Fam  CBT_Fam.1  
           7 172504 CBT_Fam  CBT_Fam.2  
           8 172507 TAU      TAU        
           9 172507 CBT_Educ CBT_Educ   
          10 172507 CBT_MI   CBT_MI 
          

          【讨论】:

            【解决方案5】:

            tidyverse 的另一种可能性可能是:

             dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(n() > 1) paste(name, letters[1:length(name)], sep = "_") else name)
            
               id     name     unique_name
               <chr>  <chr>    <chr>      
             1 172262 Fam      Fam_a      
             2 172262 Fam      Fam_b      
             3 172262 Fam      Fam_c      
             4 172262 CM_fam   CM_fam     
             5 172504 CBT_Fam  CBT_Fam_a  
             6 172504 CBT_Fam  CBT_Fam_b  
             7 172504 CBT_Fam  CBT_Fam_c  
             8 172507 TAU      TAU        
             9 172507 CBT_Educ CBT_Educ   
            10 172507 CBT_MI   CBT_MI 
            

            首先,它按“id”和“name”分组。然后,如果每组的病例数大于1,则将“name”中的值与“name”长度的字母序列组合,否则将“name”中的值赋值。

            或者用length()代替n()

            dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(length(name) > 1) paste(name, letters[1:length(name)], sep = "_") else name)
            

            或者用seq_along()代替n()

            dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(any(seq_along(name) != 1)) paste(name, letters[1:length(name)], sep = "_") else name)
            

            或者使用gl() 生成字母的稍微不同的方法:

            dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(n() > 1) paste(name, gl(length(name), 1, n(), letters), sep = "_") else name) 
            
               id     name     unique_name
               <chr>  <chr>    <chr>      
             1 172262 Fam      Fam_a      
             2 172262 Fam      Fam_b      
             3 172262 Fam      Fam_c      
             4 172262 CM_fam   CM_fam     
             5 172504 CBT_Fam  CBT_Fam_a  
             6 172504 CBT_Fam  CBT_Fam_b  
             7 172504 CBT_Fam  CBT_Fam_c  
             8 172507 TAU      TAU        
             9 172507 CBT_Educ CBT_Educ   
            10 172507 CBT_MI   CBT_MI 
            

            或者:

            dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(length(name) > 1) paste(name, gl(length(name), 1, n(), letters), sep = "_") else name)
            

            或者:

            dat %>%
             group_by(id, name) %>%
             mutate(unique_name = if(any(seq_along(name) != 1)) paste(name, gl(length(name), 1, n(), letters), sep = "_") else name)
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2020-11-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-01-04
              • 2014-07-27
              • 1970-01-01
              相关资源
              最近更新 更多