#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"
Но мои вопросы таковы:
- Как применить эту функцию ко всему фрейму данных?
- Как преобразовать
"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