# A tibble: 6 Γ 6
country continent year lifeExp pop gdpPercap
<fct> <fct> <int> <dbl> <int> <dbl>
1 Afghanistan Asia 1952 28.8 8425333 779.
2 Afghanistan Asia 1957 30.3 9240934 821.
3 Afghanistan Asia 1962 32.0 10267083 853.
4 Afghanistan Asia 1967 34.0 11537966 836.
5 Afghanistan Asia 1972 36.1 13079460 740.
6 Afghanistan Asia 1977 38.4 14880372 786.
Tools for a Reproducible, Shareable & Communicable Science
DiplΓ΄me Universitaire Public Health Data Science
Foreword and Instructions
βοΈ Setup (to be completed before starting the class)
Install the latest version of β https://cran.r-project.org/
Install the latest version of RStudio β https://posit.co/download/rstudio-desktop/
Then paste the following lines into your console:
install.packages(c(
"tidyverse", # ggplot2, dplyr, readr, β¦
"gapminder", # our main dataset
"renv", # dependency management
"here", # portable file paths
"quarto", # render .qmd from R
"knitr", # knitting engine
"sessioninfo" # document your environment
))Verify that Quarto is installed and available from the terminal: in the RStudio Terminal tab, type quarto --version.
The command should return a version number if everything works correctly (e.g.Β β₯ 1.4).
Install Python β₯ 3.10 β https://www.python.org/downloads/
Install VS Code β https://code.visualstudio.com/ (with the Python extension)
or use JupyterLab: pip install jupyterlab
Create and activate a virtual environment, then install packages:
# Create project folder and virtual environment
mkdir repro-phds && cd repro-phds
python -m venv .venv
# Activate (choose the one line right for your OS)
source .venv/bin/activate # for macOS or Linux
# .venv\Scripts\activate # for Windows (PowerShell)
# Install python libraries
pip install pandas plotnine jupyterlab quarto-cli pyprojrootVerify Quarto is indeed available by running quarto --version in your terminal (it should return a version number).
If you run into installation errors, search the error message on the web: troubleshooting is a core data-science skill! Most errors have a quickly identifiable solution on Stack Overflow for instance⦠GenAI chatbots such as Vibe from Mistral AI can also be very helpful.
1 Reproducibility: what it is and why it matters?
β± ~45 minutes1.1 π Readings: the reproducibility crisis
1.2 π‘ The Reproducibility Spectrum
βReproducibleβ is used loosely, and several definitions co-exist in different scientific fields. In this class, we will use the definition below focusing on computational reproducibility:
| Same data? | Same code/method? | What does it test? | |
|---|---|---|---|
| Reproducible | β | β | Can you re-run the analysis and get the exact same numbers? |
| Replicable | β | β | Does the same method generalise to new data? |
| Robust | β | β | Do different analytical choices reach the same conclusion? |
| Generalisable | β | β | Does the finding hold beyond the original context? |
The diagram below (from The Turing Way community) relates these 4 levels to one another:
1.3 π‘ Why reproducibility matters
What is the value of a non reproducible article?
If it isnβt reproducible, is it science?
Should we just trust each other?
Researchers are accountable for their scientific results, while public funding demands accountability. Scientific credibility and each researcherβs professionalism rest on this. Working reproducibly, by making oneβs methods and results verifiable, helps meet these expectations.
- π Scientific journals require some level of reproducibility: peer reviewers no longer just read and assess manuscripts, they now also check the code, the data and the computational workflows/pipelines before final acceptance for publication.
- π‘οΈ Reproducibility acts as a methodological shield: it reduces the likelihood of undetected errors and spurious findings.
- πͺπΊπ«π· Institutional law: the European Union, the French National Research Agency (ANR) and the French High Council for the Evaluation of Research and Higher Education (HcΓ©res), among others, all require some level of reproducibility for the research they fund.
- π§± Increased impact: reproducible articles are cited and reused more (they provide more trustworthy foundations for future works).
In public health, reproducibility carries an additional importance:
- Policy decisions are made from published findings. A wrong or irreproducible result can harm patients and populations at scale.
- Publicly-funded research should be accountable: if the public paid for the study, the code and data should (where possible) be publicly accessible. This idea is very much related to open-science, a concept connected to reproducibility, but different (β‘οΈπ more details here)
Barriers, motivations and solutions
| Barrier | Concrete example | Practical solution |
|---|---|---|
| Cultural | βMy PI never does thisβ | Frame it as a career investment, not overhead |
| Technical | Simulation takes 3 days to run | Store pre-computed intermediate results; provide a fast reduced-run mode |
| Legal | Patient data under GDPR | Generate synthetic data of same structure; grant temporary restricted auditor access |
| Time | βIβll clean up the code laterβ | Start early: it costs extra work and time upfront, but saves a lot of time during later revisions |
Reproducibility is a safeguard, not a burden
Reproducibility and its testimonies strengthen trust. Indeed, scientists who care about the reproducibility of their work are more efficient in the long term: they can build on their own past work more easily.
Reproducibility benefits science at several levels:
| π Research field | π₯ Research group | π§βπ¬ Yourself |
|---|---|---|
| Stronger methodological credibility | Faster transmission to collaborators | Faster article completion & revisions |
| Cumulative, extendable knowledge | Reduced technical debt | Transparent & trustable for audience |
| Lower risk of published errors | Clear, defensible archival | Hard skills & efficient workflow |
| More citations |
1.4 π§βπ» Exercise 1: Create Your Reproducible Project Skeleton
2 Literate Programming with Quarto
β± ~45 minutes2.1 π Reading: One Document to Rule Them All
2.2 π‘ Anatomy of a .qmd File
A Quarto file has three building blocks:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 1. YAML header -> title, author, output type... β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 2. Markdown text -> prose, headings, lists, links β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β 3. Code chunks -> R or Python code + its output β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Quarto (like its predecessor, Rmarkdown) lets you blend code and narrative text into a single dynamic document. Artwork by Allison Horst β CC-BY 4.0.
1. The YAML header
A YAML header sits at the very beginning of the document, between two lines containing only ---. It defines, among other things, the output settings and the overall format of the document. Here is an example. Be careful: indentation matters in YAML.
---
title: "Global Health Trends β Gapminder Data"
author: "Your Name"
date: today # auto-fills today's date
format:
html:
toc: true # table of contents
code-fold: true # hide code by default (reader can expand)
embed-resources: true # self-contained `HTML` file (portable)
execute:
echo: true # show code in output by default
warning: true # keep showing warnings
message: false # suppress messages (eg when loading packages)
---2. Markdown text
Between code chunks, narrative text is written in Markdown (.md), a lightweight markup language. Here are a few syntax basics:
# Heading level 1
## Heading level 2
A paragraph. Make text **bold**, *italic*, or `code-styled`.
- Bullet list item one
- Bullet list item two
1. Numbered item one
2. Numbered item two
[Descriptive link text](https://url.com)
{width=50%}3. Code chunks
These chunks are enclosed in triple back-ticks ```, followed by {r} or {python} (for instance) to specify the programming language interpreter to use. Chunk-specific options are indicated by setting values to specific keywords at the beginning of the code chunk with the following syntax: #| keyword: value (which can override the defaults set in the header if needed). Below is a code chunk example, followed by its output:
```{r}
#| label: summary-table-r
#| echo: true
#| eval: true
library(gapminder)
head(gapminder)
```Key chunk options (written after #| inside the chunk):
| Option | Default | Effect |
|---|---|---|
echo |
true |
Show the code in output |
eval |
true |
Run the code |
include |
true |
Include the chunk output |
message |
true |
Show package-load messages |
warning |
true |
Show warnings |
cache |
false |
Cache results (useful for slow code) |
label |
β | Name for cross-referencing figures/tables |
fig-cap |
β | Figure caption |
fig-width / fig-height |
β | Figure dimensions in inches |
Inline code
You can also insert automatically computed values inline, directly into the text, using `r ` or `{python} `:
The dataset covers
`r length(unique(gapminder$country))`countries from`r min(gapminder$year)`to`r max(gapminder$year)`
When rendered, this becomes:
The dataset covers 142 countries from 1952 to 2007
β¨ No copy-paste, no stale numbers, no manual update!
2.3 π§βπ» Exercise 2: Your First Reproducible Quarto Report
3 Data Visualisation
β± ~40 minutes3.1 π Reading: The Grammar of Graphics
3.2 π‘ Building Plots Layer by Layer
The seven layers
| Component | In ggplot2 | Example |
|---|---|---|
| Data | ggplot(data = ...) |
gapminder |> filter(year == 2007) |
| Aesthetics | aes(x, y, colour, size, shape) |
aes(x = gdpPercap, y = lifeExp, colour = continent) |
| Geometry | geom_*() |
geom_point(), geom_line(), geom_boxplot() |
| Scales | scale_*() |
scale_x_log10(), scale_colour_viridis_d() |
| Facets | facet_wrap() / facet_grid() |
facet_wrap(~ continent) |
| Theme | theme_*() |
theme_minimal() |
| Labels | labs() |
labs(title = "...", x = "...", caption = "...") |
A complete worked example
library(ggplot2)
library(gapminder)
library(dplyr)
gap_2007 <- gapminder |> filter(year == 2007)
ggplot(
data = gap_2007,
aes(x = gdpPercap, y = lifeExp,
colour = continent,
size = pop) # bubble size = population
) +
geom_point(alpha = 0.7) +
scale_x_log10(labels = scales::dollar_format()) + # log GDP axis
scale_colour_viridis_d(option = "plasma") + # colour-blind safe
scale_size(range = c(2, 14), guide = "none") +
labs(
title = "Wealth and Health in 2007",
subtitle = expression("Each bubble is a country; size" %prop% "population"),
x = "GDP per capita (log scale, USD)",
y = "Life expectancy (years)",
colour = "Continent",
caption = "Source: Gapminder Foundation"
) +
theme_minimal(base_size = 13)from plotnine import (ggplot, aes, geom_point, scale_x_log10, scale_size,
scale_colour_cmap_d, labs, theme_minimal)
from mizani.labels import label_dollar
gap_2007 = gapminder[gapminder["year"] == 2007]
(
ggplot(gap_2007,
aes(x="gdpPercap", y="lifeExp",
colour="continent",
size="pop")) # bubble size = population
+ geom_point(alpha=0.7)
+ scale_x_log10(labels=label_dollar(precision=0, big_mark=",")) # log GDP axis
+ scale_colour_cmap_d("plasma") # colour-blind safe
+ scale_size(range=(2, 14), guide=None)
+ labs(
title="Wealth and Health in 2007",
subtitle="Each bubble is a country; size β population",
x="GDP per capita (log scale, USD)",
y="Life expectancy (years)",
colour="Continent",
caption="Source: Gapminder Foundation"
)
+ theme_minimal(base_size=13)
)Saving figures programmatically
Saving a figure manually (by right-clicking or clicking βExportβ in the Plots pane) is not reproducible: the size, resolution, and format vary each time, and the step is not recorded in your code (it remains invisible).
Always save figures inside your script, with fixed dimensions and resolution.
# 1. Assign your plot to a named variable
p <- ggplot(gap_2007, aes(...)) + ...
# 2. Save: provide either a vector format (e.g. `.PDF`) or a raster format (e.g. `.PNG`) depending on the nature of your graph
ggsave(
filename = here::here("output", "figures", "fig01_wealth_health_2007.pdf"),
plot = p,
width = 10, height = 6, units = "in"
)
ggsave(
filename = here::here("output", "figures", "fig01_wealth_health_2007.png"),
plot = p,
width = 10, height = 6, units = "in",
dpi = 300 # 300 dpi = publication quality
)from pyprojroot import here
# 1. Assign your plot to a named variable
p = ggplot(gap_2007, aes(...)) + ...
... your plotting code ...
# 2. Save in a vector format (e.g. `.PDF`) or a raster format (e.g. `.PNG`) depending on the nature of your graph
p.save(
here("output/figures/fig01_wealth_health_2007.pdf"),
width=10, height=6, units="in", verbose=False
)
p.save(
here("output/figures/fig01_wealth_health_2007.png"),
width=10, height=6, units="in",
dpi=300, verbose=False # 300 dpi = publication quality
)File naming convention: use a numeric prefix consistent with the order of figures and tables in your document β fig01_, fig02_, β¦, tab01_, tab02_, β¦
Choosing accessible colour palettes
About 8 % of men in France have some form of colour-vision deficiency (according to the SociΓ©tΓ© FranΓ§aise dβOphtalmologie). Here are a few palettes designed to be perceptually uniform and colour-blind safe:
| Palette | Package /library | Use case |
|---|---|---|
viridis, plasma, magma |
viridis / built-in ggplot2 |
Sequential / continuous |
scale_colour_viridis_d() |
ggplot2 | Discrete categorical |
scale_colour_brewer(palette = "Set2") |
ggplot2 | Categorical (up to 8 groups) |
scale_colour_cmap_d("viridis") |
plotnine | Discrete categorical |
scale_colour_cmap("viridis") |
plotnine | Continuous colour mapping |
3.3 π§βπ» Exercise 3: Figures
4 Sharing & the Reproducibility Checklist
β± ~35 minutes4.1 π Reading β The Journal Audit
4.2 π‘ Tools for Sharing and Archiving
The full reproducibility toolchain
Getting from βworks on my machineβ to βanyone can run itβ requires stacking a few layers:
π Project organisation RStudio Projects / standard folder structure
π Dependency management `renv` (R) / `venv` + requirements.txt (Python)
π Literate document `Quarto` (.qmd)
π² Random seeds `set.seed()` / `np.random.seed()`
ποΈ Version control `git`
π Remote hosting GitHub
π¦ Permanent archiving Zenodo β DOI you can cite in a paper
renv in and virtual environments in
renv creates a project-local library and records exact package versions in renv.lock.
renv::snapshot() # update lockfile after installing / changing packages
renv::restore() # collaborator command: install exact same versions
renv::status() # check whether lockfile and current state agreeAt the end of your report, always record your session environment:
sessioninfo::session_info()This prints version, OS, and the exact version of every loaded package.
In the terminal, as mentioned in SectionΒ 1.
pip freeze > requirements.txt # save exact versions
# Collaborator setup
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txtAt the end of your notebook:
## Requirements
```{python}
import sys, importlib.metadata
print(f"Python {sys.version}")
for pkg in ["pandas", "numpy", "plotnine"]:
print(f" {pkg}: {importlib.metadata.version(pkg)}")
```Git and GitHub : the essentials
Why use git and GitHub ?
π Off-site backup: if your laptop dies, nothing is lost
π Full history: you can go back in time to any previous version, without worrying that you inadvertently deleted some code that you might end up using later in time
π Collaboration: others can contribute easily, without emailing files
π Integration with Zenodo for citable DOIs
gitis a version control system that allows to track changes to all the files within a project, and synchronize those changes across multiple computers and contributors.- GitHub is a web platform from Microsoft providing
gitintegration with cloud hosting.
You do not need to be a git expert. Three commands cover most of everyday use:
git add . # stage all changes
git commit -m "Add life expectancy analysis and figures" # message describing the staged changes
git push # send to GitHubgit setup
git config --global user.name "Your chosen user name"
git config --global user.email "you@domain.ext"In RStudio: Tools β Global Optionsβ¦ β Git/SVN β point to the git executable. For trouble shooting in setting-up git in RStudio, you can refer to the Happy git with R online book.
Zenodo: a permanent DOI for your code (and optionally data)
Zenodo is a CERN-hosted (European Organization for Nuclear Research) archive that gives your code or data a DOI (which stands for Digital Object Identifier), a permanent, citable identifier. This is what goes in your paperβs Data Availability statement.
How it works in three steps:
- Push your project to a public GitHub repository
- Connect GitHub at https://zenodo.org/account/settings/github
- Create a GitHub release (e.g.Β
v1.0) β Zenodo archives it automatically and issues a DOI
Your paper then cites (by replacing XXXXXXX with the actual DOI numbers): > βAll analysis code is available at https://doi.org/10.5281/zenodo.XXXXXXXβ
The fact that it has a DOI and is hosted in Europe makes it a lot more future proof than just having a GitHub repo (especially as they are not exclusive).
4.3 π§βπ» Exercise 4: Self-Audit and Preparing to Share
π Summary & wrap-up
What we learned:
π Adopting a clean, portable structure for easy navigation inside your project
π Running everything end-to-end with a single command
π Programmatically saving figures and result tables
π Locking your dependencies, so the environment is reproducible
π Including inline computed values directly in the text
π Passing the standard of a journal reproducibility checklist
The one rule
Start small, start now. Reproducibility is an ideal, each small step forward brings you closer.
Key tools
| Tool | Problem it solves | One command to remember |
|---|---|---|
| RStudio Project | No more absolute paths; self-contained workspace |
Open .Rproj
|
| Quarto | Code + prose in one document; no copy-paste of values |
quarto render
|
| renv / venv | Exact package versions β βit worked last yearβ insurance |
renv::snapshot()
|
| here / pyprojroot | Portable file paths that work on any OS |
here::here(βoutputβ,βfig.pdfβ) / here(βoutput/fig.pdfβ)
|
| ggplot2 / plotnine | Programmatic, consistent, exportable figures |
ggsave() / p.save()
|
| Git + GitHub | Version history, collaboration, off-site backup |
git add . && git commit && git push
|
| Zenodo | Permanent DOI for code citation in papers | Connect GitHub in Zenodo settings |
Going further
| Resource | What for |
|---|---|
| The Turing Way | Comprehensive, community-maintained reproducible research guide |
| R for Data Science (2e) | Tidyverse, ggplot2, Quarto β free online book |
| What They Forgot to Teach You About R | Project organisation, naming, file paths |
| Quarto documentation | Everything Quarto |
| ggplot2 book (3e) | Deep dive into the grammar of graphics |
| Python Data Science Handbook | NumPy, Pandas, Matplotlib, scikit-learn |
| Hejblum et al.Β (2020) | Reproducible research for biostatistics |
| Hornung et al.Β (2026) | Overcoming computational reproducibility barriers in scientific publishing |
| Ouvrir la Science (in English) | French open science guides and passports (MESRI) |