Reproducibilidad: renv y Docker
Reproducibilidad: cuatro niveles
No todos los análisis necesitan el mismo rigor. Hay cuatro niveles, cada uno con coste creciente:
| Nivel | Qué bloquea | Cómo |
|---|---|---|
| 1. Código versionado | El qué se hizo | Git |
| 2. Versiones de paquetes | Las librerías exactas | renv (R), uv/requirements.txt (Python) |
| 3. Versión de R/Python | El intérprete | .Rprofile / pyproject.toml |
| 4. Sistema completo | OS, librerías de sistema | Docker |
Para análisis personal: nivel 1. Para análisis publicado o que pasa de un colaborador a otro: nivel 2 mínimo. Para análisis regulado (clínico, auditable): nivel 4.
renv: lockfile para R
renv es el sistema oficial moderno de Posit para gestionar dependencias de paquetes en R. Reemplaza al viejo packrat.
Inicializar
En la carpeta del proyecto:
install.packages("renv")
renv::init()Esto:
- Crea una librería local del proyecto en
renv/library/. - Genera
renv.lockcon las versiones exactas de los paquetes que usa el código. - Modifica
.Rprofilepara que al abrir el proyecto, R use la librería local.
A partir de aquí, todos los install.packages() van a la librería del proyecto, no a la global. Los proyectos quedan aislados.
El archivo renv.lock
{
"R": {
"Version": "4.4.1",
"Repositories": [...]
},
"Packages": {
"DESeq2": {
"Package": "DESeq2",
"Version": "1.44.0",
"Source": "Bioconductor",
"Hash": "abc123..."
},
"tidyverse": {
"Package": "tidyverse",
"Version": "2.0.0",
...
}
}
}Cada paquete con versión + hash + fuente. Versionado en Git. Cualquiera que clone el repo puede restaurar el entorno exacto:
renv::restore()Workflow típico
# Al instalar un paquete nuevo
install.packages("nuevo-paquete")
# Actualizar el lockfile
renv::snapshot()snapshot() revisa qué paquetes usas (escaneando el código) y los añade al renv.lock. Es explícito, no graba cualquier paquete instalado, solo los referenciados desde tu código.
Para Quarto
renv se integra automáticamente con Quarto: al renderizar dentro de un proyecto con renv, usa la librería local. No requiere configuración adicional.
Quitar renv de un proyecto
Si decides que no compensa:
renv::deactivate()Vuelve a la librería global. renv.lock se queda como historial pero no se aplica.
uv para Python
Para Python, uv es el equivalente moderno a renv. Más rápido que pip, más conciso que conda, gestiona todo: versiones de Python, librerías, virtualenvs.
Inicializar
uv init mi-proyecto
cd mi-proyectoEstructura inicial:
mi-proyecto/
├── pyproject.toml ← config del proyecto
├── uv.lock ← lockfile
├── README.md
└── .python-version ← versión Python pinneada
Añadir dependencias
uv add pandas numpy matplotlib seaborn
uv add jupyter # para Quarto con Pythonuv add descarga, instala en un venv del proyecto, y graba en pyproject.toml + uv.lock.
Restaurar en otra máquina
uv syncCrea el venv, instala todo desde uv.lock. Reproducible y rápido (segundos).
Para Quarto
Activa el venv antes de renderizar:
uv run quarto renderuv run ejecuta el comando con el entorno del proyecto. Quarto usa el Python del venv automáticamente.
requirements.txt: el clásico
Si tu proyecto Python no usa uv, lo mínimo es requirements.txt:
pandas==2.1.0
numpy==1.26.0
matplotlib==3.8.0
seaborn==0.13.0
jupyter==1.0.0
Restaurar:
pip install -r requirements.txtrequirements.txt es menos riguroso que uv.lock, no bloquea sub-dependencias, no versiona Python. Para reproducibilidad real, uv o un lockfile generado con pip-compile (de pip-tools).
Versión de R y Python
renv graba la versión de R en renv.lock pero no la fuerza. Para bloquearla:
# .Rprofile
if (getRversion() != "4.4.1") {
stop("Este proyecto requiere R 4.4.1, tienes ",
getRversion())
}Para Python con uv:
# pyproject.toml
[project]
requires-python = "==3.11.7"O en .python-version:
3.11.7
uv honra esto y descarga la versión correcta si hace falta.
Docker: el nivel máximo
renv y uv bloquean paquetes y lenguaje. No bloquean:
- Versión del sistema operativo.
- Librerías de sistema (libxml2, openssl, etc.).
- Versión de LaTeX para PDFs.
- Versión de Quarto mismo.
Para análisis que tienen que reproducirse dentro de 5 años con bit-perfect output, Docker.
Imagen base
Para proyectos R + Quarto:
FROM rocker/r-ver:4.4.1
RUN apt-get update && apt-get install -y \
libxml2-dev libcurl4-openssl-dev libssl-dev \
libfontconfig1-dev \
&& rm -rf /var/lib/apt/lists/*
# Quarto
RUN curl -LO https://github.com/quarto-dev/quarto-cli/releases/download/v1.5.57/quarto-1.5.57-linux-amd64.deb \
&& dpkg -i quarto-1.5.57-linux-amd64.deb \
&& rm quarto-1.5.57-linux-amd64.deb
# renv
RUN R -e 'install.packages("renv", repos = "https://cloud.r-project.org")'
WORKDIR /project
COPY . .
RUN R -e 'renv::restore()'
CMD ["quarto", "render"]rocker/r-ver:4.4.1 es la imagen oficial de Posit con R fijado. Encima:
- Instala librerías de sistema necesarias para paquetes R.
- Instala Quarto en versión específica.
- Instala
renv. - Copia el proyecto y restaura dependencias.
Build y run:
docker build -t mi-analisis .
docker run --rm -v $PWD:/project mi-analisisEl render ocurre dentro del contenedor. Outputs van a tu carpeta local vía el volume mount.
Para Bioconductor
Hay imágenes oficiales de Bioconductor:
FROM bioconductor/bioconductor_docker:RELEASE_3_20Trae R + Bioconductor + librerías de sistema esperables (rsamtools, samtools, etc.). Imprescindible para pipelines RNA-seq reproducibles.
Imagen multi-lenguaje
Para R + Python juntos:
FROM rocker/r-ver:4.4.1
# Python via uv
RUN curl -LsSf https://astral.sh/uv/install.sh | sh
ENV PATH="/root/.local/bin:${PATH}"
# Quarto + el resto...uv se instala como binario CLI, no como paquete pip. Compatible con cualquier imagen base.
Cuándo Docker, cuándo no
| Situación | Solución |
|---|---|
| Análisis personal | Nada / Git |
| Compartir con un colega | renv o uv |
| Publicar reproducible | renv + _freeze/ commitado |
| Análisis regulado (clínico, auditable) | Docker + renv |
| Pipeline de producción | Docker + CI |
Docker tiene coste: 30-60 minutos de setup inicial, learning curve, build times. Para un análisis ad-hoc es overhead. Para algo que tiene que correr dentro de 3 años, es lo único que garantiza el output exacto.
Documentar el entorno
Independientemente del nivel, siempre sessionInfo() al final del documento:
```{r}
#| label: session-info
#| echo: false
sessionInfo()
```
Para Python:
```{python}
#| label: session-info
#| echo: false
import platform, sys
print(f"Python: {sys.version}")
print(f"Platform: {platform.platform()}")
print("\nPackages:")
import pip
for line in pip.main(["list", "--format=freeze"]).split("\n"):
if line:
print(f" {line}")
```
Lo más sencillo. Al final del informe, queda escrito qué versiones se usaron. Para reproducibilidad básica, esto + Git ya es mucho.
El workflow recomendado completo
Para un proyecto Quarto serio:
git initdesde el principio.renv::init()(R) ouv init(Python).freeze: autoen_quarto.ymly commit de_freeze/.- CI que sirve el sitio sin recomputar (Netlify/GitHub Pages).
sessionInfo()al final de cada documento.- Docker si el análisis es crítico para reproducir en años.
Coste total: medio día de setup. Beneficio: cualquiera puede regenerar tu análisis dentro de 2 años. Vale la pena.
Trampas habituales
- Olvidar
renv::snapshot()después de instalar un paquete. La librería tiene el paquete perorenv.lockno, colaboradores no lo verán. - Commit de
renv/library/. Está en.gitignoreautomáticamente, son binarios pesados. Solo commit delrenv.lock. uvsin venv: si llamas aquarto rendersinuv run, usa el Python del sistema, no el del proyecto.- Docker con
latesten imagen base.rocker/r-ver:latestcambia con el tiempo. Pin a la versión exacta (4.4.1). - Asumir que
renv+ Git basta para auditoría regulatoria. No, los reviewers piden Docker + lockfile + datos hashed. Conoce el nivel de rigor que se te pide.
Has terminado la ruta
Con esto cierras Quarto y reproducibilidad. Has visto:
- Quarto vs Rmd, instalación, primer documento.
- Front matter, secciones, output multi-formato.
- Chunks con R y Python conviviendo.
- Figuras y tablas publicables.
- Cross-references y bibliografía.
- Parametrización para informes en lote.
- Includes y child documents para modularidad.
- Cache y freeze para iterar rápido.
- Websites y libros como proyectos.
- Publicación en GitHub Pages, Netlify, QuartoPub.
- Reproducibilidad real con
renv,uvy Docker.
A partir de aquí, las direcciones naturales: extensiones de Quarto (_extensions/), revistas con quarto-journals, dashboards con quarto-dashboards, integración con targets para pipelines analíticos serios.
Cuando publiquemos el libro Quarto profesional, cubriremos plantillas corporativas, multilingüe, casos completos con repositorio reproducible público y un dashboard real desde cero.