Van egy “MATCH” nevű oszlop egy adatkeretben és egy minták listája “PATTERN”.
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
A grepl használatával szeretném összehasonlítani a MATCH oszlopot a PATTERN-rel, ha igaz, akkor alkalmazni fogom a függvényeimet. A kívánt eredmény az “ABC”, az “ABC” és az “ABC abc” egyezés lenne. Ezt a kódot használtam:
df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...
Hibát kapok:
"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used"
I megértem, hogy a grepl-et nem használhatom a vektorok listájára. Van valami megoldás a megoldására?
Megjegyzések
Válasz
TL; DR:
grepl
arra számít, hogy az első argumentuma karakterlánc (1. hosszúság), nem vektor. Ezt megoldhatjasapply
éslapply
(lásd alább), de jobban szolgál, ha egyetlen reguláris kifejezést használ, amely captu res, amit meg akar illeszteni adf1.MATCH
mezőben, és egyáltalán ne használja adf2.PATTERN
elemet. Ez a második lehetőség sokkal gyorsabb (ha kevésbé intelligens) egy nagy adathalmaz esetében. Az ilyen típusú munkához érdemes megtanulni, hogyan használjuk ki a reguláris kifejezéseket teljes mértékben.
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
Magyarázat
A grepl
dokumentációja a következő felhasználást mutatja:
grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
Az pattern
argumentum az első, és ennek az argumentumnak karakterláncnak (egy elemnek) kell lennie. A (z) df1.MATCH
szolgáltatást adja meg ennek az argumentumnak, amely vektor.
Használhatjuk a sapply
-t a grepl
a df1.MATCH
minden eleméhez.
sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE
Azonban nézze meg a kimenetet! Valószínűleg nem akartál mátrixot. Mi történik, ha a grepl
futtatjuk a df1.MATCH
első elemét?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
Vektorot kapunk, mert grepl
ellenőrzi a ABC
elemeket a df2.PATTERN
. A szűréshez hasznos logikai vektor megszerzéséhez vissza kell adnia egy df1.MATCH
azonos hosszúságú logikai vektort. Kétféleképpen látom.
1. módszer: Használja a
any
Mivel tudni szeretné, hogy a (z) df1.MATCH
elem mely elemei illeszkednek a df2.PATTERN
elemhez, Ön használhatja a any
parancsot, amely visszaadja TRUE
ha argumentumában bármely elem TRUE
. Egy kicsit más szintaxisra van szükségünk ahhoz, hogy ez működjön. Be kell csomagolnunk a grepl
elemet a lapply
fájlba, hogy három vektorból álló listát készítsünk (egyet a df1.MATCH1
), amely a sapply
csomagolásba csomagolva any
. Ha csak a következőt használjuk: sapply
, akkor a any
csak egy értéket ad vissza, mivel van mátrixbemenetünk.
any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE
2. módszer: Írjon jobb szabályos kifejezést.
A (z) df1.MATCH
tartalmát össze kell hangolni a lehetséges értékekkel, amelyek úgy néznek ki, mint abc
, ABC
, ABC ABC
, vagy ABC abc
stb. Mindezeket egyetlen regex karaktersorozatba foglalhatja. A kívánt karakterlánc
"^((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
Ezután használja a grepl
elemet a ignore.case = TRUE
:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE
Összehasonlítás
Nagy adatkészletben ezek egyike gyorsabban fog teljesíteni. Tudjuk meg. Az összehasonlító eredmények a gép erőforrásaitól függően változnak.
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
Úgy tűnik, hogy a továbbfejlesztett regex módszer lényegesen gyorsabb. Ez azért van, mert soronként csak egy műveletet hajt végre (grepl
) a szűrés előtt. A másik módszer soronként négy műveletet hajt végre: lapply
háromszor hajt végre grepl
-et (egyet a df2.PATTERN
és sapply
minden eleméhez, majd a any
elemet végrehajtja az egyes listaelemekhez (minden sorhoz).
Megjegyzések
- A
str_detect
használata apackage::stringr
szolgáltatásból hasonlóan agrepl
megközelítéshez:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))
df1.MATCH <- c("ABC", "abc" ,"ABC")
helyett a az utolsó karakterlánc"BCD"
?