a74a5a047f
Mejoras transversales del motor AutomaticEDA (PDF + PPTX) sobre el modelo de bloques: 1. DPI alto global: toda figura/imagen embebida se rasteriza a 220 dpi (antes 150, y en PDF la página se guardaba a ~100 dpi re-rasterizando los imshow). En PDF se aplica savefig.dpi=220 a la página; el texto sigue vectorial y seleccionable. Permite ampliar en el móvil sin pixelar. Imagen embebida medida: ~1081px (antes ~492px). 2. Tabla ancha → imagen de alta resolución: cuando un DataTable tiene demasiadas columnas para ser legible como texto (criterio _table_fits_as_text), se dibuja entera como una imagen nítida (nueva función render_table_as_figure_py_datascience: cabecera sombreada + zebra) escalada para caber completa, de modo que el lector hace zoom y la lee sin perder datos. Las tablas que sí caben siguen como texto seleccionable / tabla nativa. Aplica en PDF y PPTX. El df.head de 19 columnas del dataset sintético ya no se corta: sale como imagen. 3. Group.layout: nuevo hint retrocompatible (default "stack"). "side_by_side" coloca la tabla a la izquierda (~55%) y la figura a la derecha (~45%) en la misma slide PPTX (cae a apilado si no hay par tabla+figura o no caben); en PDF se trata como "stack" (el ancho A5 móvil no admite dos columnas). Pensado para que el capítulo cat_distr ponga el gráfico al lado de la tabla en PPT. 4. Portada con índice clicable: la lista de capítulos pasa de "Este informe incluye..." (markdown) a un Heading "Índice" + un TocEntry por capítulo. El renderer registra el inicio de cada capítulo y cablea cada entrada como salto real (PDF: link GOTO PyMuPDF; PPTX: salto a slide nativo), reutilizando el mecanismo del glosario clicable. Modelo: Group gana `layout`; nuevo bloque TocEntry; normalizers y __init__ actualizados. Contrato: documentado en docs/automatic_eda_contract.md §11.4 (incluye el contrato exacto del campo layout para el agente de cat_distr). Tests: nuevo render_quality_test.py (13 golden: DPI alto real, tabla ancha→imagen PDF/PPTX, narrow→texto, side_by_side PPTX dos columnas / PDF apilado, índice clicable PDF+PPTX, retrocompatibilidad layout por defecto). render_features_test actualizado al índice nuevo. Suite: 188 passed (módulo) + 38 passed/1 skipped (acceptance + pipeline). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
7.7 KiB
7.7 KiB
id, name, kind, lang, domain, version, purity, signature, description, tags, uses_functions, uses_types, returns, returns_optional, error_type, imports, example, tested, tests, test_file_path, file_path, params, output
| id | name | kind | lang | domain | version | purity | signature | description | tags | uses_functions | uses_types | returns | returns_optional | error_type | imports | example | tested | tests | test_file_path | file_path | params | output | |||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| render_table_as_figure_py_datascience | render_table_as_figure | function | py | datascience | 1.0.0 | impure | def render_table_as_figure(header, rows, title=None, note=None, fontsize=9.0, max_cell_chars=40) -> "matplotlib.figure.Figure" | Dibuja un bloque tabular (cabecera + filas) como una matplotlib.figure.Figure nítida, lista para rasterizar a DPI alto. Pensada para tablas que NO caben como texto en una página/slide del informe EDA: se rasteriza a alta resolución (el caller usa dpi=220, bbox_inches='tight') y el usuario hace zoom en el móvil para leerla entera sin perder datos. Cabecera sombreada (#eef3f6) y en negrita, filas pares (1-based) con zebra suave (#f6f8fa), tinta oscura (#1b1b1b) sobre blanco, rejilla gris muy fina (#cccccc). Trunca cada celda a max_cell_chars con elipsis y str()-ea cada valor (None -> ""). figsize proporcional al contenido (ancho por nº y longitud de columnas, alto por nº de filas) para que sea legible con zoom. Backend Agg sin pyplot global. Defensiva: header/rows vacíos o None, filas irregulares o cualquier error interno devuelven una Figure placeholder con texto centrado "(tabla no disponible)". NUNCA lanza. |
|
false | error_go_core |
|
from datascience.render_table_as_figure import render_table_as_figure header = ["columna", "n_nulos", "%_nulos", "distintos", "tipo", "ejemplo"] rows = [ ["ingresos", 12, "1.2%", 980, "float64", "2345.67"], ["edad", 0, "0.0%", 88, "int64", "37"], ["ciudad", 5, "0.5%", 412, "object", "Madrid"], ] fig = render_table_as_figure(header, rows, title="Resumen de columnas", note="rasteriza a dpi=220 y haz zoom") fig.savefig("/tmp/tabla.png", dpi=220, bbox_inches="tight") | true |
|
python/functions/datascience/render_table_as_figure_test.py | python/functions/datascience/render_table_as_figure.py |
|
Un matplotlib.figure.Figure (figsize proporcional al contenido: ancho ≈ 0.9-1.6" por columna según su texto, total acotado a 3-26"; alto ≈ 0.32" por fila + cabecera + espacio para título/nota, acotado) con un Axes sin ejes que contiene un ax.table(...) NO cerrado. Cabecera fondo #eef3f6 texto #1b1b1b bold; filas pares (1-based) zebra #f6f8fa, impares blanco; tinta #1b1b1b; bordes/rejilla #cccccc lw 0.4; texto alineado a la izquierda. Título encima (bold) y nota debajo (gris itálica) si se pasan. Si header/rows son vacíos o None, o ante cualquier error interno, devuelve una Figure placeholder pequeña con el texto centrado "(tabla no disponible)". NUNCA lanza. El caller la rasteriza (dpi=220, bbox_inches='tight') y la cierra; la función no la muestra ni la guarda. |
Ejemplo
import sys, os
sys.path.insert(0, os.path.join("python", "functions"))
from datascience.render_table_as_figure import render_table_as_figure
# Tabla que no cabe como texto en la slide -> se rasteriza y se lee con zoom.
header = ["columna", "n_nulos", "%_nulos", "distintos", "tipo", "ejemplo"]
rows = [
["ingresos", 12, "1.2%", 980, "float64", "2345.67"],
["edad", 0, "0.0%", 88, "int64", "37"],
["ciudad", 5, "0.5%", 412, "object", "Madrid"],
["categoria_producto", 0, "0.0%", 1840, "object",
"un_valor_categorico_muy_largo_que_se_trunca"],
]
fig = render_table_as_figure(
header,
rows,
title="Resumen de columnas",
note="rasteriza a dpi=220 y haz zoom en el móvil",
fontsize=9.0,
max_cell_chars=40,
)
# El renderer del informe lo rasteriza a alta resolución; aquí lo persistimos.
fig.savefig("/tmp/tabla.png", dpi=220, bbox_inches="tight")
Cuando usarla
Úsala en un informe EDA cuando una tabla no cabe como texto en una página o
slide y prefieres una imagen nítida que el lector pueda ampliar en el móvil para
leerla entera (perfiles de columnas, matrices de conteo, tablas de frecuencias
con muchas filas o columnas anchas). Pásale la cabecera y las filas tal cual (los
valores se str()-ean por ti) más un title/note opcionales; el llamante la
rasteriza a dpi=220 con bbox_inches='tight'. Es la pareja "tabla-como-imagen"
de los gráficos build_boxplots_figure / categorical_top_pie_figure: misma
paleta y mismo contrato (Agg, sin pyplot, el caller cierra la figura).
Gotchas
- Impura por matplotlib. Toca la maquinaria de render. Usa el backend
Aggy la API orientada a objetosFigure/add_subplot— NUNCApyplot.*aquí, para no tocar el estado global ni filtrar figuras entre llamadas.pyplotNO es thread-safe; esta función construye elFiguredirectamente, así que es segura de llamar en bucle desde el renderer. - El caller cierra la figura. Devuelve el
Figurepero no lo muestra ni lo guarda. Quien la consume debe rasterizarla y luego liberarla (matplotlib.pyplot.close(fig)) para no acumular memoria en lotes grandes. - Pensada para rasterizar a DPI alto. El
figsizees proporcional al contenido pero la legibilidad real viene del DPI: rasteriza condpi=220ybbox_inches='tight'. Una tabla con muchísimas filas crece en alto (capado a ~60") — para miles de filas, parte la tabla o resume antes de pasarla. - Truncación de celda visible. Cada celda se recorta a
max_cell_chars(default 40) con…final y los saltos de línea/tabs se colapsan a espacios, para que ninguna celda desborde a otras filas. Subemax_cell_charssi necesitas ver el valor completo (a costa de ancho). - Defensiva, nunca lanza.
header/rowsvacíos oNone, filas escalares, filas de distinta longitud o cualquier error interno se manejan sin propagar: en el peor caso devuelve unaFigureplaceholder con "(tabla no disponible)". No envuelvas la llamada en try/except por miedo a un raise — no lo hay.