Figuras y tablas publicables

quarto
reproducibilidad
Tablas con gt, flextable y kableExtra (cada una para su contexto), exportación de figuras con calidad imprenta, captions y referencias cruzadas listas para PDF y HTML.

Tres librerías de tablas: cuándo cada una

Paquete Punto fuerte Cuándo usar
gt HTML muy elegante Reportes web, dashboards
flextable Word/PowerPoint fiel Informes corporativos en docx
kableExtra PDF/LaTeX preciso Documentos académicos en PDF

knitr::kable() está bien para tablas rápidas. Para algo presentable, una de las tres.

gt: el estándar para HTML

library(gt)
library(dplyr)

mtcars |>
    rownames_to_column("modelo") |>
    head(8) |>
    select(modelo, mpg, cyl, hp) |>
    gt() |>
    tab_header(
        title    = "Consumo y potencia",
        subtitle = "Top 8 vehículos por orden alfabético"
    ) |>
    fmt_number(columns = c(mpg, hp), decimals = 1) |>
    cols_label(
        modelo = "Modelo",
        mpg    = "Consumo (mpg)",
        cyl    = "Cilindros",
        hp     = "Potencia (HP)"
    ) |>
    tab_options(
        table.font.size = 13,
        heading.title.font.size = 16,
        column_labels.font.weight = "bold"
    )

Lo idiomático de gt:

  • tab_header(): título + subtítulo.
  • fmt_number(), fmt_currency(), fmt_percent(): formatos por columna.
  • cols_label(): renombrar columnas visualmente sin tocar los datos.
  • tab_options(): control global de la apariencia.

Salida elegante out-of-the-box, mejor que casi todo lo demás en HTML. Documentación excelente en gt.rstudio.com.

flextable: para Word

Lo que justifica flextable: control fino sobre Word, respeta plantillas corporativas mejor que gt o kable:

library(flextable)

mtcars |>
    head(8) |>
    select(mpg, cyl, hp, wt) |>
    flextable() |>
    set_caption("Resumen de vehículos") |>
    autofit() |>
    theme_vanilla() |>
    bold(part = "header")

autofit() ajusta anchos. theme_vanilla() aplica un estilo limpio. Hay docena de temas + customización completa.

Si tu informe va a Word para distribuir a stakeholders, flextable es la opción.

kableExtra: para PDF/LaTeX

kable (de knitr) + kableExtra (extensiones) es el camino estándar para PDF académico:

library(kableExtra)

mtcars |>
    head(8) |>
    select(mpg, cyl, hp) |>
    kbl(caption = "Vehículos seleccionados", booktabs = TRUE,
        format.args = list(big.mark = ".")) |>
    kable_styling(latex_options = c("striped", "hold_position")) |>
    column_spec(1, bold = TRUE)
  • booktabs = TRUE: usa \toprule, \midrule, \bottomrule (tablas LaTeX limpias, sin verticales).
  • hold_position: la tabla aparece donde la pones, no flotante.
  • striped: filas alternas con tono.

Las tablas en PDF tienen sus propias reglas tipográficas (Tufte: nunca verticales, evitar filas alternas excesivas). kableExtra respeta esto si dejas los defaults razonables.

La misma tabla en tres formatos

Una pregunta frecuente: si renderizo a HTML + PDF + Word, ¿qué uso?

Tres estrategias:

A) Una sola tabla con knitr::kable(), funciona en los tres pero estética básica.

B) Condicional al formato:

if (knitr::is_html_output()) {
    tabla |> gt()
} else if (knitr::is_latex_output()) {
    tabla |> kbl(booktabs = TRUE)
} else {
    tabla |> flextable()
}

Más trabajo, mejor resultado.

C) gt para todo, exportando. gt 1.0+ exporta a HTML y a PNG (vía chrome). En PDF, queda como imagen embebida. Funciona pero pierde búsqueda y selección en PDF.

Recomendación pragmática: B. La función de tabla en un archivo, llamada en el chunk.

Figuras con calidad publicable

El chunk típico para una figura “lista para imprenta”:

`​`​`{r}
#| label: fig-tendencia
#| fig-cap: "Tendencia de ventas mensuales 2024."
#| fig-width: 7
#| fig-height: 4.5
#| dpi: 300
#| dev: ragg_png
#| fig-align: center

ggplot(datos, aes(mes, ventas)) +
    geom_line(linewidth = 0.7, color = "#5F8575") +
    geom_point(size = 2.5) +
    scale_y_continuous(labels = scales::label_currency(prefix = "€")) +
    labs(x = NULL, y = "Ventas") +
    theme_minimal(base_size = 11) +
    theme(panel.grid.minor = element_blank())
`​`​`

Detalles que cuentan:

  • dev: ragg_png: device PNG con buen rendering de texto. Mucho mejor que el png() por defecto. Necesita el paquete ragg.
  • dpi: 300: resolución para imprenta.
  • base_size en el theme, 11-12 para informes, no el default 11 ni el mini 8.
  • panel.grid.minor = element_blank(): ruido visual fuera.

Fuentes consistentes

Para que la tipografía del documento coincida con la de las figuras:

# Setup
sysfonts::font_add_google("Inter", "Inter")
showtext::showtext_auto()

theme_set(theme_minimal(base_size = 12, base_family = "Inter"))

Con showtext, las figuras usan la misma fuente que el texto del PDF. Detalle de carpintería que distingue un informe profesional.

Exportar manualmente con ggsave

A veces necesitas la figura como archivo separado (para una presentación, para enviar suelta):

ggsave("figuras/tendencia.pdf",
       plot = mi_plot,
       width = 7, height = 4.5,
       units = "in",
       device = cairo_pdf)

ggsave("figuras/tendencia.png",
       plot = mi_plot,
       width = 7, height = 4.5,
       units = "in",
       dpi = 300)

cairo_pdf para PDFs con transparencias correctas. Para PNG, dpi = 300 es el estándar de imprenta.

Captions y referencias

Para que las figuras y tablas sean referenciables en el texto, necesitan un label con prefijo correcto:

  • Figuras: #| label: fig-xxx o {#fig-xxx} para markdown puro.
  • Tablas: #| label: tbl-xxx.

Luego desde el texto:

La figura @fig-tendencia muestra el crecimiento sostenido.
Como aparece en la tabla @tbl-resumen, los grupos difieren significativamente.

Quarto produce automáticamente: “Figura 1”, “Tabla 2”, etc. Y enlaza al elemento en HTML.

El prefijo es obligatorio: fig- para figuras, tbl- para tablas. Sin él, no se reconoce como elemento referenciable.

Tablas dinámicas con DT y reactable

Para HTML interactivo (búsqueda, sort, paginación):

library(DT)

datatable(mtcars,
          options = list(pageLength = 10),
          filter  = "top")

O con reactable (más moderno, mejor estética por defecto):

library(reactable)

reactable(mtcars,
          searchable = TRUE,
          defaultPageSize = 10,
          striped = TRUE)

Útiles para dashboards y exploraciones. No para informes formales con paginado fijo, esas dependen de tablas estáticas.

Imágenes externas

Si tu figura no se genera con código (logo, diagrama, captura), inclúyela con sintaxis markdown:

![Diagrama del pipeline.](figuras/pipeline.png){#fig-pipeline width=80%}

width=80% controla el tamaño en el output. #fig-pipeline es referenciable.

Cómo decidir tamaño de figura

Un patrón razonable que funciona en HTML, PDF y Word:

  • Figura de cuerpo de texto: fig-width: 7, fig-height: 4.5. Cabe en una página A4 con márgenes estándar.
  • Figura grande / full-page: fig-width: 8, fig-height: 6.
  • Figura cuadrada (heatmaps): fig-width: 6, fig-height: 6.
  • Figura ancha (timeline, barras horizontales con muchos elementos): fig-width: 9, fig-height: 4.

Trampas habituales

  • gt en PDF saca PNG borroso. Si necesitas tablas precisas en PDF, usa kableExtra o flextable.
  • Captions sin prefijo fig- / tbl- quedan visibles pero no referenciables. La cross-reference (@fig-x) fallará silenciosamente.
  • DPI default (72) en figuras de PDF. Se ven pixeladas al imprimir. Pon dpi: 300 siempre que vayas a imprenta.
  • Tipografías inconsistentes. Si el texto del PDF usa Computer Modern (LaTeX default) y las figuras Helvetica, contrasta mal. Setea las dos a la misma fuente.
  • Tablas demasiado anchas para Word. Las exportadas de R suelen ser pequeñas. Las grandes Word las rompe. autofit() con flextable o ancho manual.

En la siguiente entrega

Cierras el bloque de fundamentos. Lo que viene es estructura editorial: cross-references entre figuras, tablas y secciones, citas con .bib y bibliografía. Lo que separa un documento de un texto académico. Lo siguiente.