grep przy użyciu wektora znaków z wieloma wzorami


136

Próbuję użyć, grepaby sprawdzić, czy wektor ciągów jest obecny w innym wektorze, czy nie, i wyprowadzić wartości, które są obecne (pasujące wzorce).

Mam taką ramkę danych:

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

Mam wektor ciągów wzory można znaleźć w kolumnach „litera”, na przykład: c("A1", "A9", "A6").

Chciałbym sprawdzić, czy którykolwiek z ciągów w wektorze wzorcowym jest obecny w kolumnie „Letter”. Jeśli tak, chciałbym uzyskać unikalne wartości.

Problem w tym, że nie wiem, jak używać grepwielu wzorów. Próbowałem:

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

Ale daje mi 0 dopasowań, co nie jest prawdą, jakieś sugestie?


3
Nie możesz tego użyć, fixed=TRUEponieważ wzorzec jest prawdziwym wyrażeniem regularnym.
Marek

6
Używanie matchlub %in%nawet ==jest jedynym prawidłowym sposobem porównywania dokładnych dopasowań. regex jest bardzo niebezpieczne w przypadku takiego zadania i może prowadzić do nieoczekiwanych wyników.
David Arenburg

Odpowiedzi:


273

Oprócz komentarza @Marka dotyczącego nieuwzględniania fixed==TRUE, w wyrażeniu regularnym nie musisz też mieć spacji. Tak powinno być "A1|A9|A6".

Wspomniałeś również, że jest wiele wzorów. Zakładając, że są w wektorze

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

Następnie możesz bezpośrednio utworzyć wyrażenie regularne za pomocą pastei collapse = "|".

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

Czy można to zrobić, gdy lista ciągów zawiera operatory regex jako znaki interpunkcyjne?
user124123

@ user1987097 Powinno działać w ten sam sposób, z innymi operatorami regex lub bez nich. Czy masz konkretny przykład, dla którego to nie zadziałało?
Brian Diggs

@ user1987097 użyj 2 odwrotnych ukośników przed kropką lub nawiasem. Pierwszy lewy ukośnik to znak zmiany znaczenia, który interpretuje drugi, potrzebny do wyłączenia operatora.
mbh86

3
Używanie wyrażenia regularnego do dokładnych dopasowań wydaje mi się niebezpieczne i może mieć nieoczekiwane rezultaty. Dlaczego nie po prostu toMatch %in% myfile$Letter?
David Arenburg

@ user4050 Bez konkretnego powodu. Wersja w pytaniu miała to i prawdopodobnie po prostu to przeprowadziłem, nie zastanawiając się, czy to konieczne.
Brian Diggs

36

Dobre odpowiedzi, ale nie zapomnij o 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
Myślę, że grepldziała z jednym wzorcem na raz (potrzebujemy wektora o długości 1), mamy 3 wzorce (wektor o długości 3), więc możemy je połączyć z jednym używając przyjaznego dla grepl separatora - |spróbuj szczęścia z innymi :)
Adamm

3
och, teraz rozumiem. Więc jest to kompresowany sposób na wyświetlenie czegoś takiego jak A1 | A2, więc gdyby ktoś chciał wszystkich warunków, załamanie byłoby ze znakiem &, fajne dzięki.
Ahdee

1
Cześć, używając )|(do odrębnych wzorców może uczynić to bardziej wytrzymałe: paste0("(", paste(patterns, collapse=")|("),")"). Niestety staje się też nieco mniej elegancki. Powoduje to wzór (A1)|(A9)|(A6).
fabern

16

To powinno działać:

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

Lub jeszcze prościej:

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

11
%like%nie znajduje się w podstawowym R, więc powinieneś wspomnieć, jakie pakiety są potrzebne, aby go użyć.
Gregor Thomas

1
Dla innych, którzy patrzą na tę odpowiedź, %like%jest częścią data.tablepakietu. Podobne w data.tableto like(...), %ilike%i %flike%.
steveb

8

Na podstawie postu Briana Digga, oto dwie pomocne funkcje do filtrowania list:

#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

Czy próbowałeś już funkcji match()or charmatch()?

Przykładowe zastosowanie:

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

1
Należy zauważyć match, że nie używa on wzorców, ale oczekuje dokładnego dopasowania.
steveb

5

Nie jestem pewien, czy ta odpowiedź już się pojawiła ...

W przypadku konkretnego wzorca w pytaniu możesz to zrobić za pomocą jednego grep()połączenia,

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

4

Aby dodać do odpowiedzi Briana Diggsa.

inny sposób użycia grepl zwróci ramkę danych zawierającą wszystkie twoje wartości.

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

Może trochę czystszy ... może?


2

Usuń przestrzenie. Więc zrób:

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

1

Używając 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

Proponuję napisać mały skrypt i przeprowadzić wiele wyszukiwań z Grepem. Nigdy nie znalazłem sposobu na wyszukiwanie wielu wzorów i wierz mi, szukałem!

Podobnie jak plik powłoki, z osadzonym ciągiem znaków:

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

Następnie uruchom, wpisując myshell.sh.

Jeśli chcesz móc przekazać łańcuch w wierszu poleceń, zrób to w ten sposób, z argumentem powłoki - to jest notacja bash btw:

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

I tak dalej.

Jeśli jest wiele wzorców do dopasowania, możesz umieścić je w pętli for.


Dziękuję ChrisBean. Wzorów jest w rzeczywistości dużo i może wtedy lepiej byłoby użyć pliku. Jestem nowy w BASH, ale może coś takiego powinno działać… #! / Bin / bash for i in 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j if [$ j -eq o] then echo $ i >> matching.txt fi gotowe
user971102

nie działa… komunikat o błędzie to „[grep: nie znaleziono polecenia”… Mam grep w folderze / bin, a / bin jest na mojej $ PATH… Nie wiem, co się dzieje… Czy możesz pomóc?
user971102
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.