Cómo detectar datos duplicados en Machine Learning: paso a paso
Este tutorial muestra cómo detectar y tratar datos duplicados en Machine Learning para mejorar la calidad del entrenamiento y reducir el sesgo. Explicaré por qué las duplicaciones son problemáticas y presentaré un paso a paso práctico con Python, pandas y scikit-learn.
Requisitos previos
- Python 3.8+ con pandas y scikit-learn instalados
- Un archivo CSV o DataFrame con registros que puedan repetirse
- Conocimientos básicos de manipulación de DataFrame (pandas)
Paso 1: Entender qué tipo de duplicados existen
Los duplicados pueden ser filas exactamente idénticas, registros con claves repetidas (ID) o registros casi iguales (pequeñas diferencias en los valores). Identificar el tipo orienta la estrategia: eliminar, agregar o marcar.
Paso 2: Cargar los datos
Carga el CSV en un DataFrame y mira las primeras filas. Esto ayuda a identificar columnas clave y la existencia de NaNs.
import pandas as pd
df = pd.read_csv('dados.csv')
print(df.head())
print(df.shape)
Paso 3: Duplicados exactos (filas idénticas)
Usa pandas para contar filas exactamente iguales y, si tiene sentido, eliminarlas. Antes de eliminar, guarda el recuento para auditoría.
# contar duplicados exactos (mantiene la primera ocurrencia)
num_dup = df.duplicated(keep='first').sum()
print(f'Duplicados exactos: {num_dup}')
# eliminar duplicados exactos
df_no_exact_dup = df.drop_duplicates(keep='first')
print(df_no_exact_dup.shape)
Paso 4: Duplicados por clave (ID) — localizar y decidir
Si existe una columna ID que debería ser única, identifica IDs repetidos. Después decide: mantener la primera, la última, o agregar (por ejemplo la media).
# encontrar IDs duplicados
id_col = 'customer_id' # ajusta para tu columna
dups = df[df.duplicated(subset=[id_col], keep=False)].sort_values(id_col)
print(dups.head())
# ejemplo: mantener el registro más reciente por fecha
df[id_col] = df[id_col].astype(str)
df['date'] = pd.to_datetime(df['date'])
# ordenar por date y eliminar duplicados manteniendo el más reciente
df_latest = df.sort_values('date').drop_duplicates(subset=[id_col], keep='last')
print(df_latest.shape)
Paso 5: Duplicados casi iguales (fuzzy duplicates)
Para registros con pequeñas diferencias (errores tipográficos o variaciones) usa técnicas de fuzzy matching. Un enfoque simple: crear hashes de columnas normalizadas o usar la distancia de Levenshtein. Aquí muestro una versión con normalización y comparación por similitud usando scikit-learn y fingerprints.
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
# ejemplo para columnas nombre + dirección
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
# encuentra pares con similitud 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 semejantes:', len(pairs))
Paso 6: Agregar o marcar duplicados para entrenamiento
Decide si vas a eliminar, agregar (por ejemplo media de métricas) o marcar con una flag. Para modelos, a veces es preferible mantener y añadir una columna is_duplicate para que el modelo aprenda patrones.
# ejemplo: añadir flag de duplicado por clave
df['is_duplicate_by_id'] = df.duplicated(subset=[id_col], keep='first')
# ejemplo: agregar métricas por ID
agg = df.groupby(id_col).agg({'value': 'mean', 'is_active': 'max'}).reset_index()
print(agg.head())
Paso 7: Integrar en el pipeline de Machine Learning
Incluye la limpieza de duplicados antes del split train/test. Si eliminas duplicados después del split, corres el riesgo de tener registros iguales en ambos conjuntos (data leakage).
# limpieza antes del 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 el resultado
Confirma que ya no existen duplicados problemáticos: cuenta duplicados exactos y por clave, verifica pares fuzzy sospechosos y asegura que no hay leakage entre entrenamiento y prueba (IDs en común).
# verificar duplicados exactos y por ID
print('exact dup after:', X_train.duplicated().sum() + X_test.duplicated().sum())
print('IDs comunes entre entrenamiento y prueba:', set(X_train[id_col]).intersection(set(X_test[id_col])))
Conclusión
Tratar duplicados mejora la calidad del conjunto de entrenamiento y reduce sesgo y leakage. Próximos pasos: automatizar la detección con funciones reutilizables, experimentar con thresholds para fuzzy matching y registrar los cambios en un pipeline ETL. Consejo: haz siempre una copia de seguridad de los datos originales antes de eliminar o agregar — ¿prefieres marcar o eliminar?