Ho una colonna denominata “MATCH” in un dataframe e un elenco di pattern denominati “PATTERN”.

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

Voglio usare grepl per confrontare la colonna MATCH con PATTERN, se è vero, applicherò le mie funzioni. Il risultato desiderato sarebbe “ABC” corrisponde a “ABC” e “ABC abc”. Questo è il codice che ho usato:

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

Ottengo un errore:

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

I capisco che non posso “utilizzare grepl per un elenco di vettori. Cè un modo per risolverlo?

Commenti

  • Posso usare il filtro (grepl (incolla (df1.MATCH, collapse = " | "), df2.PATTERN)) e funziona per questo esempio. Tuttavia, il mio vero dataframe ha ~ 1 milione di righe e ho ricevuto un errore durante lutilizzo di questo codice.
  • Nella prima riga del tuo esempio di codice, dovrebbe essere indicato df1.MATCH <- c("ABC", "abc" ,"ABC") anziché lultima stringa è "BCD"?

Risposta

TL; DR: grepl si aspetta che il suo primo argomento sia una stringa (lunghezza 1), non un vettore. Puoi risolvere questo problema con combinazioni di sapply e lapply (vedi sotto), ma è meglio utilizzare ununica espressione regolare che captu Cerca ciò che desideri trovare in df1.MATCH e non utilizzare affatto df2.PATTERN. Questa seconda opzione è molto più veloce (anche se meno intelligente) per un set di dati di grandi dimensioni. Per questo tipo di lavoro, vale la pena imparare a utilizzare le espressioni regolari al massimo delle loro potenzialità.

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

Spiegazione

La documentazione per grepl mostra il seguente utilizzo:

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

Largomento pattern è il primo e questo argomento dovrebbe essere una stringa (un elemento). Stai fornendo df1.MATCH a questo argomento, che è un vettore.

Potremmo usare sapply per applicare grepl a ogni elemento di df1.MATCH.

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

Tuttavia, guarda loutput! Probabilmente non volevi una matrice. Cosa succede quando eseguiamo il tuo grepl solo il primo elemento di df1.MATCH?

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

Otteniamo un vettore perché grepl sta verificando ABC rispetto a ogni elemento di df2.PATTERN. Per ottenere un vettore logico utile per il filtraggio, è necessario restituire un vettore logico della stessa lunghezza di df1.MATCH. Vedo due modi per farlo.

Metodo 1: utilizza any

Dato che desideri sapere quali elementi in df1.MATCH corrispondono a qualsiasi elemento in df2.PATTERN, può utilizzare any, che restituisce TRUE se qualsiasi elemento nei suoi argomenti è TRUE. Abbiamo bisogno di una sintassi leggermente diversa per farlo funzionare. Dobbiamo inserire grepl in lapply per creare un elenco di tre vettori (uno per ogni elemento in df1.MATCH1) che alimenta sapply wrapping any. Se usiamo solo sapply, any restituirà solo un valore poiché abbiamo un input di matrice.

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

Metodo 2: scrivi unespressione regolare migliore.

Desideri abbinare i contenuti di df1.MATCH a possibili valori simili a abc, ABC , ABC ABC o ABC abc e così via. Puoi racchiudere tutto questo in una singola stringa regex. La stringa che desideri è

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

Quindi utilizza grepl con ignore.case = TRUE:

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

Benchmarking

In un set di dati di grandi dimensioni, uno di questi funzionerà più velocemente. Scopriamolo. I risultati del tuo benchmark varieranno in base alle risorse della tua macchina.

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 

Sembra che il metodo regex migliorato sia significativamente più veloce. Questo perché esegue una sola operazione per riga (grepl) prima del filtro. Laltro metodo esegue quattro operazioni per riga: lapply esegue grepl tre volte (una per ogni elemento di df2.PATTERN e sapply quindi esegue any per ogni elemento dellelenco (ogni riga).

Commenti

  • Lutilizzo di str_detect da package::stringr esegue in modo simile allapproccio grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *