grep mit einem Zeichenvektor mit mehreren Mustern


132

Ich versuche zu greptesten, ob ein Vektor von Zeichenfolgen in einem anderen Vektor vorhanden ist oder nicht, und die vorhandenen Werte (die übereinstimmenden Muster) auszugeben.

Ich habe einen Datenrahmen wie diesen:

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

Ich habe einen Vektor von Zeichenfolgenmustern in den Spalten "Buchstabe", zum Beispiel : c("A1", "A9", "A6").

Ich möchte überprüfen, ob eine der Zeichenfolgen im Mustervektor in der Spalte "Buchstabe" vorhanden ist. Wenn ja, möchte ich die Ausgabe eindeutiger Werte.

Das Problem ist, ich weiß nicht, wie ich grepmit mehreren Mustern arbeiten soll. Ich habe es versucht:

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

Aber es gibt mir 0 Übereinstimmungen, was nicht stimmt, irgendwelche Vorschläge?


3
Sie können nicht verwenden, da fixed=TRUEIhr Muster ein echter regulärer Ausdruck ist.
Marek

6
Die Verwendung von matchoder %in%oder sogar ==ist der einzig richtige Weg, um genaue Übereinstimmungen zu vergleichen. Regex ist für eine solche Aufgabe sehr gefährlich und kann zu unerwarteten Ergebnissen führen.
David Arenburg

Antworten:


269

Zusätzlich zu @ Mareks Kommentar zum Nichteinschließen fixed==TRUEmüssen Sie auch keine Leerzeichen in Ihrem regulären Ausdruck haben. Es sollte sein "A1|A9|A6".

Sie erwähnen auch, dass es viele Muster gibt. Angenommen, sie befinden sich in einem Vektor

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

Dann können Sie Ihren regulären Ausdruck direkt mit pasteund erstellen collapse = "|".

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

Gibt es eine Möglichkeit, dies zu tun, wenn Ihre Liste von Zeichenfolgen Regex-Operatoren als Interpunktion enthält?
user124123

@ user1987097 Es sollte genauso funktionieren, mit oder ohne andere Regex-Operatoren. Hatten Sie ein bestimmtes Beispiel, für das dies nicht funktioniert hat?
Brian Diggs

@ user1987097 Verwenden Sie 2 Backslahes vor einem Punkt oder einer Klammer. Der erste Backslash ist ein Escape-Zeichen, um das zweite zu interpretieren, das zum Deaktivieren des Operators erforderlich ist.
mbh86

3
Die Verwendung von Regex für genaue Übereinstimmungen erscheint mir gefährlich und kann zu unerwarteten Ergebnissen führen. Warum nicht einfach toMatch %in% myfile$Letter?
David Arenburg

@ user4050 Kein besonderer Grund. Die Version in der Frage hatte es und ich habe es wahrscheinlich nur durchgeführt, ohne darüber nachzudenken, ob es notwendig war.
Brian Diggs

33

Gute Antworten, aber vergessen Sie nicht filter()von 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
Ich denke, das greplfunktioniert mit jeweils einem Muster (wir brauchen einen Vektor mit der Länge 1), wir haben 3 Muster (Vektor mit der Länge 3), also können wir sie mit einem kombinieren, indem wir ein für Grepl geeignetes Trennzeichen verwenden - |versuchen Sie Ihr Glück mit anderen :)
Adamm

3
Oh, ich verstehe es jetzt. Es ist also eine komprimierte Möglichkeit, etwas wie A1 | auszugeben A2 Wenn man also alle Bedingungen haben möchte, dann wäre der Zusammenbruch mit einem & Zeichen, cool danke.
Ahdee

1
Hallo, die Verwendung )|(zum Trennen von Mustern könnte dies robuster machen : paste0("(", paste(patterns, collapse=")|("),")"). Leider wird es auch etwas weniger elegant. Dies führt zu einem Muster (A1)|(A9)|(A6).
Fabern

14

Das sollte funktionieren:

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

Oder noch einfacher:

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

11
%like%befindet sich nicht in Basis R, daher sollten Sie angeben, welche Pakete für die Verwendung erforderlich sind.
Gregor Thomas

1
Für andere, die sich diese Antwort ansehen, %like%ist sie Teil des data.tablePakets. Auch ähnlich data.tablesind like(...), %ilike%und %flike%.
Steveb

8

Basierend auf Brian Diggs Beitrag gibt es hier zwei hilfreiche Funktionen zum Filtern von Listen:

#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

Haben Sie die match()oder charmatch()Funktionen ausprobiert ?

Anwendungsbeispiel:

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

1
Eine Sache, die zu beachten matchist, ist, dass es keine Muster verwendet, sondern eine genaue Übereinstimmung erwartet.
Steveb

5

Nicht sicher, ob diese Antwort bereits erschienen ist ...

Für das jeweilige Muster in der Frage können Sie dies einfach mit einem einzigen grep()Aufruf tun.

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

4

Zur Antwort von Brian Diggs hinzufügen.

Bei einer anderen Verwendung von grepl wird ein Datenrahmen zurückgegeben, der alle Ihre Werte enthält.

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

Vielleicht ein bisschen sauberer ... vielleicht?


2

Nehmen Sie die Räume weg. Dann mach's:

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

1

Verwendung der 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

Ich schlage vor, ein kleines Skript zu schreiben und mehrere Suchvorgänge mit Grep durchzuführen. Ich habe nie einen Weg gefunden, nach mehreren Mustern zu suchen, und glauben Sie mir, ich habe gesucht!

So wie Ihre Shell-Datei mit einer eingebetteten Zeichenfolge:

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

Führen Sie dann myshell.sh aus.

Wenn Sie in der Lage sein möchten, die Zeichenfolge in der Befehlszeile zu übergeben, gehen Sie folgendermaßen mit einem Shell-Argument vor - dies ist übrigens die Bash-Notation:

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

Und so weiter.

Wenn viele Muster übereinstimmen, können Sie sie in eine for-Schleife einfügen.


Vielen Dank, ChrisBean. Die Muster sind tatsächlich viele, und vielleicht wäre es dann besser, eine Datei zu verwenden. Ich bin neu in BASH, aber vielleicht sollte so etwas funktionieren ... #! / Bin / bash für i in 'pattern.txt' echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j wenn [$ j -eq o] dann echo $ i >> match.txt fi done
user971102

funktioniert nicht ... die Fehlermeldung lautet "[grep: Befehl nicht gefunden" ... Ich habe grep im Ordner / bin und / bin befindet sich auf meinem $ PATH ... Ich bin mir nicht sicher, was passiert ... Können Sie bitte helfen?
user971102
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.