Реплицировать каждую строку data.frame при появлении

Столкнулся с каверзным вопросом, буду рад помощи.

У меня есть фрейм данных с идентификатором, принимающим разные структуры. Что-то вроде следующего:

ID
bbb-5p/mi-98/6134
abb-4p
bbb-5p/mi-98

Каждый раз, когда у меня есть этот "/", я хотел бы продублировать строку. Каждая строка должна быть продублирована столько раз, сколько раз мы находим этот «/». Тогда имя дублированной строки должно быть корнем + символы сразу после «/».

Например это:

ID
bbb-5p/mi-98/6134

должен дать:

ID
bbb-5p
bbb-5p-mi-98
bbb-5p-6134

Также мой исходный фрейм данных имеет 5 переменных:

  [ID, varA, varB, varC, varD]

И каждый раз, когда у меня есть этот "/", я хотел бы продублировать всю строку. Затем я ожидаю получить новый фрейм данных с чем-то вроде

  newID         newvarA  newvarB  newvarC  newvarD   
  bbb-5p        varA(1)  varB(1)  varC(1)  varD(1)
  bbb-5p-mi-98  varA(1)  varB(1)  varC(1)  varD(1)
  bbb-5p-6134   varA(1)  varB(1)  varC(1)  varD(1)
  abb-4p        varA(2)  varB(2)  varC(2)  varD(2)
  bbb-5p        varA(3)  varB(3)  varC(3)  varD(3)
  bbb-5p-mi-98  varA(3)  varB(3)  varC(3)  varD(3)

Есть идеи? заранее спасибо

Питер


person Peter    schedule 13.12.2016    source источник
comment
Это повторяющийся вопрос, см. stackoverflow.com/questions/30818840/ и stackoverflow.com/questions/13773770/   -  person Iaroslav Domin    schedule 13.12.2016
comment
@akrun это не точный обман, вопрос здесь заключается не только в разделении строк и их вертикальной укладке ..   -  person mtoto    schedule 13.12.2016
comment
@mtoto О, хорошо, снова открыл. Я думал, что две предоставленные ссылки имеют некоторые ссылки на этот. Извиняюсь   -  person akrun    schedule 13.12.2016


Ответы (3)


Вы можете сделать это в base R, используя lapply() с пользовательской функцией. Во-первых, вы разбиваете свой столбец символов на "/", в результате чего получается список векторов:

l <- strsplit(df$ID,"/")

Затем вы применяете пользовательскую функцию к каждому элементу l, используя lapply():

l_stacked <- lapply(l, function(x) 
          if(length(x) > 1) { 
          c(x[1], paste0(x[1],"-",x[-1])) } 
          else { x })

Функция сначала проверяет, есть ли у вектора length > 1. Если это так, он объединяет все элементы с первым элементом, разделенным "-". Если length <= 1, это означает, что строка не содержит "/", поэтому она возвращается как есть. Наконец, мы сглаживаем наш вывод, используя unlist(), чтобы можно было преобразовать в data.frame.

data.frame(ID = unlist(l_stacked))
#            ID
#1       bbb-5p
#2 bbb-5p-mi-98
#3  bbb-5p-6134
#4       abb-4p
#5       bbb-5p
#6 bbb-5p-mi-98
person mtoto    schedule 13.12.2016
comment
Спасибо за ваш ответ ! Похоже, этот код выполняет свою работу... Однако моя точка зрения заключается в том, как связать этот новый идентификатор с другими переменными моего data.frame? - person Peter; 14.12.2016
comment
можете уточнить, пожалуйста? это не указано в вашем исходном вопросе. - person mtoto; 14.12.2016
comment
Допустим, мой исходный фрейм данных имеет 5 переменных [ID, varA, varB, varC, varD]. Я имею в виду, что каждый раз, когда у меня есть это / я хотел бы продублировать строку. Но держите весь ряд. Затем я ожидаю получить новый фрейм данных с чем-то вроде [newID(1), varA(1), varB(1), varC(1), varD(1)] ..... [newID(n), varA(n), varB(n), varC(n), varD(n)]... Так понятнее? - person Peter; 14.12.2016

Один из способов добиться этого заключается в следующем:

library(dplyr)
library(tidyr)
res <- df %>% mutate(i=row_number(),
                     ID = strsplit(ID,split='/')) %>%
              unnest() %>% 
              group_by(i) %>%
              mutate(ID=ifelse(ID==first(ID),first(ID),paste(first(ID),ID,sep='-'))) %>%
              ungroup() %>% select(-i)
### A tibble: 6 x 1
##            ID
##         <chr>
##1       bbb-5p
##2 bbb-5p-mi-98
##3  bbb-5p-6134
##4       abb-4p
##5       bbb-5p
##6 bbb-5p-mi-98

Заметки:

  1. Во-первых, создайте столбец индексации i для последующего группирования, чтобы мы могли сгруппировать каждый «корень».
  2. Используйте strsplit, чтобы разделить каждую строку на "|".
  3. tidyr::unnest результат в отдельные строки.
  4. group_by созданный индекс i и затем, если строка является первой строкой, просто вернуть корень; в противном случае paste для добавления корня к строке с разделителем "-".
  5. Наконец, ungroup и удалите созданный индексный столбец i.

Данные

df <- structure(list(ID = c("bbb-5p/mi-98/6134", "abb-4p", "bbb-5p/mi-98"
)), .Names = "ID", row.names = c(NA, -3L), class = "data.frame")
                 ID
1 bbb-5p/mi-98/6134
2            abb-4p
3      bbb-5p/mi-98
person aichao    schedule 13.12.2016

Вот один из вариантов использования data.table. Преобразуйте «data.frame» в «data.table» (setDT(df1, ..)) и создайте столбец имен строк, сгруппированных по «rn», разделите «ID» на /, выполните цикл по последовательности строк, paste элементы разделения на основе на индекс.

library(splitstackshape)
library(data.table)
setDT(df1, keep.rownames=TRUE)[, unlist(strsplit(ID, "/")), 
         by = rn][, .(ID=sapply(seq_len(.N), function(i) 
             paste(V1[unique(c(1,i))], collapse="-"))) , rn]

Или вариант с dplyr/tidyr/tibble. Создайте столбец с именами строк с помощью tibble::rownames_to_column, разделите строки в длинный формат с помощью separate_rows, сгруппируйте их по «rn», мы mutate «ID» pasteing элементов на основе условия длины и удалим столбец «rn».

library(dplyr)
library(tidyr)
library(tidyr)
rownames_to_column(df1, var = "rn") %>% 
         separate_rows(ID, sep="/") %>%
         group_by(rn)  %>%
         mutate(ID = if(n()>1) c(ID[1], paste(ID[1], ID[-1], sep="-")) else ID) %>%
         ungroup() %>%
         select(-rn)
 #         ID
 #        <chr>
 #1       bbb-5p
 #2 bbb-5p-mi-98
 #3  bbb-5p-6134
 #4       abb-4p
 #5       bbb-5p 
 #6 bbb-5p-mi-98
person akrun    schedule 13.12.2016