Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.4 KiB
eda — Exploratory Data Analysis por tabla y base
Grupo de capacidad para perfilar tablas y bases de datos completas y entender datasets nuevos rápido, repetible y sin reinventar lógica. Motor DuckDB SQL push-down: los agregados (SUMMARIZE, COUNT DISTINCT, corr(), 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, correlación mixta, modelos).
Orquestadores one-shot:
profile_table_py_pipelines— "hazme un EDA de esta tabla" →TableProfilecompleto + report markdown + JSON. Flagsrun_models(modelos baratos) yrun_llm(interpretación LLM).profile_database_py_pipelines— "hazme un EDA de esta base" → perfila todas las tablas + infiere FK + join graph (mermaid).
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-registryy la reglanotebook_collaboration.md.
Funciones
Perfilado base (tabla y columna)
| ID | Pureza | Qué hace |
|---|---|---|
summarize_table_duckdb_py_datascience |
impure | Corazón (DuckDB): SUMMARIZE push-down + COUNT DISTINCT exacto (≤200k filas) → esqueleto del TableProfile. |
summarize_table_pg_py_datascience |
impure | Adaptador PostgreSQL: mismo esqueleto TableProfile vía SQL push-down (information_schema + count/distinct/min/max/avg/stddev/percentile_cont). |
describe_numeric_py_datascience |
pure | Bloque numérico: min/max/mean/median/std/cv, p1-p99, IQR, skew, kurtosis, outliers, distribución, histograma. |
summarize_categorical_py_datascience |
pure | top-k frecuencias, mode, distinct, entropía, imbalance, longitudes. |
infer_semantic_type_py_datascience |
pure | Tipo semántico por regex (email/url/ip/uuid/iban/currency/datetime/...). |
column_quality_score_py_datascience |
pure | Score 0-100 (completeness/validity/consistency) + issues. |
render_eda_markdown_py_datascience |
pure | TableProfile → report markdown con sparklines ASCII. |
summary_stats_py_datascience |
pure | Descriptiva mínima (n, mean, median, p25, p75). |
Correlación / asociación
| ID | Pureza | Qué hace |
|---|---|---|
pearson_py_datascience |
pure | Correlación lineal num↔num (preexistente). |
spearman_corr_py_datascience |
pure | Correlación de rangos (monotónica no lineal) num↔num. |
cramers_v_py_datascience |
pure | Asociación simétrica cat↔cat (corrección Bergsma-Wicher). |
theils_u_py_datascience |
pure | Asociación direccional U(a|b) cat↔cat. |
correlation_ratio_py_datascience |
pure | η: cuánto explica una categórica a una numérica. |
mutual_info_columns_py_datascience |
pure | Información mutua (no lineal, general) entre cualquier par. |
association_matrix_py_datascience |
pure | Matriz unificada: elige métrica por par de tipos + pares fuertes. |
correlation_matrix_duckdb_py_datascience |
impure | Matriz Pearson push-down (corr() SQL) para muchas filas. |
Relaciones inter-tabla
| ID | Pureza | Qué hace |
|---|---|---|
infer_fk_containment_duckdb_py_datascience |
impure | Infiere FK candidatas por containment de valores (inclusion coefficient). |
build_join_graph_py_datascience |
pure | FK candidates → grafo (roles fact/dimension) + diagrama Mermaid. |
Modelos baratos (flag run_models)
| ID | Pureza | Qué hace |
|---|---|---|
pca_explained_py_datascience |
pure | PCA: varianza explicada + loadings + proyección. |
kmeans_segments_py_datascience |
pure | Segmentos naturales, auto-k por silhouette. |
isolation_forest_outliers_py_datascience |
pure | Outliers multivariante (filas anómalas). |
normality_tests_py_datascience |
pure | Jarque-Bera + D'Agostino + Shapiro → ¿normal? |
trend_slope_py_datascience |
pure | Tendencia de una serie (up/down/flat) por regresión lineal. |
run_eda_models_py_datascience |
pure | Wrapper: compone PCA + KMeans + IsolationForest + normalidad → bloque models. |
Capa LLM y entrega
| ID | Pureza | Qué hace |
|---|---|---|
eda_llm_insights_py_datascience |
impure | 1 call LLM sobre el perfil agregado (no filas crudas): data dictionary, resumen, granularidad de fila, PII/RGPD, limpieza, análisis sugeridos. |
build_eda_notebook_py_datascience |
impure | Genera un .ipynb (nbformat v4) que perfila la tabla, listo para lanzar en Jupyter colaborativo. |
Orquestadores (pipelines)
| ID | Qué hace |
|---|---|
profile_table_py_pipelines |
EDA de una tabla end-to-end, backend="duckdb" (default) o "postgres" (base + correlación + run_models + run_llm) → JSON + markdown. |
profile_database_py_pipelines |
EDA de una base entera: todas las tablas + FK + join graph. |
Contrato de datos
TableProfile = {table, source, profiled_at, n_rows, n_cols, size_bytes,
duplicate_rows, duplicate_pct, constant_cols, all_null_cols, null_cell_pct,
type_breakdown:{numeric,categorical,datetime,text,boolean},
columns:[ColumnProfile], correlations, key_candidates, quality_score, llm, models}
ColumnProfile = {name, physical_type, inferred_type, semantic_type, count, n_rows,
null_count, null_pct, empty_count, empty_pct, distinct_count, unique_pct,
flags:[constant|possible_id|high_cardinality|mostly_null], quality_score,
numeric:{...}|None, categorical:{...}|None, datetime:{...}|None}
# *_pct son FRACCIONES 0-1; el render las muestra ×100
correlations = {pairs:[{a,b,a_type,b_type,method,value,extra}], strong:[...], methods_legend}
models = {n_numeric_cols, pca, kmeans, outliers, normality, note}
llm = {summary, row_meaning, dictionary:[{column,description,business_meaning,unit}],
pii:[{column,kind,severity}], cleaning:[str], analyses:[str]}
Ejemplo canónico
EDA completo de una tabla (estadística + correlación + modelos + LLM + report):
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from pipelines.profile_table import profile_table
r = profile_table("/ruta/datos.duckdb", "clientes", run_models=True, run_llm=True)
prof = r["profile"]
print(r["report_md_path"]) # reports/eda_clientes_<ts>.md
print(prof["correlations"]["strong"]) # pares correlacionados
print(prof["models"]["kmeans"]["best_k"]) # segmentos
print(prof["llm"]["row_meaning"]) # qué representa 1 fila
EDA de una base entera con relaciones:
from pipelines.profile_database import profile_database
r = profile_database("/ruta/datos.duckdb") # todas las tablas
print(r["db_profile"]["join_graph"]["mermaid"]) # diagrama de relaciones FK
Notebook ejecutable:
from datascience import build_eda_notebook
build_eda_notebook("/ruta/datos.duckdb", "clientes", "/tmp/eda.ipynb", run_models=True)
Fronteras
- NO carga la tabla entera a RAM: metadata SQL + muestra por columna/filas (
sample, default 5000). - Distinct exacto hasta 200k filas; por encima aproximado capado.
- Correlación de tabla se calcula sobre la muestra de filas alineadas; excluye columnas id-like (alta cardinalidad) para evitar asociación espuria.
correlation_matrix_duckdbofrece Pearson push-down exacto a escala si hace falta. - Modelos (
run_models) requieren ≥2 columnas numéricas para PCA/KMeans/IsolationForest; normalidad funciona con 1. - LLM (
run_llm) hace 1 llamada (haiku) y envía solo el perfil agregado, nunca filas crudas; requiere token OAuth de Claude. - Fuentes: DuckDB nativo (CSV/Parquet/Excel cargándolos antes a DuckDB) y PostgreSQL (
backend="postgres", DSN víaresolve_pg_dsn). BigQuery pendiente.profile_database(multi-tabla + FK) es solo DuckDB por ahora.
Estado
Implementado y validado end-to-end (152 tests verdes): perfilado base, correlación/asociación (Pearson/Spearman/Cramér's V/Theil's U/η/MI), relaciones inter-tabla (FK + join graph), modelos baratos (PCA/KMeans/IsolationForest/normalidad/tendencia), capa LLM y generación de notebook.
Validado sobre PostgreSQL real (tablas del Metabase local del proyecto captacion_clientes).
Pendiente: adaptador BigQuery; profile_database multi-tabla para PostgreSQL (hoy solo DuckDB); perfil fino de columnas datetime (profile_datetime); excluir columnas numéricas possible_id de la matriz de asociación (hoy solo se excluyen las categóricas id-like).