Создать уникальное имя из неуникальных имен в группах идентификаторов - PullRequest
0 голосов
/ 07 февраля 2019

В пределах 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  

Ответы [ 5 ]

0 голосов
/ 07 февраля 2019

Множество опций на выбор, но если вы ищете довольно простой конвейер и можете использовать несколько другой форматированный вывод, вы можете использовать make.unique из базы R:

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 
0 голосов
/ 07 февраля 2019

Просто для того, чтобы привнести сюда немного 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дубликаты имен.

0 голосов
/ 07 февраля 2019

Другая возможность 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, оно объединяет значение из «имени» с последовательностью букв длины «имя», в противном случае присваивает значение из «имени».

Или используя 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)
0 голосов
/ 07 февраля 2019

Вы можете использовать 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
0 голосов
/ 07 февраля 2019

Вы можете использовать paste или sprintf с letters ...

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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...