Стиль словаря заменяет несколько элементов
У меня есть большой data.frame символьных данных, которые я хочу преобразовать на основе того, что обычно называют словарем на других языках.
В настоящее время я собираюсь сделать это так:
foo <- data.frame(snp1 = c("AA", "AG", "AA", "AA"), snp2 = c("AA", "AT", "AG", "AA"), snp3 = c(NA, "GG", "GG", "GC"), stringsAsFactors=FALSE)
foo <- replace(foo, foo == "AA", "0101")
foo <- replace(foo, foo == "AC", "0102")
foo <- replace(foo, foo == "AG", "0103")
Это прекрасно работает, но, очевидно, не очень красиво и кажется глупым повторять оператор replace
каждый раз, когда я хочу заменить один элемент в data.frame.
Есть ли лучший способ сделать это, поскольку у меня есть словарь приблизительно 25 пар ключ/значение?
Ответы
Ответ 1
map = setNames(c("0101", "0102", "0103"), c("AA", "AC", "AG"))
foo[] <- map[unlist(foo)]
предполагая, что map
охватывает все случаи в foo
. Это будет выглядеть как "взломать" и быть более эффективным как в пространстве, так и во времени, если foo
- это матрица (character()), затем
matrix(map[foo], nrow=nrow(foo), dimnames=dimnames(foo))
Как матричные, так и кадровые варианты кадров влияют на ограничение R 2 ^ 31-1 на размер вектора, когда есть миллионы SNP и тысячи образцов.
Ответ 2
Если вы открыты для использования пакетов, plyr
является очень популярным и имеет эту удобную функцию mapvalues (), которая будет делать именно то, что вы ищете:
foo <- mapvalues(foo, from=c("AA", "AC", "AG"), to=c("0101", "0102", "0103"))
Обратите внимание, что он работает для типов данных всех видов, а не только строк.
Ответ 3
Вот быстрое решение
dict = list(AA = '0101', AC = '0102', AG = '0103')
foo2 = foo
for (i in 1:3){foo2 <- replace(foo2, foo2 == names(dict[i]), dict[i])}
Ответ 4
Обратите внимание, что этот ответ начался как попытка решить гораздо более простую проблему, опубликованную в разделе Как заменить все значения в кадре данных вектором значений? , К сожалению, этот вопрос был закрыт как дубликат актуального вопроса. Итак, я попытаюсь предложить решение, основанное на замене уровней факторов для обоих случаев, здесь.
Если есть только вектор (или один столбец фрейма данных), значения которого необходимо заменить, и нет возражений против использования фактора, мы можем принудить вектор к коэффициенту и изменить уровни факторов по мере необходимости:
x <- c(1, 1, 4, 4, 5, 5, 1, 1, 2)
x <- factor(x)
x
#[1] 1 1 4 4 5 5 1 1 2
#Levels: 1 2 4 5
replacement_vec <- c("A", "T", "C", "G")
levels(x) <- replacement_vec
x
#[1] A A C C G G A A T
#Levels: A T C G
Используя пакет forcats
, это можно сделать в однострочном режиме:
x <- c(1, 1, 4, 4, 5, 5, 1, 1, 2)
forcats::lvls_revalue(factor(x), replacement_vec)
#[1] A A C C G G A A T
#Levels: A T C G
В случае необходимости замены всех значений нескольких столбцов кадра данных подход может быть расширен.
foo <- data.frame(snp1 = c("AA", "AG", "AA", "AA"),
snp2 = c("AA", "AT", "AG", "AA"),
snp3 = c(NA, "GG", "GG", "GC"),
stringsAsFactors=FALSE)
level_vec <- c("AA", "AC", "AG", "AT", "GC", "GG")
replacement_vec <- c("0101", "0102", "0103", "0104", "0302", "0303")
foo[] <- lapply(foo, function(x) forcats::lvls_revalue(factor(x, levels = level_vec),
replacement_vec))
foo
# snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103 0104 0303
#3 0101 0103 0303
#4 0101 0101 0302
Обратите внимание, что level_vec
и replacement_vec
должны иметь равную длину.
Что еще более важно, level_vec
должен быть полным, т. level_vec
все возможные значения в затронутые столбцы исходного кадра данных. (Используйте unique(sort(unlist(foo)))
для проверки). В противном случае любые отсутствующие значения будут принудительно привязаны к <NA>
. Обратите внимание, что это также требование для Мартина Морганса.
Итак, если есть всего несколько различных значений, которые вы должны заменить, вам, вероятно, будет лучше с одним из других ответов, например, с Рамнатхом.
Ответ 5
Здесь что-то простое, что сделает работу:
key <- c('AA','AC','AG')
val <- c('0101','0102','0103')
lapply(1:3,FUN = function(i){foo[foo == key[i]] <<- val[i]})
foo
snp1 snp2 snp3
1 0101 0101 <NA>
2 0103 AT GG
3 0101 0103 GG
4 0101 0101 GC
lapply
выведет список в этом случае, которого мы действительно не заботимся. Вы можете присвоить результат чему-то, если хотите, а затем просто отбросить его. Я повторяю индексы здесь, но вы можете так же легко поместить ключ /vals в список и перебрать их непосредственно. Обратите внимание на использование глобального присвоения с помощью <<-
.
Я переделал способ сделать это с помощью mapply
но моя первая попытка не сработала, поэтому я переключился. Я подозреваю, что решение с mapply
возможно.
Ответ 6
Использовал @Ramnath ответ выше, но заставил его прочитать (что заменить и что заменить) из файла и использовать gsub вместо замены.
hrw <- read.csv("hgWords.txt", header=T, stringsAsFactor=FALSE, encoding="UTF-8", sep="\t")
for (i in nrow(hrw))
{
document <- gsub(hrw$from[i], hrw$to[i], document, ignore.case=TRUE)
}
hgword.txt содержит следующую вкладку
"from" "to"
"AA" "0101"
"AC" "0102"
"AG" "0103"
Ответ 7
Поскольку прошло несколько лет с момента последнего ответа, и сегодня на эту тему появился новый вопрос, и модератор закрыл его, я добавлю его здесь. Плакат имеет большой фрейм данных, содержащий 0, 1 и 2, и хочет изменить их на AA, AB и BB соответственно.
Использовать plyr
:
> df <- data.frame(matrix(sample(c(NA, c("0","1","2")), 100, replace = TRUE), 10))
> df
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
1 1 2 <NA> 2 1 2 0 2 0 2
2 0 2 1 1 2 1 1 0 0 1
3 1 0 2 2 1 0 <NA> 0 1 <NA>
4 1 2 <NA> 2 2 2 1 1 0 1
... to 10th row
> df[] <- lapply(df, as.character)
Создайте функцию над фреймом данных, используя revalue
чтобы заменить несколько терминов:
> library(plyr)
> apply(df, 2, function(x) {x <- revalue(x, c("0"="AA","1"="AB","2"="BB")); x})
X1 X2 X3 X4 X5 X6 X7 X8 X9 X10
[1,] "AB" "BB" NA "BB" "AB" "BB" "AA" "BB" "AA" "BB"
[2,] "AA" "BB" "AB" "AB" "BB" "AB" "AB" "AA" "AA" "AB"
[3,] "AB" "AA" "BB" "BB" "AB" "AA" NA "AA" "AB" NA
[4,] "AB" "BB" NA "BB" "BB" "BB" "AB" "AB" "AA" "AB"
... and so on
Ответ 8
Использование dplyr :: recode:
library(dplyr)
mutate_all(foo, funs(recode(., "AA" = "0101", "AC" = "0102", "AG" = "0103",
.default = NA_character_)))
# snp1 snp2 snp3
# 1 0101 0101 <NA>
# 2 0103 <NA> <NA>
# 3 0101 0103 <NA>
# 4 0101 0101 <NA>
Ответ 9
Мы также можем использовать dplyr::case_when
library(dplyr)
foo %>%
mutate_all(~case_when(. == "AA" ~ "0101",
. == "AC" ~ "0102",
. == "AG" ~ "0103",
TRUE ~ .))
# snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103 AT GG
#3 0101 0103 GG
#4 0101 0101 GC
Он проверяет условие и заменяет его соответствующим значением, если условие имеет значение TRUE
. Мы можем добавить больше условий, если это необходимо, и с TRUE ~.
мы сохраняем значения как есть, если ни одно из условий не соответствует. Если мы хотим изменить их на NA
мы можем удалить последнюю строку.
foo %>%
mutate_all(~case_when(. == "AA" ~ "0101",
. == "AC" ~ "0102",
. == "AG" ~ "0103"))
# snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103 <NA> <NA>
#3 0101 0103 <NA>
#4 0101 0101 <NA>
Это изменит значения на NA
если не выполняется ни одно из указанных выше условий.
Другой вариант, использующий только базу R, состоит в том, чтобы вычислить lookup
фрейм данных со старыми и новыми значениями, unlist
, match
их со старыми значениями, получить соответствующие новые значения и заменить.
lookup <- data.frame(old_val = c("AA", "AC", "AG"),
new_val = c("0101", "0102", "0103"))
foo[] <- lookup$new_val[match(unlist(foo), lookup$old_val)]