grep usando um vetor de caracteres com vários padrões


132

Estou tentando usar greppara testar se um vetor de seqüências de caracteres está presente em outro vetor ou não, e para gerar os valores que estão presentes (os padrões correspondentes).

Eu tenho um quadro de dados como este:

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

Eu tenho um vetor de padrões de cordas de ser encontrado nas colunas da "letra", por exemplo: c("A1", "A9", "A6").

Gostaria de verificar se alguma das cadeias no vetor padrão está presente na coluna "Carta". Se forem, eu gostaria da saída de valores únicos.

O problema é que não sei usar grepcom vários padrões. Eu tentei:

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

Mas me dá 0 correspondências, o que não é verdade, alguma sugestão?


3
Você não pode usar fixed=TRUEporque seu padrão é uma expressão regular verdadeira .
Marek

6
Usar matchou %in%ou até ==é a única maneira correta de comparar correspondências exatas. regex é muito perigoso para essa tarefa e pode levar a resultados inesperados.
David Arenburg 12/09

Respostas:


269

Além do comentário de @ Marek sobre não incluir fixed==TRUE, você também não precisa ter os espaços em sua expressão regular. Deveria ser "A1|A9|A6".

Você também mencionou que existem muitos padrões. Supondo que eles estejam em um vetor

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

Em seguida, você pode criar sua expressão regular diretamente usando pastee collapse = "|".

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

Existe alguma maneira de fazer isso quando sua lista de cadeias inclui operadores de expressões regulares como pontuação?
user124123

@ user1987097 Deve funcionar da mesma maneira, com ou sem outros operadores de regex. Você teve um exemplo específico para o qual não funcionou?
22415 Brian Diggs

@ user1987097 use duas barras invertidas antes de um ponto ou colchete. A primeira barra invertida é um caractere de escape para interpretar o segundo necessário para desativar o operador.
mbh86

3
Usar regex para correspondências exatas parece perigoso para mim e pode ter resultados inesperados. Por que não apenas toMatch %in% myfile$Letter?
David Arenburg 12/09

@ user4050 Não existe um motivo específico. A versão da pergunta tinha e eu provavelmente a realizei sem pensar se era necessário.
Brian Diggs

34

Boas respostas, no entanto, não se esqueça filter()do 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
Eu acho que greplfunciona com um padrão por vez (precisamos de vetor com comprimento 1), temos 3 padrões (vetor com comprimento 3), para que possamos combiná-los com um usando alguns amigáveis ​​para separador grepl - |, tente sua sorte com outros :)
Adamm 23/02

3
oh, entendi agora. Portanto, é uma maneira de compactar a saída de algo como A1 | A2, portanto, se alguém quisesse todas as condições, o colapso seria com um sinal &, obrigado legal.
Ahdee 23/02

1
Oi, usando )|(a padrões separadas pode tornar isso mais robusto: paste0("(", paste(patterns, collapse=")|("),")"). Infelizmente, também se torna um pouco menos elegente. Isso resulta em padrão (A1)|(A9)|(A6).
Fabern 9/07

14

Isso deve funcionar:

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

Ou ainda mais simplesmente:

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

11
%like%não está na base R, portanto, você deve mencionar quais pacotes são necessários para usá-lo.
Gregor Thomas

1
Para outros que olham para esta resposta, %like%faz parte do data.tablepacote. Também em semelhantes data.tablesão like(...), %ilike%e %flike%.
steveb

8

Com base na publicação de Brian Digg, aqui estão duas funções úteis para filtrar listas:

#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

Você já tentou as funções match()ou charmatch()?

Exemplo de uso:

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

1
Uma coisa a observar matché que ele não está usando padrões, está esperando uma correspondência exata.
steveb

5

Não tenho certeza se esta resposta já apareceu ...

Para o padrão específico da pergunta, você pode fazer isso com uma única grep()chamada,

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

4

Para adicionar à resposta de Brian Diggs.

de outra maneira, usando grepl retornará um quadro de dados contendo todos os seus valores.

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

Talvez um pouco mais limpo ... talvez?


2

Tire os espaços. Então faz:

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

1

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

Sugiro escrever um pequeno script e fazer várias pesquisas com o Grep. Eu nunca encontrei uma maneira de procurar vários padrões e, acredite, já procurei!

Assim, seu arquivo shell, com uma string incorporada:

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

Em seguida, execute digitando myshell.sh.

Se você quiser passar a string na linha de comando, faça o seguinte, com um argumento shell - esta é a notação bash btw:

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

E assim por diante.

Se houver muitos padrões para combinar, você pode colocá-lo em um loop for.


Obrigado ChrisBean. Na verdade, os padrões são muitos, e talvez seja melhor usar um arquivo então. Eu sou novo no BASH, mas talvez algo assim deva funcionar ... #! / Bin / bash para i em 'pattern.txt' ecoa $ ij = 'grep -c "$ {i}" myfile.txt' echo $ j se [$ j -eq o], em seguida, ecoa $ i >> match.txt fi concluído
user971102 29/09/11

não funciona ... a mensagem de erro é '[grep: comando não encontrado' ... tenho grep na pasta / bin e / bin está no meu $ PATH ... Não tenho certeza do que está acontecendo ... Você pode ajudar?
User971102
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.