Ik heb een kolom met de naam “MATCH” in een dataframe en een lijst met patronen met de naam “PATTERN”.

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

Ik wil grepl gebruiken om de MATCH-kolom met PATTERN te vergelijken, indien waar, zal ik mijn functies toepassen. Het gewenste resultaat is “ABC” komt overeen met “ABC” en “ABC abc”. Dit is de code die ik heb gebruikt:

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

Ik krijg een foutmelding:

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

I begrijp dat ik “grepl niet kan gebruiken voor een lijst met vectoren. Is er een manier om het op te lossen?

Reacties

  • Ik kan filter (grepl (plak (df1.MATCH, collapse = " | "), df2.PATTERN)) en dat werkt voor dit voorbeeld. mijn echte dataframe heeft ~ 1 miljoen rijen en ik kreeg een foutmelding bij het gebruik van deze code.
  • Op de eerste rij van uw codevoorbeeld, moet er df1.MATCH <- c("ABC", "abc" ,"ABC") staan in plaats van de laatste string is "BCD"?

Antwoord

TL; DR: grepl verwacht dat het eerste argument een string (lengte 1) is, niet een vector. Je kunt dit oplossen met combinaties van sapply en lapply (zie hieronder), maar u kunt beter één reguliere expressie gebruiken die captu res wat je wilt matchen in df1.MATCH en gebruik helemaal geen df2.PATTERN. Deze tweede optie is veel sneller (zij het minder intelligent) voor een grote dataset. Voor dit soort werk is het de moeite waard om te leren hoe u reguliere expressies optimaal kunt gebruiken.

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

Uitleg

De documentatie voor grepl toont het volgende gebruik:

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

Het pattern -argument is het eerste, en dit argument zou een string (één element) moeten zijn. U geeft df1.MATCH aan dit argument, dat een vector is.

We kunnen sapply gebruiken om grepl naar elk element van df1.MATCH.

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

Kijk echter naar de uitvoer! Waarschijnlijk wilde u geen matrix. Wat gebeurt er als we uw grepl uitvoeren op slechts het eerste element van df1.MATCH?

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

We krijgen een vector omdat grepl ABC controleert op elk element van df2.PATTERN. Om een bruikbare logische vector voor filteren te krijgen, moet u een logische vector met dezelfde lengte als df1.MATCH retourneren. Ik zie twee manieren om het te doen.

Methode 1: gebruik any

Aangezien u wilt weten welke elementen in df1.MATCH overeenkomen met elementen in df2.PATTERN, kan any gebruiken, wat TRUE retourneert als een element in zijn argumenten TRUE is. We hebben een iets andere syntaxis nodig om dit te laten werken. We moeten grepl in lapply plaatsen om een lijst van drie vectoren te maken (één voor elk element in df1.MATCH1) die wordt ingevoerd in sapply verpakt any. Als we sapply gebruiken, zal any slechts één waarde retourneren aangezien we een matrixinvoer hebben.

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

Methode 2: schrijf een betere reguliere expressie.

U wilt de inhoud van df1.MATCH vergelijken met mogelijke waarden die eruit zien als abc, ABC , ABC ABC, of ABC abc, enz. Je kunt dit allemaal samenvatten in een enkele regex-string. De gewenste string is

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

Gebruik vervolgens grepl met ignore.case = TRUE:

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

Benchmarking

In een grote dataset zal een van deze sneller werken. Laten we eens kijken. Uw benchmarkresultaten zullen variëren afhankelijk van de bronnen van uw computer.

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 

Het lijkt erop dat de verbeterde regex-methode aanzienlijk sneller is. Dat komt omdat het slechts één bewerking per rij uitvoert (grepl) voordat wordt gefilterd. De andere methode voert vier bewerkingen per rij uit: lapply voert grepl drie keer uit (één voor elk element van df2.PATTERN, en sapply voert any uit voor elk lijstelement (elke rij).

Reacties

  • Gebruik van str_detect van package::stringr presteert vergelijkbaar met de grepl benadering: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *