diff --git a/.claude/commands/eda.md b/.claude/commands/eda.md index 860c340f..c358fea8 100644 --- a/.claude/commands/eda.md +++ b/.claude/commands/eda.md @@ -27,6 +27,7 @@ Página madre del grupo: `docs/capabilities/eda.md` (léela primero para cargar - `--series` → `run_series=True` (estacionariedad ADF+KPSS, ACF/PACF, STL, retornos por columna numérica). - `--pdf` → `emit_pdf=True` (PDF A5 legacy de `render_eda_pdf`, legible en móvil). - `--legacy-only` → emite SOLO el PDF legacy (sin AutomaticEDA), para casos en que solo se quiera el PDF rápido. + - `--lite` / `--bajo-consumo` → `render_automatic_eda(profile_level="lite")`: EDA barato y rápido (CI, vistazo previo, máquina sin GPU/red). Apaga LLM y serie temporal y limita los modelos a **PCA + normalidad** (sin KMeans ni IsolationForest, lo caro en CPU), con `sample` reducido. `--full` → `profile_level="full"` (standard + narrativa LLM). Por defecto `profile_level="standard"` (comportamiento histórico). Un flag explícito (`--llm`, `--models`, ...) prima sobre el preset. Por defecto, **un EDA completo emite SIEMPRE el informe AutomaticEDA en sus dos formatos: PDF (A5 móvil) Y PPTX (16:9 para compartir)** con los 11 capítulos poblados (portada, overview, distribuciones, calidad, correlaciones, modelos, series, geoespacial, agregación, interpretación LLM). Usa el pipeline `render_automatic_eda` (o `profile_table(emit_automatic=True)`), que activa `run_models` y `run_series` para que los capítulos de modelos/series/geoespacial/agregación salgan poblados. Deja `run_llm` para cuando el usuario lo pida o interese la interpretación semántica + narrativa por capítulo (es la única parte que gasta tokens del modelo). @@ -50,7 +51,8 @@ from pipelines.render_automatic_eda import render_automatic_eda # tablas de agregación). run_llm=True añade la narrativa LLM por capítulo. r = render_automatic_eda( "/ruta/datos.duckdb", "ventas", - run_models=True, run_series=True, run_llm=False, out_dir="reports", + profile_level="standard", # "lite" = bajo consumo CPU/LLM; "full" = + narrativa LLM + out_dir="reports", ) print("status:", r["status"]) print("pdf: ", r["pdf_path"], "(", r["n_pages"], "págs )") diff --git a/python/functions/pipelines/render_automatic_eda.md b/python/functions/pipelines/render_automatic_eda.md index b157dfd2..50d4bcda 100644 --- a/python/functions/pipelines/render_automatic_eda.md +++ b/python/functions/pipelines/render_automatic_eda.md @@ -4,9 +4,9 @@ kind: pipeline lang: py domain: pipelines purity: impure -version: "1.0.0" -signature: "def render_automatic_eda(db_path: str, table: str, backend: str = \"duckdb\", sample: int = 5000, run_models: bool = True, run_series: bool = True, run_llm: bool = False, out_dir: str = \"reports\", basename: str = None, ctx_extra: dict = None) -> dict" -description: "Informe AutomaticEDA COMPLETO one-shot de una tabla DuckDB/PostgreSQL: perfila con profile_table, construye el ctx con los datos crudos (build_eda_render_ctx: raw_numeric para modelos/geo, timeseries_raw para series, geo_points para el mapa, db_path/table para la agregacion push-down) y emite PDF (A5 movil) Y PPTX (16:9) del mismo documento por capitulos, con los 11 capitulos POBLADOS de verdad (clusters pintados sobre el PCA, evolucion temporal, mapa geografico y tablas de agregacion), no degradados. Devuelve las rutas de PDF/PPTX y el manifiesto de versiones por capitulo." +version: "1.1.0" +signature: "def render_automatic_eda(db_path: str, table: str, backend: str = \"duckdb\", sample: int = None, run_models: bool = None, run_series: bool = None, run_llm: bool = None, profile_level: str = \"standard\", out_dir: str = \"reports\", basename: str = None, ctx_extra: dict = None) -> dict" +description: "Informe AutomaticEDA COMPLETO one-shot de una tabla DuckDB/PostgreSQL: perfila con profile_table, construye el ctx con los datos crudos (build_eda_render_ctx: raw_numeric para modelos/geo, timeseries_raw para series, geo_points para el mapa, db_path/table para la agregacion push-down) y emite PDF (A5 movil) Y PPTX (16:9) del mismo documento por capitulos, con los 11 capitulos POBLADOS de verdad (clusters pintados sobre el PCA, evolucion temporal, mapa geografico y tablas de agregacion), no degradados. El parametro profile_level es un preset de consumo CPU/LLM (lite/standard/full) que mapea a los flags run_models/run_series/run_llm/sample; un flag explicito siempre prima sobre el preset. lite=bajo consumo (sin LLM, sin serie, modelos solo PCA+normalidad sin KMeans/IsolationForest, sample reducido); standard=comportamiento historico; full=standard+narrativa LLM. Devuelve las rutas de PDF/PPTX y el manifiesto de versiones por capitulo." tags: [eda, duckdb, postgres, profiling, pipeline, dataops, report, pdf, pptx] uses_functions: - profile_table_py_pipelines @@ -31,13 +31,15 @@ params: - name: backend desc: "'duckdb' (default) o 'postgres'. Selecciona el motor de perfilado y muestreo." - name: sample - desc: "Maximo de filas/valores muestreados por columna para el perfil y para los datos crudos del ctx (LIMIT). Default 5000." + desc: "Maximo de filas/valores muestreados por columna para el perfil y para los datos crudos del ctx (LIMIT). Default None => lo fija el preset de profile_level (lite=2000, standard/full=5000). Un valor explicito prima sobre el preset." - name: run_models - desc: "Si True (default) corre los modelos baratos (PCA/KMeans/IsolationForest/normalidad); necesario para que el capitulo modelos pinte los clusters sobre el plano PCA." + desc: "Corre los modelos baratos (PCA/KMeans/IsolationForest/normalidad); necesario para que el capitulo modelos pinte los clusters sobre el plano PCA. Default None => lo fija el preset (True en los tres niveles); en lite los modelos se limitan a PCA+normalidad. Un valor explicito prima sobre el preset." - name: run_series - desc: "Si True (default) calcula el analisis de serie temporal por columna numerica; necesario para el analisis del capitulo timeseries (la grafica de evolucion sale de los datos crudos del ctx aunque sea False)." + desc: "Calcula el analisis de serie temporal por columna numerica; necesario para el analisis del capitulo timeseries. Default None => lo fija el preset (standard/full=True, lite=False). Un valor explicito prima sobre el preset." - name: run_llm - desc: "Si True (default False) hace la interpretacion LLM del perfil y ACTIVA la narrativa LLM de los capitulos modelos/geospatial/agregacion (titulos de segmento, descripcion de zona, seleccion de agregaciones). Con False usan su derivacion cuantitativa sin red." + desc: "Hace la interpretacion LLM del perfil y ACTIVA la narrativa LLM de los capitulos modelos/geospatial/agregacion (titulos de segmento, descripcion de zona, seleccion de agregaciones). Con False usan su derivacion cuantitativa sin red. Default None => lo fija el preset (full=True, lite/standard=False). Un valor explicito prima sobre el preset." + - name: profile_level + desc: "Preset de consumo CPU/LLM (default 'standard'). Mapea a defaults de run_models/run_series/run_llm/sample; un flag explicito SIEMPRE prima. 'lite'=bajo consumo (run_llm=False, run_series=False, sample=2000, modelos solo PCA+normalidad sin KMeans/IsolationForest); 'standard'=comportamiento historico (modelos completos, serie, sin LLM); 'full'=standard+narrativa LLM. Un nivel desconocido cae a 'standard'." - name: out_dir desc: "Directorio de salida (se crea si no existe). Default 'reports'." - name: basename @@ -52,14 +54,21 @@ output: "dict {status:'ok', pdf_path:str, pptx_path:str, manifest_path:str|None, ```python from pipelines.render_automatic_eda import render_automatic_eda -# Tabla DuckDB con categoricas + fecha + numericas: informe completo a reports/. -r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", - run_models=True, run_series=True, out_dir="reports") +# Informe completo a reports/ (standard = comportamiento por defecto historico). +r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", out_dir="reports") print(r["status"], r["pdf_path"], r["pptx_path"], r["n_pages"], r["n_slides"]) -# ok reports/aeda_ventas_20260630-120500.pdf reports/aeda_ventas_20260630-120500.pptx 14 16 +# ok reports/aeda_ventas_20260630-120500.pdf reports/aeda_ventas_20260630-120500.pptx 37 39 -# Con narrativa LLM (titulos de segmento, descripcion geografica, etc.): -r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", run_llm=True) +# Bajo consumo (CPU/LLM): vistazo rapido y barato — sin LLM, sin serie, modelos +# solo PCA + normalidad (sin KMeans/IsolationForest), sample reducido. +r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", profile_level="lite") + +# Maximo: standard + narrativa LLM por capitulo (titulos de segmento, etc.). +r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", profile_level="full") + +# Precedencia: el flag explicito SIEMPRE prima sobre el preset. lite pero con LLM: +r = render_automatic_eda("/tmp/ventas.duckdb", "ventas", + profile_level="lite", run_llm=True) # el LLM SI se ejecuta ``` ## Cuando usarla @@ -72,20 +81,41 @@ llama a los dos renderers": este pipeline orquesta `profile_table` -> entregable para compartir un EDA, o como el motor detras de `profile_table( emit_automatic=True)` y del skill `/eda`. +Para un EDA **barato/rapido** (CI, vistazo previo, maquina sin GPU o sin red) usa +`profile_level="lite"`: evita KMeans + IsolationForest (lo caro en CPU), la serie +temporal y el LLM. Para el **maximo** con interpretacion narrativa por capitulo, +`profile_level="full"`. El default `"standard"` mantiene el comportamiento previo. + ## Gotchas - Impura: ESCRIBE el PDF, el PPTX y `automatic_eda_manifest.json` en `out_dir`. - `db_path` debe existir: DuckDB read-only no crea la base. -- `run_models=True` y `run_series=True` por defecto encarecen el perfil (PCA/ - KMeans/IsolationForest + ADF/KPSS/STL por columna). Para un informe mas barato - ponlos a False: los capitulos modelos/timeseries se omiten o se reducen, pero - el resto del informe sale igual. -- `run_llm=True` hace llamadas de red (interpretacion del perfil + narrativa por - capitulo). Sin red, dejalo en False: los capitulos siguen completos con su - derivacion cuantitativa (titulos de segmento derivados, nota geografica - derivada, seleccion de agregaciones cuantitativa). +- **Precedencia de flags vs preset**: `profile_level` solo fija los DEFAULTS de + `run_models`/`run_series`/`run_llm`/`sample` (los que quedan en None). Cualquiera + de esos flags pasado explicito gana al preset. Ej: `profile_level="lite", + run_llm=True` ejecuta el LLM pese a que lite lo apaga por defecto. +- **lite y la seleccion de features de modelo**: en lite los modelos (PCA + + normalidad) corren sobre la muestra numerica cruda (`ctx['raw_numeric']`), sin la + poda fina de features que aplica el modo standard (que excluye ids enteros y + columnas de baja cardinalidad antes de PCA/KMeans). Es el coste de mantener el + cableado minimo y barato; para el analisis fino de modelos usa standard/full. +- `profile_level="standard"`/`"full"` corren PCA/KMeans/IsolationForest + + ADF/KPSS/STL por columna (caro). Para un informe mas barato usa `"lite"` (o pon + los flags a False a mano): los capitulos modelos/timeseries se reducen pero el + resto del informe sale igual. +- `run_llm=True` (preset full o flag explicito) hace llamadas de red + (interpretacion del perfil + narrativa por capitulo). Sin red, usa lite/standard: + los capitulos siguen completos con su derivacion cuantitativa. - El PPTX requiere `python-pptx`; si no esta instalado, `pptx_path` sera None y `pptx_note` lo explica (el PDF se emite igual). - Los datos crudos del ctx se muestrean con `sample` (LIMIT), no se trae la tabla entera a RAM; con tablas enormes sube `sample` si quieres mas representatividad (coste: mas memoria). + +## Capability growth log + +- v1.1.0 (2026-06-30) — anade el parametro `profile_level` (lite/standard/full), + preset de consumo CPU/LLM que mapea a los flags run_models/run_series/run_llm/ + sample. lite limita los modelos a PCA+normalidad (cableado a run_eda_models con + run_kmeans=False/run_isolation=False) y apaga LLM/serie. Cambio aditivo y + retro-compatible: sin profile_level el comportamiento es identico al de v1.0.0. diff --git a/python/functions/pipelines/render_automatic_eda.py b/python/functions/pipelines/render_automatic_eda.py index c0b58065..8090bc1f 100644 --- a/python/functions/pipelines/render_automatic_eda.py +++ b/python/functions/pipelines/render_automatic_eda.py @@ -34,21 +34,62 @@ from datascience import ( build_eda_render_ctx, render_automatic_eda_pdf, render_automatic_eda_pptx, + run_eda_models, ) from pipelines.profile_table import profile_table # Tokens de almacenamiento por backend (para la portada del informe). _STORAGE = {"duckdb": "DuckDB", "postgres": "PostgreSQL"} +# Presets de consumo CPU/LLM: cada profile_level fija SOLO los DEFAULTS de los +# flags que controlan el coste (un flag explícito del caller siempre prima sobre +# el preset). model_opts != None marca el camino "modelos baratos" (lite): los +# modelos NO los corre profile_table (que ejecutaría KMeans + IsolationForest), +# sino run_eda_models con esa granularidad, de modo que el coste CPU de los +# multivariantes nunca se paga. model_opts None => modelos completos como hasta +# ahora (profile_table los corre con todos los algoritmos). +_PROFILE_PRESETS = { + # Bajo consumo: sin LLM, sin serie, sample reducido y modelos limitados a + # PCA + normalidad (sin KMeans ni IsolationForest, lo caro en CPU). Vistazo + # rápido y barato de una tabla. + "lite": { + "run_models": True, + "run_series": False, + "run_llm": False, + "sample": 2000, + "model_opts": {"run_kmeans": False, "run_isolation": False}, + }, + # Default: idéntico al comportamiento histórico del pipeline (modelos + # completos, serie temporal, sin LLM, sample 5000). + "standard": { + "run_models": True, + "run_series": True, + "run_llm": False, + "sample": 5000, + "model_opts": None, + }, + # Máximo: standard + narrativa LLM (interpretación del perfil y de los + # capítulos modelos/geospatial/agregacion). Es la única parte que gasta + # tokens del modelo. + "full": { + "run_models": True, + "run_series": True, + "run_llm": True, + "sample": 5000, + "model_opts": None, + }, +} + def render_automatic_eda( db_path: str, table: str, backend: str = "duckdb", - sample: int = 5000, - run_models: bool = True, - run_series: bool = True, - run_llm: bool = False, + sample: int = None, + run_models: bool = None, + run_series: bool = None, + run_llm: bool = None, + profile_level: str = "standard", out_dir: str = "reports", basename: str = None, ctx_extra: dict = None, @@ -60,19 +101,39 @@ def render_automatic_eda( table: nombre de la tabla a perfilar. backend: "duckdb" (default) o "postgres". sample: máximo de filas/valores muestreados por columna para el perfil - y para los datos crudos del ctx (LIMIT). Default 5000. - run_models: si True (default) corre los modelos baratos + y para los datos crudos del ctx (LIMIT). Default None => lo fija el + preset de profile_level (lite=2000, standard/full=5000). + run_models: corre los modelos baratos (PCA/KMeans/IsolationForest/normalidad). Necesario para que el - capítulo `modelos` pinte los clusters sobre el plano PCA. - run_series: si True (default) calcula el análisis de serie temporal por + capítulo `modelos` pinte los clusters sobre el plano PCA. Default + None => lo fija el preset (True en los tres niveles); en `lite` los + modelos se limitan a PCA + normalidad (ver profile_level). + run_series: calcula el análisis de serie temporal por columna numérica. Necesario para el análisis del capítulo `timeseries` (la gráfica de evolución sale de los datos crudos del - ctx aunque run_series sea False). - run_llm: si True (default False) hace la interpretación LLM del perfil y + ctx aunque run_series sea False). Default None => lo fija el preset + (standard/full=True, lite=False). + run_llm: hace la interpretación LLM del perfil y ACTIVA además la narrativa LLM de los capítulos modelos/geospatial/ agregacion (títulos de segmento, descripción de la zona, selección de agregaciones). Con False esos capítulos usan su derivación - cuantitativa (siguen completos, sin llamadas de red). + cuantitativa (siguen completos, sin llamadas de red). Default None => + lo fija el preset (full=True, lite/standard=False). + profile_level: preset de consumo CPU/LLM. Mapea a defaults de los flags + anteriores; un flag explícito SIEMPRE prima sobre el preset (el + preset solo fija el default cuando el flag se deja en None): + + - "lite" bajo consumo: run_llm=False, run_series=False, + sample=2000 y modelos limitados a **PCA + normalidad** (SIN KMeans + ni IsolationForest, que es lo caro en CPU). Pensado para un vistazo + rápido y barato. El capítulo `modelos` sale con PCA + normalidad, + sin el scatter de clusters. + - "standard" (default): comportamiento histórico — modelos completos + (PCA/KMeans/IsolationForest/normalidad), serie temporal, sin LLM. + - "full" standard + narrativa LLM (run_llm=True). + + Ejemplo de precedencia: profile_level="lite" con run_llm=True + explícito => el LLM SÍ se ejecuta (el flag explícito gana al preset). out_dir: directorio de salida (se crea si no existe). Default "reports". basename: nombre base de los archivos sin extensión. Default "aeda_