grep menggunakan vektor karakter dengan beberapa pola

132

Saya mencoba menggunakan grepuntuk menguji apakah vektor string ada dalam vektor lain atau tidak, dan untuk menghasilkan nilai yang ada (pola yang cocok).

Saya memiliki bingkai data seperti ini:

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

Saya memiliki vektor pola string dapat ditemukan di "Surat" kolom, misalnya: c("A1", "A9", "A6").

Saya ingin memeriksa apakah salah satu string dalam vektor pola ada di kolom "Surat". Jika ya, saya ingin output nilai unik.

Masalahnya adalah, saya tidak tahu bagaimana menggunakannya grepdengan banyak pola. Saya mencoba:

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

Tapi itu memberi saya 0 kecocokan yang tidak benar, ada saran?

pengguna971102
sumber
3
Anda tidak dapat menggunakan fixed=TRUEkarena pola Anda adalah ekspresi reguler yang benar .
Marek
6
Menggunakan matchatau %in%atau bahkan ==adalah satu - satunya cara yang benar untuk membandingkan kecocokan yang tepat. regex sangat berbahaya untuk tugas seperti itu dan dapat menyebabkan hasil yang tidak terduga.
David Arenburg

Jawaban:

269

Selain komentar @ Marek tentang tidak termasuk fixed==TRUE, Anda juga tidak perlu memiliki spasi dalam ekspresi reguler Anda. Seharusnya begitu "A1|A9|A6".

Anda juga menyebutkan bahwa ada banyak pola. Dengan asumsi mereka berada dalam vektor

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

Kemudian Anda dapat membuat ekspresi reguler Anda secara langsung menggunakan pastedan collapse = "|".

matches <- unique (grep(paste(toMatch,collapse="|"), 
                        myfile$Letter, value=TRUE))
Brian Diggs
sumber
Adakah cara untuk melakukan ini ketika daftar string Anda menyertakan operator regex sebagai tanda baca?
user124123
@ user1987097 Seharusnya bekerja dengan cara yang sama, dengan atau tanpa operator regex lainnya. Apakah Anda memiliki contoh khusus yang tidak berhasil untuk ini?
Brian Diggs
@ user1987097 gunakan 2 backslahes sebelum titik atau braket. Backslash pertama adalah karakter pelarian untuk menafsirkan karakter kedua yang diperlukan untuk menonaktifkan operator.
mbh86
3
Menggunakan regex untuk pencocokan tepat sepertinya berbahaya bagi saya dan dapat memiliki hasil yang tidak terduga. Kenapa tidak adil toMatch %in% myfile$Letter?
David Arenburg
@ user4050 Tidak ada alasan khusus. Versi dalam pertanyaan itu memilikinya dan saya mungkin hanya membawanya tanpa memikirkan apakah itu perlu.
Brian Diggs
34

Jawaban yang bagus, namun jangan lupakan tentang 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
Adamm
sumber
3
Saya pikir itu greplbekerja dengan satu pola pada saat itu (kita perlu vektor dengan panjang 1), kita punya 3 pola (vektor panjang 3), jadi kita bisa menggabungkannya dengan satu menggunakan beberapa ramah untuk pemisah grepl - |, coba keberuntunganmu dengan yang lain :)
Adamm
3
oh saya mengerti sekarang. Jadi cara kompres untuk menampilkan sesuatu seperti A1 | A2 jadi jika seseorang ingin semua kondisi maka keruntuhan akan dengan tanda &, keren terima kasih.
Ahdee
1
Hi, menggunakan )|(pola yang terpisah mungkin membuat ini lebih kuat: paste0("(", paste(patterns, collapse=")|("),")"). Sayangnya itu menjadi juga sedikit kurang elegan. Ini menghasilkan pola (A1)|(A9)|(A6).
Fabern
14

Ini seharusnya bekerja:

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

Atau bahkan lebih sederhana:

library(data.table)
myfile$Letter %like% 'A1|A9|A6'
Dewan Komisaris
sumber
11
%like%tidak ada dalam basis R, jadi Anda harus menyebutkan paket apa yang diperlukan untuk menggunakannya.
Gregor Thomas
1
Bagi yang lain melihat jawaban ini, %like%adalah bagian dari data.tablepaket. Juga di sama data.tableadalah like(...), %ilike%, dan %flike%.
steveb
8

Berdasarkan posting Brian Digg, berikut adalah dua fungsi yang membantu untuk memfilter daftar:

#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)
}
Austin D
sumber
5

Sudahkah Anda mencoba match()ataucharmatch() fungsinya?

Contoh penggunaan:

match(c("A1", "A9", "A6"), myfile$Letter)
pengguna3877096
sumber
1
Satu hal yang perlu diperhatikan matchadalah bahwa ia tidak menggunakan pola, ia mengharapkan kecocokan yang tepat.
steveb
5

Tidak yakin apakah jawaban ini sudah muncul ...

Untuk pola tertentu dalam pertanyaan, Anda bisa melakukannya dengan satu grep()panggilan,

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

Untuk menambah jawaban Brian Diggs.

cara lain menggunakan grepl akan mengembalikan bingkai data yang berisi semua nilai Anda.

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

Mungkin sedikit lebih bersih ... mungkin?

StatGenGeek
sumber
2

Singkirkan ruang. Begitu juga:

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

Menggunakan 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
dondapati
sumber
-1

Saya sarankan menulis skrip kecil dan melakukan beberapa pencarian dengan Grep. Saya tidak pernah menemukan cara untuk mencari beberapa pola, dan percayalah, saya sudah melihat!

Seperti itu, file shell Anda, dengan string yang disematkan:

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

Kemudian jalankan dengan mengetikkan myshell.sh.

Jika Anda ingin dapat meneruskan string pada baris perintah, lakukan seperti ini, dengan argumen shell - ini adalah bash notation btw:

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

Dan seterusnya.

Jika ada banyak pola yang cocok, Anda bisa memasukkannya dalam for loop.

ChrisBean
sumber
ChrisBean terima kasih. Polanya sebenarnya banyak, dan mungkin akan lebih baik menggunakan file itu. Saya baru mengenal BASH, tetapi mungkin sesuatu seperti ini seharusnya bekerja ... #! / Bin / bash untuk saya di 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j jika [$ j -eq o] maka echo $ i >> match.txt fi selesai
user971102
tidak berfungsi ... pesan kesalahannya adalah '[grep: command not found' ... Saya punya grep di folder / bin, dan / bin ada di $ PATH saya ... Tidak yakin apa yang terjadi ... Bisakah Anda membantu?
user971102