gandula
gandula is a Python library developed by the Sports Analytics Lab at the Federal University of Minas Gerais (UFMG) for working with Gradient football tracking and event data.
Gandula is the word for ball boy in Brazilian Portuguese. It originates from the 1930s, from the word "gandulo", that in archaic Portuguese means slacker/beggar. Back in the 30s, the word started to be used to refer to vagabond boys who did nothing else but watch football in the pitches in Rio. These "gandulas" would help by bringing the kicked-out balls. In 1939, Clube de Regatas Vasco da Gama hired the Argentinian striker Bernardo Gandulla, who was known to bring back the ball as fair play. The gandula then got popularized over the country. In our gandula, the ball is the data, and the data scientists/analysts are the stars of the game.
Data Sources
gandula supports two data sources:
-
S3 tracking data — Stream or download tracking frames (player/ball positions at 30fps) directly from Gradient's S3 bucket. Requires AWS credentials (
PFF_AWS_ACCESS_KEY_ID/PFF_AWS_SECRET_ACCESS_KEY). Each match includes tracking data (.jsonl.bz2), metadata (metadata.json), and rosters (rosters.json). -
Local event data (Gradient v2.6) — Load match events from local JSON files in the Gradient v2.6 format (
{game_id}.json). Each file contains possession events with embedded tracking snapshots, video URLs, grades, and more.
Quick Start
Installation (development)
git clone git@github.com:SALabUFMG/gandula.git
cd gandula
Set up the environment with uv:
uv sync
For S3 access:
uv sync --extra s3
For pitch control (requires PyTorch):
uv sync --extra pitch-control
Setup
Create a .env file in the project root with your AWS credentials:
PFF_AWS_ACCESS_KEY_ID='your_access_key'
PFF_AWS_SECRET_ACCESS_KEY='your_secret_key'
Then load them in your shell before running code:
export $(cat .env | xargs)
Or load them in Python:
import os
os.environ['PFF_AWS_ACCESS_KEY_ID'] = 'your_access_key'
os.environ['PFF_AWS_SECRET_ACCESS_KEY'] = 'your_secret_key'
All gandula S3 functions will pick up the credentials automatically.
Usage
The best way to get started is the walkthrough notebook, which covers every major feature end-to-end:
- Loading event data from Gradient v2.6 JSON files
- Exploring and filtering events by type
- Converting events to DataFrames
- Loading tracking data from S3 (frames, metadata, rosters)
- Visualizing tracking frames (single frame & animated sequences)
- Converting frames to DataFrames
- Joining events with tracking data
- Exporting frames as GIF, PNG, and MP4
- Feature engineering (player speed, ball speed)
- Pitch coordinate transformation
- Pitch control computation & visualization
- Accessing video URLs
Quick examples
import gandula
# --- Event data ---
events = gandula.get_events('41177.json')
df = gandula.gradient_events_to_dataframe(events)
# --- S3 tracking data ---
matches = gandula.list_s3_matches(competition_id=1, season='2025-2026')
frames = gandula.get_s3_frames(matches[0])
# --- Visualize ---
gandula.view(frames[0])
# --- Export ---
gandula.export(frames[100:200], fmt='gif', filename='play')
# --- Pitch control ---
from gandula.utils import compute_pitch_control_from_frames
result = compute_pitch_control_from_frames(
frames,
attacking_team='home',
start_frame=frames[100].frame_id,
end_frame=frames[400].frame_id,
period=1,
)
gandula.view(result, frame_index=0)
More notebooks
| Notebook | What it shows |
|---|---|
pff-load-from-json.ipynb |
Load and explore Gradient v2.6 event data |
pff-data-transformation.ipynb |
Transform events to DataFrames, filter, group |
pff-search.ipynb |
Search events by type, extract video URLs |
pff-tracking.ipynb |
Load, visualize, and export S3 tracking data |
pff-defensive-line-height.ipynb |
Defensive line metric from tracking data |
pff-events-withing-tracking-to-pandas.ipynb |
Join events with tracking data in pandas |
Documentation
Development
Install dev dependencies and pre-commit hooks:
uv sync --extra dev
pre-commit install
Run tests:
uv run pytest tests/
License & Copyright
The main image is "Ballkid at soccer, China" by Micah Sittig, licensed under CC BY 2.0
Metadata
Release files for gandula 2.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| gandula-2.1.0.tar.gz | 3.5 MB | Without provenance | Details for gandula-2.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| gandula-2.1.0-py3-none-any.whl | Python 3 | none | any | Details for gandula-2.1.0-py3-none-any.whl |
Total release size: 3.5 MB
Release files / gandula-2.1.0.tar.gz
| Download URL | gandula-2.1.0.tar.gz |
|---|---|
| Size | 3.5 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
26f9490b2e3f5c0fbb0d0c64d6fc8aa24a9072acc0f5cf1422115de406d38836
|
|
BLAKE2b-256 checksum How to use checksums |
698f2116bdd9e687a8fa8e5d231e325104a45125f95d57e375a5009d0a14936b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.19
|
Release files / gandula-2.1.0-py3-none-any.whl
| Download URL | gandula-2.1.0-py3-none-any.whl |
|---|---|
| Size | 47.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f72353c0a84c8e96f1156946f5732c0e9a724f19bd63caf1ab25261f87718680
|
|
BLAKE2b-256 checksum How to use checksums |
4e45975d89674aa8abf53ccaeb99893b9818b45806c260317817d874b9721c2d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.19
|
Security for gandula 2.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.