Стиль словаря заменяет несколько элементов

У меня есть большой 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)]