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 de sapply et lapply (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 dans df1.MATCH et ne pas utiliser du tout df2.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).

Commentaires

  • Lutilisation de str_detect de package::stringr effectue similaire à lapproche grepl: str_detect(df1.MATCH, regex("^((ABC)( )*)+$", ignore_case = TRUE))

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *