Scientific workflows: Tools and Tips 🛠️
2026-07-16
📅 Every 3rd Thursday 🕓 4-5 p.m. 📍 Webex
1861 ecology papers from 7 journals (2017-2024)
My paper is accepted: “Temperature and antibiotic resistance in soil bacteria”.
Now I need to publish code and data
temperature-resistance/
├── clean.R
├── analysis_final.R
├── makeplots.R
├── growth_assay.csv
├── growth_assay_clean.csv
├── results.csv
└── Rplot01.png
Let’s make this repo publishable!
Can someone understand it?
temperature-resistance/
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── growth_assay_clean.csv
├── analysis/
│ ├── clean.R
│ ├── analysis_final.R
│ └── makeplots.R
└── output/
├── figures/
│ └── Rplot01.png
└── tables/
└── results.csv
Our messy project, sorted into folders
temperature-resistance/
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
Our project with nicer file names
temperature-resistance/
├── README.md
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
At minimum, a README should answer:
# Temperature and antibiotic resistance in soil bacteria
Code and data for: Baldauf et al. (2026), *Journal*, doi:10.xxxx/xxxxx
Cite as: Baldauf et al. (2026). Data and code for "Temperature and antibiotic resistance in soil bacteria". doi:10.xxxx/xxxxx
Code is licensed under MIT, data under CC-BY-4.0. See `LICENSE` and `data/LICENSE` for details.
## How to run
Download or clone the repository to run it locally.
The project needs R version 4.0.0 or higher.
1. Restore the environment: `renv::restore()` - This will create a project library and install all required packages.
2. Run `main.R`, which runs all analysis scripts in order
## Project structure
- `analysis/` analysis scripts, run in numerical order
- `data/raw/` the original growth-assay measurements (read-only)
- `data/clean/` cleaned data produced by the scripts
- `output/` figures and tables produced by the scripts
### `analysis/`
- `01_clean-data.R`: cleans the raw data and saves it
- Input: `data/raw/growth_assay.csv`
- Output: `data/clean/cleaned_data.csv`
- `02_fit-models.R`: fits the models and saves the results table
- Input: `data/clean/cleaned_data.csv`
- Output: `output/tables/model-results.csv`
- `03_make-figures.R`: makes the manuscript figures
- Input: `data/clean/cleaned_data.csv`, `output/tables/model-results.csv`
- Output: `output/figures/linear-model-fit.png` (Figure 2)
### `data/`
See `data/README.md` for a description of all data variables.
## Contact
selina.baldauf@fu-berlin.deCan someone rerun it?
Level 1: Number your scripts 01_, 02_, etc. and add this to the “How to run” section in the README.
temperature-resistance/
├── README.md
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
Level 2: Create a main script that calls all scripts in order, so this is the only file someone needs to open.
# Run the whole analysis pipeline
# Step 1: Clean the data
# Input: `data/raw/growth_assay.csv`
# Output: `data/clean/cleaned_data.csv`
source("analysis/01_clean-data.R")
# Step 2: Fit the models and produce table 1
# Input: `data/clean/cleaned_data.csv`
# Output: `output/tables/model-results.csv`
source("analysis/02_fit-models.R")
# Step 3: Make all manuscript figures
# Input: `data/clean/cleaned_data.csv`, `output/tables/model-results.csv`
# Output: `output/figures/linear-model-fit.png` (Figure 2)
source("analysis/03_make-figures.R")# Run the whole analysis pipeline
from clean_data import main as clean_data
from fit_models import main as fit_models
from make_figures import main as make_figures
if __name__ == "__main__":
# Step 1: Clean the data
# Input: `data/raw/growth_assay.csv`
# Output: `data/clean/cleaned_data.csv`
clean_data()
# Step 2: Fit the models and produce table 1
# Input: `data/clean/cleaned_data.csv`
# Output: `output/tables/model-results.csv`
fit_models()
# Step 3: Make all manuscript figures
# Input: `data/clean/cleaned_data.csv`, `output/tables/model-results.csv`
# Output: `output/figures/linear-model-fit.png` (Figure 2)
make_figures()temperature-resistance/
├── README.md
├── main.R
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
Level 3: Use workflow management tools like targets (R) or Snakemake (any language). Define workflows, automatically run the steps in the right order and only rerun what changed.
Let people know what they need to install to make the code work.
Level 1: Copy-paste dependencies into the README. Easy, but less convenient for people who need to install the software.
sessionInfo() at the end of your pipeline (so every package is loaded), then paste the package list into the README.renv::dependencies() also lists the packages your code uses, but without versions.Level 2: Snapshot your local environment so users can recreate them easily.
Use the renv package to:
renv::init(): set up a project library and record all packages in a renv.lock filerenv::snapshot(): update your lockfile with new packagesrenv::restore(): recreate the environment on another machineUse uv to:
uv init: set up a project that records dependencies in a pyproject.tomluv add pandas: install a package and pin all exact versions in a uv.lock fileuv sync: recreate the environment on another machine (including the right Python version)Level 3: Package the whole environment in a container (e.g. Docker) so the code runs identically on any machine. Powerful but also more to learn and maintain.
python base image (e.g. python:3.12-slim) and install your dependencies in ituv also provides Docker imagesrequirements.txt or environment.ymlC:/Users/selina/PhD/...) will break the projectUse an R project (.Rproj, in RStudio or Positron) and the here package. here() finds the root by searching upward for a marker (.Rproj, .git, .here), independent of the working directory:
Use the pyprojroot package, the equivalent of R’s here. It also has a here() function that finds the root, so paths resolve correctly from everywhere:
Can someone understand the data situation?
.csv, .tsv, .txt, .json, .parquet, etc..xlsx, .sav, .dta, etc.temperature-resistance/
├── README.md
├── main.R
├── renv.lock
├── data/
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
Describe all your data files: Variable names, units, missing data codes, and other relevant information.
E.g. one table per file:
| Variable | Meaning | Unit / coding |
|---|---|---|
sample_id |
Unique sample identifier | text |
temp |
Incubation temperature | °C |
resistant |
Grew on antibiotic plate | 0 = no, 1 = yes |
mic |
Minimum inhibitory concentration | mg/L |
data/README.md for all data filesMain README:
temperature-resistance/
├── README.md
├── main.R
├── renv.lock
├── data/
│ ├── README.md
│ ├── raw/
│ │ └── growth_assay.csv
│ └── clean/
│ └── cleaned_data.csv
├── analysis/
│ ├── 01_clean-data.R
│ ├── 02_fit-models.R
│ └── 03_make-figures.R
└── output/
├── figures/
│ └── linear-model-fit.png
└── tables/
└── model-results.csv
Can someone legally reuse and cite it?
Standard open licences:
| Licence | What it does | For |
|---|---|---|
| CC0 | Public-domain waiver, no attribution required | Data |
| CC BY | Reuse for any purpose, attribution required | Data |
| MIT | Permissive: reuse freely, keep the notice | Code |
| GNU GPL v3 | Copyleft: derivatives stay open, same licence | Code |
Many different ways. In general: Add a LICENSE file containing the licence text into the repository.
LICENSE fileAdd file → Create new file → name it LICENSE → “Choose a license template”
temperature-resistance/
├── README.md
├── LICENSE
├── main.R
├── renv.lock
├── data/
│ ├── README.md
│ ├── LICENSE
│ ├── raw/
│ └── clean/
├── analysis/
└── output/
Make it clear in the README what is the licence for code and data, e.g.:
All files in `/data` are licensed under CC-BY-4.0, all other files are licensed under MIT. See `data/LICENSE` and `LICENSE` for details.
Level 1: add a “How to cite” section in the README.
Level 2: add a CITATION.cff file
cffinit website and add to project)Some final checks before you publish:
.DS_Store, .Rhistory, __pycache__/, … (use a .gitignore)temperature-resistance/
├── README.md
├── LICENSE
├── CITATION.cff
├── main.R
├── renv.lock
├── data/
│ ├── raw/
│ ├── clean/
│ └── README.md
├── analysis/
└── output/
Can someone find the exact version you used?
Tip
You can link your GitHub repo to Zenodo/Figshare to archive your project directly.
Once you have a DOI, add it in two places:
Code is successfully shared and published!
Note
Find example prompts to create READMEs and to audit your projects based on the criteria we discussed today on the lecture website
A repo-reviewer AI tool: Review your repository and get a prioritized report on what to improve before publishing
Summer/Conference break in August & September!
📅 15.10.2026 🕓 4-5/6 p.m. 📍 Webex
Questions?
here)data/README.md so the code can be reviewed even without dataSelina Baldauf // Good practices for sharing research code