Reproducible workflows in R

Practical

Author
Affiliation

Dr Ewan Carr

Department of Biostatistics & Health Informatics
King’s College London

Published

July 21, 2026

Welcome

This practical will bring together the steps covered in the lecture by:

  1. Building a reproducible R pipeline
  2. Committing your project to GitHub
  3. Restoring it from GitHub

In June 2026, Spain beat England 4-0 — England’s heaviest defeat in 17 years — and denied them automatic qualification for the 2027 World Cup. The two sides meet again in the UEFA Women’s Nations League on 15 November 2026. We’ll build a reproducible pipeline to predict England’s chances in that rematch, using their real results over the past year.

You will:

  • Build models to predict England’s goals scored and conceded.
  • Simulate the match to estimate the chance of a win, draw or loss.
  • Use here for file paths and renv to lock package versions.
  • Create a Git repository, version your code with git, and push it to GitHub.
  • Make the project fully reproducible and shareable.

Setup

Software

To complete this practical, you’ll need:

  • R (≥ 4.5)
  • RStudio
  • Git, used for version control, plus either:
    • GitHub Desktop, a user-friendly way to interact with Git and GitHub; or
    • the GitHub CLI (gh), if you’d rather work at the command line.

GitHub Desktop includes its own copy of Git, so if you only ever use GitHub Desktop, that’s all you need.

WarningIf you’re new to the command line, use GitHub Desktop

GitHub Desktop provides a simple interface for common tasks like committing, pushing, and pulling code — no terminal required. It’s a good starting point if you’re unfamiliar with the command line.

Installing Git

Git is often pre-installed on macOS. You can check by typing in the terminal:

git --version

If it’s not installed, run:

xcode-select --install

You can optionally install GitHub Desktop, a graphical interface for Git and GitHub, by following the instructions here.

You can download Git for Windows from https://git-scm.com. Run the installer and accept default options, in particular:

“Use Git from the command line and also from 3rd-party software”

You can optionally install GitHub Desktop, a graphical interface for Git and GitHub, by following the instructions here.

Telling Git who you are

Git records a name and email address against every commit. If you have never used Git on this machine, set these once:

git config --global user.name "Your Name"
git config --global user.email "your_email@example.com"
git config --global init.defaultBranch main

Use the same email address as your GitHub account, so that your commits are linked to your profile. If you’re using GitHub Desktop, the name and email are filled in for you when you sign in.

Authenticating with GitHub

To push and pull from GitHub, you need to authenticate. Pick whichever route you’ll be using for the rest of the practical:

GitHub CLI (gh) is GitHub’s official command line tool. It handles authentication for you, and later saves us a trip to the browser when creating the repository.

  1. Install it:

  2. Log in:

    gh auth login

    Follow the prompts: choose GitHub.com, then HTTPS, then Login with a web browser. Copy the one-time code shown in the terminal, press Enter, and paste it into the page that opens.

  3. Check it worked:

    gh auth status

    You should see Logged in to github.com as your-username.

GitHub Desktop is a good place to start if you’re new to Git.

When you first sign in via the application, it stores a token securely on your computer. You can clone, commit, push, and pull without dealing with passwords or keys.

Using SSH keys is recommended for longer-term use.

To set them up will require access to a command line.

  1. Generate a key (if you don’t have one):

    ssh-keygen -t ed25519 -C "your_email@example.com"
  2. Copy the key to your clipboard

pbcopy < ~/.ssh/id_ed25519.pub
cat ~/.ssh/id_ed25519.pub | clip
  1. Add it to GitHub:

See these instructions if you get stuck.

If you use SSH, use the git@github.com:... form of the repository URL wherever https://github.com/... appears below.

Pre-flight check

Before starting, check that everything is in place. It’s much quicker to fix this now than halfway through.

1git --version
2git config --global user.name
3gh auth status
1
You should see a version number, e.g. git version 2.50.1.
2
You should see the name you set above.
3
You should see Logged in to github.com. If you get gh: command not found, install the GitHub CLI as described above; if you’re not logged in, run gh auth login.
  1. Open GitHub Desktop.
  2. Check you’re signed in: GitHub DesktopSettingsAccounts (macOS), or FileOptionsAccounts (Windows). You should see your GitHub username.
  3. If not, click Sign in to GitHub.com and follow the prompts.
Note👀Hidden files

Several files in this practical begin with a dot (.gitignore, .Rprofile, and the .git folder itself). These are hidden by default, and you’ll need to see them:

  • macOS: press ++. in Finder.
  • Windows: File Explorer → ViewShowHidden items.

RStudio’s Files pane has a MoreShow Hidden Files option, and GitHub Desktop always shows them.

1 Create a project in RStudio

Create a new RStudio Project and give it an appropriate name (e.g., england-prediction):

FileNew ProjectNew Directory

2 Download the scripts and datasets

Right click the links below to download the required scripts and dataset:

The fixtures.csv file contains England’s real results over the past year. If you’re curious how it was assembled, the optional 00-fetch.R script documents where the data came from — but you don’t need to run it.

Put these inside your project folder and recreate the structure shown below:

1-data/
├── raw/
│   └── fixtures.csv
└── clean/          ← create this folder; it starts empty
2-scripts/
├── 01-clean.R
├── 02-analysis.R
└── 03-plot.R
outputs/            ← create this folder; it starts empty

The 1-data/clean/ and outputs/ folders are empty to begin with — the scripts will fill them. You can create them by hand, or from the R console:

dir.create("1-data/clean", recursive = TRUE)
dir.create("outputs")

3 Initialise renv and install the required packages

In the R console, type:

1install.packages("renv")
2renv::init()
1
Install the renv package. You only need to do this once—not for each project.
2
Initialise renv for the current project.

renv::init() does three things: it scans your scripts for library() calls, installs the packages it finds, and writes them to a lockfile called renv.lock. Because the scripts are already in the project folder, this is all you need — there’s no separate install step. This may take a few minutes the first time.

It also creates a hidden .Rprofile file at the project root containing a single line, source("renv/activate.R"). That line is what switches renv on each time the project is opened, so it matters later when we commit our files.

Open the renv.lock lockfile to understand its contents.

Tip💡Adding packages later

If you add a new library() call to a script later on, install it with renv::install("packagename") and then run renv::snapshot() to record it in renv.lock.

4 Run the three scripts

Once packages are installed and renv is initialised, you’re ready to run the analysis.

Run 01-clean.R in RStudio. This script:

  1. Imports the fixtures.csv dataset from the 1-data/raw folder.
  2. Derives new variables: whether each match was won, drawn or lost; days of rest since the previous fixture; and recent form (wins and goals scored over the previous three matches).
  3. Saves a cleaned dataset to 1-data/clean/fixtures.rds.

Run 02-analysis.R in RStudio. This script:

  1. Fits two Poisson regression models to predict:
    • England’s goals
    • Spain’s goals
  2. Simulates 10,000 match results
  3. Saves the probabilities to outputs/results.rds

Run 03-plot.R in RStudio. This script:

  1. Loads the saved probabilities (outputs/results.rds).
  2. Creates a bar chart of predicted win/draw/loss.
  3. Saves the figure to outputs/prediction_plot.png.

5 Putting it all together

Create a new script run.R at the project root with the contents:

run.R
library(here)
source(here("2-scripts", "01-clean.R"))
source(here("2-scripts", "02-analysis.R"))
source(here("2-scripts", "03-plot.R"))

This script uses source to run the three scripts sequentially, avoiding the need to run them separately.

Run the run.R script either by clicking Run or by typing at the console:

source("run.R")

6 Initialise the Git repository

We’ve now set up our project, initialised renv, and created a run.R script that automates our data cleaning and analysis.

In this section, we’ll initialise a new, empty Git repository. Git will allow us to track changes to our files over time and restore previous versions.

You can complete this section via the terminal or using a desktop application, such as GitHub Desktop.

Open a terminal in the project root and type:

git init
  • You can do this from within RStudio in the ‘Terminal’ pane.
  • If you haven’t yet installed Git, see the instructions above.
  1. Open GitHub Desktop.
  2. Go to FileAdd Local Repository….
  3. Click Choose… and select your existing project folder.
  4. You should see the prompt:

The directory does not appear to be a Git repository. Would you like to create a repository here instead?”

  1. Click “Create Repository”.

7 Tell Git what to ignore

Not everything in a project folder belongs in version control. Some files are generated automatically, some are specific to your computer, and some (e.g., data) must never leave your machine at all.

Create a file called .gitignore at the project root containing:

.gitignore
.Rproj.user/
.Rhistory
.RData
.Ruserdata
.DS_Store
*.log

A ready-made version is available here: .gitignore.

Each line is a pattern; *.log ignores every file ending in .log, wherever it appears. In GitHub Desktop you can also right-click a file or folder in the Changes panel and choose Ignore file or Ignore folder, which writes these lines for you.

Warning🔒In real projects, ignore your data first

