如何加速 R 中的正则表达式搜索?

How to speed up regex search in R?

我有密码

matrix<-outer(df$text, df1$regexp, str_count)

df 超过 1000 个文本,每个约 1500 个符号 和带有 500 个正则表达式否定表达式的 df1,例如

(?<!(no|not|n`t|n’t|neither|never|no one|nobody|none|nor|nothing|nowhere|hardly|barely|scarcely|unlikely|seldom|rarely))[ ][aA][bB][aA][nN][dD][oO][nN]

所以我的代码运行宁了一个多小时

如何加速我的代码?

示例:

library(stringr)
df<-data.frame(names=c("text1","text2"), text=c("one two three four five","six seven eight nine ten"))
regex<-data.frame(names=c("1","2"), regexp=c("(?<!(no|not))[ ][oO][nN][eE]","(?<!(no|not))[ ][fF][iI][vV][eE]"))
matrix<-outer(df$text, as.character(regex$regexp), str_count)

我已经尝试 运行 代码与

并行
library(stringr)
library(parallel)
no_cores <- detectCores() - 1
df<-data.frame(names=c("text1","text2"), text=c("one two three four five","six seven eight nine ten"))
regex<-data.frame(names=c("1","2"), regexp=c("(?<!(no|not))[ ][oO][nN][eE]","(?<!(no|not))[ ][fF][iI][vV][eE]"))
cl <- makeCluster(no_cores)
matrix<-parSapply(cl,regex$regexp, str_count, string=df$text)
stopCluster(cl)

现在在我的 4 核 PC 上编码速度提高了大约 40%

我已经像 Wiktor 推荐的那样更改了所有正则表达式,并且使用代码 运行 比使用旧正则表达式

的并行代码快大约 25%
(?<!n(?:[`’]t|e(?:ither|ver)|o(?:t| one|body|ne|r|thing|where){0,1})|hardly|barely|scarcely|unlikely|seldom|rarely)[ ][aA][bB][aA][nN][dD][oO][nN]

预先正确创建数据(字符而不是因子)

df <- data.frame(names=c("text1","text2"),
               text=c("one two three four five",
                      "six seven eight nine ten"),
               stringsAsFactors=FALSE)

regex <- data.frame(names=c("1","2"), 
                    regexp=c("(?<!(no|not))[ ][oO][nN][eE]",
                             "(?<!(no|not))[ ][fF][iI][vV][eE]"),
                    stringsAsFactors=FALSE)

R函数一般都是'vectorized',也就是说每个正则表达式都可以作用于字符串的向量

str_count(pattern=regex$regex[1], string=df$text)

sapply(regex$regex, str_count, string=df$text)

例如,

> sapply(regex$regex, str_count, string=df$text)
     (?<!(no|not))[ ][oO][nN][eE] (?<!(no|not))[ ][fF][iI][vV][eE]
[1,]                            0                                1
[2,]                            0                                0

这可能会在两个维度上线性扩展,但随着 length(df$text) 的增加,速度要快得多(与使用 outer() 相比)。

stringr 中使用的正则表达式风格是 ICU(因此,无法测试它是否在 regex101.com 有效)并且这种风格不需要完全固定宽度的后视。在一些简单的情况下,它确实支持限制量词以及常规 *+(尽管后两者与其说是功能,不如说是错误,可能会在以后修复)。

因此,您的正则表达式运行缓慢,因为多个交替分支以相同的子字符串开头。这会造成过多的回溯。您需要确保每个分支不能在同一个位置匹配。

使用

(?<!n(?:[`’]t|e(?:ither|ver)|o(?:t| one|body|ne|r|thing|where){0,1})|hardly|barely|scarcely|unlikely|seldom|rarely)[ ][aA][bB][aA][nN][dD][oO][nN]