105e56cf05
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>
5.5 KiB
5.5 KiB
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. |
|
false |
|
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 |
|
python/functions/datascience/compute_text_duplicates_test.py | python/functions/datascience/compute_text_duplicates.py |
|
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_dupdegrada a{"available": False, "n_near_dup_docs": 0}(sin clavethreshold) 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_dupcuenta repeticiones, no grupos. Con 3 copias de un mismo texto,n_exact_dupes 2 (las dos copias extra), no 1. Equivale an_docs - n_unique.exact_dup_pctesNonecon corpus vacío (noZeroDivisionError); en cualquier otro caso es un float redondeado a 2 decimales.sample_maxsolo limita el near-dup. El conteo de duplicados exactos recorre todo el corpus; el near-dup muestrea los primerossample_maxdocumentos para acotar memoria. Si el corpus está ordenado, considera barajar antes para que la muestra sea representativa.- Elementos no-str se descartan.
True/Falseno cuentan como str y se ignoran igual queNone;n_docsrefleja solo los documentos válidos.