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потому что ваш шаблон является истинным регулярным выражением.
Марек

6
Использование matchили %in%или даже ==- единственный правильный способ сравнить точные совпадения. регулярное выражение очень опасно для такой задачи и может привести к неожиданным результатам.
Дэвид Аренбург

Ответы:


269

В дополнение к комментарию @Marek об исключении 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 используйте две обратные косые черты перед точкой или скобкой. Первая обратная косая черта - это escape-символ для интерпретации второй, необходимой для отключения оператора.
mbh86

3
Использование регулярного выражения для точных совпадений кажется мне опасным и может привести к неожиданным результатам. Почему не просто toMatch %in% myfile$Letter?
Дэвид Аренбург,

@ user4050 Нет особой причины. В той версии, о которой идет речь, это было, и я, вероятно, просто выполнил это, не думая о том, было ли это необходимо.
Брайан Диггс

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 разделитель - |попробуйте удачи с другим :)
Адамм

3
о, теперь я понял. Таким образом, это сжатый способ вывести что-то вроде A1 | A2, так что если бы кто-то хотел все условия, то коллапс был бы со знаком &, круто, спасибо.
Ahdee

1
Привет, используя )|(для отдельных моделей может сделать это более надежным: paste0("(", paste(patterns, collapse=")|("),")"). К сожалению, это становится немного менее элегантным. В результате получается узор (A1)|(A9)|(A6).
fabern 09

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.tableэто like(...), %ilike%и %flike%.
Стивеб

8

Основываясь на сообщении Брайана Дигга, вот две полезные функции для фильтрации списков:

#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()или charmatch()?

Пример использования:

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

1
Следует отметить, matchчто он не использует шаблоны, он ожидает точного совпадения.
Стивеб

5

Не уверен, что этот ответ уже появлялся ...

Для конкретного шаблона в вопросе вы можете просто сделать это с помощью одного grep()вызова,

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

4

Чтобы добавить к ответу Брайана Диггса.

другой способ использования 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. Я никогда не находил способа найти несколько шаблонов, и, поверьте, я искал!

Так, ваш файл оболочки со встроенной строкой:

 #!/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.

Если вы хотите передать строку в командной строке, сделайте это так, с аргументом оболочки - это нотация bash, кстати:

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

И так далее.

Если нужно сопоставить много шаблонов, вы можете поместить его в цикл for.


Спасибо, ChrisBean. На самом деле шаблонов очень много, и, может быть, тогда было бы лучше использовать файл. Я новичок в BASH, но, возможно, что-то вроде этого должно сработать ... #! / Bin / bash for i в 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j if [$ j -eq o] then echo $ i >> match.txt fi done
user971102

не работает ... сообщение об ошибке: «[grep: command not found» ... У меня есть grep в папке / bin, а / bin находится в моем $ PATH ... Не уверен, что происходит ... Не могли бы вы помочь?
user971102
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.