Como fazer deteção de dados duplicados em Machine Learning: passo a passo
Este tutorial mostra como detetar e tratar dados duplicados em Machine Learning para melhorar a qualidade do treino e reduzir o viés. Explicarei por que as duplicações são problemáticas e apresentarei um passo a passo prático com Python, pandas e scikit-learn.
Pré-requisitos
- Python 3.8+ com pandas e scikit-learn instalados
- Um ficheiro CSV ou DataFrame com registos que possam repetir-se
- Conhecimentos básicos de manipulação de DataFrame (pandas)
Passo 1: Perceber que tipo de duplicados existem
Duplicados podem ser linhas exactamente idênticas, registos com chaves repetidas (ID) ou registos quase iguais (pequenas diferenças nos valores). Identificar o tipo orienta a estratégia: eliminar, agregar ou marcar.
Passo 2: Carregar os dados
Carrega o CSV para um DataFrame e vê as primeiras linhas. Isto ajuda a perceber colunas-chave e a existência de NaNs.
import pandas as pd
df = pd.read_csv('dados.csv')
print(df.head())
print(df.shape)
Passo 3: Duplicados exactos (linhas idênticas)
Usa pandas para contar linhas exactamente iguais e, se fizer sentido, removê-las. Antes de remover, guarda a contagem para auditoria.
# contar duplicados exactos (mantém a primeira ocorrência)
num_dup = df.duplicated(keep='first').sum()
print(f'Duplicados exactos: {num_dup}')
# remover duplicados exactos
df_no_exact_dup = df.drop_duplicates(keep='first')
print(df_no_exact_dup.shape)
Passo 4: Duplicados por chave (ID) — localizar e decidir
Se existir uma coluna ID que devia ser única, identifica IDs repetidos. Depois decide: manter a primeira, a última, ou agregar (por exemplo média).
# encontrar IDs duplicados
id_col = 'customer_id' # ajusta para a tua coluna
dups = df[df.duplicated(subset=[id_col], keep=False)].sort_values(id_col)
print(dups.head())
# exemplo: manter o registo mais recente por data
df[id_col] = df[id_col].astype(str)
df['date'] = pd.to_datetime(df['date'])
# ordena por date e remove duplicados mantendo o mais recente
df_latest = df.sort_values('date').drop_duplicates(subset=[id_col], keep='last')
print(df_latest.shape)
Passo 5: Duplicados quase iguais (fuzzy duplicates)
Para registos com pequenas diferenças (erros tipográficos ou variações) usa técnicas de fuzzy matching. Uma abordagem simples: criar hashes de colunas normalizadas ou usar a distância de Levenshtein. Aqui mostro uma versão com normalização e comparação por similaridade usando scikit-learn e fingerprints.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# exemplo para colunas nome + morada
df['name_addr'] = (df['name'].fillna('').str.lower().str.replace(r'\W+', ' ', regex=True)
+ ' ' + df['address'].fillna('').str.lower().str.replace(r'\W+', ' ', regex=True))
vec = TfidfVectorizer().fit_transform(df['name_addr'])
sim = cosine_similarity(vec[:1000], vec[:1000]) # limita para evitar OOM
# encontra pares com similaridade alta
threshold = 0.85
pairs = []
for i in range(sim.shape[0]):
for j in range(i+1, sim.shape[1]):
if sim[i, j] > threshold:
pairs.append((i, j, sim[i, j]))
print('pares fuzzy semelhantes:', len(pairs))
Passo 6: Agregar ou marcar duplicados para treino
Decide se vais eliminar, agregar (por exemplo média de métricas) ou marcar com uma flag. Para modelos, por vezes é preferível manter e adicionar uma coluna is_duplicate para que o modelo aprenda padrões.
# exemplo: adicionar flag de duplicado por chave
df['is_duplicate_by_id'] = df.duplicated(subset=[id_col], keep='first')
# exemplo: agregar métricas por ID
agg = df.groupby(id_col).agg({'value': 'mean', 'is_active': 'max'}).reset_index()
print(agg.head())
Passo 7: Integrar no pipeline de Machine Learning
Inclui a limpeza de duplicados antes do split treino/teste. Se removeres duplicados após o split, corres o risco de ter registos iguais nos dois conjuntos (data leakage).
# limpeza antes do split
from sklearn.model_selection import train_test_split
df_clean = df_no_exact_dup.copy()
X = df_clean.drop('target', axis=1)
y = df_clean['target']
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
Verificar o resultado
Confirma que já não existem duplicados problemáticos: conta duplicados exactos e por chave, verifica pares fuzzy suspeitos e assegura que não há leakage entre treino e teste (IDs em comum).
# verificar duplicados exactos e por ID
print('exact dup after:', X_train.duplicated().sum() + X_test.duplicated().sum())
print('IDs comuns entre treino e teste:', set(X_train[id_col]).intersection(set(X_test[id_col])))
Conclusão
Tratar duplicados melhora a qualidade do conjunto de treino e reduz viés e leakage. Próximos passos: automatizar a deteção com funções reutilizáveis, experimentar thresholds para fuzzy matching e registar as alterações num pipeline ETL. Dica: faz sempre um backup dos dados originais antes de remover ou agregar — preferes marcar ou eliminar?