Essa é uma aula de introdução à manipulação de dados textuais, daqui em diante chamados de strings, com foco em expressões regulares (regex). Expressões regulares são úteis pois strings, geralmente, contêm informações não estruturadas e regex é uma linguagem concisa para descrever e manipular padrões em strings.
Para tratar strings utilizaremos as funções básicas do Python e as bibliotecas re
e pandas
:
import re import pandas as pd
Você já aprendeu previamente a criar objetos que representam textos, vamos criar alguns:
texto1 = "Essa é uma string" texto2 = 'Para incluir "aspas duplas" em uma string deve-se utilizar aspas simples' print(texto1)
# Essa é uma string
print(texto2)
# Para incluir "aspas duplas" em uma string deve-se utilizar aspas simples
Escapando caracteres especiais
Para inserir outros caracateres especiais em uma string é necessário utilizar a contrabarra para "escapar" o caracter, assim o Python não gera um erro ou entende o caracter de maneira inesperada. Exemplos:
print("string com \"aspas\" dentro")
# string com "aspas" dentro
print("c:\users\fernando") # gera um erro pois a "\" é um caracter especial
# (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3: truncated \uXXXX escape (<string>, line 1)
print("c:\\users\\fernando") # adicione uma contrabarra para escapar uma contrabarra
# c:\users\fernando
Pense nessa contrabarra como uma instrução interna do Python dizendo para — quando esse objeto for salvo em um arquivo de texto ou usado como uma legenda em um gráfico, por exemplo — incluir o caracter especial ao usar a string em um trecho de código adiante. Note também que a representação impressa da string não é a mesma que a própria string.
Como contar caracteres?
A função len()
informa o número de caracteres em uma string e Series.str.len()
em uma pandas
Series:
len("c:\\Win") # contrabarra não conta
# 6
pd.Series(["c:\\Win", "Análise Macro", "Fernando"]).str.len()
Como combinar strings?
Para concatenar duas ou mais strings use o +
:
txt1 = "Última atualização" txt2 = pd.to_datetime("today").ctime() txt1 + ": " + txt2
# 'Última atualização: Wed Sep 11 09:20:50 2024'
E para concatenar strings utilizando um separador usejoin()
, seguindo a sintaxe"separador".join(["texto 1", "texto 2"])
:
txt3 = ["a", "b", "c", "d", "e"] ", ".join(txt3)
# 'a, b, c, d, e' Por fim, em um exemplo mais avançado, para concatenar duas listas de strings elemento a elemento, primeiro combine os elementos tal como[(lista_X[0], lista_Y[0]), (lista_X[1], lista_Y[1]), ..., (lista_X[n], lista_Y[n])]
com a funçãozip()
e em seguida iterejoin()
sobre este resultado:
txt4 = ["1", "2", "3", "4", "5"] list(map("-".join, zip(txt3, txt4)))
# ['a-1', 'b-2', 'c-3', 'd-4', 'e-5']
Este processo é muito mais simples ao trabalhar com pandas
Series:
pd.Series(txt3) + "-" + pd.Series(txt4)
Como filtrar strings?
Para extrair partes de uma string podemos utilizar o índice dos caracteres da mesma ("string"[indice]
):
texto1[<span class="hljs-number">0</span>] <span class="hljs-comment"># 1º caracter</span>
# 'E'
texto1[0:6] # do 1º caracter até o 5º caracter, ou seja inicio:fim (fim não incluso)
# 'Essa é'
texto1[-1] # valores negativos contam de trás pra frente (último caracter)
# 'g'
texto1[-3:-1] # do antepenúltimo ao penúltimo
# 'in'
texto1[-3:] # do antepenúltimo ao último
# 'ing'
Em pandas
Series o procedimento é semelhante com str.slice()
:
pd.Series([texto1, texto2]).str.slice(start = -1)
pd.Series([texto1, texto2]).str.slice(stop = 5)
pd.Series([texto1, texto2]).str.slice(start = -3, stop = -1)
Caixa alta, caixa baixa e sentença
A função lower()
permite converter a string para letras minúsculas:
texto2.lower()
# 'para incluir "aspas duplas" em uma string deve-se utilizar aspas simples'
A função upper()
permite converter a string para letras maiúsculas:
texto2.upper()
# 'PARA INCLUIR "ASPAS DUPLAS" EM UMA STRING DEVE-SE UTILIZAR ASPAS SIMPLES'
A função capitalize()
permite converter a string para uma sentença (primeira letra maiúscula):
texto2.upper().capitalize()
# 'Para incluir "aspas duplas" em uma string deve-se utilizar aspas simples'
Padrões de correspondência com regex
Regex é uma linguagem que permite descrever padrões em strings. É difícil e demorado para entender (não é uma linguagem humana), mas depois de algum treinamento notamos que é um recurso extremamente útil. Vamos aprender sobre regex de uma forma intuitiva e didática primeiro, depois avançamos para exemplos práticos.
Para essa primeira parte definimos uma função para visualizar um padrão de uma regex em um texto (você já aprendeu a definir funções, a única novidade aqui é a função re.finditer()
, sobre a qual abordaremos adiante):
# Função para visualizar padrão em um texto def str_view(texto, regex): padroes = list(re.finditer(regex, texto)) if padroes: for p in reversed(padroes): inicio = p.start() fim = p.end() texto = texto[:inicio] + "<" + texto[inicio:fim] + ">" + texto[fim:] else: texto = "" return texto
O padrão mais simples é a busca por um padrão exato em uma string, por exemplo:
txt5 = ["maça", "banana", "ameixa"] str_view(txt5[2], "am")
# '<am>eixa'
A str_view()
é uma função apenas didática que serve para visualizar um padrão em uma string. Aqui procuramos pelo padrão "am" e a função destaca este padrão nos elementos (se encontrado) com os símbolos <>
.
Tornando o padrão procurado um pouco mais complexo, podemos usar o ".
" para procurar por qualquer caracter (antes e depois do "a"):
str_view(txt5[<span class="hljs-number">0</span>], <span class="hljs-string">".a."</span>)
# '<maç>a'
list(map(lambda x: str_view(x, ".a."), txt5)) # iterando para todos os items da lista
# ['<maç>a', '<ban>ana', '']
Para procurar por um padrão no começo ou no final de uma string utilize as âncoras:
^
procura por padrão no começo da string;$
procura por padrão no final da string.
Exemplos:
list(map(<span class="hljs-keyword">lambda</span> x: str_view(x, <span class="hljs-string">"^a"</span>), txt5))
# ['', '', '<a>meixa']
list(map(<span class="hljs-keyword">lambda</span> x: str_view(x, <span class="hljs-string">"a$"</span>), txt5))
# ['maç<a>', 'banan<a>', 'ameix<a>']
Conforme a documentação da biblioteca re
, existem diversos caracteres especiais úteis para encontrar padrões:
# Para encontrar números, usamos: str_view("Python é 10!", "\d")
# 'Python é <1><0>!'
str_view("Python é 10!", "\w") # alfanuméricos
# '<P><y><t><h><o><n> <é> <1><0>!'
str_view("Python é 10!", "\s") # espaços
# 'Python< >é< >10!'
str_view("Python é 10!", "[^\w\s]") # pontuações: [^] significa qualquer caracter exceto a regex em seguida
# 'Python é 10<!>'
Aplicando regex
Agora que você aprendeu o básico de expressões regulares, vamos explorar algumas funções úteis que a biblioteca pandas
oferece para manipular strings. Em resumo:
pd.Series.str.contains()
para verificar se um padrão é encontrado em uma string;pd.Series.str.extract()
para extrair um grupo de padrão encontrado de uma string;pandas.Series.str.replace()
para substituir um padrão encontrado em uma string.
Vamos aos exemplos (consulte a documentação para detalhes):
# pandas Series de exemplo txt6 = pd.Series(["Python é 10!", "python é 10", "Pythoné10!"]) # Verificando se há números seguidos de uma pontuação no final da string txt6.str.contains(pat = "[\d]!$")
# Extraindo letras maiúsculas do início da string txt6.str.extract(pat = "(^[A-Z])", expand = False)
# Substituindo espaços por quebras de linha na string txt6.str.replace(pat = "\s", repl = "\n", regex = True)
Tenha acesso ao código e suporte desse e de mais 500 exercícios no Clube AM!
Quer o código desse e de mais de 500 exercícios de análise de dados com ideias validadas por nossos especialistas em problemas reais de análise de dados do seu dia a dia? Além de acesso a vídeos, materiais extras e todo o suporte necessário para você reproduzir esses exercícios? Então, fale com a gente no Whatsapp e veja como fazer parte do Clube AM, clicando aqui.