Parametrizar informes

quarto
reproducibilidad
Cómo convertir un .qmd en una plantilla que produce un informe distinto según parámetros: cliente, periodo, región. Sintaxis params, render desde CLI y desde R, y patrones para generar lotes.

El problema

Tienes un informe mensual de ventas. La estructura es siempre la misma: portada, sección por región, gráfico anual, tabla resumen. Lo único que cambia: el mes y el subconjunto de datos.

Sin parametrización, copias el .qmd, cambias 5 valores hard-coded y renderizas. Multiplicado por 12 meses × 6 regiones = 72 archivos casi idénticos. Insostenible.

Con parametrización: un solo .qmd + un script que lo renderiza 72 veces con distintos parámetros. Cambios estructurales se hacen una vez.

Sintaxis básica

En el front matter:

---
title: "Informe regional"
format: html
params:
  region: "Norte"
  periodo: "2024-Q4"
  destacar_top: 10
---

Desde un chunk R:

params$region          # "Norte"
params$periodo         # "2024-Q4"
params$destacar_top    # 10

Y desde Python:

params["region"]

params es una lista (R) o dict (Python) con las claves del YAML. Los valores son los defaults del informe.

Usar parámetros en el código

library(dplyr)
library(ggplot2)

datos_region <- ventas |>
    filter(region == params$region,
           periodo == params$periodo)

ggplot(datos_region, aes(mes, ventas)) +
    geom_line() +
    labs(title = paste("Ventas de", params$region),
         subtitle = params$periodo)

Cada vez que renderices con un region distinto, el filtro cambia, el título cambia, todo el informe se adapta. Es el mismo documento sirviendo de plantilla.

Renderizar con parámetros distintos

Desde la CLI

quarto render informe.qmd -P region:Sur -P periodo:"2024-Q3"

-P clave:valor sobreescribe el default del YAML. Funciona en bash y PowerShell.

Desde R

quarto::quarto_render(
    input  = "informe.qmd",
    output_file = "informe-sur.html",
    execute_params = list(region = "Sur", periodo = "2024-Q3")
)

execute_params toma una lista R que se convierte a YAML internamente.

Desde Python

import subprocess
subprocess.run([
    "quarto", "render", "informe.qmd",
    "-P", "region:Sur", "-P", "periodo:2024-Q3"
])

No hay API Python nativa todavía, se llama a la CLI desde Python con subprocess.

Output a archivo distinto

Por defecto, informe.qmd renderiza a informe.html. Con varios parámetros, vas a sobrescribir el output cada vez. Soluciones:

output-file en YAML

Dinámico:

params:
  region: "Norte"
  periodo: "2024-Q4"

Y desde el chunk:

quarto::quarto_render(
    input  = "informe.qmd",
    output_file = paste0("informe-", params$region, "-", params$periodo, ".html"),
    execute_params = list(region = "Sur", periodo = "2024-Q3")
)

Output: informe-Sur-2024-Q3.html. Conserva la versión anterior si renderizas otro region.

--output en CLI

quarto render informe.qmd \
    -P region:Sur -P periodo:2024-Q3 \
    --output informe-sur-Q3.html

Lotes: el script que genera N informes

El uso típico de parametrización es producción en lote. Patrón en R:

library(tidyverse)

configs <- expand_grid(
    region  = c("Norte", "Sur", "Este", "Oeste"),
    periodo = c("2024-Q1", "2024-Q2", "2024-Q3", "2024-Q4")
)

walk2(configs$region, configs$periodo, function(r, p) {
    quarto::quarto_render(
        input          = "informe.qmd",
        output_file    = paste0("informes/informe-", r, "-", p, ".html"),
        execute_params = list(region = r, periodo = p),
        quiet = TRUE
    )
    message("✓ ", r, " ", p)
})

16 informes en una sola pasada. expand_grid crea todas las combinaciones. walk2 itera sobre dos vectores paralelos. quiet = TRUE reduce ruido del log.

Para Python:

import subprocess
from itertools import product

regiones = ["Norte", "Sur", "Este", "Oeste"]
periodos = ["2024-Q1", "2024-Q2", "2024-Q3", "2024-Q4"]

for r, p in product(regiones, periodos):
    subprocess.run([
        "quarto", "render", "informe.qmd",
        "-P", f"region:{r}",
        "-P", f"periodo:{p}",
        "--output", f"informes/informe-{r}-{p}.html"
    ])
    print(f"✓ {r} {p}")

Validación de parámetros

Defensa razonable contra parámetros mal pasados:

valores_validos <- c("Norte", "Sur", "Este", "Oeste")

if (!params$region %in% valores_validos) {
    stop("Region '", params$region, "' no válida. ",
         "Valores: ", paste(valores_validos, collapse = ", "))
}

Sin esto, un valor incorrecto produce un informe vacío o con errores silenciosos, datos filtrados a 0 filas, gráficos en blanco. Falla pronto y claro.

Parámetros condicionales

Mostrar contenido según parámetro:

#| label: solo-bonus
#| echo: false
#| eval: !expr params$incluir_bonus

# Este chunk solo se ejecuta si incluir_bonus = TRUE
generar_seccion_extra()

!expr evalúa la expresión R como condición. Útil para informes resumen vs detallado desde la misma plantilla.

En texto markdown, condicional con shortcode:

::: {.content-visible when-meta="incluir_apendice"}

## Apéndice

Contenido solo si `incluir_apendice: true`.

:::

Tipos de parámetros que admite YAML

params:
  string_simple: "Norte"
  numero: 12
  decimal: 0.05
  booleano: true
  lista:
    - "uno"
    - "dos"
    - "tres"
  fecha: 2024-12-31
  null_explicito: null

Para listas y fechas, conviene convertir explícitamente en el código:

fecha_inicio <- as.Date(params$fecha)
mis_regiones <- unlist(params$lista)

Default vs override

El YAML define defaults. Cuando renderizas sin pasar -P, esos defaults se usan. Cuando pasas -P region:Sur, solo ese parámetro se sobreescribe. Los demás mantienen el default.

Patrón útil: defaults para render local de prueba (Norte, Q4), parámetros explícitos para producción (todos los combos).

Workflow recomendado

Para informes parametrizados:

proyecto/
├── informe.qmd           ← plantilla con params
├── generar_lote.R        ← script que renderiza todos
├── datos/
│   └── ventas.csv
└── informes/
    └── (outputs generados, no versionados)

informe.qmd con defaults para development. generar_lote.R para producción. Carpeta informes/ en .gitignore (output regenerable).

Trampas habituales

  • Hard-coding valores en el código olvidando el params$. La plantilla deja de ser plantilla. Cualquier valor “de configuración” debe pasar por params.
  • Sobrescribir output sin querer. Si renderizas 10 veces sin output_file, te quedas con el último. Define output dinámico desde el principio.
  • No validar parámetros. Un region: "Estte" (con typo) filtra a 0 filas y el informe queda vacío sin error. Valida al inicio.
  • Tipos mal interpretados desde CLI. -P x:false puede llegar como string "false" en R/Python, no como booleano. Convierte explícito: as.logical(params$x).
  • YAML estricto con guiones, dos puntos. Los strings con caracteres especiales necesitan comillas: region: "Norte (sub-1)". Sin comillas, el : rompe el parseo.

En la siguiente entrega

Tienes la plantilla parametrizada. Lo siguiente es reusar fragmentos entre documentos: includes para insertar pedazos comunes, child documents para descomponer un documento grande. Es lo que mantiene un proyecto Quarto grande manejable. Lo siguiente.