web-dev-qa-db-ja.com

辞書スタイルは複数のアイテムを置き換えます

他の言語で一般に辞書と呼ばれているものに基づいて変換したい文字データの大きな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")

これは問題なく機能しますが、data.frame内の1つのアイテムを置き換えるたびにreplaceステートメントを繰り返すのは明らかに美しくなく、ばかげているようです。

約25のキーと値のペアの辞書があるので、これを行うためのより良い方法はありますか?

32
Stedy
map = setNames(c("0101", "0102", "0103"), c("AA", "AC", "AG"))
foo[] <- map[unlist(foo)]

mapfooのすべてのケースをカバーすると仮定します。 fooが(character()の)行列である場合、これは「ハック」のような感じが少なく、空間と時間の両方でより効率的です。

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

数百万のSNPと数千のサンプルがある場合、マトリックスとデータフレームの両方のバリアントは、ベクトルサイズのRの2 ^ 31-1制限に違反します。

27
Martin Morgan

パッケージを使用することにオープンである場合、plyrは非常に人気のあるものであり、この便利な mapvalues() 関数を使用すると、目的の機能を実行できます。

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

文字列だけでなく、あらゆる種類のデータ型で機能することに注意してください。

30
c.gutierrez

ここに簡単な解決策があります

dict = list(AA = '0101', AC = '0102', AG = '0103')
foo2 = foo
for (i in 1:3){foo2 <- replace(foo2, foo2 == names(dict[i]), dict[i])}
13
Ramnath

この回答は、 データフレーム内のすべての値を値のベクトルで置き換える方法? 。残念ながら、この質問は実際の質問の重複としてクローズされました。だから、私はここで、両方のケースの因子レベルを置き換えることに基づいた解決策を提案しようとします。


値を置き換える必要があるベクトル(または1つのデータフレーム列)だけがある場合および係数を使用することに異議がない場合は、ベクトルを強制して係数を変更し、必要に応じて因子レベル:

_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
_

forcatspackageを使用すると、これをワンライナーで実行できます。

_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
_

データフレームの複数の列のall値を置き換える必要がある場合、アプローチを拡張できます。

_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>_に強制変換されます。これは Martin Morgansの回答 の要件でもあることに注意してください。

したがって、置き換えられる異なる値が数個しかない場合は、おそらく Ramnath's のように、他の答えの1つを使用したほうがよいでしょう。

5
Uwe

これは仕事をする簡単なものです:

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を使用した解決策は可能だと思います。

5
joran

上記の@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" 
1
Fathi Alwosaibi

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
Ronak Shah

前回の回答から数年が経ち、今夜、このトピックに関する新しい質問が出て、モデレーターがそれを閉じたので、ここに追加します。ポスターには、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
mysteRious

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>
0
zx8754