A project to analyze sports event and tracking data
Project description
๐ pip install unravelsports
๐ Documentation
Finally, we have documentation! Check out:
๐ unravelsports.readthedocs.io
๐ What is it?
The unravelsports package aims to aid researchers, analysts and enthusiasts by providing intermediary steps in the complex process of converting raw sports data into meaningful information and actionable insights.
This package currently supports:
- โฝ ๐ Polars DataFrame Conversion
- โฝ ๐ Graph Neural Network Training, Graph Conversion and Prediction [๐ Bekkers & Sahasrabudhe (2023)]
- โฝ Pressing Intensity [๐ Bekkers (2024)]
- โฝ Formation and Position Identification (EFPI) [๐ Bekkers (2025)]
๐ Features
Polars DataFrames
โฝ๐ Convert Tracking Data into Polars DataFrames for rapid data conversion and data processing.
โฝ For soccer we rely on Kloppy and as such we support Sportec, SkillCorner, PFF / GradientSports, Metrica, StatsPerform, Tracab (CyronHego), SecondSpectrum, HawkEye and Signality tracking data.
from unravel.soccer import KloppyPolarsDataset
from kloppy import sportec
kloppy_dataset = sportec.load_open_tracking_data()
kloppy_polars_dataset = KloppyPolarsDataset(
kloppy_dataset=kloppy_dataset
)
| period_id | timestamp | frame_id | ball_state | id | x | y | z | team_id | position_name | game_id | vx | vy | vz | v | ax | ay | az | a | ball_owning_team_id | is_ball_carrier | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 0 days 00:00:00 | 10000 | alive | DFL-OBJ-00008F | -20.67 | -4.56 | 0 | DFL-CLU-000005 | RCB | DFL-MAT-J03WPY | 0.393 | -0.214 | 0 | 0.447 | 0 | 0 | 0 | 0 | DFL-CLU-00000P | False |
| 1 | 1 | 0 days 00:00:00 | 10000 | alive | DFL-OBJ-0000EJ | -8.86 | -0.94 | 0 | DFL-CLU-000005 | UNK | DFL-MAT-J03WPY | -0.009 | 0.018 | 0 | 0.02 | 0 | 0 | 0 | 0 | DFL-CLU-00000P | False |
| 2 | 1 | 0 days 00:00:00 | 10000 | alive | DFL-OBJ-0000F8 | -2.12 | 9.85 | 0 | DFL-CLU-00000P | RM | DFL-MAT-J03WPY | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | DFL-CLU-00000P | False |
| 3 | 1 | 0 days 00:00:00 | 10000 | alive | DFL-OBJ-0000NZ | 0.57 | 23.23 | 0 | DFL-CLU-00000P | RB | DFL-MAT-J03WPY | 0.179 | -0.134 | 0 | 0.223 | 0 | 0 | 0 | 0 | DFL-CLU-00000P | False |
| 4 | 1 | 0 days 00:00:00 | 10000 | alive | DFL-OBJ-0001HW | -46.26 | 0.08 | 0 | DFL-CLU-000005 | GK | DFL-MAT-J03WPY | 0.357 | 0.071 | 0 | 0.364 | 0 | 0 | 0 | 0 | DFL-CLU-00000P | False |
๐ For American Football we use BigDataBowl Data directly.
from unravel.american_football import BigDataBowlDataset
bdb = BigDataBowlDataset(
tracking_file_path="week1.csv",
players_file_path="players.csv",
plays_file_path="plays.csv",
)
Graph Neural Networks
โฝ๐ Convert Polars Dataframes into Graphs to train graph neural networks. These Graphs can be used with PyTorch Geometric or Spektral.
unravelsports allows you to randomize and split data into train, test and validation sets along matches, sequences or possessions to avoid leakage and improve model quality. And finally, train, validate and test your (custom) Graph model(s) and easily predict on new data.
from unravel.soccer import SoccerGraphConverter
converter = SoccerGraphConverter(
dataset=kloppy_polars_dataset,
self_loop_ball=True,
adjacency_matrix_connect_type="ball",
adjacency_matrix_type="split_by_team",
label_type="binary",
defending_team_node_value=0.1,
non_potential_receiver_node_value=0.1,
random_seed=False,
pad=False,
verbose=False,
)
Pressing Intensity
Compute Pressing Intensity for a whole game (or segment) of Soccer tracking data.
See Pressing Intensity Jupyter Notebook for an example how to create mp4 videos.
from unravel.soccer import PressingIntensity
import polars as pl
model = PressingIntensity(
dataset=kloppy_polars_dataset
)
model.fit(
start_time = pl.duration(minutes=1, seconds=53),
end_time = pl.duration(minutes=2, seconds=32),
period_id = 1,
method="teams",
ball_method="max",
orient="home_away",
speed_threshold=2.0,
)
Formation and Position Identification
Compute Elastic Formation and Position Identification, EFPI for individual frames, possessions, periods or specific time intervals for Soccer.
For more information on all possibilities for "every" check out Polars Documentation.
from unravel.soccer import EFPI
model = EFPI(dataset=kloppy_polars_dataset)
model.fit(
# Default 65 formations , or specify a subset (e.g. ["442" , "433"])
formations=None,
# specific time intervals (e.g. 1m, 1m14s, 2m30s etc.), or specify "possession", "period" or "frame".
every="5m",
substitutions="drop",
change_threshold=0.1,
change_after_possession=True,
)
โ More to come soon...!
๐ Quick Start
๐ โฝ The Quick Start Jupyter Notebook explains how to convert any positional tracking data from Kloppy to Spektral GNN in a few easy steps while walking you through the most important features and documentation.
๐ โฝ The Graph Converter Tutorial Jupyter Notebook gives an in-depth walkthrough.
๐ ๐ The BigDataBowl Converter Tutorial Jupyter Notebook gives an guide on how to convert the BigDataBowl data into Graphs.
๐ โฝ The Pressing Intensity Tutorial Jupyter Notebook gives a description on how to create Pressing Intensity videos.
๐ Additional Reading
๐ A Graph Neural Network Deep-dive into Successful Counterattacks {A. Sahasrabudhe J. Bekkers, 2023}
๐ค Cutting Edge Football Analytics: using polars, keras and spektral (PyData London, 2025)
๐ Installation
The easiest way to get started is:
pip install unravelsports
๐ Licenses
This project is licensed under the Mozilla Public License Version 2.0 (MPL), which requires that you include a copy of the license and provide attribution to the original authors. Any modifications you make to the MPL-licensed files must be documented, and the source code for those modifications must be made open-source under the same license.
๐ Citation
If you use this repository for any educational purposes, research, project etc., please reference both:
๐ The unravelsports package.
BibTex
@software{unravelsports2024repository,
author = {Bekkers, Joris},
title = {unravelsports},
version = {2.0.0},
year = {2024},
publisher = {GitHub},
url = {https://github.com/unravelsports/unravelsports}
}
BibTex
@inproceedings{sahasrabudhe2023graph,
title={A Graph Neural Network deep-dive into successful counterattacks},
author={Sahasrabudhe, Amod and Bekkers, Joris},
booktitle={17th Annual MIT Sloan Sports Analytics Conference. Boston, MA, USA: MIT},
pages={15},
year={2023}
}
BibTex
@article{bekkers2024pressing,
title={Pressing Intensity: An Intuitive Measure for Pressing in Soccer},
author={Bekkers, Joris},
journal={arXiv preprint arXiv:2501.04712},
year={2024}
}
BibTex
@article{bekkers2025efpi,
title={EFPI: Elastic Formation and Position Identification in Football (Soccer) using Template Matching and Linear Assignment},
author={Bekkers, Joris},
journal={arXiv preprint arXiv:2506.23843},
year={2025}
}
๐ Social Media
<img alt="LinkedIn" width="40px" src="https://upload.wikimedia.org/wikipedia/commons/c/ca/LinkedIn_logo_initials.png"/>
<img alt="Bluesky" width="40px" src="https://upload.wikimedia.org/wikipedia/commons/thumb/7/7a/Bluesky_Logo.svg/2319px-Bluesky_Logo.svg.png"/>
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unravelsports-1.2.1.tar.gz.
File metadata
- Download URL: unravelsports-1.2.1.tar.gz
- Upload date:
- Size: 108.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba674a29ae945394cce3d5a816c6ce67e665a05abbad35e9424e6aba9726afd9
|
|
| MD5 |
a18e9eff22612cf21b3a8ec335625efa
|
|
| BLAKE2b-256 |
f4d1397e8042611c9247bcd2f943d69b1f13dd049f843fd3aec69055417c999c
|
File details
Details for the file unravelsports-1.2.1-py3-none-any.whl.
File metadata
- Download URL: unravelsports-1.2.1-py3-none-any.whl
- Upload date:
- Size: 108.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4e85d94d475dfd1c07d7665e2b89c86e21fee60ea1288ba750d049ed77c85200
|
|
| MD5 |
6ea246fb42f8163b427018601262eb95
|
|
| BLAKE2b-256 |
55cca418b8cc6bc441180a1bea34b7da687f54c7ef77afb23771845997d34cde
|