Mám v datovém rámci sloupec s názvem „MATCH“ a seznam vzorů s názvem „PATTERN“.
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
Chci použít grepl k porovnání sloupce MATCH s PATTERN, pokud je to pravda, použiji své funkce. Požadovaným výsledkem by bylo „ABC“ odpovídá „ABC“ a „ABC abc“. Toto je kód, který jsem použil:
df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...
Zobrazuje se mi chyba:
"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used"
I pochopím, že nemohu použít grepl na seznam vektorů. Existuje nějaký způsob, jak to vyřešit?
Komentáře
odpověď
TL; DR:
grepl
očekává, že jeho první argument bude řetězec (délka 1), ne vektor. Můžete to vyřešit kombinacísapply
alapply
(viz níže), ale lépe vám bude sloužit jediný regulární výraz, který res, co chcete porovnat vdf1.MATCH
a vůbec nepoužívejtedf2.PATTERN
. Tato druhá možnost je u velké datové sady mnohem rychlejší (pokud je méně inteligentní). U tohoto typu práce stojí za to naučit se používat regulární výrazy naplno.
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
Vysvětlení
Dokumentace pro grepl
ukazuje následující použití:
grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
pattern
argument je první a tento argument by měl být řetězec (jeden prvek). Poskytujete df1.MATCH
tomuto argumentu, který je vektorový.
K použití iv bychom mohli použít sapply
id = „f8e8fef57c“>
každému prvku df1.MATCH
.
sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE
Podívejte se však na výstup! Pravděpodobně jste nechtěli matici. Co se stane, když spustíme váš grepl
jen první prvek df1.MATCH
?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
Dostaneme vektor, protože grepl
kontroluje ABC
proti každému prvku df2.PATTERN
. Chcete-li získat užitečný logický vektor pro filtrování, musíte vrátit logický vektor stejné délky jako df1.MATCH
. Vidím dva způsoby, jak to udělat.
Metoda 1: Použijte
any
Protože chcete vědět, které prvky v df1.MATCH
odpovídají všem prvkům v df2.PATTERN
, může použít any
, který vrátí TRUE
, pokud je některý prvek v jeho argumentech TRUE
. Aby to fungovalo, potřebujeme trochu jinou syntaxi. Musíme grepl
zabalit do lapply
, abychom vytvořili seznam tří vektorů (jeden pro každý prvek v df1.MATCH1
), který se přivádí do sapply
zabaleného any
. Pokud použijeme sapply
, any
vrátí pouze jednu hodnotu, protože máme maticový vstup.
any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE
Metoda 2: Napište lepší regulární výraz.
Chcete porovnat obsah df1.MATCH
s možnými hodnotami, které vypadají jako abc
, ABC
, ABC ABC
nebo ABC abc
atd. Toto vše můžete zahrnout do jediného řetězce regulárního výrazu. Požadovaný řetězec je
"^((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
Poté použijte grepl
s ignore.case = TRUE
:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE
Benchmarking
Ve velkém souboru dat bude jeden z nich fungovat rychleji. Pojďme to zjistit. Výsledky benchmarku se budou lišit podle zdrojů vašeho stroje.
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
Vypadá to, že vylepšená metoda regulárního výrazu je podstatně rychlejší. Je to proto, že před filtrováním provádí pouze jednu operaci na řádek (grepl
). Druhá metoda provádí čtyři operace na řádek: lapply
provádí grepl
třikrát (jeden pro každý prvek df2.PATTERN
a sapply
poté provádí any
pro každý prvek seznamu (každý řádek).
Komentáře
- Použití
str_detect
zpackage::stringr
provádí podobně jako přístupgrepl
:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))
df1.MATCH <- c("ABC", "abc" ,"ABC")
spíše než poslední řetězec je"BCD"
?