R grep:将一个字符串与多个模式进行匹配

30
在R中,grep通常会使用一个正则表达式来匹配多个字符串的向量。 问题:有没有可能用多个正则表达式来匹配单个字符串?(不使用循环遍历每个单独的正则表达式模式) 背景:我有7000多个关键字作为几个类别的指标。我不能更改该关键词词典。该词典具有以下结构(第1列中的关键字,数字表示属于这些关键字的类别)。
ab  10  37  41
abbrach*    38
abbreche    39
abbrich*    39
abend*  37
abendessen* 60  63
aber    20  23  45
abermals    37

使用 "|" 连接那么多关键词并不可行(而且我不知道哪一个关键词生成了匹配结果)。同时,仅仅颠倒 "patterns" 和 "strings" 是不可行的,因为这些模式有截断,反向操作会导致失效。

[相关问题,其他编程语言]


2
我喜欢丹的建议,但是在处理大数据集时,你可能会遇到一些显著的速度问题。如果你想在字典中查找某个单词并返回相应的值,我建议采用不同的方法:将句子分解为单独的单词向量,并使用哈希表进行快速查找。我认为你可能还想将关键词和类别指示符分别放在字典的两个单独列中。在你更清楚最终结果之后,我可以提供帮助。 - Tyler Rinker
同意重构字典数据并使用哈希表进行查找(根据所需结果),但匹配速度应该相对较快,取决于字符串数量,即使有大量关键字。我会在我的答案中添加一个快速基准测试。 - danpelota
1
如果你真的有很多单词(通常是人类语言中的所有单词,谷歌索引的所有单词等),你可以使用前缀树(有时也称为“Trie”)。但我不知道R语言中是否有任何实现。 - Vincent Zoonekynd
3个回答

34

那么对关键词向量应用regexpr函数怎么样?

keywords <- c("dog", "cat", "bird")

strings <- c("Do you have a dog?", "My cat ate by bird.", "Let's get icecream!")

sapply(keywords, regexpr, strings, ignore.case=TRUE)

     dog cat bird
[1,]  15  -1   -1
[2,]  -1   4   15
[3,]  -1  -1   -1

    sapply(keywords, regexpr, strings[1], ignore.case=TRUE)

 dog  cat bird 
  15   -1   -1 

返回的值是匹配中第一个字符的位置,-1 表示无匹配。

如果匹配的位置不重要,可以使用 grepl 代替:

sapply(keywords, grepl, strings, ignore.case=TRUE)

       dog   cat  bird
[1,]  TRUE FALSE FALSE
[2,] FALSE  TRUE  TRUE
[3,] FALSE FALSE FALSE

更新: 在我的系统上,即使有大量关键词,这个操作运行得相对很快:

# Available on most *nix systems
words <- scan("/usr/share/dict/words", what="")
length(words)
[1] 234936

system.time(matches <- sapply(words, grepl, strings, ignore.case=TRUE))

   user  system elapsed 
  7.495   0.155   7.596 

dim(matches)
[1]      3 234936

感谢您的回答和评论!最终,我采用了一种综合方法:使用trie将可能匹配的集合减少到原始大小的约5%,然后使用sapply函数进行grep。 - Felix S
然后为了查看每个句子/字符串中有多少关键词匹配,在最终的数据框上执行以下操作:num.matches <- apply(data.frame(matches), 1, function(z) sum(z==TRUE))而要查找哪个原始字符串有,假设有2个匹配项,执行以下操作:strings[num.matches==2] # 输出:[1] "My cat ate by bird." - Paul Rigor
如果您想进行一次替换,不仅要读取要搜索的关键字,而且还要读取它们的替换内容,该怎么办? - user1603472

3

re2r包能够同时匹配多个模式。以下是一个简单的示例:

# compile patterns
re <- re2r::re2(keywords)
# match strings
re2r::re2_detect(strings, re, parallel = TRUE)

2

更深入地了解其他答案,要将sapply()输出转换为有用的逻辑向量,需要进一步使用apply()步骤。

keywords <- c("dog", "cat", "bird")
strings <- c("Do you have a dog?", "My cat ate by bird.", "Let's get icecream!")
(matches <- sapply(keywords, grepl, strings, ignore.case=TRUE))
#        dog   cat  bird
# [1,]  TRUE FALSE FALSE
# [2,] FALSE  TRUE  TRUE
# [3,] FALSE FALSE FALSE

了解哪些字符串包含任意一个关键字(模式):

apply(matches, 1, any)
# [1]  TRUE  TRUE FALSE

要知道在提供的字符串中匹配了哪些关键字(模式):

apply(matches, 2, any)
#  dog  cat bird 
# TRUE TRUE TRUE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接