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

  • Puedo usar filter (grepl (pega (df1.MATCH, collapse = " | "), df2.PATTERN)) y eso funciona para este ejemplo. Sin embargo, mi marco de datos real tiene ~ 1M filas y obtuve un error al usar este código.
  • En la primera fila de su ejemplo de código, ¿debería decir df1.MATCH <- c("ABC", "abc" ,"ABC") en lugar de la última cadena es "BCD"?

Respuesta

TL; DR: grepl espera que su primer argumento sea una cadena (longitud 1), no un vector. Puede resolver esto con combinaciones de sapply y lapply (ver más abajo), pero es mejor utilizar una sola expresión regular que captu resuelva lo que desea hacer coincidir en df1.MATCH y no use df2.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 de package::stringr realiza de manera similar al enfoque grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *