Стиль словаря заменяет несколько элементов - PullRequest
27 голосов
/ 25 сентября 2011

У меня есть большой 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 пар ключ / значение?

Ответы [ 9 ]

30 голосов
/ 11 сентября 2014

Если вы открыты для использования пакетов, plyr очень популярен и имеет эту удобную функцию mapvalues ​​() , которая будет делать то, что вы ищете:

foo <- mapvalues(foo, from=c("AA", "AC", "AG"), to=c("0101", "0102", "0103"))

Обратите внимание, что он работает для всех типов данных, а не только для строк.

27 голосов
/ 25 сентября 2011
map = setNames(c("0101", "0102", "0103"), c("AA", "AC", "AG"))
foo[] <- map[unlist(foo)]

при условии, что map охватывает все случаи в foo.Это было бы менее похоже на «взлом» и было бы более эффективным как в пространстве, так и во времени, если бы foo была матрицей (символа ()), тогда

matrix(map[foo], nrow=nrow(foo), dimnames=dimnames(foo))

И матрица, и варианты фрейма данных сталкиваютсяR 2 ^ 31-1 ограничение на размер вектора, когда есть миллионы SNP и тысячи образцов.

13 голосов
/ 25 сентября 2011

Вот быстрое решение

dict = list(AA = '0101', AC = '0102', AG = '0103')
foo2 = foo
for (i in 1:3){foo2 <- replace(foo2, foo2 == names(dict[i]), dict[i])}
5 голосов
/ 30 января 2017

Примечание этот ответ начался как попытка решить гораздо более простую проблему, опубликованную в Как заменить все значения во фрейме данных вектором значений? .К сожалению, этот вопрос был закрыт как дубликат реального вопроса.Итак, я постараюсь предложить решение, основанное на замене уровней факторов для обоих случаев, здесь.


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

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 должно быть complete , т. Е. Включать все возможные значения в соответствующих столбцах исходного фрейма данных.(Используйте unique(sort(unlist(foo))) для подтверждения).В противном случае любые пропущенные значения будут приведены к <NA>.Обратите внимание, что это также является требованием для ответа Мартина Морганса .

Так что, если нужно заменить только несколько различных значений, вам, вероятно, будет лучше с одним из других ответов,например, Рамната .

5 голосов
/ 25 сентября 2011

Вот что-то простое, что сделает эту работу:

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 выведет список в этом случае, который нас на самом деле не волнует. Вы можете присвоить результат чему-либо, если хотите, а затем просто отбросить его. Я перебираю здесь индексы, но вы можете так же легко поместить ключи / значения в список сами и перебирать их напрямую. Обратите внимание на использование глобального присваивания с <<-.

Я нашел способ сделать это с mapply, но моя первая попытка не сработала, поэтому я переключился. Я подозреваю, что решение с mapply возможно, однако.

1 голос
/ 06 мая 2019

Мы также можем использовать 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)]
1 голос
/ 27 марта 2018

Поскольку с момента последнего ответа прошло несколько лет, и сегодня вечером возник новый вопрос по этой теме, и модератор закрыл его, я добавлю его здесь. Постер имеет большой фрейм данных, содержащий 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
1 голос
/ 09 декабря 2015

Использовал ответ @ 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" 
0 голосов
/ 06 декабря 2018

Использование 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...