Tengo una columna llamada «COINCIDIR» en un marco de datos y una lista de patrones nombrados «PATRÓN».
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
Quiero usar grepl para comparar la columna COINCIDIR con PATRÓN, si es verdadero, aplicaré mis funciones. El resultado deseado sería «ABC» coincide con «ABC» y «ABC abc». Este es el código que utilicé:
df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...
Recibo el error:
"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used"
I Entiendo que no puedo usar grepl para una lista de vectores. ¿Hay alguna forma de resolverlo?
Comentarios
Respuesta
TL; DR:
grepl
espera que su primer argumento sea una cadena (longitud 1), no un vector. Puede resolver esto con combinaciones desapply
ylapply
(ver más abajo), pero es mejor utilizar una sola expresión regular que captu resuelva lo que desea hacer coincidir endf1.MATCH
y no usedf2.PATTERN
en absoluto. Esta segunda opción es mucho más rápida (aunque menos inteligente) para un gran conjunto de datos. Para este tipo de trabajo, vale la pena aprender a usar expresiones regulares en todo su potencial.
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
Explicación
La documentación de grepl
muestra el siguiente uso:
grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
El argumento pattern
es el primero, y este argumento debe ser una cadena (un elemento). Está proporcionando df1.MATCH
a este argumento, que es un vector.
Podríamos usar sapply
para aplicar grepl
a cada elemento de df1.MATCH
.
sapply(df1.MATCH, grepl, x = df2.PATTERN) ABC abc BCD [1,] TRUE FALSE FALSE [2,] FALSE TRUE FALSE [3,] TRUE TRUE FALSE
Sin embargo, ¡mira el resultado! Probablemente no querías una matriz. ¿Qué sucede cuando ejecutamos su grepl
solo el primer elemento de df1.MATCH
?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
Obtenemos un vector porque grepl
está comparando ABC
con cada elemento de df2.PATTERN
. Para obtener un vector lógico útil para el filtrado, debe devolver un vector lógico de la misma longitud que df1.MATCH
. Veo dos formas de hacerlo.
Método 1: Use
any
Dado que desea saber qué elementos de df1.MATCH
coinciden con los elementos de df2.PATTERN
, puede usar any
, que devuelve TRUE
si algún elemento en sus argumentos es TRUE
. Necesitamos una sintaxis un poco diferente para que esto funcione. Necesitamos envolver grepl
en lapply
para hacer una lista de tres vectores (uno para cada elemento en df1.MATCH1
) que se alimenta en sapply
envuelto any
. Si usamos sapply
, any
solo devolverá un valor ya que tenemos una entrada de matriz.
any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE
Método 2: Escriba una expresión regular mejor.
Desea hacer coincidir el contenido de df1.MATCH
con valores posibles que se parecen a abc
, ABC
, ABC ABC
, o ABC abc
, etc. Puede incluir todo esto en una sola cadena de expresiones regulares. La cadena que desea es
"^((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
Luego use grepl
con ignore.case = TRUE
:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE
Evaluación comparativa
En un conjunto de datos grande, uno de estos funcionará más rápido. Averigüémoslo. Los resultados de su evaluación comparativa variarán según los recursos de su máquina.
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
Parece que el método regex mejorado es significativamente más rápido. Eso «se debe a que solo realiza una operación por fila (grepl
) antes de filtrar. El otro método realiza cuatro operaciones por fila: lapply
está realizando grepl
tres veces (una para cada elemento de df2.PATTERN
y sapply
luego realiza any
para cada elemento de la lista (cada fila).
Comentarios
- El uso de
str_detect
depackage::stringr
realiza de manera similar al enfoquegrepl
:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))
df1.MATCH <- c("ABC", "abc" ,"ABC")
en lugar de la última cadena es"BCD"
?