Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 64 additions & 33 deletions .github/workflows/pytest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,61 +12,92 @@ jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.11']
include:
# Python 3.11 with Spektral/TensorFlow
- os: ubuntu-latest
python-version: '3.11'
test-deps: 'test-py311'
pytest-markers: ''
- os: windows-latest
python-version: '3.11'
test-deps: 'test-py311'
pytest-markers: ''
- os: macos-latest
python-version: '3.11'
test-deps: 'test-py311'
pytest-markers: ''

# Python 3.12 with PyTorch only
- os: ubuntu-latest
python-version: '3.12'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'
- os: windows-latest
python-version: '3.12'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'
- os: macos-latest
python-version: '3.12'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'

# Python 3.13 with PyTorch only
- os: ubuntu-latest
python-version: '3.13'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'
- os: windows-latest
python-version: '3.13'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'
- os: macos-latest
python-version: '3.13'
test-deps: 'test-torch'
pytest-markers: '-m "not spektral"'

runs-on: ${{ matrix.os }}

steps:
- uses: actions/checkout@v2

- name: Free up disk space (Linux only)
if: runner.os == 'Linux'
run: |
echo "Disk space before cleanup:"
df -h
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf /usr/local/share/boost
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
sudo docker image prune --all --force
echo "Disk space after cleanup:"
df -h

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}

# Linux/macOS - Install dependencies
- name: Install dependencies (Linux/macOS)
if: runner.os != 'Windows' # Only run on Linux/macOS
if: runner.os != 'Windows'
env:
PYTHONIOENCODING: utf-8 # Ensure Python uses UTF-8 encoding
PYTHONIOENCODING: utf-8
run: |
python -m pip install --upgrade pip
python -m pip install -e .[test]
shell: bash

# Install FFmpeg on Ubuntu
- name: Install FFmpeg (Ubuntu)
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y ffmpeg
python -m pip install -e .[${{ matrix.test-deps }}]
shell: bash

# Install FFmpeg on macOS
- name: Install FFmpeg (macOS)
if: runner.os == 'macOS'
run: |
brew install ffmpeg
shell: bash

# Install FFmpeg on Windows
- name: Install FFmpeg (Windows)
if: runner.os == 'Windows'
run: |
choco install ffmpeg -y
shell: pwsh

# Windows - Install dependencies
- name: Install dependencies (Windows)
if: runner.os == 'Windows' # Only run on Windows
if: runner.os == 'Windows'
env:
PYTHONIOENCODING: utf-8 # Ensure Python uses UTF-8 encoding
PYTHONUTF8: 1 # Force Python to use UTF-8 mode
PYTHONIOENCODING: utf-8
PYTHONUTF8: 1
run: |
chcp 65001 # Change code page to UTF-8
chcp 65001
python -m pip install --upgrade pip setuptools
python -m pip install -e .[test]
python -m pip install -e .[${{ matrix.test-deps }}]
shell: pwsh

- name: Code formatting
Expand All @@ -76,4 +107,4 @@ jobs:

