grep sử dụng một vectơ ký tự có nhiều mẫu


132

Tôi đang cố gắng sử dụng grepđể kiểm tra xem một vectơ của chuỗi có hiện diện trong một vectơ khác hay không và để xuất các giá trị hiện diện (các mẫu phù hợp).

Tôi có một khung dữ liệu như thế này:

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

Tôi có một vectơ các mẫu chuỗi được tìm thấy trong các cột "Chữ", ví dụ : c("A1", "A9", "A6").

Tôi muốn kiểm tra xem có bất kỳ chuỗi nào trong vectơ mẫu có trong cột "Thư" không. Nếu có, tôi muốn đầu ra của các giá trị duy nhất.

Vấn đề là, tôi không biết cách sử dụng grepvới nhiều mẫu. Tôi đã thử:

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

Nhưng nó cho tôi 0 trận không đúng, có gợi ý nào không?


3
Bạn không thể sử dụng fixed=TRUEvì mẫu của bạn là biểu thức chính quy thực sự .
Marek

6
Sử dụng matchhoặc %in%thậm chí ==là cách chính xác duy nhất để so sánh các trận đấu chính xác. regex rất nguy hiểm cho một nhiệm vụ như vậy và có thể dẫn đến kết quả bất ngờ.
David Arenburg

Câu trả lời:


269

Ngoài nhận xét của @ Marek về việc không bao gồm fixed==TRUE, bạn cũng cần không có khoảng trắng trong biểu thức thông thường. Nó phải "A1|A9|A6".

Bạn cũng đề cập rằng có rất nhiều mẫu. Giả sử rằng chúng ở trong một vectơ

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

Sau đó, bạn có thể tạo biểu thức chính quy của bạn trực tiếp bằng cách sử dụng pastecollapse = "|".

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

Bất kỳ cách nào để làm điều này khi danh sách các chuỗi của bạn bao gồm các toán tử regex là dấu chấm câu?
dùng124123

@ user1987097 Nó nên hoạt động theo cùng một cách, có hoặc không có bất kỳ toán tử regex nào khác. Bạn đã có một ví dụ cụ thể mà điều này không làm việc cho?
Brian Diggs

@ user1987097 sử dụng 2 backslahes trước dấu chấm hoặc dấu ngoặc. Dấu gạch chéo đầu tiên là một ký tự thoát để diễn giải cái thứ hai cần thiết để vô hiệu hóa toán tử.
mbh86

3
Sử dụng regex cho các trận đấu chính xác có vẻ nguy hiểm đối với tôi và có thể có kết quả bất ngờ. Tại sao không chỉ toMatch %in% myfile$Letter?
David Arenburg

@ user4050 Không có lý do cụ thể. Phiên bản trong câu hỏi đã có nó và tôi có lẽ chỉ cần thực hiện nó mà không suy nghĩ về việc liệu nó có cần thiết hay không.
Brian Diggs

34

Câu trả lời hay, tuy nhiên đừng quên filter()từ 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
Tôi nghĩ rằng nó greplhoạt động với một mẫu tại thời điểm đó (chúng ta cần vectơ có độ dài 1), chúng ta có 3 mẫu (vectơ có độ dài 3), vì vậy chúng ta có thể kết hợp chúng với một mẫu bằng cách sử dụng một số thân thiện cho dấu tách grepl - |, hãy thử vận ​​may của bạn với mẫu khác :)
Adamm

3
oh tôi hiểu rồi Vì vậy, đây là cách nén để xuất ra thứ gì đó như A1 | Vì vậy, nếu một người muốn tất cả các điều kiện thì sự sụp đổ sẽ có dấu &, cảm ơn tuyệt vời.
Ahdee

1
Xin chào, sử dụng )|(để tách các mẫu có thể làm cho điều này mạnh mẽ hơn : paste0("(", paste(patterns, collapse=")|("),")"). Thật không may, nó cũng trở nên hơi kém thanh lịch. Điều này dẫn đến mô hình (A1)|(A9)|(A6).
fabern

14

Điều này sẽ làm việc:

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

Hoặc thậm chí đơn giản hơn:

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

11
%like%không có trong cơ sở R, vì vậy bạn nên đề cập đến những gói cần thiết để sử dụng nó.
Gregor Thomas

1
Đối với những người khác nhìn vào câu trả lời này, %like%là một phần của data.tablegói. Cũng trong tương tự data.tablelike(...), %ilike%, và %flike%.
steveb

8

Dựa trên bài đăng của Brian Digg, đây là hai chức năng hữu ích để lọc danh sách:

#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

Bạn đã thử match()hoặc các charmatch()chức năng?

Ví dụ sử dụng:

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

1
Một điều cần lưu ý matchlà nó không sử dụng các mẫu, nó đang mong đợi một kết hợp chính xác.
steveb

5

Không chắc câu trả lời này đã xuất hiện chưa ...

Đối với mẫu cụ thể trong câu hỏi, bạn chỉ có thể thực hiện với một grep()cuộc gọi duy nhất ,

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

4

Để thêm vào câu trả lời của Brian Diggs.

một cách khác sử dụng grepl sẽ trả về một khung dữ liệu chứa tất cả các giá trị của bạn.

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

Có lẽ sạch hơn một chút ... có lẽ?


2

Lấy đi những khoảng trống. Làm như vậy:

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

1

Sử dụng 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

Tôi đề nghị viết một đoạn script nhỏ và thực hiện nhiều tìm kiếm với Grep. Tôi chưa bao giờ tìm thấy một cách để tìm kiếm nhiều mẫu và tin tôi đi, tôi đã xem!

Giống như vậy, tệp shell của bạn, với một chuỗi nhúng:

 #!/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";

Sau đó chạy bằng cách gõ myshell.sh.

Nếu bạn muốn có thể truyền vào chuỗi trên dòng lệnh, hãy làm như thế này, với một đối số shell - đây là ký hiệu bash btw:

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

Và kể từ đó trở đi.

Nếu có rất nhiều mẫu phù hợp, bạn có thể đặt nó trong một vòng lặp for.


Cảm ơn bạn ChrisBean. Các mẫu thực sự rất nhiều, và có lẽ sẽ tốt hơn nếu sử dụng một tệp sau đó. Tôi chưa quen với BASH, nhưng có lẽ một cái gì đó như thế này sẽ hoạt động được # # / Bin / bash cho tôi trong 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j if [$ j -eq o] thì echo $ i >> Match.txt fi xong
user971102

không hoạt động, thông báo lỗi là '[grep: không tìm thấy lệnh' Tôi có grep trong thư mục / bin và / bin nằm trên $ PATH của tôi Không chắc chắn điều gì đang xảy ra, bạn có thể giúp tôi không?
user971102
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.