Files
fn_registry/python/functions/datascience/render_eda_pdf.md
T
Egutierrez 7ac69ab4fb feat(eda): series temporales + rigor anti-data-mining + PDF movil + /eda + benchmark issues
Bloque del grupo eda (sesion ausente EDA-benchmark):
- 8 funciones nuevas: adf_kpss_stationarity, acf_pacf, stl_decompose, to_returns,
  fdr_correction, suggest_reexpression, exploratory_caveats, render_eda_pdf
- integracion: profile_table (run_series, emit_pdf), association_matrix (FDR Benjamini-Hochberg),
  render_eda_markdown (secciones series/reexpresion/caveats)
- slash commands /eda y /capitulos
- issues 0173-0177: mejoras del /eda derivadas del benchmark sobre 12 datasets reales
  (outlier_pct x100, periodo estacional, FK inference, render models, tipos id-like)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 03:34:01 +02:00

6.4 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
render_eda_pdf function py datascience 1.0.0 impure def render_eda_pdf(profile: dict, out_path: str, title: str = None) -> dict Renderiza un TableProfile del grupo eda en un PDF multipágina portátil pensado para LEER Y EXPLORAR EN EL MÓVIL. Páginas A5 retrato, una columna, tipografía grande; diseño Tufte (alto data-ink ratio, histogramas reales como small multiples, barras top-k, heatmap de asociación, integridad de ejes desde 0). Lee todo el profile defensivamente con .get y sólo renderiza las secciones presentes; bloques nuevos del profile (models, caveats, ...) se vuelcan genéricamente (forward-compatible). dict-no-throw: nunca lanza, devuelve {pdf_path, n_pages, note}. Motor matplotlib PdfPages, cero dependencias nuevas.
eda
pdf
render
report
mobile
tufte
visualization
matplotlib
profiling
datascience
python
false error_go_core
os
textwrap
datetime
matplotlib
numpy
name desc
profile TableProfile dict del grupo de capacidad eda (el dict que profile_table devuelve bajo la clave 'profile'). Puede tener muchas claves ausentes o None; un profile None/vacío genera igualmente un PDF de 1 página. Claves consumidas: table, source, profiled_at, n_rows, n_cols, size_bytes, duplicate_rows/_pct, null_cell_pct, quality_score, type_breakdown, constant_cols, all_null_cols, key_candidates, columns[] (con numeric.histogram [{lo,hi,count}], categorical.top [{value,count,pct}], quality_score, flags/issues), correlations.pairs [{a,b,value}], llm. Cualquier otra clave de nivel superior se vuelca en una página forward-compat.
name desc
out_path ruta del archivo PDF de salida. Los directorios padre se crean si faltan.
name desc
title título opcional para la portada. Por defecto 'EDA — <table>'.
dict (nunca lanza): {pdf_path: str, n_pages: int, note: str}. En éxito pdf_path es la ruta escrita, n_pages el número de páginas generadas y note un resumen ('N páginas', con detalle de las secciones omitidas si alguna falló). En error fatal de escritura pdf_path es None y note explica la causa. true
test_golden_genera_pdf_multipagina
test_edge_profile_vacio_no_revienta
test_edge_profile_none_no_revienta
test_edge_solo_numericas
test_forward_compat_seccion_desconocida
python/functions/datascience/render_eda_pdf_test.py python/functions/datascience/render_eda_pdf.py

Ejemplo

from datascience import render_eda_pdf

# TableProfile mínimo (en la práctica viene de profile_table(...)["profile"]).
profile = {
    "table": "ventas",
    "source": "data/ventas.csv",
    "n_rows": 1000,
    "n_cols": 2,
    "null_cell_pct": 0.02,
    "quality_score": 92.5,
    "type_breakdown": {"numeric": 1, "categorical": 1},
    "columns": [
        {
            "name": "precio",
            "inferred_type": "numeric",
            "quality_score": 95.0,
            "numeric": {
                "min": 1.0, "max": 100.0, "median": 40.0, "mean": 42.5,
                "std": 12.3, "outlier_pct": 1.2,
                "histogram": [
                    {"lo": 0.0, "hi": 25.0, "count": 100},
                    {"lo": 25.0, "hi": 50.0, "count": 500},
                    {"lo": 50.0, "hi": 75.0, "count": 300},
                    {"lo": 75.0, "hi": 100.0, "count": 50},
                ],
            },
        },
        {
            "name": "categoria",
            "inferred_type": "categorical",
            "quality_score": 99.0,
            "categorical": {
                "entropy": 1.05,
                "top": [
                    {"value": "neumaticos", "count": 500, "pct": 0.5},
                    {"value": "aceite", "count": 300, "pct": 0.3},
                    {"value": "filtros", "count": 200, "pct": 0.2},
                ],
            },
        },
    ],
}

res = render_eda_pdf(profile, "reports/eda_ventas.pdf", title="EDA — ventas")
print(res)   # -> {'pdf_path': 'reports/eda_ventas.pdf', 'n_pages': 5, 'note': '5 páginas'}

Cuando usarla

Cuando quieras una 4ª salida portátil del EDA para revisar en el teléfono: después de profile_table(...), pásale el profile resultante para emitir un PDF que el usuario recibe y explora desde el móvil, sin abrir notebooks ni markdown. Úsala como capa de presentación del grupo eda (junto al report markdown, el JSON sidecar y el notebook Jupyter): histogramas reales en small multiples, barras top-k de las categóricas, heatmap de correlaciones y una portada con el score de calidad, todo maquetado para pantalla pequeña con criterios de Tufte (alto data-ink ratio, ejes honestos desde 0). No recalcula nada del perfil — sólo lo dibuja.

Gotchas

  • Impura: escribe un archivo en out_path (crea los directorios padre). Usa el backend headless Agg de matplotlib, así que corre en agentes/CI sin display.
  • Nunca lanza (dict-no-throw): cada sección se construye aislada; si una falla, se omite y se anota en note, pero el PDF se genera igual. Un profile None/{} produce un PDF de 1 página válido.
  • Forward-compatible: sólo conoce un conjunto fijo de claves de nivel superior; cualquier bloque nuevo del profile (p.ej. models, caveats, series temporales que añadan otras funciones del grupo) se vuelca en una página genérica "Otras secciones" en vez de perderse o romper. No asume claves que quizá no existan.
  • Registro en el package: el ## Ejemplo usa from datascience import render_eda_pdf, que requiere que la función esté añadida al __init__.py del paquete (lo hace fn index + la integración del orquestador). El test importa el módulo directo (from render_eda_pdf import render_eda_pdf) para no depender de ese registro.
  • Histograma real, no ASCII: necesita numeric.histogram como lista de bins {lo, hi, count} (el formato que emite describe_numeric). Si una columna numérica no trae histograma, esa columna se salta en la página de distribuciones.
  • Heatmap de correlaciones: reconstruye la matriz simétrica desde correlations.pairs ({a, b, value}); anota los valores en celda sólo si hay ≤8 columnas para no saturar la pantalla del móvil.
  • PDF con texto seleccionable (pdf.fonttype=42, TrueType embebido), legible y buscable en visores móviles.