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

  • Mohu použít filtr (grepl (paste (df1.MATCH, collapse = " | "), df2.PATTERN)) a to funguje pro tento příklad. můj skutečný datový rámec má ~ 1M řádků a při používání tohoto kódu se mi zobrazila chyba.
  • Na prvním řádku vašeho příkladu kódu by mělo být uvedeno df1.MATCH <- c("ABC", "abc" ,"ABC") spíše než poslední řetězec je "BCD"?

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 a lapply (viz níže), ale lépe vám bude sloužit jediný regulární výraz, který res, co chcete porovnat v df1.MATCH a vůbec nepoužívejte df2.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 z package::stringr provádí podobně jako přístup grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *