ある文字列 x
中に文字列ベクトル vec
に含まれる文字列がある場合、その文字列にプレフィクスをつけたい。
R
x <- "hoge fuga hoge piyo"
vec <- c("fuga", "piyo")
prefix <- "PREFIX."
f(x, vec, prefix)
#> "hoge PREFIX.fuga hoge PREFIX.piyo"
これを行う方法として正規表現を使うと楽である。
R
f1 <- function(x, vec, prefix) {
pattern <- sprintf("(%s)", paste(vec, collapse = "|"))
#> "(fuga|piyo)"
replacement <- sprintf("%s\\1", prefix)
#> "PREFIX.\\1"
gsub(pattern, replacement, x)
}
f1(x, vec, prefix)
#> "hoge PREFIX.fuga hoge PREFIX.piyo"
この正規表現は vec
を一度に置換できるが、次のように vec
内の文字列ひとつひとつに対してこの正規表現を使った置換を for
で回しても良い。
R
f2 <- function(x, vec, prefix) {
result <- x
replacement <- sprintf("%s\\1", prefix)
for (item in vec) {
pattern <- sprintf("(%s)", item)
result <- gsub(pattern, replacement, result)
}
result
}
f2(x, vec, prefix)
#> "hoge PREFIX.fuga hoge PREFIX.piyo"
一見、正規表現で一発で置換した方が速そうだが、私が使いたいのは vec
のサイズが 800 〜 8000 程度の場合である。
速度比較してみた。
R
library(microbenchmark)
set.seed(71)
result <- data.frame()
for (N in seq(100, 900, by = 100)) {
cat(N, " ")
vec <- replicate(N, paste0(sample(letters, 10, replace = TRUE), collapse = ""))
x <- sprintf("hoge %s hoge %s", sample(vec, 1), sample(vec, 1))
prefix <- "PREFIX."
microbenchmark(
only_regex = f1(x, vec, prefix),
use_for = f2(x, vec, prefix)
) -> res_bench
result <- rbind(result, data.frame(N, res_bench))
}
library(ggplot2)
ggplot(result, aes(x = factor(N), y = time)) +
geom_boxplot(aes(fill=expr)) + ylim(0, 6e7)
結果は、vec
の長さが 400 を境に for
で回した方が速くなった。
私の使用用途の場合は for
を使った方が良さそうだ。
Enjoy!
補足
ちなみに vec
が 1000 を超えると一発置換の方はエラーとなった。
R
N <- 1000
vec <- replicate(N, paste0(sample(letters, 10, replace = TRUE), collapse = ""))
x <- sprintf("hoge %s hoge %s", sample(vec, 1), sample(vec, 1))
prefix <- "PREFIX."
pattern <- sprintf("(%s)", paste(vec, collapse = "|"))
replacement <- sprintf("%s\\1", prefix)
gsub(pattern, replacement, x)
結果
gsub(pattern, replacement, x) でエラー:
assertion 'tree->num_tags == num_tags' failed in executing regexp:
file 'R-3.3.2/src/extra/tre/tre-compile.c', line 634
追記
ほむ…gsubが重たくて遅いってことなのかな? てことはstringrのstr_replace_allでは変わってくるのかな?
— kazutan v3.3.2 (@kazutan) 2017年1月12日
stringr を使うとどうなるかだって? やってみよう。
R
library(stringr)
f3 <- function(x, vec, prefix) {
pattern <- sprintf("(%s)", paste(vec, collapse = "|"))
replacement <- sprintf("%s\\1", prefix)
str_replace_all(x, pattern, replacement)
}
f3(x, vec, prefix)
ちょっぱやだった。
Enjoy!