Mam kolumnę o nazwie „MATCH” w ramce danych i listę wzorców o nazwie „WZÓR”.

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

Chcę użyć grepl do porównania kolumny MATCH z WZORCIEM, jeśli prawda, zastosuję moje funkcje. Żądanym wynikiem byłoby dopasowanie „ABC” do „ABC” i „ABC abc”. Oto kod, którego użyłem:

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

Pojawia się błąd:

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

I rozumiem, że nie mogę użyć grepl do listy wektorów. Czy istnieje sposób, aby to rozwiązać?

Komentarze

  • Mogę użyć filtra (grepl (wklej (df1.MATCH, collapse = " | "), df2.PATTERN)) i to działa w tym przykładzie. moja rzeczywista ramka danych ma ~ 1 mln wierszy i podczas korzystania z tego kodu wystąpił błąd.
  • Czy w pierwszym wierszu przykładu kodu powinien znajdować się tekst df1.MATCH <- c("ABC", "abc" ,"ABC") zamiast ostatni ciąg to "BCD"?

Odpowiedź

TL; DR: grepl oczekuje, że pierwszym argumentem będzie łańcuch (długość 1), a nie wektor. Możesz rozwiązać ten problem za pomocą kombinacji sapply i lapply (patrz poniżej), ale lepiej jest używać pojedynczego wyrażenia regularnego, res to, co chcesz dopasować w df1.MATCH i w ogóle nie używaj df2.PATTERN. Ta druga opcja jest znacznie szybsza (jeśli mniej inteligentna) w przypadku dużego zestawu danych. Przy tego typu pracach warto nauczyć się jak w pełni wykorzystywać wyrażenia regularne.

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

Wyjaśnienie

Dokumentacja grepl przedstawia następujące użycie:

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

Argument pattern jest pierwszym, a ten argument powinien być łańcuchem (jednym elementem). Dostarczasz df1.MATCH do tego argumentu, który jest wektorem.

Możemy użyć sapply, aby zastosować grepl do każdego elementu df1.MATCH.

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

Jednak spójrz na wynik! Prawdopodobnie nie chciałeś matrycy. Co się stanie, gdy uruchomimy Twój grepl jako pierwszy element df1.MATCH?

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

Otrzymujemy wektor, ponieważ grepl sprawdza ABC z każdym elementem df2.PATTERN. Aby uzyskać użyteczny wektor logiczny do filtrowania, musisz zwrócić wektor logiczny o tej samej długości co df1.MATCH. Widzę dwa sposoby, aby to zrobić.

Metoda 1: Użyj any

Ponieważ chcesz wiedzieć, które elementy w df1.MATCH pasują do dowolnego elementu w df2.PATTERN, musisz może użyć any, co zwraca TRUE, jeśli którykolwiek element w jego argumentach to TRUE. Aby to zadziałało, potrzebujemy trochę innej składni. Musimy zawinąć grepl w lapply, aby utworzyć listę trzech wektorów (po jednym na każdy element w df1.MATCH1), który trafia do sapply opakowanego any. Jeśli użyjemy tylko sapply, any zwróci tylko jedną wartość, ponieważ mamy dane wejściowe macierzowe.

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

Metoda 2: Napisz lepsze wyrażenie regularne.

Chcesz dopasować zawartość df1.MATCH do możliwych wartości, które wyglądają jak abc, ABC , ABC ABC lub ABC abc itp. Możesz to wszystko ująć w jednym ciągu wyrażenia regularnego. Żądany ciąg to

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

Następnie użyj grepl z ignore.case = TRUE:

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

Benchmarking

W przypadku dużego zbioru danych jeden z nich będzie działać szybciej. Przekonajmy się. Wyniki testu porównawczego będą się różnić w zależności od zasobów komputera.

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 

Wygląda na to, że ulepszona metoda wyrażeń regularnych jest znacznie szybsza. Dzieje się tak dlatego, że przed filtrowaniem wykonuje tylko jedną operację na wiersz (grepl). Druga metoda wykonuje cztery operacje na wiersz: lapply wykonuje grepl trzy razy (po jednym na każdy element df2.PATTERN, a następnie sapply wykonuje any dla każdego elementu listy (każdego wiersza).

Komentarze

  • Używając str_detect z package::stringr wykonano podobnie do podejścia grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *