Opening pace and the odds of a sub-3-hour marathon among 7,344 Berlin Marathon runners (1999–2025) who reached the halfway mark within the same 60-second window.
- Aldo Seffrin — Nova O2 Sports Science, São José dos Campos, Brazil
- Elias Villiger — University of Zurich, Switzerland
- Marília dos Santos Andrade — Federal University of São Paulo, Brazil
- Thomas Rosemann — University of Zurich, Switzerland
- Katja Weiss — University of Zurich, Switzerland
- Daniel Ferreira — Nova O2 Sports Science, São José dos Campos, Brazil
- Beat Knechtle — University of Zurich, Switzerland (corresponding author)
Manuscript under peer review.
Every finisher of the Berlin Marathon from 1999 to 2025 whose halfway time lay within a 60-second window centred on the mean halfway time of finishers between 2:58:00 and 2:59:59 (1:27:36–1:28:35; n = 7,344). With halfway time fixed by design, the second half of the race is the outcome, so every association is restricted to information available before halfway.
- 53.2% of the cohort finished under 3:00:00; the proportion ranged from 25.3% to 74.2% across the 26 editions.
- The proportion finishing under three hours rose across quintiles of opening pace (0–10 km) from 27.3% in the fastest to 66.3% in the slowest quintile (adjusted odds ratio 1.40 per 1 s/km slower, 95% CI 1.36–1.44; non-linear), in the same direction in every edition.
- From the information available at 5 km the outcome was discriminated with a cross-validated AUC of 0.63, rising to 0.74 with all first-half splits.
- K-means clustering of whole-race profiles separates a near-even profile from a late-race collapse; because the profiles contain the second half, the clusters restate the outcome and are reported as description.
Download the dataset from Zenodo (https://doi.org/10.5281/zenodo.19342683) and place it in data/. See data/README.md for the cleaning step and notebooks/README.md for the execution order, the design safeguards and the reproducibility guarantees (fixed seeds, one computation thread, a content hash of the frozen input in every result file, byte-identical re-runs).
python -m venv .venv && .venv/bin/pip install -r requirements.txt
.venv/bin/python notebooks/r1_freeze_baseline.py # then the scripts in the order given in notebooks/README.mddata/ # dataset (download from Zenodo) and cleaning notes
notebooks/
├── CLEANING.ipynb # raw CSV → cleaned parquet
├── _r1_common.py # single source of definitions: window, outcome, covariates, guards
├── r1_*.py # one script per analysis (cohort, descriptives, opening pace, checkpoints, edition, clustering, extreme groups, sensitivity)
├── generate_tables.py # every table, from the result files
├── generate_figures.py # every figure, from the result files
├── r1_selfcheck.py # formulas, definitions, data integrity, privacy scan
└── results/r1/ # result files with provenance headers
figures/ # Fig1–5.tif, S1–S3_Fig.tif
The source file contains runners' names. They are read for one purpose only: to derive a one-way key that identifies repeat participants, used in one sensitivity analysis and to keep a runner out of both sides of a cross-validation split. No name or bib number appears in any result file, table or figure; the cleaned working copy of the source file is kept locally and is not distributed.
MIT — Copyright (c) 2026 Nova O2 Sports Science