Includes y child documents

quarto
reproducibilidad
Cómo descomponer un documento grande en piezas reutilizables. Includes para fragmentos comunes (autoría, footers, secciones de metodología), child documents para descomponer secciones y patrones para no duplicar contenido.

Tres formas de descomponer

Cuando un documento Quarto crece, necesitas dividirlo. Tres mecanismos, cada uno para un caso:

Mecanismo Para qué
Include ({{< include >}}) Insertar markdown (texto, no chunks ejecutables) reutilizable
Child document Insertar un .qmd completo con sus chunks ejecutables
Source de R/Python Reutilizar funciones en chunks

Los tres pueden convivir. La regla simple:

  • Texto que se repite entre documentos → include.
  • Sección compleja del mismo documento → child.
  • Lógica de análisis (funciones, transformaciones) → archivo .R o .py con source() o import.

{{< include >}}: el shortcode

Sintaxis básica en un .qmd:

## Sobre los autores

{{< include _autores.qmd >}}

## Análisis

Contenido principal...

{{< include _footer.qmd >}}

Y en _autores.qmd (nombre con underscore inicial, convención):

**Ana López** — analista de datos en Telómera.

**Carlos Ruiz** — bioinformático freelance.

[Contactar al equipo](mailto:info@ejemplo.com)

Quarto sustituye el {{< include >}} por el contenido del archivo, antes de renderizar. El output final es como si todo estuviera en un solo .qmd.

Por qué el underscore

Por convención, los archivos que son partes de otros documentos se nombran con _ inicial. Tres razones:

  • Quarto los ignora al renderizar un directorio completo (no genera _autores.html).
  • En listings, no aparecen como documentos navegables.
  • Convención común en herramientas estáticas, fácil de reconocer.

Cualquier .qmd con _ inicial no se incluye como output. Es la forma idiomática de marcar parciales.

Qué puede ir en un include

Solo markdown. Frente al include, Quarto procesa el texto Markdown del archivo pero no ejecuta chunks del archivo incluido como si fueran chunks propios, los hereda, pero pueden tener problemas con params o variables locales.

Lo seguro a incluir:

  • Texto narrativo que se repite (introducción estándar, footer, agradecimientos).
  • Tablas Markdown estáticas.
  • Imágenes externas con captions.
  • Bloques de callout estándar (advertencias legales, disclaimers).

Lo que no conviene incluir:

  • Chunks que dependen de params del documento padre, funcionan pero conceptualmente raro.
  • Documentos parametrizados que tienen su propio YAML.

Para chunks ejecutables: child documents

{{< include >}} carga markdown. Para incluir chunks que se ejecutan en el contexto del padre, el patrón clásico (heredado de Rmd) es el child document:

`​`​`{r}
#| child: "_seccion-resultados.qmd"
`​`​`

#| child: le dice a knitr que ejecute el contenido del archivo como si fuera parte del documento padre. Las variables del padre están disponibles, los chunks se ejecutan en orden, todo limpio.

_seccion-resultados.qmd no necesita YAML propio (es fragmento, no documento independiente):

## Resultados

`​`​`{r}
#| label: tbl-resumen
#| tbl-cap: "Resumen estadístico."

datos |>
    group_by(grupo) |>
    summarise(media = mean(x), n = n()) |>
    knitr::kable()
`​`​`

Como aparece en @tbl-resumen, el grupo A destaca.

Al renderizar el padre, el child se inserta y se ejecuta.

Estructura típica de un documento grande

informe-largo.qmd          ← documento principal con setup y orquestación
_intro.qmd                 ← child: introducción
_metodologia.qmd           ← child: metodología (suele ser reutilizable)
_resultados.qmd            ← child: resultados
_discusion.qmd             ← child: discusión
_footer.qmd                ← include: footer estándar
R/                         ← código auxiliar
  ├── funciones.R
  └── carga-datos.R

informe-largo.qmd:

---
title: "Informe técnico anual"
---

