Files
fn_registry/python/functions/datascience/build_eda_render_ctx.md
T
egutierrez f3d427d9e4 feat(eda): wiring AutomaticEDA — build_eda_render_ctx + pipeline render_automatic_eda + profile_table(emit_automatic)
Conecta el motor AutomaticEDA con los datos crudos para que los 4 capítulos
dependientes de ctx (modelos, timeseries, geospatial, agregacion) salgan
POBLADOS en vez de degradar a una nota.

- build_eda_render_ctx (datascience, impure, dict-no-throw): dado db_path+table
  y el TableProfile agregado, construye el ctx con los datos crudos que el
  perfil no incluye: raw_numeric {col:[float|None]} alineado por fila (modelos /
  geospatial), timeseries_raw {time_col,t,series} vía extract_timeseries_raw,
  geo_points {lats,lons} desde el par lat/lon detectado, y db_path/table para el
  groupby/pivot push-down de agregacion. Muestrea con LIMIT (no trae la tabla
  entera a RAM). Compone detect_time_column / extract_timeseries_raw /
  detect_latlon_columns / duckdb_query_readonly (imports lazy para evitar ciclo).
- render_automatic_eda (pipeline): one-shot perfil -> ctx -> PDF + PPTX con los
  11 capítulos poblados; devuelve rutas + manifest de versiones por capítulo.
- profile_table: flag aditivo emit_automatic=True emite el AutomaticEDA PDF+PPTX
  además del flujo legacy (emit_pdf/render_eda_pdf intacto). Nuevas claves de
  retorno aeda_pdf_path / aeda_pptx_path / aeda_manifest_path.

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

8.7 KiB

name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, params, output, tested, tests, test_file_path, file_path
name kind lang domain version purity signature description tags uses_functions uses_types returns returns_optional error_type imports params output tested tests test_file_path file_path
build_eda_render_ctx function py datascience 1.0.0 impure def build_eda_render_ctx(db_path: str, table: str, profile: dict, backend: str = 'duckdb', sample: int = 5000, base_ctx: dict = None) -> dict Constructor del `ctx` de datos crudos del motor AutomaticEDA. Dado un db_path+table (DuckDB o Postgres) y el TableProfile AGREGADO ya calculado por profile_table, produce el dict ctx que los renderers (render_automatic_eda_pdf/_pptx -> build_document(profile, ctx)) pasan a los capitulos que necesitan DATOS CRUDOS no presentes en el perfil agregado: modelos (project_clusters_2d en vivo), timeseries, geospatial y agregacion (groupby/pivot push-down). NO trae tablas enteras a RAM: muestrea con LIMIT sample y delega el push-down de la serie en extract_timeseries_raw. Construye el lector read-only query_fn(sql)->dict igual que profile_table (closure sobre duckdb_query_readonly / pg_query). Estilo dict-no-throw del grupo eda: NUNCA lanza; si una pieza falla, degrada esa clave a ausente/[] y sigue. Devuelve el ctx dict directamente (NO un wrapper {status,...}); se pasa tal cual como meta={'ctx': <ese dict>}. Claves de datos que produce: raw_numeric (muestra cruda alineada por fila), timeseries_raw (fechas+series), geo_points (lats/lons) y db_path+table para el push-down de agregacion. Respeta base_ctx: parte de una copia y solo AÑADE las claves de datos; las de presentacion (dataset_name, source_origin, ...) no se pisan.
eda
datascience
automatic-eda
render
ctx
extraction
read-only
duckdb
postgres
python
detect_time_column_py_datascience
extract_timeseries_raw_py_datascience
detect_latlon_columns_py_datascience
duckdb_query_readonly_py_infra
pg_query_py_infra
false error_go_core
name desc
db_path ruta al archivo DuckDB, o DSN PostgreSQL si backend='postgres'. Se guarda tal cual en ctx['db_path'] (el capitulo agregacion lo usa para el groupby/pivot push-down via DuckDB) y se inyecta en el closure query_fn. No se valida aqui: si la base no existe, las queries devuelven status error y las claves de datos se omiten.
name desc
table nombre de la tabla. Se escapa con comillas dobles en las queries (raw_numeric y timeseries) y se guarda en ctx['table'].
name desc
profile TableProfile AGREGADO producido por profile_table. Solo se lee su clave `columns` (lista de ColumnProfile dict con name / inferred_type / numeric.{min,max} / semantic_type). Lectura defensiva: si no es dict o no tiene columns, se trata como []. NO se traen las filas crudas de aqui — se muestrean de la base.
name desc
backend 'duckdb' (default) o 'postgres'. Selecciona el lector read-only del registry (duckdb_query_readonly / pg_query). Cualquier otro valor devuelve el base_ctx tal cual, SIN añadir claves de datos (ni siquiera db_path/table).
name desc
sample maximo de filas a muestrear (clausula LIMIT) tanto para raw_numeric (una sola query SELECT de las numericas) como para timeseries_raw (max_rows de extract_timeseries_raw). Default 5000. Acota memoria y tiempo de render.
name desc
base_ctx dict opcional con claves de PRESENTACION ya preparadas (dataset_name, source_origin, ...). Se parte de una copia y NO se pisan sus claves; solo se añaden las de datos. Default None -> {}.
El dict `ctx` directamente (NO un wrapper {status,...}); se pasa tal cual como meta={'ctx': <ese dict>} a render_automatic_eda_pdf/pptx. Nunca lanza. Para backends validos contiene SIEMPRE db_path + table, y opcionalmente: raw_numeric {col:[float|None,...]} (muestra cruda alineada por fila; omitida si no hay numericas o falla la query), timeseries_raw {time_col, t:[iso...], series:{col:[float|None,...]}} (solo si hay columna temporal + numericas y trae filas), geo_points {lats:[...], lons:[...]} (solo si se detecta par lat/lon y ambas estan en raw_numeric). Ante fallo global devuelve al menos {**base_ctx, 'db_path': db_path, 'table': table}. Backend desconocido -> base_ctx tal cual sin claves de datos. true
test_db_path_y_table_en_ctx
test_raw_numeric_con_columnas_numericas
test_timeseries_raw_con_fecha
test_geo_points_con_latlon
test_sin_fecha_no_hay_timeseries
test_base_ctx_preservado
python/functions/datascience/build_eda_render_ctx_test.py python/functions/datascience/build_eda_render_ctx.py