- name: Test with pytest
run: |
pytest --color=yes
pytest --color=yes ${{ matrix.pytest-markers }}
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -195,4 +195,6 @@ examples/models/*
*.mp4
*.png
*.json
diffs/
diffs/

lightning_logs/
20 changes: 4 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ bdb = BigDataBowlDataset(

### **Graph Neural Networks**

⚽🏈 Convert **[Polars Dataframes](#polars-dataframes)** into [Graphs](examples/graphs_faq.md) to train **graph neural networks**. These [Graphs](examples/graphs_faq.md) can be used with [**Spektral**](https://github.com/danielegrattarola/spektral) - a flexible framework for training graph neural networks.
⚽🏈 Convert **[Polars Dataframes](#polars-dataframes)** into [Graphs](examples/graphs_faq.md) to train **graph neural networks**. These [Graphs](examples/graphs_faq.md) can be used with [**PyTorch Geometric**](https://pytorch-geometric.readthedocs.io/en/latest/) or [**Spektral**](https://github.com/danielegrattarola/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.

```python
Expand Down Expand Up @@ -142,9 +142,9 @@ model.fit(

🌀 Quick Start
-----
📖 ⚽ The [**Quick Start Jupyter Notebook**](examples/0_quick_start_guide.ipynb) 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 [**Quick Start Jupyter Notebook**](examples/0_quick_start_guide_pyg.ipynb) 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**](examples/1_kloppy_gnn_train.ipynb) gives an in-depth walkthrough.
📖 ⚽ The [**Graph Converter Tutorial Jupyter Notebook**](examples/1_kloppy_gnn_train_pyg.ipynb) gives an in-depth walkthrough.

📖 🏈 The [**BigDataBowl Converter Tutorial Jupyter Notebook**](examples/2_big_data_bowl_guide.ipynb) gives an guide on how to convert the BigDataBowl data into Graphs.

Expand All @@ -169,18 +169,6 @@ The easiest way to get started is:
pip install unravelsports
```

⚠️ Due to compatibility issues **unravelsports** currently only works on Python 3.11 with:
```
spektral==1.20.0
tensorflow==2.14.0
keras==2.14.0
kloppy==3.17.0
polars==1.2.1
```
These dependencies come pre-installed with the package. It is advised to create a [virtual environment](https://virtualenv.pypa.io/en/latest/).

This package is tested on the latest versions of Ubuntu, MacOS and Windows.

🌀 Licenses
----
This project is licensed under the [Mozilla Public License Version 2.0 (MPL)](LICENSE), 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.
Expand All @@ -196,7 +184,7 @@ If you use this repository for any educational purposes, research, project etc.,
@software{unravelsports2024repository,
author = {Bekkers, Joris},
title = {unravelsports},
version = {0.3.0},
version = {2.0.0},
year = {2024},
publisher = {GitHub},
url = {https://github.com/unravelsports/unravelsports}
Expand Down
49 changes: 49 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import pytest
import sys
import os


def pytest_configure(config):
config.addinivalue_line(
"markers", "spektral: tests that require spektral (Python 3.11 only)"
)
config.addinivalue_line("markers", "torch: tests that require PyTorch")
config.addinivalue_line(
"markers", "local_only: tests that should only run in local environment"
)


def pytest_collection_modifyitems(config, items):
"""Automatically skip tests based on available dependencies and environment"""

try:
import spektral

has_spektral = True
except ImportError:
has_spektral = False

try:
import torch
import torch_geometric

has_torch = True
except ImportError:
has_torch = False

# Check if running in CI or non-local environment
is_ci = os.getenv("CI") is not None

skip_spektral = pytest.mark.skip(reason="Spektral not installed")
skip_torch = pytest.mark.skip(reason="PyTorch/PyG not installed")
skip_local = pytest.mark.skip(
reason="Skipping local-only tests in CI/non-local environment"
)

for item in items:
if "spektral" in item.keywords and not has_spektral:
item.add_marker(skip_spektral)
if "torch" in item.keywords and not has_torch:
item.add_marker(skip_torch)
if "local_only" in item.keywords and is_ci:
item.add_marker(skip_local)
46 changes: 24 additions & 22 deletions examples/0_quick_start_guide.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"source": [
"## 🌀 Quick Start Guide: It's all starting to unravel!\n",
"\n",
"⚠️ It is recommended to use the [PyTorch implementation](0_quick_start_guide_pyg.ipynb) over this Spektral version.\n",
"\n",
"In this example we'll run through all the basic features the `unravelsports` package offers for converting a `kloppy` dataset of soccer tracking data into graphs for training binary classification graph neural networks using the `spektral` library.\n",
"\n",
"This guide will go through the following steps:\n",
Expand Down Expand Up @@ -104,7 +106,7 @@
},
{
"cell_type": "code",
"execution_count": 3,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
Expand All @@ -129,7 +131,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -166,7 +168,7 @@
},
{
"cell_type": "code",
"execution_count": 5,
"execution_count": 4,
"metadata": {},
"outputs": [
{
Expand All @@ -180,17 +182,17 @@
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/jbekkers/PycharmProjects/unravelsports/.venv311/lib/python3.11/site-packages/keras/src/initializers/initializers.py:120: UserWarning: The initializer GlorotUniform is unseeded and being called multiple times, which will return identical values each time (even if the initializer is unseeded). Please update your code to provide a seed to the initializer, or avoid using the same initializer instance more than once.\n",
"/Users/jbekkers/PycharmProjects/unravelsports/.venv311-test/lib/python3.11/site-packages/keras/src/initializers/initializers.py:120: UserWarning: The initializer GlorotUniform is unseeded and being called multiple times, which will return identical values each time (even if the initializer is unseeded). Please update your code to provide a seed to the initializer, or avoid using the same initializer instance more than once.\n",
" warnings.warn(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"11/11 [==============================] - 1s 16ms/step - loss: 21.7806 - auc: 0.5278 - binary_accuracy: 0.5419 - val_loss: 5.1682 - val_auc: 0.5000 - val_binary_accuracy: 0.5000\n",
"11/11 [==============================] - 1s 16ms/step - loss: 82.8534 - auc: 0.5290 - binary_accuracy: 0.5375 - val_loss: 5.5782 - val_auc: 0.5308 - val_binary_accuracy: 0.5595\n",
"Epoch 2/10\n",
" 1/11 [=>............................] - ETA: 0s - loss: 9.2846 - auc: 0.3651 - binary_accuracy: 0.5000WARNING:tensorflow:Your input ran out of data; interrupting training. Make sure that your dataset or generator can generate at least `steps_per_epoch * epochs` batches (in this case, 3 batches). You may need to use the repeat() function when building your dataset.\n"
" 1/11 [=>............................] - ETA: 0s - loss: 70.9456 - auc: 0.3438 - binary_accuracy: 0.3438WARNING:tensorflow:Your input ran out of data; interrupting training. Make sure that your dataset or generator can generate at least `steps_per_epoch * epochs` batches (in this case, 3 batches). You may need to use the repeat() function when building your dataset.\n"
]
},
{
Expand All @@ -204,32 +206,32 @@
"name": "stdout",
"output_type": "stream",
"text": [
"11/11 [==============================] - 0s 6ms/step - loss: 4.5155 - auc: 0.5366 - binary_accuracy: 0.5449\n",
"11/11 [==============================] - 0s 6ms/step - loss: 33.6154 - auc: 0.4974 - binary_accuracy: 0.4835\n",
"Epoch 3/10\n",
"11/11 [==============================] - 0s 4ms/step - loss: 2.0773 - auc: 0.4515 - binary_accuracy: 0.4731\n",
"11/11 [==============================] - 0s 6ms/step - loss: 14.5440 - auc: 0.4738 - binary_accuracy: 0.4895\n",
"Epoch 4/10\n",
"11/11 [==============================] - 0s 5ms/step - loss: 1.1006 - auc: 0.5205 - binary_accuracy: 0.5150\n",
"11/11 [==============================] - 0s 5ms/step - loss: 7.6316 - auc: 0.4957 - binary_accuracy: 0.4985\n",
"Epoch 5/10\n",
"11/11 [==============================] - 0s 4ms/step - loss: 0.9159 - auc: 0.4915 - binary_accuracy: 0.5180\n",
"11/11 [==============================] - 0s 5ms/step - loss: 4.4984 - auc: 0.5123 - binary_accuracy: 0.4985\n",
"Epoch 6/10\n",
"11/11 [==============================] - 0s 5ms/step - loss: 0.8020 - auc: 0.4873 - binary_accuracy: 0.5060\n",
"11/11 [==============================] - 0s 5ms/step - loss: 3.3299 - auc: 0.5680 - binary_accuracy: 0.5495\n",
"Epoch 7/10\n",
"11/11 [==============================] - 0s 4ms/step - loss: 0.8067 - auc: 0.4960 - binary_accuracy: 0.5299\n",
"11/11 [==============================] - 0s 5ms/step - loss: 2.9137 - auc: 0.4771 - binary_accuracy: 0.4775\n",
"Epoch 8/10\n",
"11/11 [==============================] - 0s 6ms/step - loss: 0.7808 - auc: 0.5055 - binary_accuracy: 0.5299\n",
"11/11 [==============================] - 0s 5ms/step - loss: 2.1859 - auc: 0.5167 - binary_accuracy: 0.5075\n",
"Epoch 9/10\n",
"11/11 [==============================] - 0s 4ms/step - loss: 0.7661 - auc: 0.4937 - binary_accuracy: 0.5060\n",
"11/11 [==============================] - 0s 4ms/step - loss: 2.2885 - auc: 0.4226 - binary_accuracy: 0.4474\n",
"Epoch 10/10\n",
"11/11 [==============================] - 0s 5ms/step - loss: 0.7406 - auc: 0.5098 - binary_accuracy: 0.5329\n"
"11/11 [==============================] - 0s 4ms/step - loss: 1.5413 - auc: 0.5234 - binary_accuracy: 0.5195\n"
]
},
{
"data": {
"text/plain": [
"<keras.src.callbacks.History at 0x39fe49d10>"
"<keras.src.callbacks.History at 0x36b977b50>"
]
},
"execution_count": 5,
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
Expand Down Expand Up @@ -267,14 +269,14 @@
},
{
"cell_type": "code",
"execution_count": 6,
"execution_count": 5,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"3/3 [==============================] - 0s 6ms/step - loss: 0.7001 - auc: 0.5000 - binary_accuracy: 0.4819\n"
"3/3 [==============================] - 0s 6ms/step - loss: 0.8078 - auc: 0.3817 - binary_accuracy: 0.4819\n"
]
}
],
Expand All @@ -296,7 +298,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 6,
"metadata": {},
"outputs": [
{
Expand All @@ -315,7 +317,7 @@
],
"metadata": {
"kernelspec": {
"display_name": ".venv311",
"display_name": ".venv313-test",
"language": "python",
"name": "python3"
},
Expand All @@ -329,7 +331,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.11"
"version": "3.13.2"
}
},
"nbformat": 4,
Expand Down
Loading