Jag har en kolumn med namnet ”MATCH” i en dataram och en lista med mönster som heter ”PATTERN”.

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

Jag vill använda grepl för att jämföra MATCH-kolumnen med PATTERN, om det är sant kommer jag att tillämpa mina funktioner. Det önskade resultatet skulle vara ”ABC” matchar ”ABC” och ”ABC abc”. Det här är koden jag använde:

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

Jag får fel:

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

I förstår att jag inte kan använda grepl till en lista med vektorer. Finns det något sätt att lösa det?

Kommentarer

  • Jag kan använda filter (grepl (klistra in (df1.MATCH, kollaps = " | "), df2.PATTERN)) och det fungerar för detta exempel. min riktiga dataram har ~ 1 M rader och jag fick fel när jag använde den här koden.
  • På den första raden i ditt kodexempel ska det stå df1.MATCH <- c("ABC", "abc" ,"ABC") snarare än sista strängen är "BCD"?

Svar

TL; DR: grepl förväntar sig att dess första argument är en sträng (längd 1), inte en vektor. Du kan lösa detta med kombinationer av sapply och lapply (se nedan), men du tjänar bättre med ett enda reguljärt uttryck som captu res vad du vill matcha i df1.MATCH och inte alls använda df2.PATTERN. Det andra alternativet är mycket snabbare (om det är mindre intelligent) för en stor datamängd. För denna typ av arbete är det värt att lära sig att använda reguljära uttryck till sin fulla potential.

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

Förklaring

Dokumentationen för grepl visar följande användning:

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

Argumentet pattern är först och detta argument bör vara en sträng (ett element). Du tillhandahåller df1.MATCH till detta argument, som är en vektor.

Vi kan använda sapply för att tillämpa grepl till varje element i df1.MATCH.

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

Titta dock på utdata! Du ville antagligen inte ha en matris. Vad händer när vi kör din grepl en bara det första elementet i df1.MATCH?

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

Vi får en vektor eftersom grepl kontrollerar ABC mot varje element i df2.PATTERN. För att få en användbar logisk vektor för filtrering måste du returnera en logisk vektor med samma längd som df1.MATCH. Jag ser två sätt att göra det.

Metod 1: Använd any

Eftersom du vill veta vilka element i df1.MATCH matchar alla element i df2.PATTERN, du kan använda any, som returnerar TRUE om något element i dess argument är TRUE. Vi behöver lite olika syntax för att detta ska fungera. Vi måste slå in grepl i lapply för att skapa en lista med tre vektorer (en för varje element i df1.MATCH1) som matas in i sapply insvept any. Om vi bara använder sapply kommer any bara att returnera ett värde eftersom vi har en matrisingång.

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

Metod 2: Skriv ett bättre reguljärt uttryck.

Du vill matcha innehållet i df1.MATCH mot möjliga värden som ser ut som abc, ABC , ABC ABC, eller ABC abc, etc. Du kan omfatta allt detta i en enda regexsträng. Strängen du vill ha är

"^((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 

Använd sedan grepl med ignore.case = TRUE:

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

Benchmarking

I en stor dataset kommer en av dessa att fungera snabbare. Låt oss ta reda på det. Ditt riktmärkesresultat varierar beroende på maskinens resurser.

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 

Det ser ut som att den förbättrade regexmetoden är betydligt snabbare. Det beror på att den bara utför en operation per rad (grepl) innan filtrering. Den andra metoden utför fyra operationer per rad: lapply utför grepl tre gånger (en för varje element i df2.PATTERN och sapply sedan utför any för varje listelement (varje rad).

Kommentarer

  • Använd str_detect från package::stringr utför på samma sätt som grepl tillvägagångssätt: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *