строка разбивает значения на два столбца, а затем объединяет их в новый столбец

#r #string #dataframe #apply #strsplit

Вопрос:

Я пытаюсь вызвать str_split функцию для обоих столбцов ( Proteins и Positions.within.proteins ), а затем объединить соответствующие значения в новом вызываемом столбце ID .

 df <- data.frame(Proteins = c("Q99755;A2A3N6", "O00329", "O00444", 
"O14965", "O14976", "Q6A1A2;O15530", "O43318", "O43526", "O43930;P51817", 
"O60331"), Positions.within.proteins = c("276;223", "708", "41", 
"162", "175", "84;111", "63", "628", "78;78", "270"))
 

Вот мои коды.

 my.function <- function(x, y){
  protein.names <- str_split(x, ";")[[1]]
  position.names <- str_split(y, ";")[[1]]
  ID <- list()
    for (i in 1:length(protein.names)){
      ID[i] <- paste(protein.names[i], position.names[i], sep ="_")
    }
  ID.2 <- unlist(ID)
  return(ID.2)
}
 

В какой-то степени это работает, когда я вызываю функцию в одной строке.

 row1 <- my.function(df$Proteins[1], df$Positions.within.proteins[1])
 

"Q99755_276" "A2A3N6_223"

Но мои вопросы таковы:

  1. Как применить эту функцию ко всему фрейму данных?
  2. Как преобразовать "Q99755_276" "A2A3N6_223" в то, что я хочу "Q99755_276;A2A3N6_223"

Я хотел бы использовать apply функцию, но не уверен, может ли apply функция принимать два аргумента.

Здесь показано, как это должно выглядеть.

 df.final <- data.frame(Proteins = c("Q99755;A2A3N6", "O00329", "O00444", 
"O14965", "O14976", "Q6A1A2;O15530", "O43318", "O43526", "O43930;P51817", 
"O60331"), Positions.within.proteins = c("276;223", "708", "41", 
"162", "175", "84;111", "63", "628", "78;78", "270"), ID = c("Q99755_276;A2A3N6_223", 
"O00329_708", "O00444_41", "O14965_162", "O14976_175", "Q6A1A2_84;O15530_111", 
"O43318_63", "O43526_628", "O43930_78;P51817_78", "O60331_270"
))
 

Кто-нибудь знает, как этого добиться? Большое спасибо за любую помощь!

Комментарии:

1.Можете ли вы показать пример таблицы с перечисленными идентификаторами и как выглядят данные ? Похоже, тебе нужно join group_by и concatenate

2. @JasonMathews Спасибо за ваш ответ! Я обновил свой пост. Я показал первые 10 строк для примера таблицы.

3. Изображение не помогает, пожалуйста, поделитесь воспроизводимым образцом ваших данных dput(head(data)) .

4. @AnoushiravanR Приятно знать эту функцию dput(head(data)) . Очень полезно. Я обновил свой пост.

5. @GuedesBF Если я предоставил редактируемый код. Не могли бы вы, пожалуйста, объяснить, что (x) происходит в вашем коде?

Ответ №1:

Вы можете использовать свою функцию и tidyverse

Используйте mutate() с map2(.f = my.function) для создания вложенного столбца идентификаторов, содержащего столбец списка со всеми идентификаторами на строку(некоторые имеют 1 идентификатор, некоторые-два в данных примера). Затем вы можете unnest_wider() создать несколько различных столбцов идентификаторов, которые можно свернуть с помощью tidyr::unite()

 library(tidyr)
library(dplyr)
library(stringr)
library(purrr)

df %>% mutate(ID=map2(Proteins, Positions.within.proteins, my.function))%>%
        unnest_wider(ID, names_sep = '.')%>%
        unite(contains('ID'), col='ID', remove = TRUE, sep=";", na.rm=TRUE)

# A tibble: 10 x 3
   Proteins      Positions.within.proteins ID                   
   <chr>         <chr>                     <chr>                
 1 Q99755;A2A3N6 276;223                   Q99755_276;A2A3N6_223
 2 O00329        708                       O00329_708           
 3 O00444        41                        O00444_41            
 4 O14965        162                       O14965_162           
 5 O14976        175                       O14976_175           
 6 Q6A1A2;O15530 84;111                    Q6A1A2_84;O15530_111 
 7 O43318        63                        O43318_63            
 8 O43526        628                       O43526_628           
 9 O43930;P51817 78;78                     O43930_78;P51817_78  
10 O60331        270                       O60331_270 
 

Ответ №2:

То, что вы ищете, это tidyr::unite() :

 tidyr::unite(data = iris, col = "new_column", Species, Sepal.Length, sep = ";")
 

Попробуй это. Для этого требуется кадр данных (в данном случае радужная оболочка), имя вашего нового столбца (new_column), столбцы, которые вы хотели бы объединить (Вид и чашелистик.Длина) и значение, на которое вы хотели бы разделить их (точка с запятой). tidyr::separate() является противоположностью unite() -он создает два новых столбца на основе разделителя, найденного в оригинале.

