The data
Every real-data number on this site comes from one of the sources below. Most pieces end with a "Reproduce the analysis" snippet; this page gets you the files they read.
Results and odds: football-data.co.uk
football-data.co.uk publishes a file for each season of the Scottish Premiership (SC0) and Championship (SC1). This site uses 2000/01 to 2025/26: 26 seasons, 10,471 matches. Each row is one match: the date, the teams, the full-time and half-time scores, and bookmakers' odds (Bet365's from 2002/03). The Premiership files also record shots, shots on target, corners, fouls and cards; the Championship's are mainly results. Their notes explain every column.
The files are football-data.co.uk's, used here with their agreement. This site doesn't host copies: the script below downloads them from football-data.co.uk itself. The season in progress, 2026/27, is left out on purpose, so your numbers match the articles. 54 articles read these files.
Get the files
- Make an empty folder and save get_football_data.py in it (or copy it from below).
- Run
python get_football_data.pythere. It fetches all 52 files, about 3 MB, in under a minute, and skips any you already have. - Run any article's snippet in the same folder. The files are named the way the snippets expect, such as
SC0_2425.csv.
Show the Python20 lines, ready to copy and run.
# From Football Data Science by Bryan McGuire. Free to use with credit.
# https://www.footballdatascience.co.uk/data
# Downloads the Scottish results and odds files the site's snippets use, straight from football-data.co.uk
# (their files, not this site's). Run it once in an empty folder, then run any article's snippet there.
import os
import time
import urllib.request
SEASONS = [f"{y % 100:02d}{(y + 1) % 100:02d}" for y in range(2000, 2026)] # 2000/01 to 2025/26
DIVISIONS = ("SC0", "SC1") # Premiership, Championship
for div in DIVISIONS:
for season in SEASONS:
name = f"{div}_{season}.csv"
if os.path.exists(name):
continue
urllib.request.urlretrieve(f"https://www.football-data.co.uk/mmz4281/{season}/{div}.csv", name)
print("saved", name)
time.sleep(0.5) # be polite to their server
print("All", len(DIVISIONS) * len(SEASONS), "files are here.")
It needs only Python 3. Some snippets also need numpy or scipy; each article says so.
Shots with every player's position: StatsBomb open data
The Shot Lab and expected goals from scratch use StatsBomb's free event data: 95,162 shots, each with where every player in the picture was standing. Get it from their open-data repository on GitHub; the article explains which files it reads. StatsBomb's terms ask for a credit and no commercial use. Only the fitted model is published here, never their data.
Data: StatsBomb open data. The analysis is ours, not StatsBomb's. For education.
Published here
manager-changes-spfl.csv: 131 in-season managerial changes in the Scottish top flight, 2000/01 to 2025/26, compiled for Does sacking the manager bring a bounce? from each season's Wikipedia page, with the source for each row. 2010/11 is missing; that page has no table.