PassesPerMinute is a Python-based analytical project that evaluates the average number of passes per minute by player position using StatsBomb Open Data, accessible via a processing pipeline or an interactive Streamlit dashboard.
It covers matches from professional competitions (2009β2024), highlighting tactical trends and positional behaviors across modern football.
License:
CC0-1.0(see LICENSE for details)
- Python 3.12-3.14
- Streamlit β interactive web dashboard for data visualization
- requests β fetching event data from StatsBomb Open Data
- matplotlib β creating visualizations (bar charts, football pitch map)
- concurrent.futures β parallel match data processing
- logging β unified structured logs for analysis runs
- Poetry β dependency and package management
- requirements.txt / requirements-dev.txt β pip installation
- MkDocs β documentation site (
docs/,mkdocs.yml) - pre-commit β code quality enforcement (Black, Ruff, mypy)
- CI/CD β GitHub Actions (
.github/workflows/ci.yml) - Black β code formatting
- Ruff β linting and style enforcement
- mypy β static type checking
The project analyzes the average number of passes per minute for each position on the football pitch using event-based data from the StatsBomb Open Data repository.
By analyzing matches from professional competitions across various leagues and tournaments (2009β2024), it identifies patterns and trends in passing behavior by position.
The analysis is based on event-level data (passes, substitutions, lineups) and visualized using static plots and a dynamic web interface (Streamlit) to improve interpretation and allow for real-time data exploration.
Matches analyzed: 2617 games
Time frame: 2009β2024
Source: StatsBomb Open Data Repository
The dataset includes only elite-level competitions, ensuring a consistent level of tactical and technical quality.
- 1. Bundesliga: 2023/2024, 2015/2016
- African Cup of Nations: 2023
- Champions League: 2009/2010β2018/2019
- Copa America: 2024
- FIFA World Cup: 2018, 2022
- La Liga: 2009/2010β2020/2021
- Ligue 1: 2015/2016, 2021/2022, 2022/2023
- Premier League: 2015/2016
- Serie A: 2015/2016
- UEFA Euro: 2020, 2024
-
Data Collection
Matches are selected by competition & season filters. Event data (passes, substitutions, lineups) are fetched for each match. -
Player Position Tracking
Player positions are tracked using starting formations and substitution events. -
Pass and Time Aggregation
For each position, total passes and minutes played are calculated using timestamps. -
Average Pass Calculation
Formula:
Average Passes Per Minute = Total Passes / Minutes Played -
Visualization
Results are shown both as a bar chart and football pitch heatmap.
The project includes an interactive Streamlit dashboard that allows users to explore data dynamically without writing code.
π Launch Live App
- Fast Mode (Offline): Instantly visualizes pre-computed data from the local
granular_stats.jsondatabase. - Live Mode (Online): Connects to the StatsBomb API to fetch and process specific competitions/seasons on demand.
- Interactive Visualizations: Toggle between Bar Charts, Pitch Maps, and detailed Data Tables.
- Custom Filters: Filter statistics by specific year ranges and competition IDs.
PassesPerMinute/
ββ .github/
β ββ workflows/
β ββ ci.yml # GitHub Actions pipeline
β
ββ docs/
β ββ css/
β β ββ mkdocstrings.css # Styles for mkdocstrings API pages
β β ββ theme-variants.css # Theme variants / overrides
β ββ gen_ref_pages/
β β ββ config.py # mkdocstrings config helpers
β β ββ context.py # Context building for generators
β β ββ gen_ref_pages.py # Entry script to generate ref pages
β β ββ generate.py # Generator orchestration
β β ββ helpers.py # Utility functions for ref generation
β β ββ traverse.py # Module traversal utilities
β ββ index.md # Docs homepage
β
ββ readme_images/
β ββ bar_chart.png # Bar chart visualization
β ββ football_pitch_map.png # Football pitch map visualization
β
ββ src/
β ββ passes_per_minute/
β ββ __main__.py # Module entry point (python -m passes_per_minute)
β ββ app.py # Core execution script / CLI
β ββ streamlit_app.py # Streamlit Web Application
β ββ logging_config.py # Logging setup
β ββ passes_counter/
β β ββ competition_manager.py # Select competitions/seasons & enumerate matches
β β ββ competition_processor.py # Process an entire competition in parallel
β β ββ http_client.py # HTTP client for StatsBomb Open Data
β β ββ match_processor.py # Per-match event parsing & aggregation
β β ββ player_position_stats.py # Track minutes by position and count passes
β ββ plotter/
β ββ bar_chart.py # Bar chart of passes per minute by position
β ββ football_pitch_chart.py # Football pitch heatmap of passes per minute
β
ββ tests/
β ββ passes_counter/
β β ββ test_competition_manager.py # Tests for competition selection
β β ββ test_competition_process.py # Tests for competition processing
β β ββ test_http_client.py # Tests for HTTP client
β β ββ test_match_processor.py # Tests for match processing
β β ββ test_player_position_stats.py # Tests for position stats aggregation
β ββ plotter/
β ββ test_bar_chart.py # Tests for bar chart rendering
β ββ test_football_pitch_chart.py # Tests for football pitch chart
β
ββ .gitignore # Git ignore rules
ββ .pre-commit-config.yaml # Pre-commit hooks (Black, Ruff, mypy, etc.)
ββ LICENSE # CC0-1.0 public domain license
ββ mkdocs.yml # MkDocs configuration
ββ poetry.lock # Poetry lockfile (exact dependency versions)
ββ pyproject.toml # Project metadata & Poetry config
ββ pytest.ini # pytest configuration
ββ README.md # Project overview
ββ requirements.txt # Runtime dependencies
ββ requirements-dev.txt # Development dependencies
ββ run_dashboard.py # Streamlit app launcher
Users (runtime only):
python -m venv .venv
source .venv/bin/activate # macOS/Linux
.venv\Scripts\activate # Windows
pip install --upgrade pip
pip install -r requirements.txt
pip install -e .Developers (runtime + dev):
pip install -r requirements.txt -r requirements-dev.txt
pip install -e .Users (without dev):
poetry install --without dev
poetry run passesperminuteDevelopers (with dev):
poetry install
poetry run passes_per_minutepython -m passes_per_minutepoetry run passesperminutepassesperminutepoetry run streamlit run src/passes_per_minute/streamlit_app.pyDocumentation is built with MkDocs.
mkdocs serve # local preview (http://127.0.0.1:8000)
mkdocs build # build into site/mypy src/ruff check .black --check .pre-commit run --all-files| Position | Passes/Min |
|---|---|
| Center Defensive Midfield | 0.67622 |
| Left Center Midfield | 0.59383 |
| Left Back | 0.58943 |
| Right Back | 0.58879 |
| Left Defensive Midfield | 0.58681 |
| Right Center Midfield | 0.57829 |
| Right Defensive Midfield | 0.56643 |
| Center Midfield | 0.52749 |
| Left Center Back | 0.51183 |
| Left Wing Back | 0.50638 |
| Right Center Back | 0.49786 |
| Center Back | 0.49286 |
| Right Wing Back | 0.48488 |
| Right Attacking Midfield | 0.48143 |
| Left Attacking Midfield | 0.47896 |
| Center Attacking Midfield | 0.47598 |
| Right Wing | 0.42216 |
| Left Wing | 0.41283 |
| Left Midfield | 0.40420 |
| Right Midfield | 0.39661 |
| Secondary Striker | 0.34428 |
| Goalkeeper | 0.33203 |
| Right Center Forward | 0.31038 |
| Left Center Forward | 0.29252 |
| Center Forward | 0.28389 |
- Missing events = partial gaps in positional time tracking.
- Substitution times are approximate.
- Event-centric approach may miss contextual gameplay data.
Released under CC0-1.0 (public domain). You may copy, modify, distribute, and use it commercially without asking for permission.


