Files
fn_registry/docs/capabilities/eda.md
T
egutierrez 763e06c127 feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-20 18:22:23 +02:00

5.7 KiB
Raw Blame History

eda — Exploratory Data Analysis por tabla

Grupo de capacidad para perfilar tablas y entender datasets nuevos rápido, repetible y sin reinventar lógica. Motor DuckDB SQL push-down: los agregados (SUMMARIZE, COUNT DISTINCT, percentiles) se calculan en SQL sin traer las filas a RAM; solo una muestra pequeña baja a Python para lo estadístico fino (skew, kurtosis, histograma, outliers).

El orquestador one-shot es profile_table_py_pipelines: "hazme un EDA de esta tabla" → un TableProfile completo + report markdown + JSON sidecar en reports/.

Cuando Enmanuel pide un EDA, el flujo acordado es: perfilar con este grupo, escribir el report, y generar un analysis Jupyter lanzado en el navegador colaborativo y ejecutado por Claude para verlo en vivo. Ver la memoria eda-workflow-registry y la regla notebook_collaboration.md.

Funciones

ID Pureza Qué hace
summarize_table_duckdb_py_datascience impure Corazón: SUMMARIZE push-down → esqueleto del TableProfile con perfil base por columna (tipo inferido, nulls, distinct exacto ≤200k filas, flags). Reusa duckdb_query_readonly.
describe_numeric_py_datascience pure Bloque numeric sobre una muestra: min/max/mean/median/mode/std/cv, percentiles p1-p99, IQR, skew, kurtosis, outliers, %zeros/%neg, tipo de distribución, histograma.
summarize_categorical_py_datascience pure Bloque categorical: top-k frecuencias, mode, distinct, entropía de Shannon (bits), imbalance, longitudes.
infer_semantic_type_py_datascience pure Tipo semántico por regex (email/url/ip/uuid/iban/currency/datetime/integer/decimal/...) sin LLM. Primera pasada barata.
column_quality_score_py_datascience pure Score de calidad 0-100 (completeness/validity/consistency) + issues legibles para un ColumnProfile.
render_eda_markdown_py_datascience pure TableProfile → report markdown autosuficiente (Overview, Columnas, Numéricas con sparkline ASCII, Categóricas, Calidad).
summary_stats_py_datascience pure Descriptiva mínima (n, mean, median, p25, p75) de una lista de floats.
profile_table_py_pipelines pipeline Orquestador end-to-end: compone todo lo anterior, promueve tipos VARCHAR→numeric/datetime por contenido, y emite TableProfile + report markdown + JSON.

Contrato de datos

Todas las funciones producen/consumen el mismo shape (dict JSON), lo que desacopla cálculo, render y (futuro) LLM:

TableProfile = {
  table, source, profiled_at, n_rows, n_cols, size_bytes,
  duplicate_rows, duplicate_pct, constant_cols:[str], all_null_cols:[str],
  null_cell_pct, type_breakdown:{numeric,categorical,datetime,text,boolean},
  columns:[ColumnProfile], correlations, key_candidates:[str],
  quality_score, llm, models
}

ColumnProfile = {
  name, physical_type, inferred_type,   # numeric|categorical|datetime|boolean|text|id
  semantic_type, count, n_rows, null_count, null_pct, empty_count, empty_pct,
  distinct_count, unique_pct,           # *_pct son FRACCIONES 0-1; el render las muestra ×100
  flags:[constant|possible_id|high_cardinality|mostly_null],
  quality_score,
  numeric: {min,max,mean,median,mode,std,variance,cv,p1,p5,p25,p50,p75,p95,p99,iqr,
            skew,kurtosis,n_outliers,outlier_pct,zero_pct,negative_pct,distribution_type,
            histogram:[{lo,hi,count}]} | None,
  categorical: {top:[{value,count,pct}],mode,mode_pct,n_distinct,entropy,imbalance,
                len_mean,len_min,len_max} | None,
  datetime: {min,max,range_days,granularity,n_gaps,future_pct,monotonic} | None
}

Ejemplo canónico

EDA de una tabla DuckDB en una línea (escribe reports/eda_<table>_<ts>.md + .json):

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from pipelines.profile_table import profile_table

r = profile_table(os.path.expanduser("~/.fn_freelance/freelance.duckdb"), "freelance_projects")
print(r["status"], r["report_md_path"])
prof = r["profile"]
print(prof["type_breakdown"], "key_candidates:", prof["key_candidates"], "calidad:", prof["quality_score"])

La promoción de tipo por contenido resuelve el caso típico de scrapers/CSV donde los números y fechas llegan como VARCHAR: bids ('10','20') se detecta integer y se perfila como numérica (mean/median/percentiles); scraped_at se detecta datetime_iso.

Fronteras

  • NO carga la tabla entera a RAM: solo metadata SQL + una muestra (sample, default 5000) por columna. Para distribución exacta de una columna enorme, sube sample o consulta SQL directa.
  • Distinct exacto solo hasta 200k filas; por encima usa aproximado (HyperLogLog) capado a nº de filas.
  • Solo DuckDB por ahora (CSV/Parquet/Excel entran gratis vía read_csv_auto/read_parquet/read_xlsx cargándolos antes a DuckDB). PostgreSQL y BigQuery requieren adaptador (pendiente).
  • No es estadística inferencial ni modelado: es perfilado descriptivo. Correlaciones, modelos baratos (PCA/KMeans/IsolationForest) y capa LLM son fases siguientes del grupo.

Roadmap (fases siguientes)

  • Correlación / asociación: Spearman, Cramér's V, Theil's U, correlation ratio η², Mutual Information, VIF → correlations del TableProfile.
  • Relaciones inter-tabla: FK inference por containment, cardinalidad de relación, join graph (mermaid), star-schema hints → profile_database.
  • Modelos baratos (flag --models, sklearn/scipy): PCA 2D, KMeans + silhouette, Isolation Forest, feature importance, tests de normalidad, tendencia temporal.
  • Capa LLM (flag --llm, grupo claude-direct): data dictionary, resumen ejecutivo (qué es 1 fila + granularidad), flag PII/RGPD, limpieza sugerida, análisis sugeridos.
  • Entrega notebook: analysis Jupyter auto-generado y ejecutado en el navegador colaborativo.