Редактировать

Ладно, тебе нужно проявить немного больше творчества…Попробуйте разбить каждый белок на отдельные колонки tidyr::separate() , сделайте то же самое для позиции белка, затем объедините каждый белок с его позицией. Затем объедините оба белка вместе с точкой с запятой в качестве разделителя. Наконец, удалите пропущенные значения для случаев, когда использовался только один белок (который всегда будет иметь одинаковую форму в конце в ;NA_NA формате). Альт:

 library(tidyr)
library(dplyr)
library(stringr)

df %>% 
  separate(col = Proteins, c("protein1", "protein2"), ";", remove = FALSE) %>% 
  separate(col = Positions.within.proteins, into = c("position_p1", "position_p2"), ";", remove = FALSE) %>% 
  unite(col = "id_part1", sep = "_", protein1, position_p1) %>% 
  unite(col = "id_part2", sep = "_", protein2, position_p2) %>% 
  unite(col = "id", sep = ";", id_part1, id_part2) %>% 
  mutate(id = str_remove_all(id, ";NA_NA"))
 

Еще Одна Правка

Я провел некоторый сравнительный анализ, и моя реализация тоже немного быстрее:

 rbenchmark::benchmark(
  mine = df %>% 
    separate(col = Proteins, c("protein1", "protein2"), ";", remove = FALSE) %>% 
    separate(col = Positions.within.proteins, into = c("position_p1", "position_p2"), ";", remove = FALSE) %>% 
    unite(col = "id_part1", sep = "_", protein1, position_p1) %>% 
    unite(col = "id_part2", sep = "_", protein2, position_p2) %>% 
    unite(col = "id", sep = ";", id_part1, id_part2) %>% 
    mutate(id = str_remove_all(id, ";NA_NA")),
  
alt_implementation = df %>% 
    rowwise() %>%
    mutate(ID = map2(Proteins, Positions.within.proteins, my.function)) %>%
    unnest_wider(ID, names_sep = '.') %>%
    unite(contains('ID'), col = 'ID', remove = TRUE, sep = ";", na.rm = TRUE),

replications = 1000
)
#                  test replications elapsed relative user.self sys.self user.child sys.child
# 1                mine         1000    9.06    1.000      8.97     0.05         NA        NA
# 2  alt_implementation         1000   11.77    1.299     11.73     0.00         NA        NA
 

Комментарии:

1. Нет, tidyr::unite просто соедините два столбца вместе. То, чего я хочу, гораздо сложнее. Например, tidyr::unite объединит Q99755;A2A3N6 и 276;223 подарит Q99755;A2A3N6;276;223 . Но чего я хочу, так это Q99755_276;A2A3N6_223

2. Я тоже думал о отдельном решении %>% отдельно %>>% объединить %>>>% объединить, но вы получили его первым. Отличная работа. Хотя это немного многословно, это решение легко понять. Однако я не думаю, что это небольшое различие в производительности имеет значение.

3. @GuedesBF Вы, вероятно, правы насчет производительности, но вы никогда не знаете наверняка. Медицинские наборы данных, как правило, неуправляемы. Сравнительный анализ проводился скорее из любопытства, чем из чего-либо другого. Спасибо за добрые слова.

4. Я сам врач, да, «непослушный» — хорошее описание медицинских наборов данных…

5. rowwise не требуется в альтернативной реализации

Ответ №3:

Краткое базовое решение R.

 df$ID <- apply(df, 1, (x) paste(do.call((y, z) paste0(y, "_", z), 
                                         unname(strsplit(x, ';'))), collapse=';'))
df
#         Proteins Positions.within.proteins                    ID
# 1  Q99755;A2A3N6                   276;223 Q99755_276;A2A3N6_223
# 2         O00329                       708            O00329_708
# 3         O00444                        41             O00444_41
# 4         O14965                       162            O14965_162
# 5         O14976                       175            O14976_175
# 6  Q6A1A2;O15530                    84;111  Q6A1A2_84;O15530_111
# 7         O43318                        63             O43318_63
# 8         O43526                       628            O43526_628
# 9  O43930;P51817                     78;78   O43930_78;P51817_78
# 10        O60331                       270            O60331_270
 

Ответ №4:

Вот базовый способ использования R strsplit и mapply

 df$ID <- mapply(function(x, y) paste(x, y, collapse = ';', sep = '_'), 
        strsplit(df$Proteins, ';'), strsplit(df$Positions.within.proteins, ';'))
df

#        Proteins Positions.within.proteins                    ID
#1  Q99755;A2A3N6                   276;223 Q99755_276;A2A3N6_223
#2         O00329                       708            O00329_708
#3         O00444                        41             O00444_41
#4         O14965                       162            O14965_162
#5         O14976                       175            O14976_175
#6  Q6A1A2;O15530                    84;111  Q6A1A2_84;O15530_111
#7         O43318                        63             O43318_63
#8         O43526                       628            O43526_628
#9  O43930;P51817                     78;78   O43930_78;P51817_78
#10        O60331                       270            O60331_270