Our football results are public, so we’ll commit them. Identifiable research data is different: add the data folder to .gitignore before any data files are added. Deleting a file later is not enough — the earlier version stays in the Git history, and if the repository is ever made public, so is the data.

8 Commit files to the local repository

Having initialised the empty repository, we now need to add our files.

1git add 1-data 2-scripts outputs
2git add run.R renv.lock .gitignore
3git add .Rprofile renv/activate.R renv/settings.json
4git add england-prediction.Rproj
5git status
6git commit -m "Initial commit"
1
Add the data, scripts and outputs folders.
2
Add the run.R script, the renv.lock lockfile and your new .gitignore.
3
Add the renv machinery. Don’t skip this — without .Rprofile and renv/activate.R, renv won’t switch itself on when someone else opens the project, and the restore step later won’t work.
4
Add your RStudio Project file; change the name as appropriate.
5
Check what is about to be committed before committing it.
6
Commit the staged files with a short message (specified by -m)
  1. Open GitHub Desktop and select your repository.

  2. In the Changes tab, tick the checkboxes to stage the following:

    • All files in the 1-data/, 2-scripts/ and outputs/ folders
    • The run.R file
    • The renv.lock file and your new .gitignore
    • .Rprofile, renv/activate.R and renv/settings.json — these are hidden files, so make sure hidden files are visible (see above). Without them, renv won’t switch itself on for anyone who clones your project.
    • Your .Rproj file
  3. At the bottom left, enter a commit message: Initial commit

  4. Click Commit to main.

Tip📁Git doesn’t track empty folders

Git tracks files, not folders — an empty folder simply won’t appear in the repository. That’s why we run the scripts before committing: 1-data/clean/ and outputs/ now contain files. (The scripts also create these folders if they’re missing, so the pipeline still runs from a fresh clone.)

Looking at your history

You’ve made one commit. Take a moment to look at it:

git log --oneline

Click the History tab at the top of the left panel. You should see your commit, with its message and timestamp.

9 Send your repository to GitHub

Your repository currently exists only on your computer. Now we’ll put a copy on GitHub. If you haven’t already, create a free account at GitHub.com — using your KCL email address is a good idea for research repositories.

The GitHub CLI can create the repository on GitHub, connect it to your local one, and push — all in a single command. From your project folder:

gh repo create england-prediction --private --source=. --push
  • --private keeps the repository private; you can always make it public later. Use --public if you’d rather share it now.
  • --source=. uses the repository in the current folder.
  • --push pushes your commits straight away.
Note🌐Doing it through the website instead

If you’d rather not use gh, go to https://github.com/new and create a new repository with the same name. Set it to Private, and do not tick “Add a README file”, “Add .gitignore” or “Choose a licence” — your local repository already has these, and adding them here creates a conflicting history that makes your first push fail. Then connect and push:

git remote add origin https://github.com/username/england-prediction.git
git push -u origin main

GitHub Desktop can create the repository on GitHub for you — you don’t need to visit the website first.

  1. In GitHub Desktop, click Publish repository in the top bar.

  2. Check the name (e.g., england-prediction) and tick Keep this code private. You can always make it public later.

  3. Click Publish repository.

Now go to https://github.com/username/england-prediction in your browser (gh repo view --web will open it for you). You should see your files and your commit.

10 The everyday loop

Almost everything you do with Git from now on is the same three steps: make a change, commit it, push it. Let’s practise once.

Step 1. Make a small edit — add a comment to one of the scripts, or create a new file called NOTES.md.

Step 2. Commit and push:

1git status
2git add .
3git commit -m "Describe what you changed"
4git push
1
See which files have changed.
2
Stage all of the changes. (.gitignore keeps the unwanted ones out.)
3
Commit them with a message describing what you changed, not just “changes”.
4
Send them to GitHub.
  1. Your change appears in the Changes panel.
  2. Write a clear commit message and click Commit to main.
  3. Click Push origin in the top bar.

Step 3. Refresh your repository page on GitHub. Your new commit should be at the top of the history.

11 Reproducing your analysis from GitHub

We’ll now test that we can recreate our analysis from the online repository. This ensures your project can be reliably re-run on another computer or by another user.

The steps involved are:

  1. ‘Clone’ the existing repository from GitHub.
  2. Restore the renv environment.
  3. Run the run.R script to repeat the analysis.
Important⚠️Clone somewhere else

Your original project folder is still on your computer. Clone the copy to a different location (e.g., your Desktop) and give it a different name — otherwise Git will refuse with “destination path already exists”, and you risk confusing the two projects.

