---
id: "0176"
title: "EDA render: models/series/caveats en markdown+PDF + PDF para profile_database"
status: completado
type: feature
domain:
  - registry-quality
scope: registry-only
priority: media
depends: []
blocks: []
related: ["0173", "0174", "0175", "0177"]
created: 2026-06-29
updated: 2026-06-29
tags: [eda, datascience, render_eda_markdown, render_eda_pdf, profile_database, pdf, benchmark]
---
# 0176 — EDA render: models/series/caveats en markdown+PDF + PDF para profile_database

## Contexto

El benchmark `/eda` (29/06/2026, `temp/eda_benchmark/EVALUATION.md`) confirmó que la información de
modelos (PCA/KMeans) está completa en el JSON pero **no llega legible a ningún formato**, y que el
análisis relacional no tiene salida móvil (PDF). El tercio final del PDF queda ilegible.

Hallazgos cubiertos:

| Hallazgo | Severidad | Evidencia del benchmark |
|---|---|---|
| H4 — `models` omitido en Markdown; `models`/`series`/`caveats` como dict crudo truncado en PDF | alto | wine-red `.md` (12 numéricas, PCA valioso) → cero menciones de models. PDF aapl: `- pca: {'n_components': 2, …` cortado a media línea |
| H9 — `profile_database` no genera PDF | medio | chinook y sakila con `pdf=null`; análisis relacional solo en Markdown |

### Causa raíz (verificada en código, READ-ONLY)

- `python/functions/datascience/render_eda_markdown.py`: tiene formatters para `series` (`:337`) y
  `caveats` (`:407`), pero **no para `models`** → el bloque PCA/KMeans nunca se renderiza en MD.
- `python/functions/datascience/render_eda_pdf.py:50-55`: `_KNOWN_TOP_KEYS` **no incluye** `models`,
  `series` ni `caveats`, así que caen en `_generic_pages` (`:479-495`) → `_wrap_value` →
  `str(dict)` truncado a 60-64 chars. Por eso esas tres secciones salen como dict crudo en el PDF.
- `python/functions/pipelines/profile_database.py:205-218`: solo escribe MD+JSON, nunca invoca
  `render_eda_pdf`; no tiene param `emit_pdf`.

## Tareas

1. **H4 — markdown:** añadir una sección `## Modelos` (PCA/KMeans/outliers/normalidad) a
   `render_eda_markdown.py`, formateando `models.pca` (varianza explicada, top loadings, acumulada),
   `models.kmeans` (best_k, silhouette, tamaños de cluster) y `models.outliers` como tablas legibles.
2. **H4 — PDF:** en `render_eda_pdf.py`, añadir builders dedicados para `models`, `series` y
   `caveats` (tablas/listas, no `str(dict)`) y registrarlos en `_KNOWN_TOP_KEYS` + en la lista
   `builders` (`:595-604`) para sacarlos del volcado genérico. Mantener el contrato dict-no-throw
   (una sección que falle no aborta el PDF).
3. **Unificar renderers:** asegurar que MD y PDF cubren el mismo conjunto de secciones (`models`,
   `series`, `caveats`) para que no diverjan otra vez.
4. **H9 — PDF relational:** añadir un renderer PDF DB-level (puede ser una variante en
   `render_eda_pdf.py` o una función nueva) con: portada de la base, resumen de tablas, join graph
   filtrado (tras 0175), y FK candidatas. Añadir param `emit_pdf` a `profile_database.py` que lo
   invoque y devuelva `pdf_path`.
5. Tests: `render_eda_markdown_test.py` (perfil con `models` → aparece sección Modelos);
   `render_eda_pdf_test.py` (perfil con `models`/`series`/`caveats` → NO aparecen como `str(dict)`;
   `n_pages` incrementa); test de `profile_database(emit_pdf=True)` → `pdf_path` no nulo, PDF válido.

## Definition of Done

| Escenario | Tipo | Comando / evidencia | Resultado esperado |
|---|---|---|---|
| Golden: models en MD | e2e | re-correr `profile_table(run_models=True)` sobre wine-red y leer el `.md` | sección `## Modelos` con PCA (varianza explicada) y KMeans (silhouette) legibles |
| Golden: PDF legible | e2e | re-correr sobre aapl y `pdftotext` del PDF | `models`/`series`/`caveats` como tablas, sin `{'n_components': 2, …` truncado |
| Edge: perfil sin models | unit | `render_eda_markdown_test.py`/`render_eda_pdf_test.py` con `models=None` | sección omitida limpiamente, sin crash |
| Edge: PDF relational | e2e | `profile_database(emit_pdf=True)` sobre chinook | `pdf_path` no nulo; PDF con resumen de tablas + join graph |
| Error: sección corrupta | unit | `render_eda_pdf` con una sección con tipo inesperado | esa sección se omite con nota; PDF sigue válido (≥1 página) |
| Mecánica | — | `./fn run render_eda_markdown_py_datascience`, `./fn run render_eda_pdf_py_datascience`; `fn index` | tests verdes; índice limpio |

Re-correr el benchmark sobre un single-table con modelos (wine-red) y sobre un relational (chinook)
y confirmar que models llega al MD y al PDF, y que `profile_database` emite PDF.

## Notas

Issue derivado de `temp/eda_benchmark/EDA_ISSUES.md`. Tipo `feature` porque, además de arreglar el
volcado crudo (H4, fix), añade un renderer PDF relational nuevo (H9). La información ya existe en el
JSON; este issue solo la hace legible en las dos salidas pensadas para humanos. Hermanos: 0173, 0174,
0175, 0177.


## Resolucion (2026-06-29, sesion /ausente)
Resuelto y verificado con re-corrida del benchmark EDA. Commit principal: c4cff5ed. Detalle en reports/ausente-eda-benchmark-2026-06-29.md y temp/eda_benchmark/EVALUATION.md.
