Files
fn_registry/python/functions/datascience/compute_text_duplicates.md
T
egutierrez 105e56cf05 feat(eda): capítulo text_distr (TEXTO/NLP) — primer capítulo de datos no tabulares
Añade el capítulo `text_distr` al motor AutomaticEDA: perfila columnas de texto
libre largo (reseñas, descripciones, comentarios) que la distribución categórica
no resume bien. Sigue el patrón de cat_distr/num_distr (build_text_distr(profile,
ctx) -> Chapter | None) y se registra en CHAPTER_ORDER tras cat_distr.

Activación en dos fases: gate barato desde el perfil (columna no numérica con
len_mean >= 50 chars) + confirmación con muestra cruda (mediana de palabras >= 20).
Un dataset sin texto largo (p.ej. titanic) devuelve None sin tocar el informe.

Bloques por columna (Group con page_break): resumen (longitudes, vocabulario con
TTR y % hapax, idioma dominante, % duplicados, legibilidad), histograma de
longitudes, top términos (tabla + barras), bigramas/trigramas, idiomas detectados
y nube de palabras opcional. Términos ttr/hapax enganchados al glosario clicable.

Lógica delegada a 7 funciones nuevas del registry (datascience, tag eda),
estilo dict-no-throw:
- extract_text_sample (impura, push-down SQL DuckDB/Postgres)
- compute_text_length_stats, compute_vocabulary_stats, compute_top_ngrams (puras, stdlib)
- detect_corpus_language (langdetect opcional), compute_text_readability (textstat
  opcional), compute_text_duplicates (hash + datasketch opcional)

Versión barata sin modelos pesados: las piezas que dependen de una librería
opcional (langdetect, textstat, wordcloud, datasketch) degradan a omitidas sin
lanzar. Añade langdetect y textstat (ligeras) al pyproject + uv.lock.

Verificado: golden sobre dataset de reviews multi-idioma (capítulo presente en
PDF+PPTX+MD con métricas reales), titanic sin capítulo (None), degradación sin
libs, suite automatic_eda + pipeline verde (128 passed), fn index OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 20:38:17 +02:00

5.5 KiB
Raw Blame History

id, name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, example, tested, tests, test_file_path, file_path, params, output
id name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports example tested tests test_file_path file_path params output
compute_text_duplicates_py_datascience compute_text_duplicates function py datascience 1.0.0 pure def compute_text_duplicates(texts, near_threshold=0.85, sample_max=2000) -> dict Detecta documentos duplicados en un corpus de texto. Los duplicados EXACTOS se calculan siempre con la stdlib: cada documento se normaliza (colapsa espacios, strip, lower) y se hashea con SHA-1; n_exact_dup es cuántos docs repiten uno ya visto y exact_dup_pct su porcentaje. Los CASI-duplicados (near-dup) usan la dependencia OPCIONAL datasketch (MinHash + LSH sobre 3-shingles de palabras); si no está instalada, esa parte degrada a available:False sin afectar al resto. Estilo dict-no-throw del grupo eda — nunca lanza.
eda
datascience
text
nlp
duplicates
minhash
pure
python
false
hashlib
re
from datascience.compute_text_duplicates import compute_text_duplicates texts = ["El gato come pescado", "El gato come pescado", "Un perro ladra"] result = compute_text_duplicates(texts) # {"n_docs": 3, "n_exact_dup": 1, "exact_dup_pct": 33.33, "n_unique": 2, # "near_dup": {"available": False, "n_near_dup_docs": 0}} true
test_duplicados_exactos
test_sin_duplicados
test_vacio
test_near_dup_degrada
python/functions/datascience/compute_text_duplicates_test.py python/functions/datascience/compute_text_duplicates.py
name desc
texts Lista de documentos de texto. Los elementos None o que no sean str se descartan silenciosamente; n_docs cuenta solo los documentos válidos. None como argumento se trata como lista vacía.
name desc
near_threshold Umbral de similitud Jaccard (01) para considerar dos documentos casi-duplicados en el cálculo near-dup vía MinHashLSH. Solo aplica si datasketch está instalada. Default 0.85.
name desc
sample_max Número máximo de documentos muestreados (los primeros) para el cálculo near-dup, que es O(n) en memoria de MinHashes. No afecta al conteo de duplicados exactos, que siempre recorre todo el corpus. Default 2000.
Dict con exactamente 5 claves, siempre presentes: n_docs (int, docs válidos), n_exact_dup (int, docs que repiten un texto normalizado ya visto = n_docs - n_unique), exact_dup_pct (float a 2 decimales = n_exact_dup/n_docs*100, o None si el corpus está vacío), n_unique (int, nº de textos normalizados distintos), y near_dup (sub-dict con available:bool y n_near_dup_docs:int; cuando available es True incluye además threshold con el near_threshold usado). La función nunca lanza: captura toda excepción y degrada.