`​`​`{r}
#| label: setup
#| include: false

library(tidyverse)
source("R/funciones.R")
source("R/carga-datos.R")
`​`​`

`​`​`{r}
#| child: "_intro.qmd"
`​`​`

`​`​`{r}
#| child: "_metodologia.qmd"
`​`​`

`​`​`{r}
#| child: "_resultados.qmd"
`​`​`

`​`​`{r}
#| child: "_discusion.qmd"
`​`​`

{{< include _footer.qmd >}}

Documento principal legible: la estructura del informe se ve sin scroll infinito. Cada sección en su archivo.

Funciones en archivos .R

Para lógica de análisis, no uses includes ni childs, separa en archivos R/Python normales:

# R/funciones.R

normalizar_ventas <- function(df, divisor = 1e3) {
    df |>
        mutate(ventas_norm = ventas / divisor)
}

resumen_por_grupo <- function(df, grupo) {
    df |>
        group_by({{ grupo }}) |>
        summarise(
            media = mean(valor, na.rm = TRUE),
            sd = sd(valor, na.rm = TRUE),
            n = n()
        )
}

Desde el .qmd:

source("R/funciones.R")

datos |>
    normalizar_ventas() |>
    resumen_por_grupo(region)

El .qmd queda narrativo: el “qué” del análisis. Las funciones en .R son el “cómo”, reutilizables, testeables, ordenadas.

Source vs functions inline

Hay quien pone las funciones al principio del .qmd en un chunk grande. Funciona, pero pierde dos cosas:

  • Reutilización: si otro .qmd necesita la misma función, no la tiene.
  • Testing: probar funciones en un chunk Quarto es incómodo. En un .R puedes hacer testthat.

Recomendación: funciones en R/, narrativa en .qmd. Como tener clases en archivos separados en cualquier lenguaje.

Headers e indentación

Cuando incluyes un fragmento con secciones (##), las cabeceras se respetan tal cual. Si el child tiene ## Resultados y lo metes bajo # Análisis del padre, la estructura del TOC es:

1. Análisis
  1.1. Resultados (del child)

Si te importa que se ajusten dinámicamente al nivel donde se incluyen, Quarto no lo hace automáticamente, diseña los headers del child sabiendo dónde se va a usar. Si un fragmento debe ser flexible, escríbelo sin headers y deja que el padre los añada.

Includes recursivos

Puedes incluir archivos que a su vez incluyen otros. Funciona. Útil para descomponer una “metodología estándar” en sub-fragmentos:

_metodologia.qmd
  ├─ {{< include _met-datos.qmd >}}
  ├─ {{< include _met-analisis.qmd >}}
  └─ {{< include _met-validacion.qmd >}}

Mantén la profundidad manejable (2-3 niveles). Más se hace difícil de navegar.

Path resolution

Los paths en {{< include >}} y #| child: son relativos al archivo padre. Si el padre está en informes/anual.qmd y quieres incluir _partes/intro.qmd, escribes:

{{< include _partes/intro.qmd >}}

Si organizas con subdirectorios, mantén los paths consistentes. Mover archivos rompe includes silenciosamente.

Trampas habituales

  • Olvidar el _ inicial. Si llamas a un parcial intro.qmd (sin guión bajo) y renderizas el directorio, Quarto genera intro.html huérfano.
  • Includes con YAML duplicado. Un archivo incluido no debe tener --- propio, su YAML interfiere con el del padre. Solo el padre tiene front matter.
  • Chunks en includes sin contexto. Si el include depende de un objeto definido en el padre, asegúrate de que el include va después del chunk que lo define.
  • Child documents olvidando el flag. #| child: "..." necesita la opción. Sin ella, knitr trata el path como un comentario.
  • Path absoluto vs relativo. Usa siempre paths relativos. Absolutos rompen al mover el proyecto.

En la siguiente entrega

Tienes la estructura modular. La última pieza del bloque editorial: caché y freeze, cómo evitar que el render entero recalcule chunks pesados cada vez. Es lo que distingue iterar rápido un documento grande de pasar 10 minutos por render. Lo siguiente.