grep utilisant un vecteur de caractères avec plusieurs motifs


132

J'essaie d'utiliser greppour tester si un vecteur de chaînes est présent ou non dans un autre vecteur, et pour sortir les valeurs présentes (les modèles correspondants).

J'ai une trame de données comme celle-ci:

FirstName Letter   
Alex      A1
Alex      A6
Alex      A7
Bob       A1
Chris     A9
Chris     A6

J'ai un vecteur de modèles de chaînes à trouver dans les colonnes « Lettre », par exemple: c("A1", "A9", "A6").

Je voudrais vérifier si l'une des chaînes du vecteur de motif est présente dans la colonne "Lettre". S'ils le sont, je voudrais la sortie de valeurs uniques.

Le problème est que je ne sais pas comment utiliser grepplusieurs modèles. J'ai essayé:

matches <- unique (
    grep("A1| A9 | A6", myfile$Letter, value=TRUE, fixed=TRUE)
)

Mais cela me donne 0 correspondances, ce qui n'est pas vrai, des suggestions?


3
Vous ne pouvez pas utiliser fixed=TRUEcar votre modèle est une véritable expression régulière.
Marek

6
Utiliser matchou %in%ou même ==est le seul moyen correct de comparer des correspondances exactes. regex est très dangereux pour une telle tâche et peut conduire à des résultats inattendus.
David Arenburg le

Réponses:


269

En plus du commentaire de @ Marek sur la non-inclusion fixed==TRUE, vous devez également ne pas avoir d'espaces dans votre expression régulière. Ça devrait être "A1|A9|A6".

Vous mentionnez également qu'il existe de nombreux modèles. En supposant qu'ils sont dans un vecteur

toMatch <- c("A1", "A9", "A6")

Ensuite, vous pouvez créer votre expression régulière directement en utilisant pasteet collapse = "|".

matches <- unique (grep(paste(toMatch,collapse="|"), 
                        myfile$Letter, value=TRUE))

Un moyen de le faire lorsque votre liste de chaînes inclut des opérateurs regex comme ponctuation?
user124123

@ user1987097 Cela devrait fonctionner de la même manière, avec ou sans aucun autre opérateur regex. Avez-vous eu un exemple spécifique pour lequel cela n'a pas fonctionné?
Brian Diggs

@ user1987097 utilise 2 backslahes avant un point ou un crochet. La première barre oblique inverse est un caractère d'échappement pour interpréter le second nécessaire pour désactiver l'opérateur.
mbh86

3
L'utilisation de regex pour des correspondances exactes me semble dangereuse et peut avoir des résultats inattendus. Pourquoi pas juste toMatch %in% myfile$Letter?
David Arenburg le

@ user4050 Aucune raison particulière. La version dans la question l'avait et je l'ai probablement juste poursuivie sans me demander si c'était nécessaire.
Brian Diggs

34

Bonnes réponses, mais n'oubliez pas filter()de dplyr:

patterns <- c("A1", "A9", "A6")
>your_df
  FirstName Letter
1      Alex     A1
2      Alex     A6
3      Alex     A7
4       Bob     A1
5     Chris     A9
6     Chris     A6

result <- filter(your_df, grepl(paste(patterns, collapse="|"), Letter))

>result
  FirstName Letter
1      Alex     A1
2      Alex     A6
3       Bob     A1
4     Chris     A9
5     Chris     A6

3
Je pense que cela greplfonctionne avec un motif à la fois (nous avons besoin d'un vecteur de longueur 1), nous avons 3 motifs (vecteur de longueur 3), nous pouvons donc les combiner avec un en utilisant un séparateur convivial pour grepl - |, tentez votre chance avec d'autres :)
Adamm

3
oh je comprends maintenant. C'est donc une manière compressée de produire quelque chose comme A1 | A2 donc si on voulait toutes les conditions alors l'effondrement serait avec un signe &, cool merci.
Ahdee

1
Salut, en utilisant )|(à des modèles séparés pourrait le rendre plus robuste: paste0("(", paste(patterns, collapse=")|("),")"). Malheureusement, il devient également un peu moins élégant. Il en résulte un motif (A1)|(A9)|(A6).
fabern

14

Cela devrait fonctionner:

grep(pattern = 'A1|A9|A6', x = myfile$Letter)

Ou encore plus simplement:

library(data.table)
myfile$Letter %like% 'A1|A9|A6'

11
%like%n'est pas dans la base R, vous devriez donc mentionner le (s) package (s) nécessaire (s) pour l'utiliser.
Gregor Thomas

1
Pour les autres, cette réponse %like%fait partie du data.tablepackage. Également en même data.tablesont like(...), %ilike%et %flike%.
steveb

8

Basé sur l'article de Brian Digg, voici deux fonctions utiles pour filtrer les listes:

#Returns all items in a list that are not contained in toMatch
#toMatch can be a single item or a list of items
exclude <- function (theList, toMatch){
  return(setdiff(theList,include(theList,toMatch)))
}

#Returns all items in a list that ARE contained in toMatch
#toMatch can be a single item or a list of items
include <- function (theList, toMatch){
  matches <- unique (grep(paste(toMatch,collapse="|"), 
                          theList, value=TRUE))
  return(matches)
}

5

Avez-vous essayé les fonctions match()ou charmatch()?

Exemple d'utilisation:

match(c("A1", "A9", "A6"), myfile$Letter)

1
Une chose à noter matchest qu'il n'utilise pas de motifs, il s'attend à une correspondance exacte.
steveb

5

Je ne sais pas si cette réponse est déjà apparue ...

Pour le modèle particulier de la question, vous pouvez simplement le faire avec un seul grep()appel,

grep("A[169]", myfile$Letter)

4

Pour ajouter à la réponse de Brian Diggs.

une autre façon d'utiliser grepl renverra une trame de données contenant toutes vos valeurs.

toMatch <- myfile$Letter

matches <- myfile[grepl(paste(toMatch, collapse="|"), myfile$Letter), ]

matches

Letter Firstname
1     A1      Alex 
2     A6      Alex 
4     A1       Bob 
5     A9     Chris 
6     A6     Chris

Peut-être un peu plus propre ... peut-être?


2

Otez les espaces. Alors faites:

matches <- unique(grep("A1|A9|A6", myfile$Letter, value=TRUE, fixed=TRUE))

1

En utilisant le sapply

 patterns <- c("A1", "A9", "A6")
         df <- data.frame(name=c("A","Ale","Al","lex","x"),Letters=c("A1","A2","A9","A1","A9"))



   name Letters
1    A      A1
2  Ale      A2
3   Al      A9
4  lex      A1
5    x      A9


 df[unlist(sapply(patterns, grep, df$Letters, USE.NAMES = F)), ]
  name Letters
1    A      A1
4  lex      A1
3   Al      A9
5    x      A9

-1

Je suggère d'écrire un petit script et de faire plusieurs recherches avec Grep. Je n'ai jamais trouvé un moyen de rechercher plusieurs modèles, et croyez-moi, j'ai cherché!

Comme ça, votre fichier shell, avec une chaîne intégrée:

 #!/bin/bash 
 grep *A6* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";
 grep *A7* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";
 grep *A8* "Alex A1 Alex A6 Alex A7 Bob A1 Chris A9 Chris A6";

Puis exécutez en tapant myshell.sh.

Si vous voulez pouvoir passer la chaîne sur la ligne de commande, faites-le comme ceci, avec un argument shell - c'est la notation bash btw:

 #!/bin/bash 
 $stingtomatch = "${1}";
 grep *A6* "${stingtomatch}";
 grep *A7* "${stingtomatch}";
 grep *A8* "${stingtomatch}";

Et ainsi de suite.

S'il y a beaucoup de modèles à faire correspondre, vous pouvez le mettre dans une boucle for.


Merci ChrisBean. Les modèles sont nombreux en fait, et peut-être serait-il préférable d'utiliser un fichier alors. Je suis nouveau dans BASH, mais peut-être que quelque chose comme ça devrait fonctionner… #! / Bin / bash for i dans 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j if [$ j -eq o] then echo $ i >> matches.txt fi done
user971102

ne fonctionne pas… le message d'erreur est «[grep: command not found»… J'ai grep dans le dossier / bin et / bin est sur mon $ PATH… Je ne sais pas ce qui se passe… Pouvez-vous m'aider?
user971102
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.