Ejemplo

from datascience.compute_text_duplicates import compute_text_duplicates

# Tres copias del mismo texto (con espacios/casing distintos) + dos únicos.
texts = [
    "El gato come pescado",
    "El gato come pescado",
    "el  GATO   come pescado",   # mismo tras normalizar
    "Un perro ladra",
    "La luna brilla",
]

compute_text_duplicates(texts)
# {
#   "n_docs": 5,
#   "n_exact_dup": 2,          # 3 copias del primer texto => 2 repeticiones
#   "exact_dup_pct": 40.0,     # 2 / 5 * 100
#   "n_unique": 3,             # 3 textos normalizados distintos
#   "near_dup": {"available": False, "n_near_dup_docs": 0},  # datasketch ausente
# }

# Corpus vacío: contrato estable, exact_dup_pct None, sin excepción.
compute_text_duplicates([])
# {"n_docs": 0, "n_exact_dup": 0, "exact_dup_pct": None, "n_unique": 0,
#  "near_dup": {"available": False, "n_near_dup_docs": 0}}

Cuando usarla

Úsala en la fase de calidad de un EDA de texto, cuando quieras saber cuánto de tu corpus es ruido duplicado antes de entrenar, vectorizar o muestrear: te da el porcentaje de duplicados exactos (exact_dup_pct), el número de documentos únicos (n_unique) y, si tienes datasketch instalada, una estimación de casi-duplicados (paráfrasis, copias con pequeñas ediciones) vía MinHash + LSH. Pásale directamente la columna/lista de textos crudos; la función filtra None y no-str por ti y nunca lanza, así que es segura para encadenar en pipelines de perfilado.

Gotchas

  • Near-dup requiere datasketch (opcional). Si la librería no está instalada, near_dup degrada a {"available": False, "n_near_dup_docs": 0} (sin clave threshold) y el resto del resultado se calcula igual. Los duplicados exactos funcionan siempre porque solo usan la stdlib (hash).
  • Normalización de exactos. Dos textos cuentan como el mismo duplicado exacto si coinciden tras " ".join(doc.split()).strip().lower(): se colapsan espacios/tabuladores/saltos, se recortan extremos y se ignora el caso. Cambios de puntuación o acentos SÍ los distinguen (no se eliminan).
  • n_exact_dup cuenta repeticiones, no grupos. Con 3 copias de un mismo texto, n_exact_dup es 2 (las dos copias extra), no 1. Equivale a n_docs - n_unique.
  • exact_dup_pct es None con corpus vacío (no ZeroDivisionError); en cualquier otro caso es un float redondeado a 2 decimales.
  • sample_max solo limita el near-dup. El conteo de duplicados exactos recorre todo el corpus; el near-dup muestrea los primeros sample_max documentos para acotar memoria. Si el corpus está ordenado, considera barajar antes para que la muestra sea representativa.
  • Elementos no-str se descartan. True/False no cuentan como str y se ignoran igual que None; n_docs refleja solo los documentos válidos.