In the terminal:

1cd ~/Desktop
2git clone https://github.com/username/england-prediction.git test-clone
1
Move somewhere other than your existing project folder.
2
‘Clone’ the repository from GitHub into a new folder called test-clone. Replace username and the repository name as appropriate.

Then open the .Rproj file in the cloned folder, and at the R console:

1renv::restore()
2source("run.R")
1
Download and install the exact package versions recorded in renv.lock. This can take a few minutes.
2
Re-run the whole pipeline.
  1. FileClone Repository…
  2. Choose your repository.
  3. Under Local path, choose a location outside your existing project folder (e.g., Desktop/test-clone).

Then open the .Rproj file in the cloned folder, and at the R console:

1renv::restore()
2source("run.R")
1
Download and install the exact package versions recorded in renv.lock. This can take a few minutes.
2
Re-run the whole pipeline.

When you open the cloned project, you should see a message from renv in the console, something like “Project ‘~/Desktop/test-clone’ loaded. [renv 1.1.x]”. That message comes from .Rprofile — if you don’t see it, .Rprofile or renv/activate.R probably didn’t make it into your commit.

Note🔢A note on R versions

renv.lock also records the version of R used to build the project. If you’re running a different version, renv will warn you when restoring. This is usually fine, but it’s a useful reminder that R itself is part of your computational environment — which is what the Docker extension below addresses.

Check that outputs/prediction_plot.png has been regenerated inside the cloned folder. If it has, your project is genuinely reproducible: everything needed to recreate the analysis travelled with the repository.

12 Adding a ‘README’ file

  1. Write a README.md1 file in your project folder. Briefly describe the analysis and steps needed to reproduce.
  2. Add and commit this file to your local repository, and push the changes to GitHub — the same everyday loop as before.

Once pushed, you can view the README.md on your repository page on GitHub.

Tip🌿Going further with Git

We’ve covered the core workflow: commit, push, clone. Branches, pull requests and collaborating on a shared repository are the natural next steps — these are covered in the BRC Git workshop, whose exercises you can work through in your own time.

Optional extensions

You’ve now built, versioned, and shared a fully reproducible pipeline — that’s the heart of today’s session. If you have time, pick one of the extensions below to take it further. They’re independent, so you don’t need both.

Goal: turn your pipeline’s output into a reproducible report that rebuilds its tables and figures every time it’s rendered.

  1. Create a new file report.qmd at your project root (next to run.R). A ready-made template is available here: report.qmd.

  2. It reads the saved results and displays the prediction:

    ---
    title: "Can England beat Spain?"
    format: html
    execute:
      echo: false
    ---
    
    ```{r}
    library(tidyverse); library(here)
    results <- readRDS(here("outputs", "results.rds"))
    knitr::kable(results)
    ```
    
    ![](outputs/prediction_plot.png)
  3. Render it with the Render button in RStudio. (If you’d rather render from the console, install the R package first with renv::install("quarto"), then run quarto::quarto_render("report.qmd").)

  4. Commit and push report.qmd. Its rendered output, report.html, is generated from the source, so add it to .gitignore rather than committing it.

Goal: run the whole pipeline inside a container so it behaves identically on any machine, regardless of what’s installed.

WarningBefore you start

You’ll need Docker Desktop installed and running. It’s a large download — if you don’t have it, follow along and try this later.

We build on a pre-built Rocker image (which already contains R and the tidyverse), so you’re not installing everything from scratch. From your project folder:

  1. Create a Dockerfile at the project root — a ready-made one is here: Dockerfile. It restores your exact packages from renv.lock:

    FROM rocker/tidyverse:4.5.1
    WORKDIR /project
    COPY renv.lock renv.lock
    RUN R -e "install.packages('renv'); renv::restore(prompt = FALSE)"
    COPY . .
    CMD ["Rscript", "run.R"]
  2. Add a .dockerignore alongside it (ready-made version) so that your local package library isn’t copied into the image:

    .dockerignore
    renv/library/
    renv/staging/
    .Rproj.user/
    .git/
  3. Build the image:

    docker build -t england-prediction .
  4. Run it, mounting your outputs folder so the figure appears locally:

    # macOS / Linux
    docker run --rm -v "$(pwd)/outputs:/project/outputs" england-prediction
    
    # Windows PowerShell
    docker run --rm -v "${PWD}/outputs:/project/outputs" england-prediction

Footnotes

  1. Read this if you’re not sure how to start.↩︎