LoginSignup
14
9

More than 5 years have passed since last update.

正規表現で一発で置換するより for で回した方が速かった #rstatsj

Last updated at Posted at 2017-01-12

ある文字列 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

追記

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!

14
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
9