Jeg har en kolonne med navnet “MATCH” i en dataramme og en liste over mønstre navngivet “MØNSTER”.

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

Jeg vil bruge grepl til at sammenligne MATCH-kolonnen med MØNSTER, hvis den er sand, vil jeg anvende mine funktioner. Det ønskede resultat ville være “ABC” matcher “ABC” og “ABC abc”. Dette er den kode, jeg brugte:

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

Jeg får fejl:

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

I forstår, jeg kan ikke bruge grepl til en liste over vektorer. Er der nogen måde at løse det på?

Kommentarer

  • Jeg kan bruge filter (grepl (indsæt (df1.MATCH, skjul = " | "), df2.PATTERN)), og det fungerer i dette eksempel. min rigtige dataframe har ~ 1M rækker, og jeg har fået en fejl, når jeg bruger denne kode.
  • På den første række i dit kodeeksempel skal det stå df1.MATCH <- c("ABC", "abc" ,"ABC") i stedet for sidste streng er "BCD"?

Svar

TL; DR: grepl forventer, at dets første argument er en streng (længde 1), ikke en vektor. Du kan løse dette med kombinationer af sapply og lapply (se nedenfor), men du er bedre tjent med et enkelt regulært udtryk, der captu res hvad du vil matche i df1.MATCH og slet ikke bruge df2.PATTERN. Denne anden mulighed er meget hurtigere (hvis mindre intelligent) for et stort datasæt. Til denne type arbejde er det værd at lære at bruge regulære udtryk til deres fulde potentiale.

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

Forklaring

Dokumentationen til grepl viser følgende anvendelse:

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

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

Vi kunne bruge sapply til at anvende 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 dog på output! Du ønskede sandsynligvis ikke en matrix. Hvad sker der, når vi kører din grepl et bare det første element i df1.MATCH?

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

Vi får en vektor, fordi grepl kontrollerer ABC mod hvert element i df2.PATTERN. For at få en nyttig logisk vektor til filtrering skal du returnere en logisk vektor med samme længde som df1.MATCH. Jeg ser to måder at gøre det på.

Metode 1: Brug any

Da du vil vide, hvilke elementer i df1.MATCH matcher alle elementer i df2.PATTERN kan bruge any, som returnerer TRUE hvis et element i dets argumenter er TRUE. Vi har brug for en lidt anden syntaks for at få dette til at fungere. Vi er nødt til at pakke grepl i lapply for at lave en liste over tre vektorer (en for hvert element i df1.MATCH1), der føder til sapply indpakket any. Hvis vi bare bruger sapply, returnerer any kun én værdi, da vi har en matrixindgang.

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 udtryk.

Du vil matche indholdet af df1.MATCH mod mulige værdier, der ligner abc, ABC , ABC ABC eller ABC abc osv. Alt dette kan du omfatte i en enkelt regex-streng. Den ønskede streng 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 

Brug derefter grepl med ignore.case = TRUE:

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

Benchmarking

I et stort datasæt fungerer en af disse hurtigere. Lad os finde ud af. Dine benchmarkresultater vil variere efter din maskins ressourcer.

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 ud til, at den forbedrede regex-metode er betydeligt hurtigere. Det skyldes, at det kun udfører en operation pr. Række (grepl) inden filtrering. Den anden metode udfører fire operationer pr. Række: lapply udfører grepl tre gange (en for hvert element i df2.PATTERN, og sapply derefter udfører any for hvert listeelement (hver række).

Kommentarer

  • Brug af str_detect fra package::stringr udfører på samme måde som grepl tilgang: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *