grep birden çok desen içeren bir karakter vektörü kullanarak


132

Kullanmaya çalışıyorum grep bir vektörde bir dizi vektörünün var olup olmadığını test etmek ve mevcut değerleri (eşleşen desenler) .

Bunun gibi bir veri çerçevem ​​var:

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

"Harf" sütunlarında bulunabilecek bir dizi desenleri vektörüm var, örneğin: c("A1", "A9", "A6") .

Desen vektöründeki dizelerden herhangi birinin "Harf" sütununda olup olmadığını kontrol etmek istiyorum. Öyleyse, benzersiz değerlerin çıktısını isterim.

Sorun şu ki, grepçoklu kalıplarla nasıl kullanılacağını bilmiyorum . Denedim:

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

Ama bana doğru olmayan 0 eşleşme veriyor, herhangi bir öneriniz var mı?


3
Kullanamazsınız fixed=TRUEçünkü kalıbınız gerçek bir normal ifadedir.
Marek

6
Kullanılması matchveya %in%hatta ya ==olduğu sadece kesin sonuç karşılaştırmak doğru yolu. regex, böyle bir görev için çok tehlikelidir ve beklenmedik sonuçlara yol açabilir.
David Arenburg

Yanıtlar:


269

@ Marek'in dahil edilmeyen yorumuna ek olarak fixed==TRUE , normal ifadenizde boşluk bırakmamalısınız. Olmalı"A1|A9|A6" .

Ayrıca çok sayıda desen olduğunu da söylüyorsunuz. Bir vektörde olduklarını varsayarsak

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

Daha sonra normal ifadenizi doğrudan pasteve kullanarak oluşturabilirsiniz collapse = "|".

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

Dize listeniz noktalama olarak normal ifade operatörlerini içerdiğinde bunu yapmanın bir yolu var mı?
user124123

@ user1987097 Diğer regex operatörleri ile veya onlar olmadan aynı şekilde çalışmalıdır. Bunun işe yaramadığı belirli bir örnek var mı?
Brian Diggs

@ user1987097, nokta veya köşeli parantezden önce 2 backslahes kullanın. İlk ters eğik çizgi, operatörü devre dışı bırakmak için gereken ikincisini yorumlamak için bir çıkış karakteridir.
mbh86

3
Tam eşleşmeler için normal ifadeyi kullanmak benim için tehlikeli görünüyor ve beklenmedik sonuçlar doğurabilir. Neden sadece değil toMatch %in% myfile$Letter?
David Arenburg

@ user4050 Belirli bir neden yok. Sorudaki versiyonda vardı ve muhtemelen gerekli olup olmadığını düşünmeden bunu sadece gerçekleştirdim.
Brian Diggs

34

İyi cevaplar, ancak filter()dplyr'den unutmayın :

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
Sanırım bu grepl, her seferinde bir desenle çalışıyor (uzunluk 1 olan vektör gerekiyor), 3 modelimiz var (uzunluk 3 vektörü), bu yüzden onları grepl ayırıcısı için dostça kullanarak biriyle birleştirebiliriz - |şansınızı diğeriyle deneyin :)
Adamm

3
oh şimdi anladım. Yani A1 | gibi bir çıktı vermenin sıkıştırılmış bir yolu. A2, eğer biri tüm koşulları isterse, çöküş bir & işaretiyle olur, harika teşekkürler.
Ahdee

1
Merhaba, kullanılarak )|(ayrı desenleri bu daha sağlam hale getirebileceğini: paste0("(", paste(patterns, collapse=")|("),")"). Ne yazık ki, aynı zamanda biraz daha az şık hale geliyor. Bu desenle sonuçlanır (A1)|(A9)|(A6).
fabern

14

Bu çalışmalı:

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

Ya da daha basitçe:

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

11
%like%R bazında değildir, bu yüzden onu kullanmak için hangi paketlerin gerekli olduğunu belirtmelisiniz.
Gregor Thomas

1
Bu yanıta bakan diğerleri %like%için data.tablepaketin bir parçasıdır . Ayrıca benzer içinde data.tableolan like(...), %ilike%ve %flike%.
steveb

8

Brian Digg'in gönderisine dayanarak, listeleri filtrelemek için iki yararlı işlev şunlardır:

#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()Veya charmatch()işlevlerini denediniz mi?

Örnek kullanım:

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

1
Unutulmaması gereken bir nokta, matchkalıp kullanmamasıdır, tam bir eşleşme beklemesidir.
steveb

5

Bu cevabın daha önce görünüp görünmediğinden emin değilim ...

Sorudaki belirli model için, bunu tek bir grep()aramayla yapabilirsiniz,

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

4

Brian Diggs cevabına eklemek için.

grepl kullanmanın başka bir yolu, tüm değerlerinizi içeren bir veri çerçevesi döndürür.

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

Belki biraz daha temiz ... belki?


2

Boşlukları kaldırın. Öyleyse yap:

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

1

Kullanmak 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 ile küçük bir senaryo yazıp birden çok arama yapmayı öneriyorum. Birden fazla kalıp aramanın bir yolunu bulamadım ve inan bana, baktım!

Aynı şekilde, gömülü bir dizeye sahip kabuk dosyanız:

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

Ardından myshell.sh yazarak çalıştırın.

Komut satırında dizeyi iletebilmek istiyorsanız, bunu bir kabuk argümanıyla yapın - bu bash gösterimi btw:

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

Ve benzeri.

Eşleştirilecek çok sayıda desen varsa, bunu bir for döngüsüne koyabilirsiniz.


Teşekkür ederim ChrisBean. Desenler aslında çok ve o zaman bir dosya kullanmak daha iyi olabilir. BASH'de yeniyim, ama belki bunun gibi bir şey işe yarayabilir ... #! / Bin / bash 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" dosyam.txt' echo $ j [$ j -eq o] ise echo $ i >> ile eşleşir.txt fi bitti
user971102

çalışmıyor… hata mesajı '[grep: komut bulunamadı'… / bin klasöründe grep var ve / bin $ PATH'ımda… Ne olduğundan emin değilim… Lütfen yardım edebilir misiniz?
user971102
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.