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 vonsapply
undlapply
(siehe unten), aber Sie sollten besser einen einzelnen regulären Ausdruck verwenden, der captu Res, was Sie indf1.MATCH
übereinstimmen möchten, und verwenden Siedf2.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
vonpackage::stringr
wird ausgeführt Ähnlich wie beim Ansatzgrepl
:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))