【问题标题】:Replace a set of pattern matches with corresponding replacement strings in R用 R 中的相应替换字符串替换一组模式匹配
【发布时间】:2014-12-27 20:30:37
【问题描述】:

PHP 中的str_replace(和preg_replace)函数用替换字符串替换所有出现的搜索字符串。我在这里最感兴趣的是,如果 searchreplace args 是数组(在 R 中我们称之为向量),那么 str_replace 从每个数组(向量)中获取一个值并使用它们来搜索和替换主题。

换句话说,R(或某些 R 包)是否具有执行以下操作的功能:

string <- "The quick brown fox jumped over the lazy dog."
patterns     <- c("quick", "brown", "fox")
replacements <- c("slow",  "black", "bear")
xxx_replace_xxx(string, patterns, replacements)          ## ???
## [1] "The slow black bear jumped over the lazy dog."

所以我正在寻找类似chartr 的东西,但要寻找任意数量字符的搜索模式和替换字符串。这不能通过调用gsub() 来完成,因为它的replacement 参数只能是一个字符串,请参阅?gsub。所以我目前的实现是这样的:

xxx_replace_xxx <- function(string, patterns, replacements) {
   for (i in seq_along(patterns))
      string <- gsub(patterns[i], replacements[i], string, fixed=TRUE)
   string
}

但是,如果 length(patterns) 很大,我正在寻找更快的东西 - 我有很多数据要处理并且我对当前结果不满意。

用于基准测试的示例性玩具数据:

string <- readLines("http://www.gutenberg.org/files/31536/31536-0.txt", encoding="UTF-8")
patterns <- c("jak", "to", "do", "z", "na", "i", "w", "za", "tu", "gdy",
   "po", "jest", "Tadeusz", "lub", "razem", "nas", "przy", "oczy", "czy",
   "sam", "u", "tylko", "bez", "ich", "Telimena", "Wojski", "jeszcze")
replacements <- paste0(patterns, rev(patterns))

【问题讨论】:

  • 你的 stringi 包不提供这个吗?
  • @Thomas:我正在努力解决这个问题 :) 计划为 stringi_0.3-1,请参阅问题 #102

标签: r string replace


【解决方案1】:

如果模式是由单词字符组成的固定字符串,如示例中那样,则此方法有效。 gsubfngsub 类似,只是替换参数可以是字符串、列表、函数或原型对象。如果它是一个列表,就像这里一样,它会将匹配项与带有名称的正则表达式进行比较,对于找到的那些,它会用相应的值替换它们:

library(gsubfn)

gsubfn("\\b\\w+\\b", as.list(setNames(replacements, patterns)), string)
## [1] "The slow black bear jumped over the lazy dog."

【讨论】:

  • 不错!不幸的是,它的性能(参见玩具基准数据示例)比我的示例性解决方案差 10 倍......
  • 请注意,在这种情况下,问题中的代码会更快地替换子字符串,这可能不是您想要的:"The quicker brown fox jumped over the lazy dog." 此处的代码可以正确处理。
【解决方案2】:

对于您的示例,使用 PCRE 而不是固定匹配在我的机器上花费了大约 1/3 的时间。

xxx_replace_xxx_pcre <- function(string, patterns, replacements) {
   for (i in seq_along(patterns))
      string <- gsub(patterns[i], replacements[i], string, perl=TRUE)
   string
}
system.time(x <- xxx_replace_xxx(string, patterns, replacements))
#    user  system elapsed 
#   0.491   0.000   0.491 
system.time(p <- xxx_replace_xxx_pcre(string, patterns, replacements))
#    user  system elapsed 
#   0.162   0.000   0.162 
identical(x,p)
# [1] TRUE

【讨论】:

  • 有点奇怪,不是吗?
  • @gagolews:我不知道。 ?gsub 的“性能注意事项”部分说,“通常 PCRE 会比默认的正则表达式引擎更快,而fixed = TRUE 更快(尤其是当每个模式只匹配几次时)。”我想这是不是“一般”的时代之一。该部分还说useBytes = TRUE 也会更快,但我不知道这是否适用于您的实际用例(它使 PCRE 版本快约 2 倍——在我的机器上运行 0.088 秒)。
【解决方案3】:

这可以通过使用vectorize_all 参数设置为FALSEstri_replace_*_all 函数之一来使用stringi >= 0.3-1 来完成:

library("stringi")
string <- "The quicker brown fox jumped over the lazy dog."
patterns     <- c("quick", "brown", "fox")
replacements <- c("slow",  "black", "bear")
stri_replace_all_fixed(string, patterns, replacements, vectorize_all=FALSE)
## [1] "The slower black bear jumped over the lazy dog."
stri_replace_all_regex(string, "\\b" %s+% patterns %s+% "\\b", replacements, vectorize_all=FALSE)
## [1] "The quicker black bear jumped over the lazy dog."

一些基准测试:

string <- readLines("http://www.gutenberg.org/files/31536/31536-0.txt", encoding="UTF-8")
patterns <- c("jak", "to", "do", "z", "na", "i", "w", "za", "tu", "gdy",
   "po", "jest", "Tadeusz", "lub", "razem", "nas", "przy", "oczy", "czy",
   "sam", "u", "tylko", "bez", "ich", "Telimena", "Wojski", "jeszcze")
replacements <- paste0(patterns, rev(patterns))
microbenchmark::microbenchmark(
   stri_replace_all_fixed(string, patterns, replacements, vectorize_all=FALSE),
   stri_replace_all_regex(string, "\\b" %s+% patterns %s+% "\\b", replacements, vectorize_all=FALSE),
   xxx_replace_xxx_pcre(string, "\\b" %s+% patterns %s+% "\\b", replacements),
   gsubfn("\\b\\w+\\b", as.list(setNames(replacements, patterns)), string),
   unit="relative",
   times=25
)
## Unit: relative
##                   expr       min        lq      mean    median        uq       max neval
## stri_replace_all_fixed  1.000000  1.000000  1.000000  1.000000  1.000000  1.000000    25 
## stri_replace_all_regex  2.169701  2.248115  2.198638  2.267935  2.267635  1.753289    25  
## xxx_replace_xxx_pcre    1.983135  1.967303  1.937021  1.961449  1.974422  1.469894    25  
## gsubfn                 63.067835 69.870657 69.815031 71.178841 72.503020 57.019072    25  

因此,就仅在单词边界进行匹配而言,基于 PCRE 的版本是最快的。

【讨论】:

    猜你喜欢
    • 2020-02-15
    • 1970-01-01
    • 2013-10-24
    • 1970-01-01
    • 1970-01-01
    • 2014-09-03
    • 2014-03-22
    • 2014-08-20
    相关资源
    最近更新 更多