Am o coloană numită „MATCH” într-un cadru de date și o listă de șabloane numită „PATTERN”.

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

Vreau să folosesc grepl pentru a compara coloana MATCH cu PATTERN, dacă este adevărat, îmi voi aplica funcțiile. Rezultatul dorit ar fi „ABC” se potrivește cu „ABC” și „ABC abc”. Acesta este codul pe care l-am folosit:

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

Am primit o eroare:

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

I înțeleg că nu pot folosi grepl într-o listă de vectori. Există vreo modalitate de a o rezolva?

Comentarii

  • Pot folosi filtrul (grepl (paste (df1.MATCH, collapse = " | "), df2.PATTERN)) și care funcționează pentru acest exemplu. Cu toate acestea, cadrul meu de date real are ~ 1M rânduri și am primit erori la utilizarea acestui cod.
  • În primul rând al exemplului de cod, ar trebui să scrie df1.MATCH <- c("ABC", "abc" ,"ABC") în loc de ultimul șir fiind "BCD"?

Răspuns

TL; DR: grepl se așteaptă ca primul său argument să fie un șir (lungime 1), nu un vector. Puteți rezolva acest lucru cu combinații de sapply și lapply (vezi mai jos), dar ești mai bine servit folosind o singură expresie regulată care captu rezoluți ceea ce doriți să se potrivească în df1.MATCH și nu utilizați deloc df2.PATTERN. Această a doua opțiune este mult mai rapidă (dacă este mai puțin inteligentă) pentru un set mare de date. Pentru acest tip de lucrări, merită să învățați cum să folosiți expresiile regulate la potențialul lor maxim.

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

Explicație

Documentația pentru grepl arată următoarea utilizare:

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

Argumentul pattern este primul, iar acest argument ar trebui să fie un șir (un element). Furnizați df1.MATCH acestui argument, care este un vector.

Am putea folosi sapply pentru a aplica grepl la fiecare element din df1.MATCH.

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

Cu toate acestea, uitați-vă la ieșire! Probabil că nu ați dorit o matrice. Ce se întâmplă când executăm grepl unul doar primul element din df1.MATCH?

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

Obținem un vector deoarece grepl verifică ABC împotriva fiecărui element din df2.PATTERN. Pentru a obține un vector logic util pentru filtrare, trebuie să returnați un vector logic de aceeași lungime ca df1.MATCH. Văd două moduri de a o face.

Metoda 1: Utilizați any

Deoarece doriți să știți ce elemente din df1.MATCH se potrivesc cu orice element din df2.PATTERN, poate folosi any, care returnează TRUE dacă vreun element din argumentele sale este TRUE. Avem nevoie de o sintaxă puțin diferită pentru a face acest lucru să funcționeze. Trebuie să înfășurăm grepl în lapply pentru a face o listă de trei vectori (unul pentru fiecare element din df1.MATCH1) care se alimentează în sapply înfășurat any. Dacă folosim doar sapply, any va returna o singură valoare deoarece avem o intrare matricială.

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

Metoda 2: scrieți o expresie regulată mai bună.

Doriți să potriviți conținutul df1.MATCH cu valorile posibile care arată ca abc, ABC , ABC ABC sau ABC abc etc. Puteți cuprinde toate acestea într-un singur șir regex. Șirul pe care îl doriți este

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

Apoi utilizați grepl cu ignore.case = TRUE:

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

Benchmarking

Într-un set de date mare, unul dintre acestea va funcționa mai repede. Să aflăm. Rezultatele dvs. de referință vor varia în funcție de resursele mașinii dvs.

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 

Se pare că metoda regex îmbunătățită este semnificativ mai rapidă. Asta pentru că efectuează o singură operație pe rând (grepl) înainte de filtrare. Cealaltă metodă efectuează patru operații pe rând: lapply efectuează grepl de trei ori (câte una pentru fiecare element din df2.PATTERN și sapply atunci efectuează any pentru fiecare element de listă (fiecare rând).

Comentarii

  • Folosirea str_detect din package::stringr în mod similar abordării grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *