grep usando un vettore di carattere con più motivi


132

Sto cercando di utilizzare grepper verificare se un vettore di stringhe è presente in un altro vettore o meno e per generare i valori presenti (i modelli corrispondenti).

Ho un frame di dati come questo:

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

Ho un vettore di stringhe modelli da trovare nelle colonne "lettera", per esempio: c("A1", "A9", "A6").

Vorrei verificare se una qualsiasi delle stringhe nel vettore del modello è presente nella colonna "Lettera". Se lo sono, vorrei l'output di valori univoci.

Il problema è che non so come utilizzare grepcon più schemi. Provai:

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

Ma mi dà 0 partite che non è vero, qualche suggerimento?


3
Non puoi usare fixed=TRUEperché il tuo pattern è una vera espressione regolare.
Marek,

6
L'utilizzo matcho %in%o addirittura ==è l' unico modo corretto per confrontare le corrispondenze esatte. regex è molto pericoloso per tale compito e può portare a risultati imprevisti.
David Arenburg,

Risposte:


269

Oltre al commento di @ Marek sul non includere fixed==TRUE, devi anche non avere gli spazi nella tua espressione regolare. Dovrebbe essere "A1|A9|A6".

Hai anche detto che ci sono molti schemi. Supponendo che siano in un vettore

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

Quindi puoi creare la tua espressione regolare direttamente usando pastee collapse = "|".

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

Un modo per farlo quando il tuo elenco di stringhe include operatori regex come punteggiatura?
user124123

@ user1987097 Dovrebbe funzionare allo stesso modo, con o senza altri operatori regex. Hai avuto un esempio specifico per cui non ha funzionato?
Brian Diggs,

@ user1987097 utilizzare 2 backslahes prima di un punto o di una parentesi. La prima barra rovesciata è un carattere di escape per interpretare il secondo necessario per disabilitare l'operatore.
mbh86,

3
L'uso di regex per partite esatte mi sembra pericoloso e può avere risultati inaspettati. Perché non solo toMatch %in% myfile$Letter?
David Arenburg,

@ user4050 Nessun motivo specifico. La versione nella domanda lo aveva e probabilmente l'ho portato avanti senza pensare se fosse necessario.
Brian Diggs,

34

Buone risposte, tuttavia non dimenticare di filter()da 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
Penso che greplfunzioni con un modello alla volta (abbiamo bisogno di un vettore con lunghezza 1), abbiamo 3 modelli (vettore di lunghezza 3), quindi possiamo combinarli con uno usando un amichevole per il separatore grepl - |, prova la tua fortuna con altri :)
Adamm,

3
oh ho capito adesso. Quindi è un modo comprimere per produrre qualcosa come A1 | A2, quindi se si volessero tutte le condizioni, il crollo avverrebbe con un segno &, figo grazie.
Ahdee,

1
Salve, utilizzando )|(a modelli separati potrebbe rendere questo più robusto: paste0("(", paste(patterns, collapse=")|("),")"). Purtroppo diventa anche leggermente meno elegante. Ciò si traduce in modello (A1)|(A9)|(A6).
fabern,

14

Questo dovrebbe funzionare:

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

O ancora più semplicemente:

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

11
%like%non è nella base R, quindi dovresti menzionare quali pacchetti sono necessari per usarlo.
Gregor Thomas,

1
Per gli altri guardando questa risposta, %like%fa parte del data.tablepacchetto. Anche simile a data.tablesono like(...), %ilike%e %flike%.
steveb

8

Sulla base del post di Brian Digg, ecco due utili funzioni per filtrare gli elenchi:

#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

Hai provato le funzioni match()o charmatch()?

Esempio di utilizzo:

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

1
Una cosa da notare matchè che non utilizza schemi, ma si aspetta una corrispondenza esatta.
steveb

5

Non sono sicuro che questa risposta sia già apparsa ...

Per il modello particolare nella domanda, puoi semplicemente farlo con una singola grep()chiamata,

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

4

Per aggiungere alla risposta Brian Diggs.

un altro modo usando grepl restituirà un frame di dati contenente tutti i tuoi valori.

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

Forse un po 'più pulito ... forse?


2

Porta via gli spazi. Quindi fai:

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

1

Usando il 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

Suggerisco di scrivere una piccola sceneggiatura e fare più ricerche con Grep. Non ho mai trovato un modo per cercare più schemi e, credetemi, ho cercato!

In questo modo, il tuo file shell, con una stringa incorporata:

 #!/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";

Quindi esegui digitando myshell.sh.

Se vuoi essere in grado di passare la stringa dalla riga di comando, fallo in questo modo, con un argomento shell - questa è la notazione bash btw:

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

E così via.

Se ci sono molti pattern da abbinare, puoi metterlo in un ciclo for.


Grazie ChrisBean. Gli schemi sono molti in realtà, e forse sarebbe meglio usare un file allora. Sono nuovo di BASH, ma forse qualcosa del genere dovrebbe funzionare ... #! / Bin / bash per i in 'pattern.txt' do echo $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j se [$ j -eq o] allora echo $ i >> fi
fiammifero.txt

non funziona ... il messaggio di errore è "[grep: comando non trovato" ... Ho grep nella cartella / bin e / bin è sul mio $ PATH ... Non sei sicuro di cosa stia succedendo ... Puoi per favore aiutare?
user971102
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.