Ich habe eine Spalte mit dem Namen „MATCH“ in einem Datenrahmen und eine Liste mit Mustern mit dem Namen „PATTERN“.

df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc") 

Ich möchte grepl verwenden, um die MATCH-Spalte mit PATTERN zu vergleichen. Wenn true, werde ich meine Funktionen anwenden. Das gewünschte Ergebnis wäre „ABC“ entspricht „ABC“ und „ABC abc“. Dies ist der Code, den ich verwendet habe:

df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ... 

Ich erhalte die Fehlermeldung:

"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used" 

I. Ich verstehe, dass ich grepl nicht für eine Liste von Vektoren verwenden kann. Gibt es eine Möglichkeit, es zu lösen?

Kommentare

  • Ich kann Filter (grepl) verwenden (Einfügen (df1.MATCH, Reduzieren = " | "), df2.PATTERN)) und das funktioniert für dieses Beispiel. Mein realer Datenrahmen hat ~ 1 Million Zeilen und bei der Verwendung dieses Codes ist ein Fehler aufgetreten.
  • In der ersten Zeile Ihres Codebeispiels sollte df1.MATCH <- c("ABC", "abc" ,"ABC") anstelle von “ Die letzte Zeichenfolge ist "BCD"?

Antwort

TL; DR: grepl erwartet, dass sein erstes Argument eine Zeichenfolge (Länge 1) und kein Vektor ist. Sie können dies mit Kombinationen von sapply und lapply (siehe unten), aber Sie sollten besser einen einzelnen regulären Ausdruck verwenden, der captu Res, was Sie in df1.MATCH übereinstimmen möchten, und verwenden Sie df2.PATTERN überhaupt nicht. Diese zweite Option ist für einen großen Datensatz viel schneller (wenn auch weniger intelligent). Für diese Art von Arbeit lohnt es sich zu lernen, wie man reguläre Ausdrücke voll ausschöpft.

df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE)) 

Erläuterung

Die Dokumentation für grepl zeigt die folgende Verwendung:

grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE) 

Das Argument pattern steht an erster Stelle, und dieses Argument sollte eine Zeichenfolge (ein Element) sein. Sie geben df1.MATCH für dieses Argument an, das ein Vektor ist.

Wir könnten sapply verwenden, um grepl für jedes Element von df1.MATCH.

sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE 

Schauen Sie sich jedoch die Ausgabe an! Sie wollten wahrscheinlich keine Matrix. Was passiert, wenn wir Ihre grepl ausführen, nur das erste Element von df1.MATCH?

grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE 

Wir erhalten einen Vektor, weil grepl ABC gegen jedes Element von df2.PATTERN. Um einen nützlichen logischen Vektor zum Filtern zu erhalten, müssen Sie einen logischen Vektor mit der gleichen Länge wie df1.MATCH zurückgeben. Ich sehe zwei Möglichkeiten, dies zu tun.

Methode 1: Verwenden Sie any

Da Sie wissen möchten, welche Elemente in df1.MATCH mit Elementen in df2.PATTERN übereinstimmen, sind Sie kann any verwenden, das TRUE zurückgibt, wenn ein Element in seinen Argumenten TRUE ist. Wir brauchen eine etwas andere Syntax, damit dies funktioniert. Wir müssen grepl in lapply einschließen, um eine Liste von drei Vektoren zu erstellen (einen für jedes Element in df1.MATCH1), das in sapply eingewickelt any eingespeist wird. Wenn wir nur sapply verwenden, gibt any nur einen Wert zurück, da wir eine Matrixeingabe haben.

any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE 

Methode 2: Schreiben Sie einen besseren regulären Ausdruck.

Sie möchten den Inhalt von df1.MATCH mit möglichen Werten abgleichen, die wie abc, ABC aussehen , ABC ABC oder ABC abc usw. Sie können all dies in einer einzigen Regex-Zeichenfolge zusammenfassen. Die gewünschte Zeichenfolge lautet

"^((ABC)( )*)+$" ^ # Nothing else before this (ABC) # Must contain ABC together as a group ( )* # followed by any number of spaces (including 0) ((ABC)( )*)+ # Look for the ABC (space) pattern repeated one or more times $ # And nothing else after it 

Verwenden Sie dann grepl mit ignore.case = TRUE:

grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE 

Benchmarking

In einem großen Datensatz wird einer davon schneller ausgeführt. Lassen Sie es uns herausfinden. Ihre Benchmark-Ergebnisse hängen von den Ressourcen Ihres Computers ab.

df1.MATCH <- sample(c("ABC", "abc" ,"BCD"), size = 100000, replace = TRUE) df1 <- data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc") library(rbenchmark) benchmark("any lapply" = { df1 %>% filter(sapply(lapply(df1.MATCH, grepl, x=df2.PATTERN), any) ) }, "better regex" = { df1 %>% filter(grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE)) } ) test replications elapsed relative user.self sys.self user.child sys.child 1 any lapply 100 149.13 70.678 147.67 0.39 NA NA 2 better regex 100 2.11 1.000 2.10 0.02 NA NA 

Die verbesserte Regex-Methode scheint erheblich schneller zu sein. Dies liegt daran, dass vor dem Filtern nur eine Operation pro Zeile (grepl) ausgeführt wird. Die andere Methode führt vier Operationen pro Zeile aus: lapply führt grepl dreimal aus (eines für jedes Element von df2.PATTERN und dann sapply) führt für jedes Listenelement (jede Zeile) any aus.

Kommentare

  • Die Verwendung von str_detect von package::stringr wird ausgeführt Ähnlich wie beim Ansatz grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.