Para líneas duplicadas exactas, conservando el encabezado y el orden original:
awk 'NR==1 || !seen[$0]++' input.csv > deduped.csv
Eso resuelve el caso fácil, y el caso fácil es más raro de lo que parece. La mayoría de los «duplicados» de un archivo de trabajo no son líneas idénticas, sino dos registros de la misma persona con otra capitalización, un espacio al final o un teléfono actualizado. Borrar el equivocado hace perder datos, así que el orden de las operaciones importa: decide qué hace iguales a dos filas, mira en qué se diferencian y solo entonces borra.
Paso uno: define la clave
Un duplicado son dos filas que describen lo mismo. Qué columnas lo establecen es una decisión sobre tus datos, no una propiedad del archivo.
- Un duplicado de fila completa, con todos los campos idénticos, casi siempre es un error: una importación doble, un trabajo que se volvió a ejecutar, un agregado que corrió dos veces. Se puede eliminar sin riesgo.
- Un duplicado de clave, mismo
emailo mismoorder_idpero todo lo demás distinto, es una cuestión de datos. Probablemente una de esas filas es más reciente o más completa, y tienes que elegir.
Confundir ambos es la forma típica de perder registros. Deduplicar por name en una lista de clientes borrará sin pestañear a dos personas distintas llamadas James Smith.
Paso dos: cuenta antes de borrar
Nunca ejecutes una deduplicación a ciegas. Mira lo que vas a quitar:
duckdb -c "SELECT email, count(*) n FROM read_csv('input.csv') GROUP BY email HAVING n > 1 ORDER BY n DESC LIMIT 20"
Dos números te dicen si la clave es la correcta. Si los duplicados son un puñado, probablemente son entradas dobles genuinas. Si un tercio del archivo está «duplicado», la clave está mal: estás comparando algo que nunca fue único.
Luego mira un caso concreto completo, que es el paso que todos se saltan:
duckdb -c "SELECT * FROM read_csv('input.csv') WHERE email = '[email protected]'"
Ahí descubres que las dos filas difieren justo en la columna que te importaba.
Paso tres: elige qué fila sobrevive
Cuando sabes que las filas difieren, «eliminar duplicados» se convierte en «quedarse con la correcta». Tres reglas, más o menos en orden de cuántas veces acierta cada una:
Quédate con la más reciente. Si hay una marca de tiempo, normalmente es la respuesta.
duckdb -c "COPY (SELECT * FROM (SELECT *, row_number() OVER (PARTITION BY email ORDER BY updated_at DESC) rn FROM read_csv('input.csv')) WHERE rn = 1) TO 'deduped.csv' (HEADER)"
Quédate con la más completa. Sin marca de tiempo, ordenar por el número de campos no vacíos es un buen sustituto, porque el registro más lleno suele ser el posterior. Ordena por (col_a IS NOT NULL)::INT + (col_b IS NOT NULL)::INT + … y toma la primera.
Combina las dos. A veces cada fila tiene algo que le falta a la otra. Es común y no hay atajo: es un coalesce por columna, y para un conjunto pequeño conviene más hacerlo a mano que automatizarlo mal para uno grande.
Elijas lo que elijas, escribe las filas descartadas en un segundo archivo antes de borrarlas. Una deduplicación que no puedes revertir es una que lamentarás en algún momento de las próximas dos semanas.
Casi duplicados, que son la mayoría
La coincidencia exacta pasa por alto la mayoría de los duplicados reales. Normaliza primero, compara después y conserva los valores originales.
duckdb -c "SELECT lower(trim(email)) k, count(*) n FROM read_csv('input.csv') GROUP BY k HAVING n > 1"
Las normalizaciones a aplicar, más o menos por orden de rendimiento:
- Mayúsculas y minúsculas.
[email protected]y[email protected]son el mismo buzón. - Espacios. Los espacios finales de un copiar y pegar son invisibles y muy comunes.
trim()quita los iniciales y finales; unreplacese encarga de los espacios de no separación que llegan de formularios web. - Puntuación en identificadores. Los teléfonos
+44 20 7946 0958,02079460958y(020) 7946 0958son un solo número. Quita todo lo que no sea un dígito antes de comparar. - Puntos de Gmail y direcciones con plus.
[email protected]llega a la misma bandeja que[email protected]. Que cuente como duplicado depende de si estás deduplicando personas o cuentas.
Lo que no debes hacer automáticamente: coincidencia aproximada de nombres. «Jon Smith» y «John Smith» pueden ser la misma persona o dos colegas, y un umbral de similitud que sirve para un archivo no sirve para el siguiente. Genera los pares candidatos, revísalos y luego actúa.
El editor SQL y el editor de filtros visuales son las dos mitades de este trabajo: un GROUP BY para encontrar las claves duplicadas y luego un filtro para leer cada fila que comparte una, sin cargar nada antes en una base de datos.
Qué haceEn una hoja de cálculo
Excel y Sheets tienen un comando Quitar duplicados (Remove Duplicates), con una propiedad que conviene conocer antes de usarlo: conserva la primera aparición y borra el resto, sin forma de decir cuál es la primera. El orden en que estuviera la hoja decide qué registro sobrevive.
Así que ordena a propósito antes de ejecutarlo. Ordena por tu marca de tiempo de forma descendente y «conservar la primera» pasa a ser «conservar la más nueva», que suele ser lo que querías.
El otro método en hoja de cálculo, una columna auxiliar con COUNTIF, es más lento pero no destructivo, y te muestra los duplicados en lugar de quitarlos. En un archivo grande también es lo que colgará Excel, porque COUNTIF sobre medio millón de filas es cuadrático.
En el teléfono
La versión realista de esto en un teléfono no es una pasada completa de deduplicación. Es ordenar por la columna que sospechas, desplazarte y ver las repeticiones una junto a otra, que es la forma más rápida de saber si un archivo tiene un problema de duplicados.
Cuando ya las ves, filtrar por una clave y leer las pocas filas que la comparten responde la única pregunta que importa: ¿es el mismo registro o son dos registros que casualmente comparten un campo?
Ordena por cualquier columna como en Excel y luego borra filas con el filtro aplicado sin riesgo: las filas ocultas están protegidas, así que un borrado nunca toca datos fuera del filtro. Deshacer lo cubre todo, y eso es lo que hace razonable trabajar así en un teléfono.
Qué haceVerificar
Tres números, y deben cuadrar:
wc -l input.csv deduped.csv
duckdb -c "SELECT count(*) rows, count(DISTINCT email) keys FROM read_csv('deduped.csv')"
rows y keys ahora deben ser iguales. Si no lo son, la deduplicación no se aplicó a todos los grupos. Y input − deduped debe ser igual al número de filas que querías quitar, el que contaste en el paso dos. Si quitó más, la clave era más amplia o más estrecha de lo que pensabas, y el archivo de filas descartadas que guardaste es ahora lo que te salva la tarde.