grep使用具有多个模式的字符向量


132

我试图用来grep测试字符串向量是否存在于另一个向量中,并输出存在的值(匹配模式)。

我有一个像这样的数据框:

FirstName Letter   
Alex      A1
Alex      A6
Alex      A7
Bob       A1
Chris     A9
Chris     A6

我在“字母”(Letter)列中有一个字符串模式向量,例如: c("A1", "A9", "A6")

我想检查模式向量中的任何字符串是否在“字母”列中。如果是的话,我希望输出唯一值。

问题是,我不知道如何使用grep多种模式。我试过了:

matches <- unique (
    grep("A1| A9 | A6", myfile$Letter, value=TRUE, fixed=TRUE)
)

但这给了我0个匹配,这是不正确的,有什么建议吗?


3
您不能使用,fixed=TRUE因为您的模式是真正的正则表达式。
Marek

6
使用matchor %in%或什至是比较完全匹配==唯一正确方法。正则表达式对于此类任务非常危险,并可能导致意外结果。
David Arenburg

Answers:


269

除了@Marek关于not included的注释fixed==TRUE,您还需要在正则表达式中不要包含空格。它应该是"A1|A9|A6"

您还提到了很多模式。假设它们在向量中

toMatch <- c("A1", "A9", "A6")

然后,您可以直接使用paste和创建正则表达式collapse = "|"

matches <- unique (grep(paste(toMatch,collapse="|"), 
                        myfile$Letter, value=TRUE))

当您的字符串列表中包含正则表达式运算符作为标点符号时,有什么方法可以这样做?
user124123

@ user1987097无论有没有其他正则表达式运算符,它都应以相同的方式工作。您是否有一个无法使用的特定示例?
布赖恩·迪格斯

@ user1987097在点或方括号前使用2个后退。第一个反斜杠是转义字符,用于解释禁用操作符所需的第二个反斜杠。
mbh86 '16

3
使用正则表达式进行精确匹配对我来说似乎很危险,并且可能会产生意想不到的结果。为什么不只是toMatch %in% myfile$Letter呢?
大卫·阿伦堡

@ user4050没有具体原因。问题中的版本具有它,我可能只是在没有考虑是否必要的情况下进行了。
Brian Diggs

34

好的答案,但是请不要忘记filter()dplyr:

patterns <- c("A1", "A9", "A6")
>your_df
  FirstName Letter
1      Alex     A1
2      Alex     A6
3      Alex     A7
4       Bob     A1
5     Chris     A9
6     Chris     A6

result <- filter(your_df, grepl(paste(patterns, collapse="|"), Letter))

>result
  FirstName Letter
1      Alex     A1
2      Alex     A6
3       Bob     A1
4     Chris     A9
5     Chris     A6

3
我认为grepl当时只能使用一种模式(我们需要长度为1的矢量),我们有3种模式(长度为3的矢量),因此我们可以使用一些友好的grepl分隔符将它们与一种组合- |,然后尝试与其他方法结合使用:)
Adamm '18

3
哦,我明白了。因此,它是一种输出A1之类的压缩方式| A2因此,如果一个人想要所有条件,那么崩溃将以&符号表示,谢谢。
Ahdee

1
嗨,使用)|(分隔模式可能会使此功能更强大:paste0("(", paste(patterns, collapse=")|("),")")。不幸的是,它也变得不那么优雅。这导致模式(A1)|(A9)|(A6)
fabern

14

这应该工作:

grep(pattern = 'A1|A9|A6', x = myfile$Letter)

或更简单地说:

library(data.table)
myfile$Letter %like% 'A1|A9|A6'

11
%like%不在R的基础上,因此您应该提及使用它所需的软件包。
格里戈尔·托马斯

1
对于其他正在寻找这个答案的人,这%like%data.table包装的一部分。类似的还有中data.tablelike(...)%ilike%%flike%
史蒂夫

8

根据Brian Digg的帖子,这里有两个有用的过滤列表功能:

#Returns all items in a list that are not contained in toMatch
#toMatch can be a single item or a list of items
exclude <- function (theList, toMatch){
  return(setdiff(theList,include(theList,toMatch)))
}

#Returns all items in a list that ARE contained in toMatch
#toMatch can be a single item or a list of items
include <- function (theList, toMatch){
  matches <- unique (grep(paste(toMatch,collapse="|"), 
                          theList, value=TRUE))
  return(matches)
}

5

您是否尝试过match()or charmatch()功能?

使用示例:

match(c("A1", "A9", "A6"), myfile$Letter)

1
需要注意的一件事match是它没有使用模式,而是期望完全匹配。
史蒂夫

5

不知道这个答案是否已经出现...

对于问题中的特定模式,您只需一个grep()电话就可以完成,

grep("A[169]", myfile$Letter)

4

添加到Brian Diggs答案。

使用grepl的另一种方法将返回包含所有值的数据框。

toMatch <- myfile$Letter

matches <- myfile[grepl(paste(toMatch, collapse="|"), myfile$Letter), ]

matches

Letter Firstname
1     A1      Alex 
2     A6      Alex 
4     A1       Bob 
5     A9     Chris 
6     A6     Chris

也许更清洁...也许?


2

拿走空间。这样:

matches <- unique(grep("A1|A9|A6", myfile$Letter, value=TRUE, fixed=TRUE))

1

使用 sapply

 patterns <- c("A1", "A9", "A6")
         df <- data.frame(name=c("A","Ale","Al","lex","x"),Letters=c("A1","A2","A9","A1","A9"))



   name Letters
1    A      A1
2  Ale      A2
3   Al      A9
4  lex      A1
5    x      A9


 df[unlist(sapply(patterns, grep, df$Letters, USE.NAMES = F)), ]
  name Letters
1    A      A1
4  lex      A1
3   Al      A9
5    x      A9

-1

我建议编写一个小脚本并使用Grep进行多次搜索。我从来没有找到一种搜索多种模式的方法,相信我,我已经找到了!

像这样,您的带有嵌入式字符串的shell文件:

 #!/bin/bash 
 grep *A6* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";
 grep *A7* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";
 grep *A8* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";

然后通过键入myshell.sh运行。

如果您希望能够在命令行中传递该字符串,请使用shell参数以这种方式进行操作-这是bash表示法btw:

 #!/bin/bash 
 $stingtomatch = "${1}";
 grep *A6* "${stingtomatch}";
 grep *A7* "${stingtomatch}";
 grep *A8* "${stingtomatch}";

依此类推。

如果要匹配的模式很多,可以将其放入for循环中。


谢谢ChrisBean。这些模式实际上很多,也许最好使用一个文件。我是BASH的新手,但也许类似的东西应该可以工作……#!/ bin / bash在'pattern.txt'中对我执行echo $ ij ='grep -c“ $ {i}” myfile.txt'echo $ j如果[$ j -eq o]然后回显$ i >>
matchs.txt

不起作用...错误消息是[[grep:找不到命令” ...我在/ bin文件夹中有grep,而/ bin在我的$ PATH上...不确定发生了什么...您能帮忙吗?
user971102 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.