Carregando agora

O guia de sobrevivência de expressões regulares em Python

Compartilhe:

Resumo: Este material faz parte do livro “Python para Bioinformática” (ainda não publicado). Para mais informações, consulte o autor. Expressões regulares são ferramentas poderosas para buscas de padrões em textos. Logo, todo o bioinformata deve conhecê-las. Neste artigo, você irá conhecer os principais metacaracteres e irá aprender a manipular expressões regulares usando Python. Bons estudos! Expressões… 

Este material faz parte do livro “Python para Bioinformática” (ainda não publicado). Para mais informações, consulte o autor.

Expressões regulares são ferramentas poderosas para buscas de padrões em textos. Logo, todo o bioinformata deve conhecê-las. Neste artigo, você irá conhecer os principais metacaracteres e irá aprender a manipular expressões regulares usando Python.

Bons estudos!

Expressões regulares, também conhecidas como Regex, RE ou ER, são técnicas para busca de padrões e extração ou substituição de cadeias de caracteres de forma concisa e flexível. Entender como expressões regulares podem ser construídas não é uma tarefa fácil, mas dominar essa técnica pode fazer a diferença na vida de um bioinformata.

Para ilustrar esta seção, vamos utilizar o seguinte arquivo de sequências de proteínas extraído da base de dados betagdb.

>Chamoli et al. (2016) tr|I3QIG4|I3QIG4_BACIU 6-phospho-beta-glucosidase (Fragment) OS=Bacillus subtilis GN=bg1 PE=3 SV=1
MSSNEKRFPEGFLWGGAVAANQVEGAYNEGGKGLSTADVSPNGIMSPFDESMTSLNLYHN
GIDFYHRYKEDIALFAEMGFKAFRTSIAWTRIFPNGDEEEPNEEGLRFYDDLFDELLKHH
IEPVVTISHYEMPLGLVKNYGGWKNRKVIEFYERYAKTVFKRYQHKVKYWMTFNEINVVL
HAPFTGGGLVFEEGENKLNAMYQAAHHQFVASALAVKAGHDIIPDSKIGCMIAATTTYPM
TSKPEDVFAAMENERKTLFFSDVQARGAYPGYMKRYLAENNIEIEMAEGDEELLKEHTVD
YIGFSYYMSMAASTDPEELAKSGGNLLGGVKNPYLKSSEWGWQIDPKGLRITLNTLYDRY
QKPLFIVENGLGAVDKVEEDGTIQDDYRINYLRDHLIEAREAIADGVELIGYTSWGPIDL
VSASTAEMKKRYGFIYVDRDNEGNGTFNRIKKKSFNWYQQVIATNGESL

>Yang_Y et al. (2015) tr|D5KX75|D5KX75_9BACT Beta-glucosidase OS=uncultured bacterium PE=3 SV=1
MTKISLPTCSPLLTKEFIYGVATASFQIEGGSAHRLPCIWDTFCDTPGKIADNSNGHVAC
DHYNNWKQDIDLIESLGVDAYRLSISWPRVITKSGELNPEGVKFYTDILDELKKRNIKAF
VTLYHWDLPQHLEDEGGWLNRETAYAFAHYVDLITLAFGDRVHSYATLNEPFCSAFLGYE
IGIHAPGKVGKQYGRKAAHHLLLAHGLAMTVLKQNSPTTLNGIVLNFTPCYSISEDADDI
AATAFADDYLNQWYMKPIMDGTYPAIIEQLPSAHLPDIHDGDMAIISQSIDYLGINFYTR
QFYKAHPTEIYEPIEPTGPLTDMGWEIYPKSFTELLVTLNNTYTLPPIFITENGAAMPDS
YNNGEINDVDRLDYYNSHLNAVHNATEQGVRIDGYFAWSLMDNFEWAEGYLKRFGIVYVD
YSTQQRTIKNSGLAYKALISNR

>Cao et al. (2015) tr|A0A0F7KKB7|A0A0F7KKB7_9BACT Beta-glucosidase OS=uncultured bacterium PE=3 SV=1
MTSDTARSYRFPEGFLWGAATAAYQIEGSSMADGAGESIWDRFSHTPGNMKDGDTGDVAC
DHYNRWREDIELMKRLNLQAYRFSVSWSRVIPQGRGAINPKGLAFYDRLVDGLLEAGIEP
LATLYHWDLPAALDDRGGWLNPDIADWFADYGQVLFEKFKGRVKTWGTINEPWVIVDGGY
LHGALAPGHRSAYEAVIAGHNVLRAHGAAVRRFREVGEGQIGIVLNIEPKYPASDKPEDE
AARRRAEAQMNRWFLDPLMGRGYPEELTDVYGAAWREFPKEDFELIAEPTDWMGLNWYTR
AVPENAPDAWPTRSRPVRQTQHAHTETGWEVYPPALTDTLVWLSEQTGGKLPLMVTENGS
AWYDPPHAIDGRIHDPMRVHYLQTHIKALHDAIGKGVDLRGYMAWSLLDNLEWSLGYSKR
FGIVHVNFATQERTIKDSGLLYAEVIKTHGDVLNTL

Em Python, o módulo responsável por permitir o uso de expressões regulares é o módulo re.

import re

Entretanto, expressões regulares não são uma exclusividade do Python, sendo presentes em diversas linguagens, como Perl, PHP, JavaScript, Java, Ruby e até mesmo .NET. Em geral, expressões regulares são padronizadas pela norma IEEE POSIX 1003.2 (POSIX.2) através de duas especificações: BRE (expressões regulares básicas) e ERE (expressões regulares estendidas). Além disso, algumas IDEs possuem ferramentas nativas para a busca de expressões regulares, como por exemplo o Sublime Text ou o VS Code. No exemplo abaixo, vemos como é possível fazer uma busca por expressão regular habilitando o botão .* da ferramenta Sublime Text.

Figura 1. Busca de expressão regular usando o Sublime Text. Fonte: próprio autor.

Antes de apresentar métodos do módulo  re  é necessário aprender o conceito de metacaracteres.

Metacaracteres

Metacaracteres são um conjunto de símbolos que permitem a construção de expressões regulares mais complexas, permitindo, por exemplo, realizar buscas por variações em posições específicas de uma cadeia de caracteres.

Exemplos de metacaracteres:

. [ ] ? * + { } ^ $ | ( )

Os metacaracteres podem ser divididos em quatro categorias: representantes, quantificadores, âncoras e outros metacaracteres (Jargas, 2016).

Categoria 1 – Metacaracteres representantes

Metacaracteres desta categoria permitem que os símbolos usados sejam associados a caracteres específicos. Os três principais metacaracteres representantes são:

  • ponto;
  • lista;
  • lista negada.
MetacaractereNomeSignificado
.PontoBusca qualquer caractere simples (exceto n).
[ ]ListaBusca por uma lista de caracteres indicados. Por exemplo, [abc] busca por um caractere que seja “a”, “b” ou “c”. Para buscar um grupo sequencial de caracteres basta utilizar o caractere “-“. Por exemplo, para buscar os caracteres A, B, C, D e E pode-se utilizar o padrão: [A-E].
[^]Lista negadaBusca por um conjunto de caracteres NÃO indicados na lista.
Tabela 1. Metacaracteres representantes. Fonte: adaptado de Mariano & de Melo-Minardi (2016).

Ponto pode representar qualquer caractere, logo ao realizar uma busca por este metacaractere, você obterá o primeiro caractere do texto. Uma nova busca retornará o segundo elemento, e assim sucessivamente (com exceção das quebras de linha).

Figura 2. Exemplo de uso do metacaractere ponto. Fonte: próprio autor.

Este metacaractere é útil caso desejemos fazer uma busca por um padrão que possui pequenas variações. Por exemplo, suponha que desejemos buscar nas sequências apresentadas anteriormente uma trinca de aminoácidos composta por duas glicinas separadas por qualquer outro aminoácido. Glicinas são aminoácidos especiais que possuem apenas um hidrogênio em sua cadeia lateral. Portanto, elas podem atribuir uma grande flexibilidade na estrutura da proteína. Poderíamos realizar esta busca usando a expressão regular: “G.G”, como no exemplo a seguir:

Figura 3. Usando o metacaractere ponto para buscar pares de glicinas próximas, mas que não são vizinhas. A busca por “G.G” retorna as substrings: GKG, GGG, GLG, GNG, GAG, GRG, GEG, GRG e GKG. Fonte: próprio autor.

Note que, caso desejemos buscar pelo caractere ponto e não pelo metacaractere, precisaremos usar uma notação especial: . . A barra invertida indica que o caractere que virá a frente não deve ser lido como um metacaractere.

Figura 4. Pesquisando pelo caractere ponto usando expressões regulares. Fonte: próprio autor.

A barra invertida também é considerada um metacaractere, mas falaremos mais sobre ela à frente.

O metacaractere lista é indicado por um par de colchetes (abertura e fechamento). Qualquer valor dentro dos colchetes será retornado na busca. Suponha por exemplo que usemos a expressão regular [ACDEF] . Logo, será feita uma busca por um único caractere que seja “A”, “C”, “D”, “E” ou “F”.

Podemos delimitar intervalos usando o símbolo - . Por exemplo, a expressão regular [A-Z] realiza uma busca por caracteres que sejam letras maiúsculas entre A e Z.

Figura 5. Busca qualquer letra de A a Z desde que estejam em maiúsculo. Fonte: próprio autor.

Para buscar por letras minúsculas deve-se usar a regex [a-z] .

Figura 6. Busca qualquer letra minúscula. Fonte: próprio autor.

Para buscar letras maiúsculas ou minúsculas, pode-se usar a seguinte expressão regular:

Figura 7. Busca qualquer letra maiúscula ou minúscula. Fonte: próprio autor.

Por fim, a lista negada é um metacaractere usado para realizar buscas por caracteres que não estão dentro do colchetes. Para usá-la, deve-se inserir o símbolo circunflexo dentro do colchetes seguido da lista de caracteres que não se deseja buscar. Por exemplo, a expressão regular [^a-zA-Z] busca por quaisquer caracteres que não sejam letras maiúsculas ou minúsculas. Observe um exemplo:

Figura 8. Busca quaisquer caracteres que não sejam letras. Fonte: próprio autor.

Note que essa expressão regular retorna números e outros caracteres como pontuações, parênteses, hifens, dentre outros. Para buscar apenas por números, pode-se usar o padrão [0-9] , ou seja, esse é o padrão que retoma qualquer número de 0 a 9.

Figura 9. Busca por quaisquer números. Fonte: próprio autor.

Preste atenção que os códigos apresentados anteriormente retornam apenas uma letra por vez. Para buscar mais letras é necessário combinar os metacaracteres representantes com os metacaracteres quantificadores, como no exemplo acima. O símbolo * é um metacaractere quantificador que indica buscas por números que se repetem uma ou várias vezes. Você aprenderá mais sobre os metacaracteres quantificadores na próxima seção.

Categoria 2 – Metacaracteres quantificadores

Os chamados metacaracteres quantificadores permitem determinar quantas vezes um padrão de caracteres pode se repetir no texto buscado. É importante ressaltar que a busca com metacaracteres quantificadores é do tipo gulosa, ou seja, retorna o primeiro resultado que atenda a uma determinada requisição.

MetacaractereNomeSignificado
*AsteriscoPermite buscas por caracteres que aparecem várias vezes ou mesmo nenhuma vez.O metacaractere asterisco “*” analisa se o caractere anterior ao símbolo aparece uma ou várias vezes, ou até mesmo não aparece. Por exemplo, “qwer*t” aceita os valores “qwet“, “qwert“, “qwerrt“, “qwerrrt“, e assim sucessivamente.
?OpcionalPermite buscas por caracteres que não apareçam ou que apareçam uma única vez.O metacaractere opcional “?” analisa se o padrão buscado existe ou não. Ele é ideal para buscar, por exemplo, palavras no plural, uma vez que a diferença entre singular e plural muitas vezes é apenas uma letra “s”.
+MaisPermite buscas por caracteres que aparecem várias vezes ou pelo menos uma vez.O metacaractere mais “+” analisa se o caractere anterior se repete várias vezes. Por exemplo, “baleia+” aceita “baleiaa”, “baleiaaa”, etc.
{x, y}ChavesPermite buscas por um caractere que apareça no mínimo x vezes e no máximo y vezes. Se apenas um valor for passado, ele irá buscar o número exato de repetições.
Tabela 2. Metacaracteres quantificadores. Fonte: adaptado de Mariano & de Melo-Minardi (2016).

Podemos combinar os metacaracteres quantificadores com metacaracteres representantes para realizar buscas mais robustas. Por exemplo, ao realizar uma busca pela combinação “.*” obtemos todo o texto.

Figura 10. A regex “.*” retorna todo o texto. Fonte: próprio autor.

Neste exemplo, o metacaractere ponto indica que desejamos buscar qualquer caractere do texto. Já o asterisco é um metacaractere quantificador que complementa o metacaractere anterior. Ele indica que o padrão buscado pode aparecer ou não. Caso apareça, ele pode se repetir diversas vezes. Nesse caso, a combinação .* indica qualquer caractere que se repita 0, 1 ou mais vezes. Ou seja, essa combinação retorna todo o texto.

Entretanto, podemos realizar buscas mais específicas. Por exemplo, imagine que desejemos buscar por um aminoácido negativamente carregado (e.g., glutamato ou “E”) seguido por um aminoácido positivamente carregado (e.g.lisina ou “K”). Entretanto, ao realizar essa busca, devemos considerar que entre eles pode haver um outro aminoácido de qualquer tipo. Para buscar por este padrão, podemos usar a seguinte expressão regular: E.?K .

Figura 11. A regex “E.?K” busca por uma letra E seguida por uma letra K, podendo ter qualquer letra entre eles. Fonte: próprio autor.

Neste exemplo, estamos usando o metacaractere “?”, que indica que o símbolo anterior é opcional. Como o símbolo anterior é um metacaractere ponto, pode indicar qualquer caractere.

Caso desejemos que o caractere buscado apareça pelo menos uma vez, podemos usar o metacaractere + . No exemplo a seguir, vamos buscar por pares de lisinas (representada pela letra K). Observe como podemos construir essa expressão regular:

Figura 12. Buscando por pares de lisina (K) seguidas de outras lisinas ou não. Fonte: próprio autor.

A regex KK+ indica que estamos buscando pelo padrão “KK”. Entretanto, o segundo K pode aparecer uma ou mais vezes, isto é, se o texto contivesse três, quatro, cinco caracteres “K” em sequência, todas essas letras seriam retornadas pela busca.

Por fim, digamos que por algum motivo, desejemos buscar por duas cisteínas que estejam próximas. Caso soubéssemos exatamente quantos caracteres existem entre elas, poderíamos usar o padrão {x}, onde x é a quantidade de caracteres.

Entretanto, vamos definir entre as duas cisteínas pode ter entre um e 10 resíduos. Nesse caso, devemos usar o metacaractere chaves {x,y} , onde x e y correspondem à distância mínima e máxima, respectivamente. Assim, podemos combinar o metacaractere ponto com as chaves para delimitar um intervalo de elementos entre eles. A expressão regular que faz isso é C.{1,10}C . Observe como podemos implementar isso:

Figura 13. A expressão regular C.{1,10}C retorna duas letras C desde que tenha de 1 a 10 letras entre elas. Note que o Sublime Text foi configurado para não diferenciar letras maiúsculas e minúsculas. Fonte: próprio autor.

Ao realizar essa busca usando um editor de textos como o Sublime Text, você pode obter um falso positivo no cabeçalho da sequência. Logo, para que não tenhamos problemas, é necessário realizar um pré-processamento nesses dados removendo os cabeçalhos. Podemos fazer isso usando a expressão regular >.* , que retorna linhas que sejam iniciadas com > seguidas por qualquer coisa.

Figura 14. Pode-se selecionar o cabeçalho usando a expressão regular “>.*”.Fonte: próprio autor.

Após selecionar o cabeçalho, você pode deletá-lo e repetir a busca.

Figura 15. Buscando cisteínas próximas. Fonte: próprio autor.

Vamos pensar um pouco

Os códigos apresentados anteriormente nesta seção possuem uma falha. Você consegue identificar onde o problema ocorre?

Para entender o que ocorre, observe a sequência a seguir:

AAAAAACA
ACAAAAAA

A expressão regular C.*C deveria retornar o trecho CAnAC (perceba que a quebra de linha é identificada pelo n ).

Entretanto, isso pode não ocorrer. Isso está relacionado ao ambiente ou à ferramenta que você usou para buscar a expressão regular. Muitas ferramentas realizam buscas a cada linha. Logo, o metacaractere ponto não reconhece a quebra de linha.

Para contornar este problema, podemos usar outra combinação de expressões regulares, como por exemplo:

C[A-Zn]{1,10}C

Neste exemplo, substituímos o trecho .* por [A-Zn] , que realiza uma busca por caracteres de A a Z (maiúsculos) ou por uma quebra de linha.

Categoria 3 – Metacaracteres Âncoras

Metacaracteres âncoras permitem buscas no início e fim de linhas ou strings usando os metacaracteres circunflexo e cifrão, respectivamente.

MetacaractereNomeSignificado
^CircunflexoBusca no início de uma linha/string.
$CifrãoBusca no fim de uma linha/string.
Tabela 3. Metacaracteres âncoras. Fonte: Mariano & de Melo-Minardi (2016).

Note que o metacaractere circunflexo possui um significado diferente quando está presente dentro de colchetes [^] . Nesse caso, o circunflexo indica uma lista negada.

Veja como podemos utilizar o circunflexo como um metacaractere âncora:

Figura 16. Metacaractere circunflexo. (A) Busca por M retorna todas as letras M. (B) Busca por ^M retorna apenas letras M que aparecem no começo das linhas. Fonte: próprio autor.

No lado esquerdo podemos ver a busca pelo caractere M . Ao lado direito incluímos o metacaractere circunflexo antes do M. O uso do metacaractere circunflexo indica que o próximo caractere deve ser buscado apenas no começo de cada frase.

Para realizar uma busca no final da linha, podemos usar o metacaractere cifrão. Veja um exemplo:

Figura 17. O metacaractere cifrão busca no fim de linhas. Fonte: próprio autor.

Neste exemplo, buscamos letras H no final das linhas. Note que podemos realizar essa mesma busca usando o caractere especial n . Veja:

Figura 18. Usando n para buscar no fim das linhas. Fonte: próprio autor.

É importante ressaltar que essa busca pode não funcionar se houver caracteres especiais, como r , antes da quebra de linha.

Categoria 4 – Outros metacaracteres

Por fim, a última categoria agrupa os metacaracteres remanescentes. Esta categoria agrupa outros metacaracteres com funções variadas que não se enquadram nas categorias anteriores. São exemplos de metacaracteres dessa categoria: pipe (ou), parênteses (agrupamento) e a barra invertida (escape).

MetacaractereNomeSignificado
|OuBusca pelo caractere à direita ou pelo caractere à esquerda.
( )AgrupamentoPermite a busca por grupos de caracteres.
EscapeConverte metacaractere em um caractere normal.
Tabela 4. Outros metacaracteres. Fonte: adaptado Mariano & de Melo-Minardi (2016).

É importante ressaltar que o metacaractere | é usado em conjunto com () , mas não é usado junto com [] .

No exemplo a seguir, vamos usar os metacaracteres de agrupamento para buscar por pares de aminoácidos carregados positivamente ou negativamente. São eles: EE (glutamato), DD (aspartato), HH (histidina), KK (lisina) e RR (arginina). Para buscar esse padrão vamos combinar o agrupamento com o metacaractere “ou”. Veja:

Figura 19. Busca por EE, DD, KK, RR ou HH. Fonte: próprio autor.

Neste exemplo, usamos o pipe para indicar que desejamos buscar por qualquer um dos cinco padrões de strings.

Colchetes versus parênteses

Note que parênteses e colchetes têm funções parecidos, mas possuem diferenças. Parênteses são usados para buscas de grupos de caracteres. Metacaracteres quantificadores podem ser aplicados a todo o conjunto, enquanto com colchetes são aplicados a um único caractere. Observe a diferença de uso:

[abc] retorna caracteres “a”, “b” ou “c”.
(abc) retorna apenas a string “abc”.

Para indicar outras combinações, você poderia usar o metacaractere |:
(abc|acb|bac|bca|cab|cba) retorna “abc”, “acb”, “bac”, “bca”, “cab” ou “cba”.

O metacaractere escape (barra invertida) é utilizado para inserir caracteres especiais, como por exemplo, o n usado para quebras de linhas ou o t para inserção de tabulações. Entretanto, podemos usar a barra invertida com outras letras para indicar outros significados. Por exemplo, uma busca por d retorna todos os valores numéricos (similar à expressão regular [0-9] ).

Figura 20. Busca por valores numéricos com d. Fonte: próprio autor.

Caso usemos a barra invertida em conjunto com uma letra D maiúscula, podemos obter todos os valores não numéricos:

Figura 21. Obtendo valores não numéricos com D. Fonte: próprio autor.

Note que o código acima é similar a [^0-9] .

Por fim, podemos usar a barra invertida para converter metacaracteres em caracteres literais. Por exemplo, digamos que desejemos buscar nos títulos pelo ano presente entre parênteses. Para indicar que estamos buscando por um parênteses literais e não pelo metacaractere de agrupamento precisamos usar a barra invertida antes, da seguinte forma:

Figura 22. Buscando o ano de publicação. Fonte: próprio autor.

Neste caso, usamos ( para buscar por um parêntese de abertura, seguido por um número [0-9] , que se repete quatro vezes {4} e é seguida por um parêntese de fechamento ) .

Realizando buscas com ER em Python

Como introduzido anteriormente, Python permite usar expressões regulares por meio do módulo re . Nesta seção, você irá conhecer as três funções básicas para manipulação de expressões regulares com Python:

  • re.search() : realiza buscas por expressões regulares. Retorna um único valor;
  • re.findall() : realiza buscas em expressões regulares. Retorna todas as ocorrências;
  • re.sub() : busca por expressões regulares e substitui por um outro valor.

A função re.search() recebe como entrada uma expressão regular e a string que será usada na busca.

busca = re.search(expressao_regular, string_para_busca)

Para obter o resultado da busca, acesse busca[0] .

Uma desvantagem desse método é que re.search() retorna apenas a primeira ocorrência do padrão buscado. Para obter todos os valores, devemos usar outra função do módulo re. Veremos um exemplo prático a seguir.

Para realizar buscas com expressões regulares em Python pode-se utilizar o método  findall() . Esse método recebe como argumentos uma expressão regular e uma string.

re.findall(expressao_regular, string_para_busca)

Esse método retorna uma lista com todas as ocorrências de determinado padrão. Observe a seguir um script que realiza a busca de todas as palavras que começam com a letra R da frase “O rato roeu a roupa do rei de Roma”.

import re
string = "O rato roeu a roupa do rei de Roma."
busca = re.findall("[^s]*[rR][^s.]*", string)
for item in busca:
print(item)

O código acima tem como saída o seguinte texto:

  • rato
  • roeu
  • roupa
  • rei
  • Roma

Agora, vamos entender o que a expressão regular [^s]*[rR][^s.]* quer dizer:

  • [^s]*
    • Tudo que não tenha espaços no começo. Espaços podem ser representados pelo metacaractere “s”. Note que poderíamos substituir o símbolo “s” por um simples espaço;
  • [rR]
    • Tenha uma letra R (maiúscula ou minúscula) no começo da palavra (observe que negamos os espaços na expressão anterior, assim esta expressão regular combinada com a anterior permite detectar todas as palavras iniciadas com R minúsculo ou maiúsculo);
  • [^s.]*
    • Tudo que não tenha espaços no fim (ou seja, força a detectar palavras separadas por espaço) ou ponto no final (caso a palavra seja a última, como no caso de “Roma”).

Note que não precisamos adicionar a barra invertida antes do caractere . quando ele está dentro de uma lista ou de uma lista negada.

Substituindo padrões de texto em Python

Podemos substituir trechos de texto com base em padrões usando a função re.sub( ) , que recebe como entrada: o padrão, a substituição e a string.

string = re.sub([PADRÃO-BUSCADO], [SUBSTITUIÇÃO], string)

Por exemplo, digamos que um determinado programa inseriu números incorretamente em uma sequência fasta. Em expressões regulares, podemos representar quaisquer números usando o padrão [0-9] . Assim, poderíamos remover esses números da seguinte forma:

string = "ATCGAT1GAT1514TAGAG"
string = re.sub("[0-9]", "", string)
print(string) # ATCGATGATTAGAG

Buscas por diferentes tipos de caracteres

Aprendemos nos exemplos anteriores a realizar uma busca por palavras iniciadas com o caractere R maiúsculo ou minúsculo. Agora, observe o quadro abaixo com outros exemplos de expressões regulares:

CombinaçãoSignificado
[0-9]Busca por um número
[0-9]*Busca por uma combinação de números
[0-9]*- [0-9]*Busca por uma combinação de números com um traço no meio (por exemplo, um número de telefone: 555-5555)
[a-z]Busca por uma letra minúscula
[A-Z]Busca por uma letra maiúscula
[A-Za-z]*Busca por um conjunto de letras, sejam elas maiúsculas ou minúsculas; também pode ser escrito como [A-z]*
[0-9a-z]*@[0-9a-z.]*Busca por um endereço de e-mail. Veja que no endereço pode conter números ou letras minúsculas no começo, um símbolo de arroba (item obrigatório) e no fim números, letras ou pontos.
sEspaço
dDígito numérico; o mesmo que [0-9]
DValor não numérico
(?<=…)Lookahead negativo: busca um trecho posterior ao explicitado por …
(?=…)Lookahead positivo: busca um trecho anterior ao explicitado por …
Exemplos de combinações de metacaracteres. Fonte: próprio autor.

Exercício resolvido #1

Observe o arquivo mutifasta a seguir:

>Chamoli et al. (2016) tr|I3QIG4|I3QIG4_BACIU 6-phospho-beta-glucosidase (Fragment) OS=Bacillus subtilis GN=bg1 PE=3 SV=1
MSSNEKRFPEGFLWGGAVAANQVEGAYNEGGKGLSTADVSPNGIMSPFDESMTSLNLYHN
GIDFYHRYKEDIALFAEMGFKAFRTSIAWTRIFPNGDEEEPNEEGLRFYDDLFDELLKHH
IEPVVTISHYEMPLGLVKNYGGWKNRKVIEFYERYAKTVFKRYQHKVKYWMTFNEINVVL
HAPFTGGGLVFEEGENKLNAMYQAAHHQFVASALAVKAGHDIIPDSKIGCMIAATTTYPM
TSKPEDVFAAMENERKTLFFSDVQARGAYPGYMKRYLAENNIEIEMAEGDEELLKEHTVD
YIGFSYYMSMAASTDPEELAKSGGNLLGGVKNPYLKSSEWGWQIDPKGLRITLNTLYDRY
QKPLFIVENGLGAVDKVEEDGTIQDDYRINYLRDHLIEAREAIADGVELIGYTSWGPIDL
VSASTAEMKKRYGFIYVDRDNEGNGTFNRIKKKSFNWYQQVIATNGESL

>Yang_Y et al. (2015) tr|D5KX75|D5KX75_9BACT Beta-glucosidase OS=uncultured bacterium PE=3 SV=1
MTKISLPTCSPLLTKEFIYGVATASFQIEGGSAHRLPCIWDTFCDTPGKIADNSNGHVAC
DHYNNWKQDIDLIESLGVDAYRLSISWPRVITKSGELNPEGVKFYTDILDELKKRNIKAF
VTLYHWDLPQHLEDEGGWLNRETAYAFAHYVDLITLAFGDRVHSYATLNEPFCSAFLGYE
IGIHAPGKVGKQYGRKAAHHLLLAHGLAMTVLKQNSPTTLNGIVLNFTPCYSISEDADDI
AATAFADDYLNQWYMKPIMDGTYPAIIEQLPSAHLPDIHDGDMAIISQSIDYLGINFYTR
QFYKAHPTEIYEPIEPTGPLTDMGWEIYPKSFTELLVTLNNTYTLPPIFITENGAAMPDS
YNNGEINDVDRLDYYNSHLNAVHNATEQGVRIDGYFAWSLMDNFEWAEGYLKRFGIVYVD
YSTQQRTIKNSGLAYKALISNR

>Cao et al. (2015) tr|A0A0F7KKB7|A0A0F7KKB7_9BACT Beta-glucosidase OS=uncultured bacterium PE=3 SV=1
MTSDTARSYRFPEGFLWGAATAAYQIEGSSMADGAGESIWDRFSHTPGNMKDGDTGDVAC
DHYNRWREDIELMKRLNLQAYRFSVSWSRVIPQGRGAINPKGLAFYDRLVDGLLEAGIEP
LATLYHWDLPAALDDRGGWLNPDIADWFADYGQVLFEKFKGRVKTWGTINEPWVIVDGGY
LHGALAPGHRSAYEAVIAGHNVLRAHGAAVRRFREVGEGQIGIVLNIEPKYPASDKPEDE
AARRRAEAQMNRWFLDPLMGRGYPEELTDVYGAAWREFPKEDFELIAEPTDWMGLNWYTR
AVPENAPDAWPTRSRPVRQTQHAHTETGWEVYPPALTDTLVWLSEQTGGKLPLMVTENGS
AWYDPPHAIDGRIHDPMRVHYLQTHIKALHDAIGKGVDLRGYMAWSLLDNLEWSLGYSKR
FGIVHVNFATQERTIKDSGLLYAEVIKTHGDVLNTL

Como podemos extrair o nome do organismo de cada uma dessas sequências?

Solução

O nome do organismo está presente na linha de cabeçalho, isto é, as linhas iniciadas com “ > ”. Além disso, o nome do organismo é sempre iniciado com os caracteres “ OS= ” e é composto por duas palavras.

Sabendo disso, podemos preparar a busca usando a expressão regular:

OS=[a-zA-Z]+s[a-z]+

Isto é, buscamos padrões de texto iniciados em OS= seguidos por quaisquer letras maiúsculas ou minúsculas que podem aparecer uma ou mais vezes. Esse padrão é seguido por um espaço e um conjunto de letras minúsculas que se repete uma ou mais vezes.

Se aplicarmos essa expressão regular ao texto, obteremos:

  • OS=Bacillus subtilis
  • OS=uncultured bacterium
  • OS=uncultured bacterium

Entretanto, a partir do Python 3.11, novas funções para manipulação de expressões regulares foram incluídas. Por exemplo, o padrão (?<=...) indica que desejamos buscar por um conjunto de caracteres que aparece após o trecho indicado por “…”.

Observe como podemos usar esse padrão para resolver o problema proposto:

Figura 23. Busca por duas palavras após o trecho “OS=”. Fonte: próprio autor.

Chamamos isso de lookahead negativa, isto é, a expressão regular irá verificar se o padrão “ OS= ” está presente, mas não irá incluí-lo na captura.

(?<=OS=)[A-z]+s[a-z]+

Se quisermos buscar um padrão que esteja antes do texto, podemos usar o lookahead positivo. Observe a seguinte expressão regular:

[A-z]+s[a-z]+((?=sGN=)|(?=sPE=))

Ela retorna:

  • Bacillus subtilis
  • uncultured bacterium
  • uncultured bacterium

Note que essa expressão se tornou um pouco mais complexa, mas vamos tentar entendê-la parte a parte.

  • [A-z]+
    • Este trecho busca uma ou mais letras maiúsculas ou minúsculas que estejam em sequência;
  • s
    • O trecho anterior deve ser seguido por um espaço;
  • [a-z]+
    • Seguido por uma ou mais letras minúsculas que estejam em sequência;
  • (
    • Este trecho insere um agrupamento. Isso é feito para indicar que o próximo caractere pode ser de dois tipos de padrões;
  • (?=sGN=)
    • O primeiro padrão usa o lookahead positivo para indicar que desejamos obter um trecho que aparece antes de um espaço seguido dos caracteres GN=;
  • |
    • Este metacaractere indica que o padrão retornado pode ser equivalente a expressão regular anterior ou a exibida a seguir;
  • (?=sPE=)
    • O segundo padrão possível usa o lookahead positivo para indicar que desejamos obter um trecho que aparece antes de um espaço seguido dos caracteres PE=;
  • )
    • Por fim, o agrupamento é fechado.
Exercício resolvido #2

Proteínas são macromoléculas compostas por cadeias de aminoácidos conectadas por ligações peptídicas. Os tipos dos aminoácidos são caracterizados pelos átomos na cadeia lateral. Em especial, há dois tipos de grupos de aminoácidos que realizam interações de atração e repulsão: os aminoácidos polares carregados positivamente (como a arginina, a histidina e a lisina) e os aminoácidos polares carregados negativamente (como o aspartato e o glutamato).

Na representação pelo código de uma letra, os aminoácidos positivos são indicados pelos caracteres R, H e K, respectivamente. Já os aminoácidos negativos são indicados pelos caracteres D e E, respectivamente.

Observe a seguinte sequência de aminoácidos de uma proteína lisozima:

>2LZM_1|Chain A|T4 LYSOZYME|Enterobacteria phage T4 (10665)

MNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNCNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRCALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYKNL

Queremos encontrar regiões carregadas positivamente e negativamente. Por isso, utilize expressões regulares para encontrar conjuntos de dois ou mais aminoácidos positivos ou negativos que aparecem em sequência. Quantos conjuntos desses existem?

Construa uma expressão regular para determinar aminoácidos negativos e positivos que sejam vizinhos. Calcule quantas combinações seriam necessárias para resolver esse desafio sem usar expressões regulares.

Implemente um desses exemplos em Python.

Solução

Para solucionar esse problema, vamos primeiro construir as expressões regulares.

Para encontrar conjuntos de aminoácidos negativos em sequência, podemos usar a seguinte expressão regular:

[DE]{2,10}

Nesta solução, usamos os metacaracteres “lista” e “chaves”. Aqui, buscamos caracteres que sejam D ou E, e que aparecem em sequência de duas até 10 vezes (definimos o valor 10 arbitrariamente).

Note que se aplicarmos essa expressão regular, encontraremos três resultados:

MNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNCNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRCALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYKNL

Infelizmente, nesse exemplo não temos nenhuma combinação de três ou mais vizinhos, mas o código permitiria encontrá-los se houvesse. Por exemplo, teste o padrão  [GYTI]{2,10} . Perceba quantos padrões diferentes conseguimos encontrar:

  • IY
  • GYYTIGIG
  • IG
  • IT
  • GI
  • TG
  • ITT
  • TGT

Para encontrar os aminoácidos positivos, poderíamos usar a mesma lógica:

[RKH]{2,10}

A solução seria:

MNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNCNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRCALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYKNL

Ou seja, há três pares de caracteres positivos: RR, KR e KR.

Agora, observe como podemos detectar caracteres positivos e negativos intercalados:

[RKH][DE]|[DE][RKH]

Neste caso, queremos um par de aminoácidos que seja positivo seguido de um negativo, ou que seja negativo seguido de um positivo. Como resultado, teríamos a seguinte solução: KD, DK, KD e EK.

MNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNCNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRCALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYKNL

Por fim, vamos realizar uma implementação em Python:

import re
string = "MNIFEMLRIDEGLRLKIYKDTEGYYTIGIGHLLTKSPSLNAAKSELDKAIGRNCNGVITKDEAEKLFNQDVDAAVRGILRNAKLKPVYDSLDAVRRCALINMVFQMGETGVAGFTNSLRMLQQKRWDEAAVNLAKSRWYNQTPNRAKRVITTFRTGTWDAYKNL"
busca = re.findall("[RKH][DE]|[DE][RKH]", string)
for item in busca:
print(item)

A resposta será:

  • KD
  • DK
  • KD
  • EK

Note que se quiséssemos encontrar esses padrões manualmente, precisaríamos buscar todas as combinações par-a-par entre RKH e DE, ou seja: RD, DR, RE, ER, KD, DK, KE, EK, HD, DH, HE, EH.

Viu como expressões regulares facilitam nossa vida?

Exercício resolvido # 3

Imagine que você precisa realizar uma análise de bioinformática em uma estrutura de proteína. Você acessou o banco de dados PDB e coletou a estrutura de uma lisozima cujo código identificador é 2LZM Você aprenderá mais sobre estruturas de proteínas nos capítulos a seguir, por isso não se preocupe muito com as peculiaridades deste formato de arquivos.

Estruturas 3D de proteínas podem ser armazenadas em arquivos de texto, onde cada linha iniciada com os caracteres ATOM indica que se trata de um átomo. Observe um exemplo a seguir:

ATOM 1 N MET X 1 44.209 -3.284 8.853 1.00 29.12 N
ATOM 2 CA MET X 1 43.783 -1.951 8.984 1.00 19.55 C
ATOM 3 C MET X 1 42.371 -1.965 9.526 1.00 22.27 C
ATOM 4 O MET X 1 41.674 -2.930 9.258 1.00 18.31 O
ATOM 5 CB MET X 1 43.904 -1.314 7.599 1.00 8.40 C
ATOM 6 CG MET X 1 43.398 0.111 7.644 1.00 30.36 C
ATOM 7 SD MET X 1 44.596 1.376 7.226 1.00 39.80 S
ATOM 8 CE MET X 1 45.913 0.959 8.356 1.00 44.83 C
ATOM 9 N ASN X 2 41.980 -0.931 10.310 1.00 17.74 N
ATOM 10 CA ASN X 2 40.653 -0.795 10.918 1.00 5.91 C
ATOM 11 C ASN X 2 40.294 0.681 10.931 1.00 11.34 C
ATOM 12 O ASN X 2 41.137 1.529 10.620 1.00 9.58 O
ATOM 13 CB ASN X 2 40.668 -1.464 12.355 1.00 9.68 C
ATOM 14 CG ASN X 2 41.652 -0.854 13.309 1.00 11.82 C
ATOM 15 OD1 ASN X 2 41.577 0.338 13.515 1.00 18.42 O
ATOM 16 ND2 ASN X 2 42.531 -1.639 13.919 1.00 9.43 N
HETATM 1311 O HOH X 166 38.345 12.531 18.352 1.00 12.66 O
HETATM 1312 O HOH X 167 36.026 18.923 3.146 1.00 26.18 O
HETATM 1313 O HOH X 168 43.261 16.686 1.965 1.00 6.69 O
HETATM 1314 O HOH X 169 42.002 23.279 0.963 1.00 20.51 O
HETATM 1315 O HOH X 170 41.600 2.087 -0.228 1.00 24.62 O

O código acima indica as coordenadas de átomos de dois aminoácidos. Você consegue dizer quais são?

No padrão PDB, a cadeia da proteína é indicada após a definição do aminoácido. Neste exemplo, os resíduos foram indicados pela cadeia “X”. Entretanto, gostaríamos de substituir essa cadeia pela letra “A”. Indique como poderíamos fazer isso usando expressões regulares.

Além disso, ao analisar o arquivo, há cinco átomos que indicam a coordenada de moléculas de água. Você consegue dizer quais átomos não estão representados nesse arquivo?

Construa um programa que remova todos os átomos de água usando expressões regulares.

Solução

O código apresentado anteriormente indica as coordenadas de átomos de dois aminoácidos: MET (metionina) e ASN (asparagina).

Podemos implementar a mudança da cadeia de “X” para “A” da seguinte forma:

import re
string = """
ATOM 1 N MET X 1 44.209 -3.284 8.853 1.00 29.12 N
ATOM 2 CA MET X 1 43.783 -1.951 8.984 1.00 19.55 C
ATOM 3 C MET X 1 42.371 -1.965 9.526 1.00 22.27 C
ATOM 4 O MET X 1 41.674 -2.930 9.258 1.00 18.31 O
ATOM 5 CB MET X 1 43.904 -1.314 7.599 1.00 8.40 C
ATOM 6 CG MET X 1 43.398 0.111 7.644 1.00 30.36 C
ATOM 7 SD MET X 1 44.596 1.376 7.226 1.00 39.80 S
ATOM 8 CE MET X 1 45.913 0.959 8.356 1.00 44.83 C
ATOM 9 N ASN X 2 41.980 -0.931 10.310 1.00 17.74 N
ATOM 10 CA ASN X 2 40.653 -0.795 10.918 1.00 5.91 C
ATOM 11 C ASN X 2 40.294 0.681 10.931 1.00 11.34 C
ATOM 12 O ASN X 2 41.137 1.529 10.620 1.00 9.58 O
ATOM 13 CB ASN X 2 40.668 -1.464 12.355 1.00 9.68 C
ATOM 14 CG ASN X 2 41.652 -0.854 13.309 1.00 11.82 C
ATOM 15 OD1 ASN X 2 41.577 0.338 13.515 1.00 18.42 O
ATOM 16 ND2 ASN X 2 42.531 -1.639 13.919 1.00 9.43 N
HETATM 1311 O HOH X 166 38.345 12.531 18.352 1.00 12.66 O
HETATM 1312 O HOH X 167 36.026 18.923 3.146 1.00 26.18 O
HETATM 1313 O HOH X 168 43.261 16.686 1.965 1.00 6.69 O
HETATM 1314 O HOH X 169 42.002 23.279 0.963 1.00 20.51 O
HETATM 1315 O HOH X 170 41.600 2.087 -0.228 1.00 24.62 O
"""
string = re.sub("X", "A", string)
print(string)

Como resultado, você teria a seguinte string:

ATOM 1 N MET A 1 44.209 -3.284 8.853 1.00 29.12 N
ATOM 2 CA MET A 1 43.783 -1.951 8.984 1.00 19.55 C
ATOM 3 C MET A 1 42.371 -1.965 9.526 1.00 22.27 C
ATOM 4 O MET A 1 41.674 -2.930 9.258 1.00 18.31 O
ATOM 5 CB MET A 1 43.904 -1.314 7.599 1.00 8.40 C
ATOM 6 CG MET A 1 43.398 0.111 7.644 1.00 30.36 C
ATOM 7 SD MET A 1 44.596 1.376 7.226 1.00 39.80 S
ATOM 8 CE MET A 1 45.913 0.959 8.356 1.00 44.83 C
ATOM 9 N ASN A 2 41.980 -0.931 10.310 1.00 17.74 N
ATOM 10 CA ASN A 2 40.653 -0.795 10.918 1.00 5.91 C
ATOM 11 C ASN A 2 40.294 0.681 10.931 1.00 11.34 C
ATOM 12 O ASN A 2 41.137 1.529 10.620 1.00 9.58 O
ATOM 13 CB ASN A 2 40.668 -1.464 12.355 1.00 9.68 C
ATOM 14 CG ASN A 2 41.652 -0.854 13.309 1.00 11.82 C
ATOM 15 OD1 ASN A 2 41.577 0.338 13.515 1.00 18.42 O
ATOM 16 ND2 ASN A 2 42.531 -1.639 13.919 1.00 9.43 N
HETATM 1311 O HOH A 166 38.345 12.531 18.352 1.00 12.66 O
HETATM 1312 O HOH A 167 36.026 18.923 3.146 1.00 26.18 O
HETATM 1313 O HOH A 168 43.261 16.686 1.965 1.00 6.69 O
HETATM 1314 O HOH A 169 42.002 23.279 0.963 1.00 20.51 O
HETATM 1315 O HOH A 170 41.600 2.087 -0.228 1.00 24.62 O

Apesar de ter funcionado no exemplo em questão, note que implementar mudanças na cadeia usando expressões regulares desta forma talvez não seja a melhor alternativa. Imagine que você tenha outras letras X no seu arquivo PDB. Isso faria substituições incorretas, não é?

Vamos refazer o código usando uma expressão regular mais robusta. Ao invés de apenas buscar o caractere “X”, vamos buscar apenas em linhas iniciadas em ATOM. Note que o próximo desafio é remover as águas, então não precisamos mudar a cadeia delas (além disso, podemos usá-las para verificar se o nosso código está correto comparando se as cadeias estão no lugar correto). Para deixar nossa expressão regular mais específica, vamos contar quantos caracteres existem entre o trecho ATOM e a cadeia (neste exemplo, há 17 caracteres). Vamos usar ainda os espaços entre as posições que indicam a cadeia para construir um padrão bem mais robusto (logo, nossa contagem de caracteres indica que são 16 caracteres mais um espaço).

Vamos colocar as expressões do padrão de busca e de substituição em variáveis separadas para facilitar a compreensão do código. Observe como podemos implementar essa expressão regular:

padrao = r'ATOM(.{16}) X '
substi = r'ATOM1 A '
string = re.sub(padrao, substi, string)

Note que usamos um “ r ” antes do texto dos padrões. Além disso, usamos “ 1 ” para indicar um trecho que gostaríamos que se mantivesse igual.

Nosso resultado será o seguinte:

ATOM 1 N MET A 1 44.209 -3.284 8.853 1.00 29.12 N
ATOM 2 CA MET A 1 43.783 -1.951 8.984 1.00 19.55 C
ATOM 3 C MET A 1 42.371 -1.965 9.526 1.00 22.27 C
ATOM 4 O MET A 1 41.674 -2.930 9.258 1.00 18.31 O
ATOM 5 CB MET A 1 43.904 -1.314 7.599 1.00 8.40 C
ATOM 6 CG MET A 1 43.398 0.111 7.644 1.00 30.36 C
ATOM 7 SD MET A 1 44.596 1.376 7.226 1.00 39.80 S
ATOM 8 CE MET A 1 45.913 0.959 8.356 1.00 44.83 C
ATOM 9 N ASN A 2 41.980 -0.931 10.310 1.00 17.74 N
ATOM 10 CA ASN A 2 40.653 -0.795 10.918 1.00 5.91 C
ATOM 11 C ASN A 2 40.294 0.681 10.931 1.00 11.34 C
ATOM 12 O ASN A 2 41.137 1.529 10.620 1.00 9.58 O
ATOM 13 CB ASN A 2 40.668 -1.464 12.355 1.00 9.68 C
ATOM 14 CG ASN A 2 41.652 -0.854 13.309 1.00 11.82 C
ATOM 15 OD1 ASN A 2 41.577 0.338 13.515 1.00 18.42 O
ATOM 16 ND2 ASN A 2 42.531 -1.639 13.919 1.00 9.43 N
HETATM 1311 O HOH X 166 38.345 12.531 18.352 1.00 12.66 O
HETATM 1312 O HOH X 167 36.026 18.923 3.146 1.00 26.18 O
HETATM 1313 O HOH X 168 43.261 16.686 1.965 1.00 6.69 O
HETATM 1314 O HOH X 169 42.002 23.279 0.963 1.00 20.51 O
HETATM 1315 O HOH X 170 41.600 2.087 -0.228 1.00 24.62 O

Perceba que substituímos corretamente apenas a cadeia das linhas iniciadas em ATOM.

Por fim, vamos remover os átomos de água. Note que o PDB em questão não apresenta os átomos de hidrogênio.

padrao = '.*HOH.*'
substi = ''
string = re.sub(padrao, substi, string)
print(string)

Neste caso, estamos apagando todas as linhas que tenham o trecho “HOH”. Observe que a combinação dos padrões “ .* ” indica que queremos qualquer conjunto de caracteres em uma linha. Neste caso, usamos esse padrão antes e depois de “HOH” para indicar que esse padrão está no meio de uma linha.

Poderíamos implementar essa mesma busca de outra forma, por exemplo, procurando por trechos iniciados com “HETATM”. Observe como ficaria o código:

padrao = 'HETATM.*'
substi = ''
string = re.sub(padrao, substi, string)
print(string)

Neste caso, precisamos indicar o padrão “ .* ” apenas após o trecho “HETATM”, pois esse código aparece apenas no início da linha.

Referências

Jargas, A.M. Expressões Regulares – 5a Edição: Uma Abordagem Divertida; NOVATEC, 2016; ISBN 978-85-7522-474-8.

Mariano, D.; de Melo-Minardi, R.C. Introdução à Programação Para Bioinformática Com Perl. ISBN: 978-1522803942. CreateSpace. 2016. Disponível aqui.

URL Original da postagem: Read More

Compartilhe:

Deixe uma resposta

Postagens

Charles Corrêa Blog