Uma das tarefas mais frequentes que um cientista de dados tem que enfrentar é a de limpeza e pré-processamento de bases de dados. Muitas vezes (senão todas), as variáveis presentes nestas bases são palavras ou longas sequências de texto representadas por strings. Processar e limpar estas variáveis até se obter o resultado desejado pode se tornar uma tarefa enfadonha utilizando apenas python ou bibliotecas mais genéricas como pandas. Felizmente existem algumas ferramentas que tornam todo o processamento de strings muito mais fácil. Uma das ferramentas mais utilizadas e testadas ao longo do tempo são as Regular Expressions ou regex.
Regular expressions são basicamente padrões de texto representados por uma sintaxe especial utilizados para automatizar a procura e substituição de texto.
Regex já é utilizado há muito tempo e existe todo um embasamento matemático que vem do campo de teoria dos autômatos pertencente a área de Ciência da Computação, dito isto neste artigo não iremos abordar esta parte teórica.
Em um primeiro encontro regular expressions podem parecer visualmente intimidadoras e por esta razão muitos cientistas de dados fogem delas e acabam utilizando soluções mais desajeitadas. Porém aprender, e adorar, regular expressions pode se mostrar um investimento valioso de tempo visto que:
- A partir do momento que se consolida alguns dos principais conceitos e padrões é possível realizar operações complexas envolvendo dados em formato string de maneira bem mais concisa e elegante.
- Possuem tempo de execução bem mais veloz do que as soluções mais manuais.
- Regular Expressions estão disseminadas em quase todas as linguagens de programação, ferramentas de tratamento de dados e utilidades de linhas de comando.
Raw strings
Antes de começar a falar sobre regex é bom introduzir o conceito de raw strings em python, dado que é uma boa prática sempre colocar os padrões regex em raw strings. De maneira simples raw strings são strings que vão acabar tratando backslash() de maneira literal e isso impedira que o caractere especial seja considerado como um caractere de escape. Como veremos adiante muitos metacaracteres de regex utilizam backslash() . A sintaxe das raw strings é simplesmente colocar um r (maiúsculo também é válido) antes de uma string:
#raw string
r”string”
Ex:
# string normal
>>> print(“Amorab faz bem”)
Amor faz bem
# raw string
>>> print(r”Amorab faz bem”)
Amorab faz bem
Ao utilizar raw strings evita-se possíveis erros nos padrões com caracteres especiais que usam backslash e o código se torna mais legível.
Regex
Quando se fala em regex o termo padrão é dito para descrever uma regular expression que foi escrita. Se o padrão é achado no meio do string que se está procurando diz-se que o padrão “deu match”. Se fosse desejado achar o string “me” em outros strings o padrão em regex seria simplesmente me:

É importante mencionar que as strings procuradas só vão dar match no que foi literalmente fornecido no padrão, nesse caso letras com acento e letras maiúsculas são consideradas diferentes de letras sem acento e letras minúsculas.
Módulo re do python
Python possui um módulo proṕrio para para regex chamado de módulo re. Este módulo possui um conjunto de funções e classes próprias para se trabalhar com regex. Uma das funções mais usadas é a re.search() onde o primeiro parâmetro é o padrão e o segundo é a string.
>>> match = re.search(“me”, “medalha”)
>>> print(match)
<re.Match object; span=(0, 2), match=’me’
>>>> match = re.search(“me”, “garrafa”)
>>> print(match)
None
É possível observar acima que um objeto match é retornado quando ocorre o match, já quando o match não ocorre None é retornado. Objetos match podem ser considerados como um True booleano.
>>> lista = [‘Câmera’, ‘alface’, ‘nova era’, ‘tomate’,
>>> ‘batedeira’, ‘era uma vez…’, ‘Era a última gota…’]
>>> padrão = r’era’
>>> for i in lista:
>>> if re.search(padrão ,i):
>>> print(i, ‘Deu match!’)
>>> else:
>>> print(i, ‘Não deu match :(‘)
Câmera Deu match!
alface Não deu match 🙁
nova era Deu match!
tomate Não deu match 🙁
batedeira Deu match!
era uma vez… Deu match!
Era a última gota… Não deu match 🙁
Cabe um comentário que existe uma outra função que tem uma funcionalidade parecida porém com uma diferença, a função re.match(). A função re.match() não faz a procura ao longo do string inteiro ela só vai dar match se o padrão encotrar no começo da string. Executando o código acima apenas trocando search por match temos os seguintes resultados:
>>> lista = [‘Câmera’, ‘alface’, ‘nova era’, ‘tomate’,
>>> ‘batedera’, ‘era uma vez…’, ‘Era a última gota…’]
>>> padrao = r’era’
>>> for i in lista:
>>> if re.match(padrao ,i):
>>> print(i, ‘Deu match!’)
>>> else:
>>> print(i, ‘Não deu match :(‘)
Câmera Não deu match 🙁
alface Não deu match 🙁
nova era Não deu match 🙁
tomate Não deu match 🙁
batedeira Não deu match 🙁
era uma vez… Deu match!
Era a última gota… Não deu match 🙁
Apesar de a função match ser mais restrita ela tem tempo de execução mais rápido.
Caractere especial set([ ])
Até agora nada de especial foi mostrado que não pudéssemos fazer usando apenas python, a mágica do regex realmente acontece utilizando seus caracteres especiais, algumas vezes chamados de metacaracteres.
O primeiro desses caracteres especiais a ser apresentado é chamado de set que tem forma de caracteres dentro de [ ] (colchetes).
Um set permite poder escolher entre qualquer caractere dentro dos colchetes, um padrão que tivesse formato “cas[aoe]”, pode dar match tanto nas palavras casa, caso e case.
Digamos que queremos pegar instâncias em que a palavra data science apareça porém algumas vezes ela começa com letra maiúscula, pode ser separada por hífen e pode começar nas duas palavras com letra maiúscula. Nesse caso utilizar o set ajuda no problema.
>>> lista = [‘Data Science é a melhor área’,
>>> ‘Eu trabalho com data science’,
>>> ‘Data science é divertido de se trabalhar’,
>>> ‘eu fiz um curso em data science’,
>>> ‘data-science está em alta’]
>>> padrao = r'[Dd]ata[- ][Ss]cience’
>>> for i in lista:
>>> if re.search(padrao ,i):
>>> print(‘Deu match!’)
>>> else:
>>> print(‘Não deu match :(‘)
Deu match!
Deu match!
Deu match!
Deu match!
Deu match!
Vale ressaltar que o espaço entre caracteres conta como um caractere também.
Existe ainda um outro caractere especial de regex que pode ser utilizado para abreviar os caracteres dentro de um set, o caractere “-” chamado de intervalo. Este caractere é usado para pegar tanto intervalos alfabético, digamos um padrão para pegar as letras de a à f ficaria [a-f] ou de números, um padrão que pegaria dos dígitos 1 ao 7 ficaria [1–7] .
Ex: queremos pegar todas as datas de 1970 até 2005.
lista = [‘2500’, ‘1977’, ‘1989’, ‘1999’,’2122′, ‘2005’, ‘1820’]
padrao = r'[12][90][0-9][0-9]’
for i in lista:
if re.match(padrao ,i):
print(i, ‘Deu match!’)
else:
print(i, ‘Não deu match :(‘)
2500 Não deu match 🙁
1977 Deu match!
1989 Deu match!
1999 Deu match!
2122 Não deu match 🙁
2005 Deu match!
1820 Não deu match 🙁
Usando pandas para dar matches
O módulo pandas possui uma série de métodos especiais para strings que utilizam regex por trás.Um dos mais utilizados é o método de séries .str.contains() que desempenha um search verificando se cada string da série resulta em um match com o padrão e retorna uma máscara booleana.
Ex:
>>> serie = pd.Series([‘Data Science é a melhor área’,
>>> ‘Eu trabalho com data science’,
>>> ‘Data science é divertido de se trabalhar’,
>>> ‘eu fiz um curso em data science’,
>>> ‘data-science está em alta’])
>>> padrao = r'[Dd]ata[- ][Ss]cience’
>>> mask = serie.str.contains(padrao)
>>> print(mask)
0 True
1 True
2 True
3 True
4 True
dtype: bool
Outro detalhe que vale mencionar é que como alguns caracteres especiais de regex usam caracteres como, hífens(-), colchetes([]) e etc, esses caracteres tem que ter uma barra() antes deles para identificá-los, como visto no exemplo acima com o hífen.
Quantificadores
Para certos padrões que se repetem ou ocorrem n vezes existe um conjunto de sintaxes especiais(Quantificadores) em regex que especificam isso.
Os quantificadores especificam quantas vezes o padrão ou caractere anterior tem que ocorrer.
Existem 2 maneiras de especificar quantificadores:
Numericamente:

Caracteres especiais:

Por exemplo queremos validar códigos em que os três primeiros dígitos vão de 2 à 5 e os últimos 3 são quaisquer dígitos.
>>> p = r'[2-5]{3}[0-9]{3}’
>>> serie = pd.Series([‘234000′,’502331’, ‘543121’, ‘371087’])
>>> serie.str.contains(p)
0 True
1 False
2 True
3 False
dtype: bool
Caracteres especiais
Ainda existem alguns outros caracteres especiais que representam padrões constantemente utilizados.

Dessa forma o código acima pode ser simplificado da seguinte maneira:
>>> p = r'[2-5]{3}d{3}’
>>> serie = pd.Series([‘234000′,’502331’, ‘543121’, ‘371087’])
>>> serie.str.contains(p)
0 True
1 False
2 True
3 False
dtype: bool
Âncoras
Âncoras em regex são utilizadas para verificar se um padrão ocorre no final ou no começo de uma string.

>>> p = r’^[Dd]ata’
>>> serie = pd.Series([‘Data science’, ‘Eu trabalho com data science’, ‘data risk’, ‘Big Data’])
>>> serie.str.contains(p)
0 True
1 False
2 True
3 False
dtype: bool
Grupos
Grupos, algumas vezes chamados de capturing groups, são utilizados em regex para agrupar uma sequência de caracteres. Utilizam ( )(parênteses) como sintaxe para pegar determinados grupos.
Ex:
Queremos validar códigos de 9 caracteres onde a cada grupo de 3 caracteres o primeiro é um dígito e os dois caracteres são letras sendo que esses grupos de 3 caracteres podem ser divididos por um hífen.
>>> p = r’d[a-z]{2}(-?d[a-z]{2}){2}’
>>> serie = pd.Series([‘1ab-2cv-8de’, ’54c-1yu-8bl’, ‘0fr2et6qb’, ‘1op-7xn--1kd’])
>>> serie.str.contains(p)
0 True
1 False
2 True
3 False
dtype: bool
Padrões negativos
É comum algumas vezes queremos dar match em padrões que não queremos que uma string possua, regex utiliza uma sintaxe apropriada para ter resultados dessa forma.

Ex: Queremos as linhas de uma série que contenham qualquer caractere que não seja um dígito.
>>> p = r’D’
>>> serie = pd.Series([‘1265’, ‘541y827’, ‘—–‘, ‘bola’])
>>> serie.str.contains(p)
0 False
1 True
2 True
3 True
dtype: bool
Conclusão
Para cobrir regex na totalidade seria necessário pelo menos um livro inteiro. Este artigo almeja introduzir alguns conceitos básicos e algumas possíveis ideias de como regular expressions podem ser utilizadas para facilitar o processo de limpeza e tratamento de dados. Ainda existem alguns conceitos que o leitor pode pesquisar por conta própria nos sites mais especializados mencionados nas referências.
Referências
2. rexegg — Site de referência ótimo para regex
4. Regexr Site que permite a construção de padrões com explicação e highlights
[Rr]eg(ular)?s?[Ee]x(pressions)?
PARTE 2: https://medium.com/datarisk-io/rr-eg-ular-s-ee-x-pressions-segunda-parte-2-dois-cec5a510ea8d

Cientista de dados