たとえば、あるinteger vectorの中に存在する「3, 4, 5」を「5, 4, 3」に置き換えたいとき。
ここでは、文字列用の関数であるpaste()とgsub()を使ったトリックを紹介する。
適当なinteger vectorを作る。
int <- 0:9
int.vec <- c(int, int, int)
> int.vec
[1] 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
【for()ループでやる場合】
int.vec.new <- int.vec
for(i in 1:length(int.vec)){
if(int.vec[i] == 3 & int.vec[i+1] == 4 & int.vec[i+2] == 5){
int.vec.new[i:(i+2)] <- c(5, 4, 3)
i <- i + 2
}
> int.vec.new
[1] 0 1 2 5 4 3 6 7 8 9 0 1 2 5 4 3 6 7 8 9 0 1 2 5 4 3 6 7 8 9
int.vecの中の「3, 4, 5」を「5, 4, 3」に置き換える仕事をfor()ループでやろうとすると、
このように一応はできるのだけれども、for()ループは遅い。
とくにvectorが長いほど遅さを実感する。
そこで、paste()とgsub()を使ったトリックの登場。
【paste()とgsub()でやる場合】
chr.vec <- paste(int.vec, sep = "", collapse = "")
int.vec.new <- as.integer(strsplit(gsub("345", "543", chr.vec), split = "")[[1]])
> int.vec.new
[1] 0 1 2 5 4 3 6 7 8 9 0 1 2 5 4 3 6 7 8 9 0 1 2 5 4 3 6 7 8 9
まず、int.vecの値を文字列として繋げてしまう。
paste() はintegerを与えられたとしても、それをcharacterとして扱う。
collapseをうまく使えばすべての文字が繋がって、値がひとつのcharacter vectorになる。
chr.vec <- paste(int.vec, sep = "", collapse = "")
> chr.vec
[1] "012345678901234567890123456789"
こうしてひとつなぎの文字列にしてしまえば、gsub()が使える。
sub()は1つ目のヒットだけを置換するが、gsub()はすべてのヒットを置換する。
gsub("345", "543", chr.vec)
> gsub("345", "543", chr.vec)
[1] "012543678901254367890125436789"
あとは、strsplit()で1文字ずつ切り出し、integerに戻す。
strsplit() はリストで返すので、その1つ目を採用する。
> strsplit(gsub("345", "543", chr.vec), split = "")
[[1]]
[1] "0" "1" "2" "5" "4" "3" "6" "7" "8" "9" "0" "1" "2" "5" "4" "3" "6" "7" "8"
[20] "9" "0" "1" "2" "5" "4" "3" "6" "7" "8" "9"
回りくどい仕事のように見えるが、対象が大きくなるほど早さを実感する。
0 件のコメント:
コメントを投稿