Jeg har en kolonne som heter «MATCH» i en dataramme og en liste over mønstre som heter «MØNSTER».

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

Jeg vil bruke grepl til å sammenligne MATCH-kolonne med MØNSTER, hvis det er sant, vil jeg bruke funksjonene mine. Det ønskede resultatet vil være «ABC» samsvarer med «ABC» og «ABC abc». Dette er koden jeg brukte:

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

Jeg får feil:

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

I forstår at jeg ikke kan bruke grepl til en liste over vektorer. Er det noen måte å løse det på?

Kommentarer

  • Jeg kan bruke filter (grepl (lim inn (df1.MATCH, kollaps = " | "), df2.PATTERN)) og det fungerer for dette eksemplet. Imidlertid, min virkelige dataramme har ~ 1M rader, og jeg fikk feil når jeg brukte denne koden.
  • På den første raden i kodeeksemplet ditt, skulle det stå df1.MATCH <- c("ABC", "abc" ,"ABC") i stedet for siste streng er "BCD"?

Svar

TL; DR: grepl forventer at det første argumentet er en streng (lengde 1), ikke en vektor. Du kan løse dette med kombinasjoner av sapply og lapply (se nedenfor), men du får bedre betjening ved å bruke et enkelt regulært uttrykk som captu res det du vil matche i df1.MATCH og ikke bruk df2.PATTERN i det hele tatt. Dette andre alternativet er mye raskere (hvis mindre intelligent) for et stort datasett. For denne typen arbeid er det verdt å lære å bruke vanlige uttrykk til sitt fulle potensiale.

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

Forklaring

Dokumentasjonen for grepl viser følgende bruk:

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

Argumentet pattern er først, og dette argumentet skal være en streng (ett element). Du gir df1.MATCH til dette argumentet, som er en vektor.

Vi kan bruke sapply til å bruke grepl til hvert 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 

Se imidlertid på utdataene! Du vil sannsynligvis ikke ha en matrise. Hva skjer når vi kjører grepl ett bare det første elementet i df1.MATCH?

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

Vi får en vektor fordi grepl sjekker ABC mot hvert element i df2.PATTERN. For å få en nyttig logisk vektor for filtrering, må du returnere en logisk vektor med samme lengde som df1.MATCH. Jeg ser to måter å gjøre det på.

Metode 1: Bruk any

Siden du vil vite hvilke elementer i df1.MATCH samsvarer med alle elementene i df2.PATTERN kan bruke any, som returnerer TRUE hvis noe element i argumentene er TRUE. Vi trenger litt annen syntaks for å få dette til å fungere. Vi må pakke grepl i lapply for å lage en liste over tre vektorer (en for hvert element i df1.MATCH1) som mates inn i sapply pakket any. Hvis vi bare bruker sapply, vil any bare returnere en verdi siden vi har en matriseinngang.

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

Metode 2: Skriv et bedre regulært uttrykk.

Du vil matche innholdet i df1.MATCH mot mulige verdier som ser ut som abc, ABC , ABC ABC, eller ABC abc osv. Du kan inkludere alt dette i en enkelt regex-streng. Strengen du vil ha er

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

Bruk deretter grepl med ignore.case = TRUE:

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

Benchmarking

I et stort datasett vil en av disse fungere raskere. La oss finne ut. Resultatene dine for referanser vil variere etter maskinens ressurser.

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 til at den forbedrede regex-metoden er betydelig raskere. Dette skyldes at den bare utfører en operasjon per rad (grepl) før filtrering. Den andre metoden utfører fire operasjoner per rad: lapply utfører grepl tre ganger (en for hvert element i df2.PATTERN, og sapply deretter utfører any for hvert listeelement (hver rad).

Kommentarer

  • Bruk str_detect fra package::stringr utfører på samme måte som grepl tilnærming: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *