Capítulo 17, Intermediário
Texto com o acessor str
Nomes com espaços sobrando, siglas, acentos, o mesmo valor escrito de três jeitos: a maior parte da sujeira de uma planilha é texto. O acessor `.str` aplica as operações de texto à coluna inteira, de uma vez, e deixa os ausentes passarem.
Separar e extrair
O split com expand=True devolve colunas em vez de listas, e o extract usa uma expressão regular com grupos nomeados para tirar partes de um texto:
import pandas as pd
pd.set_option("display.width", 170)
pd.set_option("display.max_columns", 20)
df = pd.read_csv("dados/funcionarios_100.csv").drop_duplicates("id_funcionario", keep="last")
partes = df["nome"].str.split(" ", n=1, expand=True)
partes.columns = ["primeiro_nome", "sobrenome"]
print(partes.head(3))
extraido = df["nome"].str.extract(r"(?P<primeiro>\S+)\s+(?P<ultimo>\S+)$")
print(extraido.head(3))
primeiro_nome sobrenome
0 Lucas Nunes
1 João Rocha
2 Olívia Nunes
primeiro ultimo
0 Lucas Nunes
1 João Rocha
2 Olívia Nunes
O n=1 separa só no primeiro espaço, e o resto fica inteiro no sobrenome (importante para nomes compostos). Os grupos nomeados (?P<primeiro>) viram os nomes das colunas do resultado.
Medir, buscar e testar
nomes = df["nome"]
print(nomes.str.len().describe()[["min", "max"]].tolist())
print(int(nomes.str.startswith("A").sum()), int(nomes.str.contains(r"^[A-C]", regex=True).sum()))
print(int(nomes.str.count("a").sum()), nomes.str.upper().head(2).tolist())
[8.0, 16.0]
4 11
211 ['LUCAS NUNES', 'JOÃO ROCHA']
O contains aceita uma expressão regular (regex=True por padrão), e o count conta as ocorrências de um padrão em cada texto. Em todos os casos, um valor ausente passa sem erro e continua ausente (ou False, nas respostas booleanas).
Montar texto
Somar colunas de texto com + funciona, mas qualquer ausente contamina o resultado: um texto somado a NaN é NaN. O str.cat deixa você escolher o que fazer com ele:
pequena = pd.DataFrame({"nome": ["Ana", "Bruno", "Carla"], "departamento": ["RH", None, "Marketing"]})
print((pequena["nome"] + " (" + pequena["departamento"] + ")").tolist())
print(pequena["nome"].str.cat(pequena["departamento"], sep=" - ", na_rep="sem depto").tolist())
print(df["id_funcionario"].astype(str).str.zfill(6).head(2).tolist())
['Ana (RH)', nan, 'Carla (Marketing)']
['Ana - RH', 'Bruno - sem depto', 'Carla - Marketing']
['001001', '001002']
O zfill completa com zeros à esquerda, útil para códigos de largura fixa (o 1001 vira 001001).
Achar o "mesmo" valor escrito de formas diferentes
Dois cadastros, João Pereira e joao pereira, são a mesma pessoa, mas o Pandas não sabe. A solução é criar uma chave normalizada: sem acento, em minúsculas e com um espaço só entre as palavras. Duplicatas aparecem na chave, e não no texto original:
def chave(textos: pd.Series) -> pd.Series:
return (
textos.str.strip().str.lower()
.str.normalize("NFKD").str.encode("ascii", errors="ignore").str.decode("ascii")
.str.replace(r"\s+", " ", regex=True)
)
cadastro = pd.Series(["João Pereira", " joao pereira ", "Maria Souza", "MARIA SOUZA"])
print(chave(cadastro).tolist())
print(int(cadastro.duplicated().sum()), int(chave(cadastro).duplicated().sum()))
['joao pereira', 'joao pereira', 'maria souza', 'maria souza']
0 2
Sem normalizar, o duplicated() não acha nenhum (0). Com a chave, acha 2. É a técnica por trás de qualquer "deduplicar clientes", e é também o princípio das junções: duas tabelas só se encontram se as chaves forem escritas igual.
Um aviso sobre expressões regulares
Com o pyarrow instalado, as operações de texto do tipo str usam, quando podem, o motor de expressões regulares do Arrow, e não o módulo re do Python. Para os padrões comuns (\s, \S, \d, \w, grupos, âncoras) o resultado é o mesmo. Mas existem diferenças nas bordas: no capítulo 10, o escape \u0300 foi rejeitado pelo Arrow, enquanto o Python o aceita. A regra prática: prefira padrões simples e portáveis, e teste em um exemplo pequeno quando usar um recurso incomum.
Texto e desempenho
Operar sobre texto é mais caro que operar sobre números, e o tipo
strdo Pandas 3 ajuda. Em colunas com poucos valores distintos (departamento, cidade, canal), o tipocategorydo capítulo 19 economiza muita memória e acelera agrupamentos.
Exercício 1
Sobrenome e iniciais
Escreva iniciais(serie), que receba uma Series de nomes completos e devolva as iniciais do primeiro e do último nome, em maiúsculas (por exemplo, Ana Souza vira AS). Teste com três nomes.