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?

user971102
fonte
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))
Brian Diggs
fonte
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
Adamm
fonte
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'
BOC
fonte
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)
}
Austin D
fonte
5

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

Exemplo de uso:

match(c("A1", "A9", "A6"), myfile$Letter)
user3877096
fonte
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)
Assaf
fonte
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?

StatGenGeek
fonte
2

Tire os espaços. Então faz:

matches <- unique(grep("A1|A9|A6", myfile$Letter, value=TRUE, fixed=TRUE))
user9325029
fonte
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
dondapati
fonte
-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.

ChrisBean
fonte
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