Ejemplo

import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience import build_eda_render_ctx, render_automatic_eda_pdf
from datascience import profile_table  # opcional: para obtener el TableProfile

# 1) Perfil agregado de la tabla (push-down, sin RAM).
prof = profile_table("data/ventas.duckdb", "ventas_geo", write_report=False)["profile"]

# 2) ctx de datos crudos para los capitulos (muestrea con LIMIT, no carga todo).
ctx = build_eda_render_ctx(
    "data/ventas.duckdb", "ventas_geo", prof,
    backend="duckdb", sample=5000,
    base_ctx={"dataset_name": "Ventas con geolocalizacion"},
)
# ctx == {
#   "dataset_name": "Ventas con geolocalizacion",   # preservado del base_ctx
#   "db_path": "data/ventas.duckdb", "table": "ventas_geo",
#   "raw_numeric": {"ventas": [1200.5, ...], "lat": [40.41, ...], "lon": [-3.70, ...]},
#   "timeseries_raw": {"time_col": "fecha", "t": ["2024-01-01", ...], "series": {...}},
#   "geo_points": {"lats": [40.41, ...], "lons": [-3.70, ...]},
# }

# 3) Se entrega tal cual a los renderers via meta={"ctx": ctx}.
render_automatic_eda_pdf(prof, "reports/eda.pdf", meta={"ctx": ctx})

Cuando usarla

Justo antes de renderizar un AutomaticEDA (PDF o PPTX), cuando ya tienes el TableProfile AGREGADO de profile_table pero los capitulos de modelos, timeseries, geospatial y agregacion necesitan DATOS CRUDOS que el perfil agregado no lleva (la muestra numerica alineada por fila, la serie cronologica, el par lat/lon, y el db_path/table para el push-down del groupby/pivot). Es el puente entre el perfil agregado y build_document(profile, ctx): una sola llamada produce el ctx completo muestreando con LIMIT en vez de cargar la tabla entera en memoria.

Gotchas

  • Impura: lee de la base de datos a traves de query_fn (closure sobre duckdb_query_readonly / pg_query). No abre conexiones fuera de esos wrappers del registry. Estilo dict-no-throw del grupo eda: NUNCA lanza; ante cualquier fallo (query, deteccion, render de una clave) degrada esa clave a ausente/[] y sigue. Ante un fallo global devuelve al menos {**base_ctx, "db_path": db_path, "table": table}.
  • error_type en el frontmatter es error_go_core por convencion del registry (toda funcion impura debe declararlo y el indexer lo exige), pero el codigo NO lanza esa excepcion: degrada al ctx parcial. Es metadata, no comportamiento.
  • Devuelve el ctx dict directamente, NO un wrapper {status,...}: a diferencia de extract_timeseries_raw / profile_table, esta funcion es el ultimo eslabon antes del render y su salida se pasa tal cual como meta={"ctx": <ese dict>}. No envuelvas su retorno.
  • Backend desconocido: con un backend que no sea duckdb ni postgres devuelve el base_ctx tal cual, SIN claves de datos (ni siquiera db_path/table). Comprueba el backend antes si dependes de esas claves.
  • Alineacion por fila de raw_numeric: raw_numeric[col] tiene una entrada por fila muestreada (un valor no convertible a float queda como None, no se descarta la fila) porque project_clusters_2d descarta filas listwise: todas las columnas deben tener la MISMA longitud. geo_points se construye desde raw_numeric para heredar esa alineacion.
  • geo_points exige lat/lon en raw_numeric: el par lat/lon solo se adjunta si ambas columnas se detectaron (nombre+rango) Y figuran en raw_numeric (es decir, son numericas en el perfil). Si la tabla guarda lat/lon como texto no promovido a numeric, no apareceran; el capitulo geospatial sabe degradar.
  • timeseries_raw depende del orden del backend: hereda el ORDER BY "time_col" de extract_timeseries_raw. Si la columna temporal esta guardada como texto no ordenable lexicograficamente (p.ej. DD/MM/YYYY), el orden no sera el cronologico real — normaliza la columna a date/timestamp antes.
  • LIMIT sample: con tablas grandes obtienes el primer tramo (raw_numeric por orden fisico, timeseries por orden cronologico), no un muestreo uniforme. Sube sample si necesitas mas cobertura.
  • No loguear los datos crudos: raw_numeric / timeseries_raw / geo_points pueden contener datos sensibles. En trazas usa solo conteos y nombres de columna, no el ctx completo.