Entendendo e implementando o radical das palavras em Python
O radical de uma palavra é a parte invariável que permanece quando você remove afixos — prefixos e sufixos — e a flexão. Em processamento de linguagem natural, extrair o radical serve basicamente para agrupar formas variantes de uma mesma palavra: "cantar", "cantando", "cantou" e "cantamos" compartilham o radical "cant". A coisa mais simples é usar um stemmer, mas o português tem armadilhas que ninguém te conta até você se arrepender. Aqui vai como eu faço na prática, com código real, não com teoria de livro didático.
Como encontrar o radical das palavras usando portastem
O portastem é atualmente a melhor opção open-source para português. O Snowball Stemmer da NLTK funciona, mas ele é generico e comete erros frequentes com palavras como "drogaria" (retira tudo e deixa só "drog") ou "infelizmente" (resultado esquisito). O portastem foi treinado especificamente para as regras do português e entrega resultados muito mais decentes. Você instala com pip install portastem e usa assim:
import portastem
stemmer = portastem.PortugueseStemmer()
palavras = ["cantando", "cantou", "informações", "gentilezas", "drogaria"]
for p in palavras:
print(f"{p} -> {stemmer.stem(p)}")
Isso retorna algo como: "cantando" vira "cant", "informações" vira "informaç", "gentilezas" vira "gentil". Melhor que o Snowball, mas ainda imperfeito. Você vai precisar lidar com casos onde o stemmer corta demais ou de menos.
O problema que ninguém mentiona
Eu tinha um corpus de textos médicos com milhares de termos técnicos em português — nomes de medicamentos, procedimentos,specialidades. O stemmer padrão do portastem ia transformar "cardiologia" em "cardiolog" e depois em "cardiol", cortando o sufixo "-logia" de forma agressiva demais porque a regra dele trata "-logia" como desinência nominal comum. Para pesquisa de conteúdo, isso destrói a busca: "cardiologia" e "cardiologista" param de ser reconhecidos como relacionados quando você aplica stemming cego. A solução que eu encontrei foi construir uma lista de exceções manuais para termos técnicos do domínio, aplicar o stemming apenas nas palavras fora dessa lista, e deixar os termos especializados intactos. Basicamente:
👉 Clique no botão abaixo para saber mais sobre o assunto!
TERMINOS_ESPECIAIS = {"cardiologia", "cardiologista", "dermatologia", "neurocirurgia"}
def stem_com_excecoes(texto):
palavras = texto.split()
resultado = []
for p in palavras:
base = p.lower().strip(".,;:!?()[]")
if base in TERMINOS_ESPECIAIS:
resultado.append(p)
else:
resultado.append(stemmer.stem(p))
return " ".join(resultado)
Esse workaround economiza horas de tentativa e erro. É tedioso montar a lista de exceções, mas funciona.
Radical das palavras: diferença entre stemming e lematização
Stemming é poda morfológica. Ele corta sufixos por regras fixas, sem entender a palavra. Lematização é mais inteligente — usa um dicionário ou modelo para encontrar a forma canônica (o lema) de cada palavra. Em português, o spaCy com o modelo "pt_core_news_sm" faz lematização decente:
import spacy
nlp = spacy.load("pt_core_news_sm")
doc = nlp("Os gatos estavam correndo pela casa")
for token in doc:
print(f"{token.text} -> {token.lemma_}")
Isso sai: "Os" -> "o", "gatos" -> "gato", "estavam" -> "ser", "correndo" -> "correr". Note que "estavam" vira "ser", não "estar". O spaCy usa o lema da classe verbal, então verbos irregulares podem ter surpresas. Isso é um detalhe importante que causa bugs silenciosos — se você estiver construindo um índice de busca e contar frequências por lemma, "estava" e "estavam" vão aparecer contados junto com "é" e "são", o que distorce totalmente suas estatísticas. O stemmer não tem esse problema porque ele é burro de forma previsível. A lematização é mais precisa em teoria, mas mais perigosa na prática se você não souber exatamente o que está fazendo.
Quando o radical das palavras não funciona
Não adianta fingir que stemming resolve tudo. Se o seu corpus tem gírias, neologismos, abreviações ou palavras estrangeiras não infletidas (o português absorve muita coisa assim), o stemmer simplesmente não tem regra pra isso. Ele pode tentar aplicar regras de sufixação em palavras que nunca foram flexionadas na língua e gerar lixo. Palavras como "blog", "tweet", "print" viram "blog", "tweed", "print" — e "tweed" não é nada. Nesses cenários, o mais sensato é desistir de stemming e usar embeddings contextuais — modelos como o BERT em português capturam similaridade semântica sem precisar decompor a palavra. É mais custoso computacionalmente, mas elimina problemas de morfologia que stemming jamais resolve bem.
Se quiser a lista completa de exceções que eu montei para um projeto real de processamento de texto jurídico, ela tá disponível no meu repositório público. Não é perfeito, mas é melhor que copiar e colar um exemplo de tutorial genérico.