Jai une colonne nommée « MATCH » dans un dataframe et une liste de modèles nommés « PATTERN ».
df1.MATCH <- c("ABC", "abc" ,"BCD") df1 <- as.data.frame(df1.MATCH) df2.PATTERN <- c("ABC", "abc", "ABC abc")
Je veux utiliser grepl pour comparer la colonne MATCH avec PATTERN, si cest vrai, jappliquerai mes fonctions. Le résultat souhaité serait « ABC » correspond à « ABC » et « ABC abc ». Voici le code que jai utilisé:
df1 %>% filter(grepl(df1.MATCH,df2.PATTERN ))%>% ...
Jobtiens une erreur:
"Warning message: In grepl(TXN_GROUP, parm[3]) :argument "pattern" has length > 1 and only the first element will be used"
I comprendre que je ne peux pas « t utiliser grepl pour une liste de vecteurs. Y a-t-il un moyen de le résoudre?
Commentaires
- Je peux utiliser filter (grepl (paste (df1.MATCH, collapse = " | "), df2.PATTERN)) et cela fonctionne pour cet exemple. Cependant, mon vrai dataframe a ~ 1M lignes et jai eu une erreur lors de lutilisation de ce code.
- Sur la première ligne de votre exemple de code, devrait-il dire
df1.MATCH <- c("ABC", "abc" ,"ABC")
plutôt que le la dernière chaîne étant"BCD"
?
Answer
TL; DR:
grepl
sattend à ce que son premier argument soit une chaîne (longueur 1), pas un vecteur. Vous pouvez résoudre ce problème avec des combinaisons desapply
etlapply
(voir ci-dessous), mais vous êtes mieux servi en utilisant une seule expression régulière qui captu res ce que vous voulez faire correspondre dansdf1.MATCH
et ne pas utiliser du toutdf2.PATTERN
. Cette deuxième option est beaucoup plus rapide (si elle est moins intelligente) pour un grand ensemble de données. Pour ce type de travail, il vaut la peine dapprendre à utiliser les expressions régulières à leur plein potentiel.
df1 %>% filter(grepl(pattern = "^((ABC)( )*)+$", x = df1.MATCH, ignore.case = TRUE))
Explication
La documentation de grepl
montre lutilisation suivante:
grepl(pattern, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE)
Largument pattern
est le premier, et cet argument doit être une chaîne (un élément). Vous fournissez df1.MATCH
à cet argument, qui est un vecteur.
Nous pourrions utiliser sapply
pour appliquer grepl
à chaque élément 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
Cependant, regardez la sortie! Vous ne vouliez probablement pas de matrice. Que se passe-t-il lorsque nous exécutons votre grepl
un seul élément de df1.MATCH
?
grepl("ABC",df2.PATTERN) [1] TRUE FALSE TRUE
Nous obtenons un vecteur car grepl
vérifie ABC
par rapport à chaque élément de df2.PATTERN
. Pour obtenir un vecteur logique utile pour le filtrage, vous devez renvoyer un vecteur logique de même longueur que df1.MATCH
. Je vois deux façons de le faire.
Méthode 1: Utilisez
any
Puisque vous souhaitez savoir quels éléments de df1.MATCH
correspondent à des éléments de df2.PATTERN
, vous peut utiliser any
, qui renvoie TRUE
si un élément de ses arguments est TRUE
. Nous avons besoin dune syntaxe un peu différente pour que cela fonctionne. Nous devons envelopper grepl
dans lapply
pour faire une liste de trois vecteurs (un pour chaque élément dans df1.MATCH1
) qui alimente sapply
enveloppé any
. Si nous nutilisons que sapply
, any
ne renverra quune seule valeur puisque nous avons une entrée matricielle.
any(grepl("ABC", df2.PATTERN)) [1] TRUE sapply( lapply(df1.MATCH, grepl, x = df2.MATCH), any) [1] TRUE TRUE FALSE
Méthode 2: rédigez une meilleure expression régulière.
Vous souhaitez faire correspondre le contenu de df1.MATCH
avec des valeurs possibles qui ressemblent à abc
, ABC
, ABC ABC
, ou ABC abc
, etc. Vous pouvez englober tout cela dans une seule chaîne de regex. La chaîne souhaitée est
"^((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
Ensuite, utilisez grepl
avec ignore.case = TRUE
:
grepl("^((ABC)( )*)+$", df1.MATCH, ignore.case = TRUE) [1] TRUE TRUE FALSE
Analyse comparative
Dans un ensemble de données volumineux, lun dentre eux fonctionnera plus rapidement. Découvrons-le. Les résultats de votre benchmark varieront en fonction des ressources de votre machine.
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
Il semble que la méthode regex améliorée soit beaucoup plus rapide. Cest parce quil neffectue quune seule opération par ligne (grepl
) avant le filtrage. Lautre méthode exécute quatre opérations par ligne: lapply
exécute grepl
trois fois (une pour chaque élément de df2.PATTERN
, et sapply
puis exécute any
pour chaque élément de liste (chaque ligne).
str_detect
depackage::stringr
effectue similaire à lapprochegrepl
:str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))