From 4a1a4eed48ce819fe16d06b50f9e854fb3d58321 Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Sun, 9 Jul 2023 14:37:43 +0200 Subject: [PATCH 01/12] Improved docs for providers, functionality, contributing to docs --- docs/contributing.md | 11 +- docs/functionality/coordinate-systems.md | 58 ++ docs/functionality/event-data.md | 223 +++++ docs/functionality/providers.md | 43 + docs/functionality/tracking-data.md | 176 ++++ .../event-data/datafactory.ipynb | 233 +++++ .../getting-started/event-data/metrica.ipynb | 247 +++++ .../getting-started/event-data/opta.ipynb | 253 +++++ .../getting-started/event-data/sportec.ipynb | 247 +++++ .../event-data/sportscode.ipynb | 236 +++++ .../event-data/statsbomb.ipynb | 740 +++++++++++++++ .../getting-started/event-data/wyscout.ipynb | 766 +++++++++++++++ .../tracking-data/metrica.ipynb | 883 ++++++++++++++++++ .../tracking-data/secondspectrum.ipynb | 183 ++++ .../tracking-data/skillcorner.ipynb | 756 +++++++++++++++ .../tracking-data/statsperform.ipynb | 288 ++++++ .../tracking-data/tracab.ipynb | 208 +++++ mkdocs.yml | 35 +- 18 files changed, 5572 insertions(+), 14 deletions(-) create mode 100644 docs/functionality/coordinate-systems.md create mode 100644 docs/functionality/event-data.md create mode 100644 docs/functionality/providers.md create mode 100644 docs/functionality/tracking-data.md create mode 100644 docs/tutorials/getting-started/event-data/datafactory.ipynb create mode 100644 docs/tutorials/getting-started/event-data/metrica.ipynb create mode 100644 docs/tutorials/getting-started/event-data/opta.ipynb create mode 100644 docs/tutorials/getting-started/event-data/sportec.ipynb create mode 100644 docs/tutorials/getting-started/event-data/sportscode.ipynb create mode 100644 docs/tutorials/getting-started/event-data/statsbomb.ipynb create mode 100644 docs/tutorials/getting-started/event-data/wyscout.ipynb create mode 100644 docs/tutorials/getting-started/tracking-data/metrica.ipynb create mode 100644 docs/tutorials/getting-started/tracking-data/secondspectrum.ipynb create mode 100644 docs/tutorials/getting-started/tracking-data/skillcorner.ipynb create mode 100644 docs/tutorials/getting-started/tracking-data/statsperform.ipynb create mode 100644 docs/tutorials/getting-started/tracking-data/tracab.ipynb diff --git a/docs/contributing.md b/docs/contributing.md index 42d520a8..de0e2632 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -6,6 +6,13 @@ is nobody already working on the same issue and to ensure your time as a contrib ## How to Contribute +There are two ways to contribute: + +1. [Contributing to Code](#contributing-to-code) +2. [Contributing to Documentation](#contributing-to-documentation) + +## Contributing to Code + All code changes happen through Pull Requests. If you would like to contribute, follow the steps below to set up the project and make changes: @@ -55,7 +62,7 @@ follow these instructions: files. *Note*: if _black_ needs to re-format a file, the commit will fail, meaning you will then need to execute `git add .` and `git commit` again to commit the files updated by _black_. -## Documentation +## Contributing to Documentation This project uses [MkDocs](https://www.mkdocs.org/) to generate documentation from pages written in Markdown. @@ -71,6 +78,8 @@ mkdocs serve Open up [http://127.0.0.1:8000/](http://127.0.0.1:8000/) in your browser to preview your documentation. +Use `mkdocs.yml` to add new page references or update the file layout. + ## Contributors (sorted alphabetically) Many thanks to the following developers for contributing to this project: diff --git a/docs/functionality/coordinate-systems.md b/docs/functionality/coordinate-systems.md new file mode 100644 index 00000000..ea74956d --- /dev/null +++ b/docs/functionality/coordinate-systems.md @@ -0,0 +1,58 @@ +Coordinate system options for data loading functionality. + +Reference: `kloppy/domain/models/common.py` + +## Kloppy `"kloppy"` +- **Origin:** Top Left +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Ranges from 0 to 1 on both axes, e.g., x-dim: [0, 1] and y-dim: [0, 1] + +## Metrica `"metrica"` +- **Origin:** Top Left +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Ranges from 0 to 1 on both axes, e.g., x-dim: [0, 1] and y-dim: [0, 1] + +## Tracab `"tracab"` +- **Origin:** Center +- **Vertical Orientation:** Bottom to Top +- **Pitch Dimensions:** Scaled to `self.length` and `self.width` in hundreds with the origin at the center, e.g., x-dim: [-5250, 5250] and y-dim: [-3400, 3400] + +## Second Spectrum `"secondspectrum"` +- **Origin:** Center +- **Vertical Orientation:** Bottom to Top +- **Pitch Dimensions:** Defined by `self.length` and `self.width` with the origin at the center, e.g., x-dim: [-52.5, 52.5] and y-dim: [-34, 34] + +## Opta `"opta"` +- **Origin:** Bottom Left +- **Vertical Orientation:** Bottom to Top +- **Pitch Dimensions:** Fixed at x-dim: [0, 100] and y-dim [0, 100]. + +## Sportec `"sportec"` +- **Origin:** Bottom Left +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Defined by `self.length` and `self.width`, e.g., x-dim: [0, 105] and y-dim: [0, 68] + +## StatsBomb `"statsbomb"` +- **Origin:** Top Left +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Fixed at 120 x 80 units. So, x-dim: [0, 120], y-dim: [0, 80]. + +## Wyscout `"wyscout"` +- **Origin:** Top Left +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Fixed at x-dim: [0, 100] and y-dim [0, 100]. + +## SkillCorner `"skillcorner"` +- **Origin:** Center +- **Vertical Orientation:** Bottom to Top +- **Pitch Dimensions:** Defined by `self.length` and `self.width` with the origin at the center, e.g., x-dim: [-52.5, 52.5] and y-dim: [-34, 34] + +## Datafactory `"datafactory"` +- **Origin:** Center +- **Vertical Orientation:** Top to Bottom +- **Pitch Dimensions:** Ranges from -1 to 1 on both axes. So, x-dim: [-1, 1], y-dim: [-1, 1]. + +## StatsPerform `"statsperform"` +- **Origin:** Bottom Left +- **Vertical Orientation:** Bottom to Top +- **Pitch Dimensions:** Defined by `self.length` and `self.width`, e.g., x-dim: [0, 105] and y-dim: [0, 68] \ No newline at end of file diff --git a/docs/functionality/event-data.md b/docs/functionality/event-data.md new file mode 100644 index 00000000..34def6a5 --- /dev/null +++ b/docs/functionality/event-data.md @@ -0,0 +1,223 @@ +## Supported Event Data Providers + +- [DataFactory](#datafactory) +- [Metrica](#metrica) +- [Opta](#opta) +- [Sportec](#sportec) +- [SportsCode](#sportscode) +- [StatsBomb](#statsbomb) +- [WyScout](#wyscout) + + +### DataFactory + +#### load +`kloppy.kloppy._providers.datafactory.load(event_data, event_types=None, coordinates=None, event_factory=None)` + +This function loads DataFactory event data into an `EventDataset`. + +##### Parameters +- `event_data: FileLike`: This should be the filename (or another file-like object) of the JSON file that contains the events to be loaded. This JSON file should follow the DataFactory's specific format. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: This is an optional parameter where you can specify a list of the types of events you're interested in. If this is `None`, then all event types in the data will be loaded. +- `coordinates: Optional[str] = None`: An optional parameter for specifying the coordinate system to be used. The default is `None`, which means the default coordinate system of the data will be used. +- `event_factory: Optional[EventFactory] = None`: An optional `EventFactory` object that will be used to create the events. If this is `None`, then the default `EventFactory` specified in the configuration (via `get_config("event_factory")`) will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded events. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + +--- + +### Metrica + +### load_event +`kloppy.kloppy._providers.datafactory.load_event(event_data, meta_data, event_types=None, coordinates=None, event_factory=None)` + +This function loads event data into an `EventDataset`. + +##### Parameters +- `event_data: FileLike`: The filename (or another file-like object) of the file that contains the event data. +- `meta_data: FileLike`: The filename (or another file-like object) of the file that contains the meta data. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: An optional parameter for specifying the coordinate system to be used. The default is `None`, which means the default coordinate system of the data will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: An optional `EventFactory` object that will be used to create the events. If this is `None`, then the default `EventFactory` specified in the configuration (via `get_config("event_factory")`) will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + +--- + +### Opta + +### load +`kloppy.kloppy._providers.opta.load(f7_data, f24_data, event_types=None, coordinates=None, event_factory=None)` + +This function loads Opta event data into an `EventDataset`. + +##### Parameters +- `f7_data: FileLike`: The filename (or another file-like object) of the file that contains the F7 Opta events data. +- `f24_data: FileLike`: The filename (or another file-like object) of the file that contains the F24 Opta lineup information. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: An optional parameter for specifying the coordinate system to be used. The default is `None`, which means the default coordinate system of the data will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: An optional `EventFactory` object that will be used to create the events. If this is `None`, then the default `EventFactory` specified in the configuration (via `get_config("event_factory")`) will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + +--- + +### Sportec + +### load +`kloppy.kloppy._providers.sportec.load(f7_data, f24_data, event_types=None, coordinates=None, event_factory=None)` + +This function loads Opta event data into an `EventDataset`. + +##### Parameters +- `event_data: FileLike`: The filename (or another file-like object) of the file that contains the events. +- `meta_data: FileLike`: The filename (or another file-like object) of the file that contains the match information. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: An optional parameter for specifying the coordinate system to be used. The default is `None`, which means the default coordinate system of the data will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: An optional `EventFactory` object that will be used to create the events. If this is `None`, then the default `EventFactory` specified in the configuration (via `get_config("event_factory")`) will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + +--- + +### Sportscode + +--- + +### load +`kloppy.kloppy._providers.sportscode.load(data)` + +This function loads SportsCode data into a `CodeDataset`. + +##### Parameters +- `data: str`: The filename (or a file-like object) of the SportsCode data file to load. + +##### Returns +- `CodeDataset`: An instance of the `CodeDataset` class, filled with the loaded SportsCode data. + +Please consult the `CodeDataset` documentation for more details on these classes. + +--- + +### save +`kloppy.kloppy._providers.sportscode.save(dataset, output_filename)` + +This function saves a `CodeDataset` to a SportsCode data file. + +##### Parameters +- `dataset: CodeDataset`: The `CodeDataset` instance to save. +- `output_filename: str`: The name of the file to save the dataset to. + +##### Returns +This function does not return any value. + +Note: The data is written in binary mode to the specified file. + +Please consult the `CodeDataset` documentation for more details on this class. + +--- + +### StatsBomb + +### load +`kloppy.kloppy._providers.statsbomb.load(event_data, lineup_data, three_sixty_data=None, event_types=None, coordinates=None, event_factory=None)` + +This function loads StatsBomb event data into an `EventDataset`. + +##### Parameters +- `event_data: FileLike`: The filename (or another file-like object) of the file containing the events. +- `lineup_data: FileLike`: The filename (or another file-like object) of the file containing the lineup information. + +##### Optional Parameters +- `three_sixty_data: Optional[FileLike] = None`: The filename (or another file-like object) of the file containing the 360 data. If this is not provided, the function will still run, but without the 360 data. +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: The `EventFactory` that will be used to create the events. If `None`, the default `EventFactory` will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +--- + +### load_open_data +`kloppy.kloppy._providers.statsbomb.load_open_data(match_id='15946', event_types=None, coordinates=None, event_factory=None)` + +This function loads StatsBomb public data into an `EventDataset`. + +##### Parameters +- `match_id: Union[str, int] = '15946'`: The ID of the match to be loaded. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: The `EventFactory` that will be used to create the events. If `None`, the default `EventFactory` will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + +##### Note +By using this function, you agree to the StatsBomb public data user agreement, which can be found [here](https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf). + +--- + +### WyScout + +### load +`kloppy.kloppy._providers.wyscout.load(event_data, event_types=None, coordinates=None, event_factory=None, data_version=None)` + +This function loads Wyscout event data into an `EventDataset`. + +##### Parameters +- `event_data: FileLike`: The filename (or another file-like object) of the XML file containing the events and metadata. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: The `EventFactory` that will be used to create the events. If `None`, the default `EventFactory` will be used. +- `data_version: Optional[str] = None`: The version of the data to load. If `None`, the deserializer will be automatically identified. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +--- + +### load_open_data +`kloppy.kloppy._providers.wyscout.load_open_data(match_id='2499841', event_types=None, coordinates=None, event_factory=None)` + +This function loads Wyscout open data into an `EventDataset`. + +##### Parameters +- `match_id: Union[str, int] = '2499841'`: The ID of the match to be loaded. + +##### Optional Parameters +- `event_types: Optional[List[str]] = None`: A list of the types of events to load. If `None`, all events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. See [Coordinate Systems](../coordinate-systems/) for more information. +- `event_factory: Optional[EventFactory] = None`: The `EventFactory` that will be used to create the events. If `None`, the default `EventFactory` will be used. + +##### Returns +- `EventDataset`: An instance of the `EventDataset` class, filled with the loaded event data. + +Please consult the `EventFactory` and `EventDataset` documentation for more details on these classes. + diff --git a/docs/functionality/providers.md b/docs/functionality/providers.md new file mode 100644 index 00000000..3f9eccbb --- /dev/null +++ b/docs/functionality/providers.md @@ -0,0 +1,43 @@ +# Supported providers + +Kloppy supports two types of providers: + +1. [Event data providers](#event-data-providers) +2. [Tracking data providers](#tracking-data-providers) + +### Event data providers + +kloppy tries to support as many as possible features for each provider. The table below show what kloppy supports per provider. Each provider might or might not include more information in their files. + +Please [open a ticket](https://github.com/PySport/kloppy/issues) when you like to implement additional features. + +||| Datafactory | Metrica | Opta | Sportec | Statsbomb | Wyscout | +|-|-|:-:|:-:|:-:|:-:|:-:|:-:| +|**File format**||JSON|JSON|XML|XML|JSON|JSON| +|**Event types**| +||[Pass][kloppy.domain.models.event.PassEvent]|✓|✓|✓|✓|✓|✓| +||[Shot][kloppy.domain.models.event.ShotEvent]|✓|✓|✓|✓|✓|✓| +||[TakeOn][kloppy.domain.models.event.TakeOnEvent]||✓|✓||✓|✓|✓| +||[Carry][kloppy.domain.models.event.CarryEvent]||✓|||✓|| +||[Substitution][kloppy.domain.models.event.SubstitutionEvent]|✓|||✓|✓|| +||[PlayerOn][kloppy.domain.models.event.PlayerOnEvent]/[Off][kloppy.domain.models.event.PlayerOffEvent]|||||✓|| +||[Card][kloppy.domain.models.event.CardEvent]|✓|||✓|✓|✓| +||[Recovery][kloppy.domain.models.event.RecoveryEvent]|✓|✓|✓|✓|✓|✓| +||[BallOut][kloppy.domain.models.event.BallOutEvent]|✓[^2]|✓|✓|✓[^2]|✓|✓| +||[FoulCommitted][kloppy.domain.models.event.FoulCommittedEvent]|✓|✓|✓|✓|✓|✓| +||[Generic][kloppy.domain.models.event.GenericEvent][^1]|✓|✓|✓|✓|✓|✓|s +|**Qualifiers**| +||SetPiece|✓[^3]|✓[^3]|✓[^3]|✓[^3]|✓[^3]|✓[^3] +||BodyPart||`Head`|`Head` `RightFoot` `LeftFoot` `Other`|`Head` `RightFoot` `LeftFoot`|`Chest` `Head` `RightFoot` `LeftFoot` `Other` [^4]|`RightFoot` `LeftFoot` +||PassType|||`Cross` `LongBall` `ThroughBall` `Launch` `ChippedBall` `Assist` `2nd Assist` `SwitchOfPlay` |||`Cross` `Hand` `Head` `High` `Launch` `Simple` `Smart` + +[^1]: All other event types +[^2]: Synthetic event generated by kloppy +[^3]: Full support means support for these types: `Corner` `FreeKick` `Penalty` `ThrowIn` `KickOff` `GoalKick` +[^4]: All body parts can be found here: https://github.com/statsbomb/open-data/blob/master/doc/StatsBomb%20Open%20Data%20Specification%20v1.1.pdf + +### Tracking data providers + +||| Metrica | SecondSpectrum | SkillCorner | StatsPerform | Tracab +|-|-|:-:|:-:|:-:|:-:|:-:| +|**File format**||CSV, EPTS|JSONL|JSON|TXT|DAT| \ No newline at end of file diff --git a/docs/functionality/tracking-data.md b/docs/functionality/tracking-data.md new file mode 100644 index 00000000..f1155754 --- /dev/null +++ b/docs/functionality/tracking-data.md @@ -0,0 +1,176 @@ +## Metrica + +### load_tracking_csv +`kloppy.kloppy._providers.datafactory.load_tracking_csv(home_data, away_data, sample_rate=None, limit=None, coordinates=None)` + +This function loads tracking data from CSV files into a `TrackingDataset`. + +##### Parameters +- `home_data: FileLike`: This is the filename (or another file-like object) of the CSV file that contains the home team's tracking data. +- `away_data: FileLike`: This is the filename (or another file-like object) of the CSV file that contains the away team's tracking data. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: The sample rate to be used when loading the tracking data. The default is `None`. +- `limit: Optional[int] = None`: The maximum number of tracking events to load from the file. The default is `None`, which means all tracking events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. See [Coordinate Systems](../coordinate-systems/) for more information. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded tracking data. + +--- + +### load_tracking_epts +`kloppy.kloppy._providers.datafactory.load_tracking_epts(meta_data, raw_data, sample_rate=None, limit=None, coordinates=None)` + +This function loads EPTS tracking data into a `TrackingDataset`. + +##### Parameters +- `meta_data: FileLike`: The filename (or another file-like object) of the file that contains the meta data. +- `raw_data: FileLike`: The filename (or another file-like object) of the file that contains the raw tracking data. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: The sample rate to be used when loading the tracking data. The default is `None`. +- `limit: Optional[int] = None`: The maximum number of tracking events to load from the file. The default is `None`, which means all tracking events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded tracking data. + +--- + +### load_open_data +`kloppy.kloppy._providers.datafactory.load_open_data(match_id="1", sample_rate=None, limit=None, coordinates=None)` + +This function loads open data for a specific match into a `TrackingDataset`. + +##### Parameters +- `match_id: Union[str, int] = "1"`: The ID of the match to load. This can be 1, 2, or 3. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: The sample rate to be used when loading the tracking data. The default is `None`. +- `limit: Optional[int] = None`: The maximum number of tracking events to load from the file. The default is `None`, which means all tracking events will be loaded. +- `coordinates: Optional[str] = None`: The coordinate system to be used. The default is `None`. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded tracking data. + +In the event that a non-supported `match_id` is provided, a `KloppyError` will be raised. + +--- + +## SecondSpectrum + +### load +`kloppy.kloppy._providers.secondspectrum.load(meta_data, raw_data, additional_meta_data, sample_rate, limit, coordinates, only_alive)` + +This function loads Second Spectrum tracking data into a `TrackingDataset`. + +##### Parameters +- `meta_data: FileLike`: The filename (or a file-like object) of the metadata file. +- `raw_data: FileLike`: The filename (or a file-like object) of the raw data file. + +##### Optional Parameters +- `additional_meta_data: Optional[FileLike] = None`: An optional filename (or a file-like object) of additional metadata file. +- `sample_rate: Optional[float] = None`: An optional sampling rate for the tracking data. If not provided, all data points will be included. ie. `1/5` or `1/2` +- `limit: Optional[int] = None`: An optional limit on the number of data points to load. If not provided, all data points will be loaded. +- `coordinates: Optional[str] = None`: An optional coordinate system to use when loading the data. If not provided, the default coordinate system will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `only_alive: Optional[bool] = False`: If set to True, only the frames where the ball is in play ('alive') will be included. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded Second Spectrum data. + +Please consult the `TrackingDataset` documentation for more details on this class. + +--- + +## SkillCorner + +### load +`kloppy.kloppy._providers.skillcorner.load(meta_data, raw_data, sample_rate, limit, coordinates, include_empty_frames)` + +This function loads SkillCorner tracking data into a `TrackingDataset`. + +##### Parameters +- `meta_data: FileLike`: The filename (or a file-like object) of the metadata file. +- `raw_data: FileLike`: The filename (or a file-like object) of the raw data file. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: An optional sampling rate for the tracking data. If not provided, all data points will be included. ie. `1/5` or `1/2` +- `limit: Optional[int] = None`: An optional limit on the number of data points to load. If not provided, all data points will be loaded. +- `coordinates: Optional[str] = None`: An optional coordinate system to use when loading the data. If not provided, the default coordinate system will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `include_empty_frames: Optional[bool] = False`: If set to True, frames with no data will be included in the dataset. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded SkillCorner data. + +Please consult the `TrackingDataset` documentation for more details on this class. + +--- + +### load_open_data +`kloppy.kloppy._providers.skillcorner.load_open_data(match_id, sample_rate, limit, coordinates, include_empty_frames)` + +This function loads SkillCorner tracking data for a specific match into a `TrackingDataset`. + +##### Parameters +- `match_id: Union[str, int]`: The ID of the match to load data for. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: An optional sampling rate for the tracking data. If not provided, all data points will be included. +- `limit: Optional[int] = None`: An optional limit on the number of data points to load. If not provided, all data points will be loaded. +- `coordinates: Optional[str] = None`: An optional coordinate system to use when loading the data. If not provided, the default coordinate system will be used. +- `include_empty_frames: Optional[bool] = False`: If set to True, frames with no data will be included in the dataset. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded SkillCorner data. + +Please consult the `TrackingDataset` documentation for more details on this class. + +--- + +## StatsPerform + +### load +`kloppy.kloppy._providers.statsperform.load(meta_data, raw_data, sample_rate, limit, coordinates, only_alive)` + +This function loads StatsPerform tracking data into a `TrackingDataset`. + +##### Parameters +- `meta_data: FileLike`: The filename (or a file-like object) of the metadata file. This corresponds to the StatsPerform MA1 file (XML format) which contains single game live data and lineups. +- `raw_data: FileLike`: The filename (or a file-like object) of the raw data file. This corresponds to the StatsPerform MA25 file (TXT format) which contains tracking data. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: An optional sampling rate for the tracking data. If not provided, all data points will be included. For example, `1/5` or `1/2`. +- `limit: Optional[int] = None`: An optional limit on the number of data points to load. If not provided, all data points will be loaded. +- `coordinates: Optional[str] = None`: An optional coordinate system to use when loading the data. If not provided, the default coordinate system will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `only_alive: Optional[bool] = False`: If set to True, only the frames where the ball is in play ('alive') will be included. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded StatsPerform data. + +Please consult the `TrackingDataset` documentation for more details on this class. + + +--- + +## TRACAB + +### load +`kloppy.kloppy._providers.tracab.load(meta_data, raw_data, sample_rate, limit, coordinates, only_alive)` + +This function loads TRACAB tracking data into a `TrackingDataset`. + +##### Parameters +- `meta_data: FileLike`: The filename (or a file-like object) of the metadata file. +- `raw_data: FileLike`: The filename (or a file-like object) of the raw data file. + +##### Optional Parameters +- `sample_rate: Optional[float] = None`: An optional sampling rate for the tracking data. If not provided, all data points will be included. For example, `1/5` or `1/2`. +- `limit: Optional[int] = None`: An optional limit on the number of data points to load. If not provided, all data points will be loaded. +- `coordinates: Optional[str] = None`: An optional coordinate system to use when loading the data. If not provided, the default coordinate system will be used. See [Coordinate Systems](../coordinate-systems/) for more information. +- `only_alive: Optional[bool] = True`: If set to True, only the frames where the ball is in play ('alive') will be included. + +##### Returns +- `TrackingDataset`: An instance of the `TrackingDataset` class, filled with the loaded TRACAB data. + +Please consult the `TrackingDataset` documentation for more details on this class. \ No newline at end of file diff --git a/docs/tutorials/getting-started/event-data/datafactory.ipynb b/docs/tutorials/getting-started/event-data/datafactory.ipynb new file mode 100644 index 00000000..c8022554 --- /dev/null +++ b/docs/tutorials/getting-started/event-data/datafactory.ipynb @@ -0,0 +1,233 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "04ea80c2", + "metadata": {}, + "source": [ + "# Datafactory\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .json event files](#load-local-files): `datafactory.load()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "3848e9aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_type
022094464PASSCOMPLETETrue133.0alive279279409750.010.010.26-0.0593357KICK_OFF
122094465PASSCOMPLETETrue166.0alive279279933570.33-0.130.38-0.2911458NaN
222094466PASSCOMPLETETrue188.0alive279279114580.45-0.490.44-0.7862686NaN
322094467PASSCOMPLETETrue11010.0alive279279626860.44-0.850.79-0.3041488NaN
422094468PASSCOMPLETETrue11616.0alive279279414880.76-0.31-0.25-0.1940975NaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp end_timestamp \\\n", + "0 22094464 PASS COMPLETE True 1 3 3.0 \n", + "1 22094465 PASS COMPLETE True 1 6 6.0 \n", + "2 22094466 PASS COMPLETE True 1 8 8.0 \n", + "3 22094467 PASS COMPLETE True 1 10 10.0 \n", + "4 22094468 PASS COMPLETE True 1 16 16.0 \n", + "\n", + " ball_state ball_owning_team team_id player_id coordinates_x coordinates_y \\\n", + "0 alive 279 279 40975 0.01 0.01 \n", + "1 alive 279 279 93357 0.33 -0.13 \n", + "2 alive 279 279 11458 0.45 -0.49 \n", + "3 alive 279 279 62686 0.44 -0.85 \n", + "4 alive 279 279 41488 0.76 -0.31 \n", + "\n", + " end_coordinates_x end_coordinates_y receiver_player_id set_piece_type \n", + "0 0.26 -0.05 93357 KICK_OFF \n", + "1 0.38 -0.29 11458 NaN \n", + "2 0.44 -0.78 62686 NaN \n", + "3 0.79 -0.30 41488 NaN \n", + "4 -0.25 -0.19 40975 NaN " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import datafactory\n", + "\n", + "dataset = datafactory.load(\n", + " event_data=\"../../kloppy/tests/files/datafactory_events.json\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"datafactory\",\n", + " event_types=[\"shot\", \"pass\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/metrica.ipynb b/docs/tutorials/getting-started/event-data/metrica.ipynb new file mode 100644 index 00000000..580e94b6 --- /dev/null +++ b/docs/tutorials/getting-started/event-data/metrica.ipynb @@ -0,0 +1,247 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "87b4ace3", + "metadata": {}, + "source": [ + "# Metrica\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .json event files](#load-local-files): `metrica.load_event()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "6aaa7038", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typebody_part_type
0NonePASSCOMPLETETrue114.4415.08aliveTEAM_ATEAM_ANone0.501250.487250.498640.48705NaNKICK_OFFNaN
1NonePASSCOMPLETETrue115.3617.04aliveTEAM_ATEAM_ANone0.497000.485000.633730.63449NaNNaNNaN
2NonePASSCOMPLETETrue118.6020.28aliveTEAM_ATEAM_ANone0.669860.597070.806020.39821NaNNaNNaN
3NonePASSCOMPLETETrue121.2023.20aliveTEAM_ATEAM_ANone0.809290.429220.799060.81522NaNNaNNaN
4NonePASSCOMPLETETrue123.9225.12aliveTEAM_ATEAM_ANone0.797560.819980.681010.98059NaNNaNNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp end_timestamp \\\n", + "0 None PASS COMPLETE True 1 14.44 15.08 \n", + "1 None PASS COMPLETE True 1 15.36 17.04 \n", + "2 None PASS COMPLETE True 1 18.60 20.28 \n", + "3 None PASS COMPLETE True 1 21.20 23.20 \n", + "4 None PASS COMPLETE True 1 23.92 25.12 \n", + "\n", + " ball_state ball_owning_team team_id player_id coordinates_x coordinates_y \\\n", + "0 alive TEAM_A TEAM_A None 0.50125 0.48725 \n", + "1 alive TEAM_A TEAM_A None 0.49700 0.48500 \n", + "2 alive TEAM_A TEAM_A None 0.66986 0.59707 \n", + "3 alive TEAM_A TEAM_A None 0.80929 0.42922 \n", + "4 alive TEAM_A TEAM_A None 0.79756 0.81998 \n", + "\n", + " end_coordinates_x end_coordinates_y receiver_player_id set_piece_type \\\n", + "0 0.49864 0.48705 NaN KICK_OFF \n", + "1 0.63373 0.63449 NaN NaN \n", + "2 0.80602 0.39821 NaN NaN \n", + "3 0.79906 0.81522 NaN NaN \n", + "4 0.68101 0.98059 NaN NaN \n", + "\n", + " body_part_type \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN " + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_event(\n", + " event_data=\"../../kloppy/tests/files/metrica_events.json\",\n", + " meta_data=\"../../kloppy/tests/files/epts_metrica_metadata.xml\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"metrica\",\n", + " event_types=[\"shot\", \"pass\"]\n", + ")\n", + "\n", + "dataset.to_df().head()\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/opta.ipynb b/docs/tutorials/getting-started/event-data/opta.ipynb new file mode 100644 index 00000000..970feaf5 --- /dev/null +++ b/docs/tutorials/getting-started/event-data/opta.ipynb @@ -0,0 +1,253 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "f3c03ff0", + "metadata": {}, + "source": [ + "# Opta\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .xml event files](#load-local-files): `opta.load()`\n", + "\n", + "### Load local files\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "00366986", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typepass_typebody_part_type
01510681159PASSCOMPLETETrue10.782Nonealive5695694833750.149.436.445.1NaNKICK_OFFNaNNaN
11646695660PASSCOMPLETETrue13.692Nonealive5695697600136.445.128.039.8NaNNaNNaNNaN
21782829017PASSCOMPLETETrue15.299Nonealive56956916426627.939.829.162.0NaNNaNNaNNaN
31909884550PASSCOMPLETETrue16.995Nonealive5695697738429.362.626.337.6NaNNaNNaNNaN
41515097980PASSCOMPLETETrue18.971Nonealive56956916426626.334.329.17.4NaNNaNNaNNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp \\\n", + "0 1510681159 PASS COMPLETE True 1 0.782 \n", + "1 1646695660 PASS COMPLETE True 1 3.692 \n", + "2 1782829017 PASS COMPLETE True 1 5.299 \n", + "3 1909884550 PASS COMPLETE True 1 6.995 \n", + "4 1515097980 PASS COMPLETE True 1 8.971 \n", + "\n", + " end_timestamp ball_state ball_owning_team team_id player_id coordinates_x \\\n", + "0 None alive 569 569 48337 50.1 \n", + "1 None alive 569 569 76001 36.4 \n", + "2 None alive 569 569 164266 27.9 \n", + "3 None alive 569 569 77384 29.3 \n", + "4 None alive 569 569 164266 26.3 \n", + "\n", + " coordinates_y end_coordinates_x end_coordinates_y receiver_player_id \\\n", + "0 49.4 36.4 45.1 NaN \n", + "1 45.1 28.0 39.8 NaN \n", + "2 39.8 29.1 62.0 NaN \n", + "3 62.6 26.3 37.6 NaN \n", + "4 34.3 29.1 7.4 NaN \n", + "\n", + " set_piece_type pass_type body_part_type \n", + "0 KICK_OFF NaN NaN \n", + "1 NaN NaN NaN \n", + "2 NaN NaN NaN \n", + "3 NaN NaN NaN \n", + "4 NaN NaN NaN " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import opta\n", + "\n", + "dataset = opta.load(\n", + " f7_data=\"../../kloppy/tests/files/opta_f7.xml\",\n", + " f24_data=\"../../kloppy/tests/files/opta_f24.xml\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"opta\",\n", + " event_types=[\"pass\", \"shot\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/sportec.ipynb b/docs/tutorials/getting-started/event-data/sportec.ipynb new file mode 100644 index 00000000..0e9f982f --- /dev/null +++ b/docs/tutorials/getting-started/event-data/sportec.ipynb @@ -0,0 +1,247 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "44465b73", + "metadata": {}, + "source": [ + "# Sportec\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .xml event files](#load-local-files): `sportec.load()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "e9adb7cb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typebody_part_type
017364900000006PASSCOMPLETETrue10.000NonealiveNoneDFL-CLU-000004DFL-OBJ-0000SP56.4168.0077.7538.71DFL-OBJ-0000ZSKICK_OFFNaN
117364900000007PASSCOMPLETETrue13.123NonealiveNoneDFL-CLU-000004DFL-OBJ-0000ZS73.9437.21NaNNaNDFL-OBJ-002G3INaNNaN
217364900000014PASSCOMPLETETrue131.797NonealiveNoneDFL-CLU-00000ADFL-OBJ-00017V35.5768.0021.2428.58DFL-OBJ-0027B9THROW_INNaN
317364900000031SHOTBLOCKEDFalse179.480NonealiveNoneDFL-CLU-000004DFL-OBJ-00270621.2428.58NaNNaNNaNNaNRIGHT_FOOT
417364900000036PASSINCOMPLETEFalse195.173NonealiveNoneDFL-CLU-000004DFL-OBJ-002G3I8.724.21NaNNaNNoneNaNNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp \\\n", + "0 17364900000006 PASS COMPLETE True 1 0.000 \n", + "1 17364900000007 PASS COMPLETE True 1 3.123 \n", + "2 17364900000014 PASS COMPLETE True 1 31.797 \n", + "3 17364900000031 SHOT BLOCKED False 1 79.480 \n", + "4 17364900000036 PASS INCOMPLETE False 1 95.173 \n", + "\n", + " end_timestamp ball_state ball_owning_team team_id player_id \\\n", + "0 None alive None DFL-CLU-000004 DFL-OBJ-0000SP \n", + "1 None alive None DFL-CLU-000004 DFL-OBJ-0000ZS \n", + "2 None alive None DFL-CLU-00000A DFL-OBJ-00017V \n", + "3 None alive None DFL-CLU-000004 DFL-OBJ-002706 \n", + "4 None alive None DFL-CLU-000004 DFL-OBJ-002G3I \n", + "\n", + " coordinates_x coordinates_y end_coordinates_x end_coordinates_y \\\n", + "0 56.41 68.00 77.75 38.71 \n", + "1 73.94 37.21 NaN NaN \n", + "2 35.57 68.00 21.24 28.58 \n", + "3 21.24 28.58 NaN NaN \n", + "4 8.72 4.21 NaN NaN \n", + "\n", + " receiver_player_id set_piece_type body_part_type \n", + "0 DFL-OBJ-0000ZS KICK_OFF NaN \n", + "1 DFL-OBJ-002G3I NaN NaN \n", + "2 DFL-OBJ-0027B9 THROW_IN NaN \n", + "3 NaN NaN RIGHT_FOOT \n", + "4 None NaN NaN " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import sportec\n", + "\n", + "dataset = sportec.load(\n", + " event_data=\"../../kloppy/tests/files/sportec_events.xml\",\n", + " meta_data=\"../../kloppy/tests/files/sportec_meta.xml\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"sportec\",\n", + " event_types=[\"pass\", \"shot\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/sportscode.ipynb b/docs/tutorials/getting-started/event-data/sportscode.ipynb new file mode 100644 index 00000000..91df0738 --- /dev/null +++ b/docs/tutorials/getting-started/event-data/sportscode.ipynb @@ -0,0 +1,236 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "eb1997f4", + "metadata": {}, + "source": [ + "# Sportscode\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .xml event files](#load-local-files): `sportscode.load()`\n", + "- [Save load files](#save-load-files): `sportscode.save()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "523a21a7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_idperiod_idtimestampend_timestampcodeTeamPacking.ValueReceiverExpected.Goal.Value
0P113.69.7PASSHenkie1.0Klaas NømeNaN
1P2168.374.5PASSHenkie3.0PietNaN
2P31103.6109.6SHOTHenkieNaNNaN0.13
\n", + "
" + ], + "text/plain": [ + " code_id period_id timestamp end_timestamp code Team Packing.Value \\\n", + "0 P1 1 3.6 9.7 PASS Henkie 1.0 \n", + "1 P2 1 68.3 74.5 PASS Henkie 3.0 \n", + "2 P3 1 103.6 109.6 SHOT Henkie NaN \n", + "\n", + " Receiver Expected.Goal.Value \n", + "0 Klaas Nøme NaN \n", + "1 Piet NaN \n", + "2 NaN 0.13 " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import sportscode\n", + "\n", + "dataset = sportscode.load(\n", + " data=\"../../kloppy/tests/files/code_xml.xml\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "6ba877ad", + "metadata": {}, + "source": [ + "### Save load files" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "728790a3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + " \n", + " \n", + " P1\n", + " 3.6\n", + " 9.7\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " P2\n", + " 68.3\n", + " 74.5\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " P3\n", + " 103.6\n", + " 109.6\n", + " SHOT\n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n" + ] + } + ], + "source": [ + "from kloppy import sportscode\n", + "\n", + "sportscode.save(dataset, \"/tmp/file.xml\")\n", + "\n", + "with open(\"/tmp/file.xml\", \"r\") as fp:\n", + " data = fp.read()\n", + " \n", + "print(data)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/statsbomb.ipynb b/docs/tutorials/getting-started/event-data/statsbomb.ipynb new file mode 100644 index 00000000..7c43739c --- /dev/null +++ b/docs/tutorials/getting-started/event-data/statsbomb.ipynb @@ -0,0 +1,740 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "3a953d55", + "metadata": {}, + "source": [ + "# Statsbomb\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .json event files](#load-local-files): `statsbomb.load()`\n", + "- [Load remote .json event files](#load-remote-files): `statsbomb.load()`\n", + "- [Load open data event files](#load-open-data): `statsbomb.load_open_data()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "12ec8092", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typebody_part_typepass_type
0bbc398f7-c784-4958-a504-37b583caf97aPASSCOMPLETETrue10.8782.788504alive9099091108659.9539.9532.4528.758963KICK_OFFRIGHT_FOOTNaN
15c210f79-9714-44a6-b2ec-387f6a117b37PASSCOMPLETETrue14.2886.764772alive909909896336.1530.3570.6575.758541NaNLEFT_FOOTLONG_BALL
28a3e6668-9680-4417-987e-8db0c6ce6a8bPASSCOMPLETETrue112.16314.093230alive914914828643.050.0515.757.456954THROW_INNaNNaN
3f8e61bb0-b618-4695-9ff9-eaa0584bdbfaPASSCOMPLETETrue116.42018.242108alive91491469543.2512.657.8536.157036NaNRIGHT_FOOTNaN
41d72ce76-31fd-43e0-a6b2-1f78c8a57a77PASSCOMPLETETrue120.02521.071484alive91491470369.0538.8519.6547.857173NaNRIGHT_FOOTNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success \\\n", + "0 bbc398f7-c784-4958-a504-37b583caf97a PASS COMPLETE True \n", + "1 5c210f79-9714-44a6-b2ec-387f6a117b37 PASS COMPLETE True \n", + "2 8a3e6668-9680-4417-987e-8db0c6ce6a8b PASS COMPLETE True \n", + "3 f8e61bb0-b618-4695-9ff9-eaa0584bdbfa PASS COMPLETE True \n", + "4 1d72ce76-31fd-43e0-a6b2-1f78c8a57a77 PASS COMPLETE True \n", + "\n", + " period_id timestamp end_timestamp ball_state ball_owning_team team_id \\\n", + "0 1 0.878 2.788504 alive 909 909 \n", + "1 1 4.288 6.764772 alive 909 909 \n", + "2 1 12.163 14.093230 alive 914 914 \n", + "3 1 16.420 18.242108 alive 914 914 \n", + "4 1 20.025 21.071484 alive 914 914 \n", + "\n", + " player_id coordinates_x coordinates_y end_coordinates_x \\\n", + "0 11086 59.95 39.95 32.45 \n", + "1 8963 36.15 30.35 70.65 \n", + "2 8286 43.05 0.05 15.75 \n", + "3 6954 3.25 12.65 7.85 \n", + "4 7036 9.05 38.85 19.65 \n", + "\n", + " end_coordinates_y receiver_player_id set_piece_type body_part_type \\\n", + "0 28.75 8963 KICK_OFF RIGHT_FOOT \n", + "1 75.75 8541 NaN LEFT_FOOT \n", + "2 7.45 6954 THROW_IN NaN \n", + "3 36.15 7036 NaN RIGHT_FOOT \n", + "4 47.85 7173 NaN RIGHT_FOOT \n", + "\n", + " pass_type \n", + "0 NaN \n", + "1 LONG_BALL \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load(\n", + " event_data=\"../../kloppy/tests/files/statsbomb_3788741_event.json\",\n", + " lineup_data=\"../../kloppy/tests/files/statsbomb_3788741_lineup.json\",\n", + " \n", + " # 360 file is optional\n", + " three_sixty_data=\"../../kloppy/tests/files/statsbomb_3788741_360.json\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"statsbomb\",\n", + " event_types=[\"pass\", \"shot\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "8fa1e495", + "metadata": {}, + "source": [ + "### Load remote files\n", + "\n", + "You can also directly read files from urls (http or https) by passing a url instead of a local path." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "f3b9119e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typebody_part_typepass_type
034208ade-2af4-45c3-970e-655937cad938PASSCOMPLETETrue10.0982.007alive206206658160.540.535.525.56855KICK_OFFLEFT_FOOTNaN
1d1cccb73-c7ef-4b02-8267-ebd7f149904bPASSINCOMPLETEFalse13.4976.785alive206206685535.528.585.572.5NoneNaNRIGHT_FOOTLONG_BALL
2f1cc47d6-4b19-45a6-beb9-33d67fc83f4bPASSCOMPLETETrue16.7858.431alive217217520334.57.534.520.55470NaNHEADHEAD_PASS
3f774571f-4b65-43a0-9bfc-6384948d1b82PASSCOMPLETETrue18.4319.576alive217217547035.520.535.51.55477NaNHEADHEAD_PASS
446f0e871-3e72-4817-9a53-af27583ba6c1PASSCOMPLETETrue110.43311.150alive217217547733.52.525.51.55211NaNRIGHT_FOOTNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success \\\n", + "0 34208ade-2af4-45c3-970e-655937cad938 PASS COMPLETE True \n", + "1 d1cccb73-c7ef-4b02-8267-ebd7f149904b PASS INCOMPLETE False \n", + "2 f1cc47d6-4b19-45a6-beb9-33d67fc83f4b PASS COMPLETE True \n", + "3 f774571f-4b65-43a0-9bfc-6384948d1b82 PASS COMPLETE True \n", + "4 46f0e871-3e72-4817-9a53-af27583ba6c1 PASS COMPLETE True \n", + "\n", + " period_id timestamp end_timestamp ball_state ball_owning_team team_id \\\n", + "0 1 0.098 2.007 alive 206 206 \n", + "1 1 3.497 6.785 alive 206 206 \n", + "2 1 6.785 8.431 alive 217 217 \n", + "3 1 8.431 9.576 alive 217 217 \n", + "4 1 10.433 11.150 alive 217 217 \n", + "\n", + " player_id coordinates_x coordinates_y end_coordinates_x \\\n", + "0 6581 60.5 40.5 35.5 \n", + "1 6855 35.5 28.5 85.5 \n", + "2 5203 34.5 7.5 34.5 \n", + "3 5470 35.5 20.5 35.5 \n", + "4 5477 33.5 2.5 25.5 \n", + "\n", + " end_coordinates_y receiver_player_id set_piece_type body_part_type \\\n", + "0 25.5 6855 KICK_OFF LEFT_FOOT \n", + "1 72.5 None NaN RIGHT_FOOT \n", + "2 20.5 5470 NaN HEAD \n", + "3 1.5 5477 NaN HEAD \n", + "4 1.5 5211 NaN RIGHT_FOOT \n", + "\n", + " pass_type \n", + "0 NaN \n", + "1 LONG_BALL \n", + "2 HEAD_PASS \n", + "3 HEAD_PASS \n", + "4 NaN " + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load(\n", + " event_data=\"https://raw.githubusercontent.com/statsbomb/open-data/master/data/events/15946.json\",\n", + " lineup_data=\"https://raw.githubusercontent.com/statsbomb/open-data/master/data/lineups/15946.json\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"statsbomb\",\n", + " event_types=[\"pass\", \"shot\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d83b2d03", + "metadata": {}, + "source": [ + "### Load open data\n", + "\n", + "For loading Statsbomb open data you can also use `statsbomb.load_open_data`. The api is very simular but you don't have to pass the urls.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "aa5df8f9", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idset_piece_typebody_part_typepass_type
034208ade-2af4-45c3-970e-655937cad938PASSCOMPLETETrue10.0982.007alive206206658160.540.535.525.56855KICK_OFFLEFT_FOOTNaN
1d1cccb73-c7ef-4b02-8267-ebd7f149904bPASSINCOMPLETEFalse13.4976.785alive206206685535.528.585.572.5NoneNaNRIGHT_FOOTLONG_BALL
2f1cc47d6-4b19-45a6-beb9-33d67fc83f4bPASSCOMPLETETrue16.7858.431alive217217520334.57.534.520.55470NaNHEADHEAD_PASS
3f774571f-4b65-43a0-9bfc-6384948d1b82PASSCOMPLETETrue18.4319.576alive217217547035.520.535.51.55477NaNHEADHEAD_PASS
446f0e871-3e72-4817-9a53-af27583ba6c1PASSCOMPLETETrue110.43311.150alive217217547733.52.525.51.55211NaNRIGHT_FOOTNaN
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success \\\n", + "0 34208ade-2af4-45c3-970e-655937cad938 PASS COMPLETE True \n", + "1 d1cccb73-c7ef-4b02-8267-ebd7f149904b PASS INCOMPLETE False \n", + "2 f1cc47d6-4b19-45a6-beb9-33d67fc83f4b PASS COMPLETE True \n", + "3 f774571f-4b65-43a0-9bfc-6384948d1b82 PASS COMPLETE True \n", + "4 46f0e871-3e72-4817-9a53-af27583ba6c1 PASS COMPLETE True \n", + "\n", + " period_id timestamp end_timestamp ball_state ball_owning_team team_id \\\n", + "0 1 0.098 2.007 alive 206 206 \n", + "1 1 3.497 6.785 alive 206 206 \n", + "2 1 6.785 8.431 alive 217 217 \n", + "3 1 8.431 9.576 alive 217 217 \n", + "4 1 10.433 11.150 alive 217 217 \n", + "\n", + " player_id coordinates_x coordinates_y end_coordinates_x \\\n", + "0 6581 60.5 40.5 35.5 \n", + "1 6855 35.5 28.5 85.5 \n", + "2 5203 34.5 7.5 34.5 \n", + "3 5470 35.5 20.5 35.5 \n", + "4 5477 33.5 2.5 25.5 \n", + "\n", + " end_coordinates_y receiver_player_id set_piece_type body_part_type \\\n", + "0 25.5 6855 KICK_OFF LEFT_FOOT \n", + "1 72.5 None NaN RIGHT_FOOT \n", + "2 20.5 5470 NaN HEAD \n", + "3 1.5 5477 NaN HEAD \n", + "4 1.5 5211 NaN RIGHT_FOOT \n", + "\n", + " pass_type \n", + "0 NaN \n", + "1 LONG_BALL \n", + "2 HEAD_PASS \n", + "3 HEAD_PASS \n", + "4 NaN " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load_open_data(\n", + " match_id=15946,\n", + " \n", + " # Optional arguments\n", + " coordinates=\"statsbomb\",\n", + " event_types=[\"pass\", \"shot\"]\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/event-data/wyscout.ipynb b/docs/tutorials/getting-started/event-data/wyscout.ipynb new file mode 100644 index 00000000..4212b1ff --- /dev/null +++ b/docs/tutorials/getting-started/event-data/wyscout.ipynb @@ -0,0 +1,766 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "0999ea6f", + "metadata": {}, + "source": [ + "# Wyscout\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .json event files](#load-local-files): `wyscout.load()`\n", + "- [Load remote .json event files](#load-remote-files): `wyscout.load()`\n", + "- [Load open data event files](#load-open-data): `wyscout.load_open_data()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f1120143", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_id...coordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idis_counter_attackpass_typeset_piece_typebody_part_typegoalkeeper_action_type
0190078343PASSCOMPLETETrue12.643377NoneNoneNone1612...50.050.029.041.0NaNFalseSIMPLE_PASSNaNNaNNaN
1190078344PASSINCOMPLETEFalse14.350302NoneNoneNone1612...29.041.071.094.0NaNFalseHIGH_PASSNaNNaNNaN
2190079073PASSINCOMPLETEFalse18.010654NoneNoneNone1631...29.06.036.03.0NaNFalseHEAD_PASSNaNNaNNaN
3190079074PASSCOMPLETETrue114.934050NoneNoneNone1631...39.00.041.013.0NaNFalseHAND_PASSTHROW_INNaNNaN
4190079076PASSCOMPLETETrue116.013375NoneNoneNone1631...41.013.063.015.0NaNFalseHIGH_PASSNaNNaNNaN
\n", + "

5 rows × 21 columns

\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp \\\n", + "0 190078343 PASS COMPLETE True 1 2.643377 \n", + "1 190078344 PASS INCOMPLETE False 1 4.350302 \n", + "2 190079073 PASS INCOMPLETE False 1 8.010654 \n", + "3 190079074 PASS COMPLETE True 1 14.934050 \n", + "4 190079076 PASS COMPLETE True 1 16.013375 \n", + "\n", + " end_timestamp ball_state ball_owning_team team_id ... coordinates_x \\\n", + "0 None None None 1612 ... 50.0 \n", + "1 None None None 1612 ... 29.0 \n", + "2 None None None 1631 ... 29.0 \n", + "3 None None None 1631 ... 39.0 \n", + "4 None None None 1631 ... 41.0 \n", + "\n", + " coordinates_y end_coordinates_x end_coordinates_y receiver_player_id \\\n", + "0 50.0 29.0 41.0 NaN \n", + "1 41.0 71.0 94.0 NaN \n", + "2 6.0 36.0 3.0 NaN \n", + "3 0.0 41.0 13.0 NaN \n", + "4 13.0 63.0 15.0 NaN \n", + "\n", + " is_counter_attack pass_type set_piece_type body_part_type \\\n", + "0 False SIMPLE_PASS NaN NaN \n", + "1 False HIGH_PASS NaN NaN \n", + "2 False HEAD_PASS NaN NaN \n", + "3 False HAND_PASS THROW_IN NaN \n", + "4 False HIGH_PASS NaN NaN \n", + "\n", + " goalkeeper_action_type \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import wyscout\n", + "\n", + "dataset = wyscout.load(\n", + " event_data=\"../../kloppy/tests/files/wyscout_events.json\",\n", + " \n", + " # Optional arguments\n", + " event_types=[\"shot\", \"pass\"],\n", + " coordinates=\"wyscout\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "caa16b3e", + "metadata": {}, + "source": [ + "### Load remote files\n", + "\n", + "You can also directly read files from urls (http or https) by passing a url instead of a local path." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ada96b31", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_id...coordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idis_counter_attackpass_typeset_piece_typegoalkeeper_action_typebody_part_type
088178642PASSCOMPLETETrue11.255990NoneNoneNone4418...50.048.047.050.0NaNFalseSIMPLE_PASSNaNNaNNaN
188178643PASSCOMPLETETrue12.351908NoneNoneNone4418...47.050.041.048.0NaNFalseSIMPLE_PASSNaNNaNNaN
288178644PASSCOMPLETETrue13.241028NoneNoneNone4418...41.048.032.035.0NaNFalseSIMPLE_PASSNaNNaNNaN
388178645PASSINCOMPLETEFalse16.033681NoneNoneNone4418...32.035.089.06.0NaNFalseHIGH_PASSNaNNaNNaN
488178648PASSINCOMPLETEFalse127.053006NoneNoneNone4418...85.00.093.016.0NaNFalseHAND_PASSTHROW_INNaNNaN
\n", + "

5 rows × 21 columns

\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp \\\n", + "0 88178642 PASS COMPLETE True 1 1.255990 \n", + "1 88178643 PASS COMPLETE True 1 2.351908 \n", + "2 88178644 PASS COMPLETE True 1 3.241028 \n", + "3 88178645 PASS INCOMPLETE False 1 6.033681 \n", + "4 88178648 PASS INCOMPLETE False 1 27.053006 \n", + "\n", + " end_timestamp ball_state ball_owning_team team_id ... coordinates_x \\\n", + "0 None None None 4418 ... 50.0 \n", + "1 None None None 4418 ... 47.0 \n", + "2 None None None 4418 ... 41.0 \n", + "3 None None None 4418 ... 32.0 \n", + "4 None None None 4418 ... 85.0 \n", + "\n", + " coordinates_y end_coordinates_x end_coordinates_y receiver_player_id \\\n", + "0 48.0 47.0 50.0 NaN \n", + "1 50.0 41.0 48.0 NaN \n", + "2 48.0 32.0 35.0 NaN \n", + "3 35.0 89.0 6.0 NaN \n", + "4 0.0 93.0 16.0 NaN \n", + "\n", + " is_counter_attack pass_type set_piece_type goalkeeper_action_type \\\n", + "0 False SIMPLE_PASS NaN NaN \n", + "1 False SIMPLE_PASS NaN NaN \n", + "2 False SIMPLE_PASS NaN NaN \n", + "3 False HIGH_PASS NaN NaN \n", + "4 False HAND_PASS THROW_IN NaN \n", + "\n", + " body_part_type \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import wyscout\n", + "\n", + "dataset = wyscout.load(\n", + " event_data=\"https://raw.githubusercontent.com/koenvo/wyscout-soccer-match-event-dataset/main/processed/files/1694390.json\",\n", + " \n", + " # Optional arguments\n", + " event_types=[\"shot\", \"pass\"],\n", + " coordinates=\"wyscout\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "a7cb2280", + "metadata": {}, + "source": [ + "### Load open data\n", + "\n", + "For loading Wyscout open data you can also use `wyscout.load_open_data`. The api is very simular but you don't have to pass the urls." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8308124e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_id...coordinates_xcoordinates_yend_coordinates_xend_coordinates_yreceiver_player_idis_counter_attackpass_typeset_piece_typegoalkeeper_action_typebody_part_type
088178642PASSCOMPLETETrue11.255990NoneNoneNone4418...50.048.047.050.0NaNFalseSIMPLE_PASSNaNNaNNaN
188178643PASSCOMPLETETrue12.351908NoneNoneNone4418...47.050.041.048.0NaNFalseSIMPLE_PASSNaNNaNNaN
288178644PASSCOMPLETETrue13.241028NoneNoneNone4418...41.048.032.035.0NaNFalseSIMPLE_PASSNaNNaNNaN
388178645PASSINCOMPLETEFalse16.033681NoneNoneNone4418...32.035.089.06.0NaNFalseHIGH_PASSNaNNaNNaN
488178648PASSINCOMPLETEFalse127.053006NoneNoneNone4418...85.00.093.016.0NaNFalseHAND_PASSTHROW_INNaNNaN
\n", + "

5 rows × 21 columns

\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id timestamp \\\n", + "0 88178642 PASS COMPLETE True 1 1.255990 \n", + "1 88178643 PASS COMPLETE True 1 2.351908 \n", + "2 88178644 PASS COMPLETE True 1 3.241028 \n", + "3 88178645 PASS INCOMPLETE False 1 6.033681 \n", + "4 88178648 PASS INCOMPLETE False 1 27.053006 \n", + "\n", + " end_timestamp ball_state ball_owning_team team_id ... coordinates_x \\\n", + "0 None None None 4418 ... 50.0 \n", + "1 None None None 4418 ... 47.0 \n", + "2 None None None 4418 ... 41.0 \n", + "3 None None None 4418 ... 32.0 \n", + "4 None None None 4418 ... 85.0 \n", + "\n", + " coordinates_y end_coordinates_x end_coordinates_y receiver_player_id \\\n", + "0 48.0 47.0 50.0 NaN \n", + "1 50.0 41.0 48.0 NaN \n", + "2 48.0 32.0 35.0 NaN \n", + "3 35.0 89.0 6.0 NaN \n", + "4 0.0 93.0 16.0 NaN \n", + "\n", + " is_counter_attack pass_type set_piece_type goalkeeper_action_type \\\n", + "0 False SIMPLE_PASS NaN NaN \n", + "1 False SIMPLE_PASS NaN NaN \n", + "2 False SIMPLE_PASS NaN NaN \n", + "3 False HIGH_PASS NaN NaN \n", + "4 False HAND_PASS THROW_IN NaN \n", + "\n", + " body_part_type \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + "\n", + "[5 rows x 21 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import wyscout\n", + "\n", + "dataset = wyscout.load_open_data(\n", + " match_id=1694390,\n", + " \n", + " # Optional arguments\n", + " event_types=[\"shot\", \"pass\"],\n", + " coordinates=\"wyscout\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/tracking-data/metrica.ipynb b/docs/tutorials/getting-started/tracking-data/metrica.ipynb new file mode 100644 index 00000000..fecc29a2 --- /dev/null +++ b/docs/tutorials/getting-started/tracking-data/metrica.ipynb @@ -0,0 +1,883 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "87b4ace3", + "metadata": {}, + "source": [ + "# Metrica\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local csv tracking files](#load-local-csv-tracking-files): `metrica.load_tracking_csv()`\n", + "- [Load remote csv tracking files](#load-remote-csv-tracking-files): `metrica.load_tracking_csv()`\n", + "- [Load open data](#load-open-tracking-data): `metrica.load_open_data()`\n", + "- [Load local EPTS tracking files](#load-local-epts-tracking-files): `metrica.load_tracking_epts()`\n", + "\n", + "### Load local csv tracking files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f3e2e6fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_11_xhome_11_y...away_26_daway_26_saway_27_xaway_27_yaway_27_daway_27_saway_28_xaway_28_yaway_28_daway_28_s
010.001NoneNone0.454720.61291None0.000820.51762...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
120.08145006NoneNoneNaNNaNNone0.904560.54644...NaNNaN0.197540.54636NaNNaN0.217980.18921NaNNaN
\n", + "

2 rows × 120 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 0.00 1 None None 0.45472 \n", + "1 2 0.08 145006 None None NaN \n", + "\n", + " ball_y ball_z home_11_x home_11_y ... away_26_d away_26_s away_27_x \\\n", + "0 0.61291 None 0.00082 0.51762 ... NaN NaN NaN \n", + "1 NaN None 0.90456 0.54644 ... NaN NaN 0.19754 \n", + "\n", + " away_27_y away_27_d away_27_s away_28_x away_28_y away_28_d away_28_s \n", + "0 NaN NaN NaN NaN NaN NaN NaN \n", + "1 0.54636 NaN NaN 0.21798 0.18921 NaN NaN \n", + "\n", + "[2 rows x 120 columns]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_tracking_csv(\n", + " home_data=\"../../kloppy/tests/files/metrica_home.csv\",\n", + " away_data=\"../../kloppy/tests/files/metrica_away.csv\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/5,\n", + " limit=100,\n", + " coordinates=\"metrica\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "f812c449", + "metadata": {}, + "source": [ + "### Load remote csv tracking files" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "0a582d72", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_11_xhome_11_y...away_22_daway_22_saway_23_xaway_23_yaway_23_daway_23_saway_24_xaway_24_yaway_24_daway_24_s
010.01NoneNone0.454720.61291None0.000820.51762...NoneNone0.436930.94998NoneNone0.378330.72617NoneNone
110.26NoneNone0.556770.61091None0.001400.51762...NoneNone0.435040.95116NoneNone0.375570.72327NoneNone
210.411NoneNone0.565040.69395None0.000850.51890...NoneNone0.431280.95690NoneNone0.369450.71909NoneNone
310.616NoneNone0.573300.77700None0.000650.51916...NoneNone0.427280.96250NoneNone0.363600.71470NoneNone
410.821NoneNone0.572470.79726None0.000660.51879...NoneNone0.422750.96700NoneNone0.358100.71129NoneNone
\n", + "

5 rows × 96 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 0.0 1 None None 0.45472 \n", + "1 1 0.2 6 None None 0.55677 \n", + "2 1 0.4 11 None None 0.56504 \n", + "3 1 0.6 16 None None 0.57330 \n", + "4 1 0.8 21 None None 0.57247 \n", + "\n", + " ball_y ball_z home_11_x home_11_y ... away_22_d away_22_s away_23_x \\\n", + "0 0.61291 None 0.00082 0.51762 ... None None 0.43693 \n", + "1 0.61091 None 0.00140 0.51762 ... None None 0.43504 \n", + "2 0.69395 None 0.00085 0.51890 ... None None 0.43128 \n", + "3 0.77700 None 0.00065 0.51916 ... None None 0.42728 \n", + "4 0.79726 None 0.00066 0.51879 ... None None 0.42275 \n", + "\n", + " away_23_y away_23_d away_23_s away_24_x away_24_y away_24_d away_24_s \n", + "0 0.94998 None None 0.37833 0.72617 None None \n", + "1 0.95116 None None 0.37557 0.72327 None None \n", + "2 0.95690 None None 0.36945 0.71909 None None \n", + "3 0.96250 None None 0.36360 0.71470 None None \n", + "4 0.96700 None None 0.35810 0.71129 None None \n", + "\n", + "[5 rows x 96 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_tracking_csv(\n", + " home_data=\"https://raw.githubusercontent.com/metrica-sports/sample-data/master/data/Sample_Game_1/Sample_Game_1_RawTrackingData_Home_Team.csv\",\n", + " away_data=\"https://raw.githubusercontent.com/metrica-sports/sample-data/master/data/Sample_Game_1/Sample_Game_1_RawTrackingData_Away_Team.csv\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/5,\n", + " limit=100,\n", + " coordinates=\"metrica\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "d3eb1a79", + "metadata": {}, + "source": [ + "### Load open tracking data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "90ae6cf1", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_11_xhome_11_y...away_22_daway_22_saway_23_xaway_23_yaway_23_daway_23_saway_24_xaway_24_yaway_24_daway_24_s
010.01NoneNone0.454720.61291None0.000820.51762...NoneNone0.436930.94998NoneNone0.378330.72617NoneNone
110.26NoneNone0.556770.61091None0.001400.51762...NoneNone0.435040.95116NoneNone0.375570.72327NoneNone
210.411NoneNone0.565040.69395None0.000850.51890...NoneNone0.431280.95690NoneNone0.369450.71909NoneNone
310.616NoneNone0.573300.77700None0.000650.51916...NoneNone0.427280.96250NoneNone0.363600.71470NoneNone
410.821NoneNone0.572470.79726None0.000660.51879...NoneNone0.422750.96700NoneNone0.358100.71129NoneNone
\n", + "

5 rows × 96 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 0.0 1 None None 0.45472 \n", + "1 1 0.2 6 None None 0.55677 \n", + "2 1 0.4 11 None None 0.56504 \n", + "3 1 0.6 16 None None 0.57330 \n", + "4 1 0.8 21 None None 0.57247 \n", + "\n", + " ball_y ball_z home_11_x home_11_y ... away_22_d away_22_s away_23_x \\\n", + "0 0.61291 None 0.00082 0.51762 ... None None 0.43693 \n", + "1 0.61091 None 0.00140 0.51762 ... None None 0.43504 \n", + "2 0.69395 None 0.00085 0.51890 ... None None 0.43128 \n", + "3 0.77700 None 0.00065 0.51916 ... None None 0.42728 \n", + "4 0.79726 None 0.00066 0.51879 ... None None 0.42275 \n", + "\n", + " away_23_y away_23_d away_23_s away_24_x away_24_y away_24_d away_24_s \n", + "0 0.94998 None None 0.37833 0.72617 None None \n", + "1 0.95116 None None 0.37557 0.72327 None None \n", + "2 0.95690 None None 0.36945 0.71909 None None \n", + "3 0.96250 None None 0.36360 0.71470 None None \n", + "4 0.96700 None None 0.35810 0.71129 None None \n", + "\n", + "[5 rows x 96 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_open_data(\n", + " match_id=1,\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/5,\n", + " limit=100,\n", + " coordinates=\"metrica\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "7a5debae", + "metadata": {}, + "source": [ + "### Load local EPTS tracking files" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2ccac53a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zTrack_1_xTrack_1_y...Track_21_xTrack_21_yTrack_21_dTrack_21_sTrack_21_mappingTrack_22_xTrack_22_yTrack_22_dTrack_22_sTrack_22_mapping
0118.0450NoneNone0.528670.70690None0.306020.97029...0.396310.77277None3.33860615.0NaNNaNNoneNaN21.0
1118.2455NoneNone0.545880.71626None0.308170.96742...0.402460.76937None3.38770115.0NaNNaNNoneNaN21.0
2118.4460NoneNone0.563100.72562None0.310710.96452...0.408630.76542None3.44251915.0NaNNaNNoneNaN21.0
3118.6465NoneNone0.580310.73497None0.313650.96167...0.414800.76085None3.51426815.0NaNNaNNoneNaN21.0
4118.8470NoneNone0.597530.74433None0.317050.95887...0.420980.75571None3.60102415.0NaNNaNNoneNaN21.0
\n", + "

5 rows × 118 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 18.0 450 None None 0.52867 \n", + "1 1 18.2 455 None None 0.54588 \n", + "2 1 18.4 460 None None 0.56310 \n", + "3 1 18.6 465 None None 0.58031 \n", + "4 1 18.8 470 None None 0.59753 \n", + "\n", + " ball_y ball_z Track_1_x Track_1_y ... Track_21_x Track_21_y \\\n", + "0 0.70690 None 0.30602 0.97029 ... 0.39631 0.77277 \n", + "1 0.71626 None 0.30817 0.96742 ... 0.40246 0.76937 \n", + "2 0.72562 None 0.31071 0.96452 ... 0.40863 0.76542 \n", + "3 0.73497 None 0.31365 0.96167 ... 0.41480 0.76085 \n", + "4 0.74433 None 0.31705 0.95887 ... 0.42098 0.75571 \n", + "\n", + " Track_21_d Track_21_s Track_21_mapping Track_22_x Track_22_y \\\n", + "0 None 3.338606 15.0 NaN NaN \n", + "1 None 3.387701 15.0 NaN NaN \n", + "2 None 3.442519 15.0 NaN NaN \n", + "3 None 3.514268 15.0 NaN NaN \n", + "4 None 3.601024 15.0 NaN NaN \n", + "\n", + " Track_22_d Track_22_s Track_22_mapping \n", + "0 None NaN 21.0 \n", + "1 None NaN 21.0 \n", + "2 None NaN 21.0 \n", + "3 None NaN 21.0 \n", + "4 None NaN 21.0 \n", + "\n", + "[5 rows x 118 columns]" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_tracking_epts(\n", + " meta_data=\"../../kloppy/tests/files/epts_metrica_metadata.xml\",\n", + " raw_data=\"../../kloppy/tests/files/epts_metrica_tracking.txt\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/5,\n", + " limit=100,\n", + " coordinates=\"metrica\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/tracking-data/secondspectrum.ipynb b/docs/tutorials/getting-started/tracking-data/secondspectrum.ipynb new file mode 100644 index 00000000..4c6796da --- /dev/null +++ b/docs/tutorials/getting-started/tracking-data/secondspectrum.ipynb @@ -0,0 +1,183 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "a4a4a60f", + "metadata": {}, + "source": [ + "# SecondSpectrum\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local jsonl tracking files](#load-local-files): `secondspectrum.load()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "efbb67de", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_z20grw_x20grw_y...56zeu_d56zeu_s27cl51_x27cl51_y27cl51_d27cl51_seh90mu_xeh90mu_yeh90mu_deh90mu_s
01160.004000alive45648.434473-16.6813110.046.299561-24.536171...NoneNone46.64691425.246787NoneNone5.033404-21.188707NoneNone
12681.7291600alive12323.364446-16.8560170.08.861703-33.088368...NoneNone-48.850250-16.447842NoneNone15.11290212.965995NoneNone
\n", + "

2 rows × 96 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 160.00 4000 alive 456 48.434473 \n", + "1 2 681.72 91600 alive 123 23.364446 \n", + "\n", + " ball_y ball_z 20grw_x 20grw_y ... 56zeu_d 56zeu_s 27cl51_x \\\n", + "0 -16.681311 0.0 46.299561 -24.536171 ... None None 46.646914 \n", + "1 -16.856017 0.0 8.861703 -33.088368 ... None None -48.850250 \n", + "\n", + " 27cl51_y 27cl51_d 27cl51_s eh90mu_x eh90mu_y eh90mu_d eh90mu_s \n", + "0 25.246787 None None 5.033404 -21.188707 None None \n", + "1 -16.447842 None None 15.112902 12.965995 None None \n", + "\n", + "[2 rows x 96 columns]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import secondspectrum\n", + "\n", + "dataset = secondspectrum.load(\n", + " meta_data=\"../../kloppy/tests/files/second_spectrum_fake_metadata.xml\",\n", + " raw_data=\"../../kloppy/tests/files/second_spectrum_fake_data.jsonl\",\n", + " \n", + " # Optional arguments\n", + " additional_meta_data=\"../../kloppy/tests/files/second_spectrum_fake_metadata.json\",\n", + " sample_rate=1/25,\n", + " limit=100,\n", + " coordinates=\"secondspectrum\",\n", + " only_alive=True\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/tracking-data/skillcorner.ipynb b/docs/tutorials/getting-started/tracking-data/skillcorner.ipynb new file mode 100644 index 00000000..38cf21c0 --- /dev/null +++ b/docs/tutorials/getting-started/tracking-data/skillcorner.ipynb @@ -0,0 +1,756 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "eb7a4c5f", + "metadata": {}, + "source": [ + "# SkillCorner\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local json tracking files](#load-local-files): `skillcorner.load()`\n", + "- [Load remote json tracking files](#load-remote-files): `skillcorner.load()`\n", + "- [Load open data](#load-open-data): `skillcorner.load_open_data()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "71e23535", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_22_xhome_22_y...away_anon_304_daway_anon_304_shome_anon_368_xhome_anon_368_yhome_anon_368_dhome_anon_368_shome_anon_423_xhome_anon_423_yhome_anon_423_dhome_anon_423_s
0112.01531NoneNaN18.88702930.0373501.24621314.19564123.489013...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1113.01541NoneNaN13.51470125.8270270.65704014.23046615.453939...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2114.01551NoneNaN16.83504027.4272330.05751615.32869617.867260...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3115.01561NoneNaN11.65688024.721404NaN13.64224822.210971...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4116.01571None103.07.42691830.572856NaNNaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

5 rows × 140 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 12.0 1531 None NaN 18.887029 \n", + "1 1 13.0 1541 None NaN 13.514701 \n", + "2 1 14.0 1551 None NaN 16.835040 \n", + "3 1 15.0 1561 None NaN 11.656880 \n", + "4 1 16.0 1571 None 103.0 7.426918 \n", + "\n", + " ball_y ball_z home_22_x home_22_y ... away_anon_304_d \\\n", + "0 30.037350 1.246213 14.195641 23.489013 ... NaN \n", + "1 25.827027 0.657040 14.230466 15.453939 ... NaN \n", + "2 27.427233 0.057516 15.328696 17.867260 ... NaN \n", + "3 24.721404 NaN 13.642248 22.210971 ... NaN \n", + "4 30.572856 NaN NaN NaN ... NaN \n", + "\n", + " away_anon_304_s home_anon_368_x home_anon_368_y home_anon_368_d \\\n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + " home_anon_368_s home_anon_423_x home_anon_423_y home_anon_423_d \\\n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + " home_anon_423_s \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN \n", + "\n", + "[5 rows x 140 columns]" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import skillcorner\n", + "\n", + "dataset = skillcorner.load(\n", + " meta_data=\"../../kloppy/tests/files/skillcorner_match_data.json\",\n", + " raw_data=\"../../kloppy/tests/files/skillcorner_structured_data.json\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/10,\n", + " limit=100,\n", + " coordinates=\"skillcorner\",\n", + " include_empty_frames=False\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "af4c1e42", + "metadata": {}, + "source": [ + "### Load remote files" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "bff4e3aa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_47_xhome_47_y...home_31_dhome_31_shome_anon_471_xhome_anon_471_yhome_anon_471_dhome_anon_471_shome_anon_501_xhome_anon_501_yhome_anon_501_dhome_anon_501_s
011.0452NoneNaN14.693770-0.9079365.222056e-02-4.630710-20.740084...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
112.0462NoneNaN14.634828-0.030155-3.604550e-02NaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2113.0572None40.037.410470-19.993496-7.256100e-1619.580474-19.368974...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3114.0582None40.042.215165-22.6378401.230652e-0126.785449-18.257435...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4115.0592None40.042.433060-28.680917NaN31.769383-22.166959...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

5 rows × 112 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 1.0 452 None NaN 14.693770 \n", + "1 1 2.0 462 None NaN 14.634828 \n", + "2 1 13.0 572 None 40.0 37.410470 \n", + "3 1 14.0 582 None 40.0 42.215165 \n", + "4 1 15.0 592 None 40.0 42.433060 \n", + "\n", + " ball_y ball_z home_47_x home_47_y ... home_31_d home_31_s \\\n", + "0 -0.907936 5.222056e-02 -4.630710 -20.740084 ... NaN NaN \n", + "1 -0.030155 -3.604550e-02 NaN NaN ... NaN NaN \n", + "2 -19.993496 -7.256100e-16 19.580474 -19.368974 ... NaN NaN \n", + "3 -22.637840 1.230652e-01 26.785449 -18.257435 ... NaN NaN \n", + "4 -28.680917 NaN 31.769383 -22.166959 ... NaN NaN \n", + "\n", + " home_anon_471_x home_anon_471_y home_anon_471_d home_anon_471_s \\\n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + " home_anon_501_x home_anon_501_y home_anon_501_d home_anon_501_s \n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + "[5 rows x 112 columns]" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import skillcorner\n", + "\n", + "dataset = skillcorner.load(\n", + " meta_data=\"https://raw.githubusercontent.com/SkillCorner/opendata/master/data/matches/4039/match_data.json\",\n", + " raw_data=\"https://raw.githubusercontent.com/SkillCorner/opendata/master/data/matches/4039/structured_data.json\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/10,\n", + " limit=100,\n", + " coordinates=\"skillcorner\",\n", + " include_empty_frames=False\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "id": "2f490d61", + "metadata": {}, + "source": [ + "### Load open data" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "797091f9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_47_xhome_47_y...home_31_dhome_31_shome_anon_471_xhome_anon_471_yhome_anon_471_dhome_anon_471_shome_anon_501_xhome_anon_501_yhome_anon_501_dhome_anon_501_s
011.0452NoneNaN14.693770-0.9079365.222056e-02-4.630710-20.740084...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
112.0462NoneNaN14.634828-0.030155-3.604550e-02NaNNaN...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2113.0572None40.037.410470-19.993496-7.256100e-1619.580474-19.368974...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3114.0582None40.042.215165-22.6378401.230652e-0126.785449-18.257435...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4115.0592None40.042.433060-28.680917NaN31.769383-22.166959...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

5 rows × 112 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 1.0 452 None NaN 14.693770 \n", + "1 1 2.0 462 None NaN 14.634828 \n", + "2 1 13.0 572 None 40.0 37.410470 \n", + "3 1 14.0 582 None 40.0 42.215165 \n", + "4 1 15.0 592 None 40.0 42.433060 \n", + "\n", + " ball_y ball_z home_47_x home_47_y ... home_31_d home_31_s \\\n", + "0 -0.907936 5.222056e-02 -4.630710 -20.740084 ... NaN NaN \n", + "1 -0.030155 -3.604550e-02 NaN NaN ... NaN NaN \n", + "2 -19.993496 -7.256100e-16 19.580474 -19.368974 ... NaN NaN \n", + "3 -22.637840 1.230652e-01 26.785449 -18.257435 ... NaN NaN \n", + "4 -28.680917 NaN 31.769383 -22.166959 ... NaN NaN \n", + "\n", + " home_anon_471_x home_anon_471_y home_anon_471_d home_anon_471_s \\\n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + " home_anon_501_x home_anon_501_y home_anon_501_d home_anon_501_s \n", + "0 NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN \n", + "\n", + "[5 rows x 112 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import skillcorner\n", + "\n", + "dataset = skillcorner.load_open_data(\n", + " match_id=4039,\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/10,\n", + " limit=100,\n", + " coordinates=\"skillcorner\",\n", + " include_empty_frames=False\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/tracking-data/statsperform.ipynb b/docs/tutorials/getting-started/tracking-data/statsperform.ipynb new file mode 100644 index 00000000..911802bf --- /dev/null +++ b/docs/tutorials/getting-started/tracking-data/statsperform.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "a93fc613", + "metadata": {}, + "source": [ + "# StatsPerform\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local txt tracking files](#load-local-files): `statsperform.load()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e803a78c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_za2s2c6anax9wnlsw1s6vunl5h_xa2s2c6anax9wnlsw1s6vunl5h_y...6wfwy94p5bm0zv3aku0urfq39_d6wfwy94p5bm0zv3aku0urfq39_s6ekdnbnk56xlxforb5owt3dn9_x6ekdnbnk56xlxforb5owt3dn9_y6ekdnbnk56xlxforb5owt3dn9_d6ekdnbnk56xlxforb5owt3dn9_sct32113pfx5q9avf2c0x208ru_xct32113pfx5q9avf2c0x208ru_yct32113pfx5q9avf2c0x208ru_dct32113pfx5q9avf2c0x208ru_s
010.01598184000000aliveNone52.35033.2500.052.80323.617...NoneNone5.26833.556NoneNoneNaNNaNNoneNone
110.11598184000100aliveNone50.61535.3250.052.55823.752...NoneNone5.26533.529NoneNoneNaNNaNNoneNone
210.21598184000200aliveNone49.63036.1400.052.31023.901...NoneNone5.26433.502NoneNoneNaNNaNNoneNone
310.31598184000300aliveNone48.72536.6250.052.05924.071...NoneNone5.26833.476NoneNoneNaNNaNNoneNone
410.41598184000400aliveNone47.89037.1300.051.80424.260...NoneNone5.27733.452NoneNoneNaNNaNNoneNone
\n", + "

5 rows × 100 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \n", + "0 1 0.0 1598184000000 alive None 52.350 \\\n", + "1 1 0.1 1598184000100 alive None 50.615 \n", + "2 1 0.2 1598184000200 alive None 49.630 \n", + "3 1 0.3 1598184000300 alive None 48.725 \n", + "4 1 0.4 1598184000400 alive None 47.890 \n", + "\n", + " ball_y ball_z a2s2c6anax9wnlsw1s6vunl5h_x a2s2c6anax9wnlsw1s6vunl5h_y \n", + "0 33.250 0.0 52.803 23.617 \\\n", + "1 35.325 0.0 52.558 23.752 \n", + "2 36.140 0.0 52.310 23.901 \n", + "3 36.625 0.0 52.059 24.071 \n", + "4 37.130 0.0 51.804 24.260 \n", + "\n", + " ... 6wfwy94p5bm0zv3aku0urfq39_d 6wfwy94p5bm0zv3aku0urfq39_s \n", + "0 ... None None \\\n", + "1 ... None None \n", + "2 ... None None \n", + "3 ... None None \n", + "4 ... None None \n", + "\n", + " 6ekdnbnk56xlxforb5owt3dn9_x 6ekdnbnk56xlxforb5owt3dn9_y \n", + "0 5.268 33.556 \\\n", + "1 5.265 33.529 \n", + "2 5.264 33.502 \n", + "3 5.268 33.476 \n", + "4 5.277 33.452 \n", + "\n", + " 6ekdnbnk56xlxforb5owt3dn9_d 6ekdnbnk56xlxforb5owt3dn9_s \n", + "0 None None \\\n", + "1 None None \n", + "2 None None \n", + "3 None None \n", + "4 None None \n", + "\n", + " ct32113pfx5q9avf2c0x208ru_x ct32113pfx5q9avf2c0x208ru_y \n", + "0 NaN NaN \\\n", + "1 NaN NaN \n", + "2 NaN NaN \n", + "3 NaN NaN \n", + "4 NaN NaN \n", + "\n", + " ct32113pfx5q9avf2c0x208ru_d ct32113pfx5q9avf2c0x208ru_s \n", + "0 None None \n", + "1 None None \n", + "2 None None \n", + "3 None None \n", + "4 None None \n", + "\n", + "[5 rows x 100 columns]" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsperform\n", + "\n", + "dataset = statsperform.load(\n", + " raw_data=\"../../kloppy/tests/files/statsperform_ma25_tracking.txt\",\n", + " meta_data=\"../../kloppy/tests/files/statsperform_ma1_metadata.xml\",\n", + " \n", + " # Optional arguments\n", + " coordinates=\"statsperform\"\n", + ")\n", + "\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/getting-started/tracking-data/tracab.ipynb b/docs/tutorials/getting-started/tracking-data/tracab.ipynb new file mode 100644 index 00000000..e927f2ba --- /dev/null +++ b/docs/tutorials/getting-started/tracking-data/tracab.ipynb @@ -0,0 +1,208 @@ +{ + "cells": [ + { + "attachments": {}, + "cell_type": "markdown", + "id": "d9cd4667", + "metadata": {}, + "source": [ + "# TRACAB\n", + "\n", + "## Available functionality:\n", + "\n", + "- [Load local .dat tracking files](#load-local-files): `tracab.load()`\n", + "\n", + "### Load local files" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "495a3f90", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zaway_19_xaway_19_yaway_19_daway_19_shome_19_xhome_19_yhome_19_dhome_19_saway_1337_xaway_1337_yaway_1337_daway_1337_s
010.00100alivehome-27.025.00.08889.0-666.0NaN0.55-1234.0-294.0None0.07NaNNaNNaNNaN
120.00200alivehome-27.025.00.0NaNNaNNaNNaN-1234.0-294.0None0.07-8889.0-666.0NaN0.55
220.08202alivehome-27.025.00.0NaNNaNNaNNaN-1234.0-294.0None0.07-8889.0-666.0NaN0.55
\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 0.00 100 alive home -27.0 \n", + "1 2 0.00 200 alive home -27.0 \n", + "2 2 0.08 202 alive home -27.0 \n", + "\n", + " ball_y ball_z away_19_x away_19_y away_19_d away_19_s home_19_x \\\n", + "0 25.0 0.0 8889.0 -666.0 NaN 0.55 -1234.0 \n", + "1 25.0 0.0 NaN NaN NaN NaN -1234.0 \n", + "2 25.0 0.0 NaN NaN NaN NaN -1234.0 \n", + "\n", + " home_19_y home_19_d home_19_s away_1337_x away_1337_y away_1337_d \\\n", + "0 -294.0 None 0.07 NaN NaN NaN \n", + "1 -294.0 None 0.07 -8889.0 -666.0 NaN \n", + "2 -294.0 None 0.07 -8889.0 -666.0 NaN \n", + "\n", + " away_1337_s \n", + "0 NaN \n", + "1 0.55 \n", + "2 0.55 " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import tracab\n", + "\n", + "dataset = tracab.load(\n", + " meta_data=\"../../kloppy/tests/files/tracab_meta.xml\",\n", + " raw_data=\"../../kloppy/tests/files/tracab_raw.dat\",\n", + " \n", + " # Optional arguments\n", + " sample_rate=1/2,\n", + " limit=10,\n", + " coordinates=\"tracab\",\n", + " only_alive=True\n", + ")\n", + "\n", + "# Because this is a test dataset the output contains some garbage\n", + "dataset.to_df().head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index 0f36f080..5a44d7bd 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -15,18 +15,22 @@ theme: nav: - Home: index.md - Open-data: open-data.md - - Getting-started: - - Datafactory: getting-started/datafactory.ipynb - - Metrica: getting-started/metrica.ipynb - - Opta: getting-started/opta.ipynb - - SecondSpectrum: getting-started/secondspectrum.ipynb - - SkillCorner: getting-started/skillcorner.ipynb - - Sportec: getting-started/sportec.ipynb - - Sportscode: getting-started/sportscode.ipynb - - Statsbomb: getting-started/statsbomb.ipynb - - StatsPerform: getting-started/statsperform.ipynb - - TRACAB: getting-started/tracab.ipynb - - Wyscout: getting-started/wyscout.ipynb + - Tutorials: + - Getting-started: + - Event-data: + - Datafactory: tutorials/getting-started/event-data/datafactory.ipynb + - Metrica: tutorials/getting-started/event-data/metrica.ipynb + - Opta: tutorials/getting-started/event-data/opta.ipynb + - Sportec: tutorials/getting-started/event-data/sportec.ipynb + - Sportscode: tutorials/getting-started/event-data/sportscode.ipynb + - Statsbomb: tutorials/getting-started/event-data/statsbomb.ipynb + - Wyscout: tutorials/getting-started/event-data/wyscout.ipynb + - Tracking-data: + - Metrica: tutorials/getting-started/tracking-data/metrica.ipynb + - SecondSpectrum: tutorials/getting-started/tracking-data/secondspectrum.ipynb + - SkillCorner: tutorials/getting-started/tracking-data/skillcorner.ipynb + - StatsPerform: tutorials/getting-started/tracking-data/statsperform.ipynb + - TRACAB: tutorials/getting-started/tracking-data/tracab.ipynb - Examples: - Event Data: examples/event_data.ipynb - Tracking Data: examples/tracking_data.ipynb @@ -43,7 +47,12 @@ nav: # - Pitch: api/domain/pitch.md # - Tracking: api/domain/tracking.md # - Event: api/domain/event.md - - Providers: 'providers.md' + - Functionality: + - Providers: 'functionality/providers.md' + - Event: 'functionality/event-data.md' + - Tracking: 'functionality/tracking-data.md' + - Coordinate-Systems: 'functionality/coordinate-systems.md' + # - Dataset-Types: 'functionality/coordinate-systems.md' - Other: - Issues: 'issues.md' - Contributing: 'contributing.md' From 917294350774a30a403af3d0fddf12482cf930ef Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Sun, 9 Jul 2023 14:41:32 +0200 Subject: [PATCH 02/12] docs update --- docs/functionality/event-data.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/functionality/event-data.md b/docs/functionality/event-data.md index 34def6a5..ff8db519 100644 --- a/docs/functionality/event-data.md +++ b/docs/functionality/event-data.md @@ -102,8 +102,6 @@ Please consult the `EventFactory` and `EventDataset` documentation for more deta ### Sportscode ---- - ### load `kloppy.kloppy._providers.sportscode.load(data)` From 462b92ff4ea36fe295d05f4d42f4eef6ef49448e Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Sun, 9 Jul 2023 14:56:23 +0200 Subject: [PATCH 03/12] Examples under Tutorials segment --- docs/tutorials/examples/adapter.ipynb | 210 + .../examples/broadcast_tracking_data.ipynb | 432 +++ docs/tutorials/examples/code_data.ipynb | 1628 ++++++++ docs/tutorials/examples/config.ipynb | 211 ++ docs/tutorials/examples/event_data.ipynb | 1903 ++++++++++ docs/tutorials/examples/navigating.ipynb | 525 +++ docs/tutorials/examples/plotting.ipynb | 165 + docs/tutorials/examples/state.ipynb | 1427 +++++++ docs/tutorials/examples/tracking_data.ipynb | 3373 +++++++++++++++++ mkdocs.yml | 20 +- 10 files changed, 9884 insertions(+), 10 deletions(-) create mode 100644 docs/tutorials/examples/adapter.ipynb create mode 100644 docs/tutorials/examples/broadcast_tracking_data.ipynb create mode 100644 docs/tutorials/examples/code_data.ipynb create mode 100644 docs/tutorials/examples/config.ipynb create mode 100644 docs/tutorials/examples/event_data.ipynb create mode 100644 docs/tutorials/examples/navigating.ipynb create mode 100644 docs/tutorials/examples/plotting.ipynb create mode 100644 docs/tutorials/examples/state.ipynb create mode 100644 docs/tutorials/examples/tracking_data.ipynb diff --git a/docs/tutorials/examples/adapter.ipynb b/docs/tutorials/examples/adapter.ipynb new file mode 100644 index 00000000..46e18c57 --- /dev/null +++ b/docs/tutorials/examples/adapter.ipynb @@ -0,0 +1,210 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "8af5cd57", + "metadata": {}, + "source": [ + "# Adapter\n", + "\n", + "Since 3.3 kloppy uses adapters to load data from external sources. kloppy is shipped with support for `http` and `s3`, but you can add your own adapters to support different external sources.\n", + "\n", + "\n", + "## S3\n", + "\n", + "kloppy uses `s3fs` to access files on s3. If preferred you can create a `s3fs.S3FileSystem` instance and pass it via `set_config`\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "828dec44", + "metadata": {}, + "outputs": [], + "source": [ + "import s3fs\n", + "\n", + "from kloppy import statsbomb\n", + "from kloppy.config import set_config\n", + "\n", + "file_system = s3fs.S3FileSystem(anon=True)\n", + "set_config(\n", + " 'adapters.s3.s3fs',\n", + " file_system\n", + ")\n", + "\n", + "# This will fail because we don't have access to 'some-bucket'\n", + "dataset = statsbomb.load(\n", + " event_data='s3://some-bucket/1234/events.json',\n", + " lineup_data='s3://some-bucket/1234/lineup.json'\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c8ddbc3c", + "metadata": {}, + "source": [ + "## Custom adapter - database\n", + "\n", + "It's possible to create your own adapter. For example a database adapter.\n", + "\n", + "First create a table within a sqlite database to hold the file content." + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "4f8c855d", + "metadata": {}, + "outputs": [], + "source": [ + "import sqlite3\n", + "\n", + "# Setup a table\n", + "con = sqlite3.connect('database.db')\n", + "\n", + "try:\n", + " con.execute(\"CREATE TABLE files(match_id INT, file_type TEXT, data TEXT, UNIQUE(match_id, file_type))\")\n", + " con.commit()\n", + "except sqlite3.OperationalError:\n", + " # already exists\n", + " pass\n", + "\n", + "# Load some open data into the database\n", + "import requests\n", + "\n", + "def load_into_table(match_id):\n", + " events_data = requests.get(f\"https://raw.githubusercontent.com/statsbomb/open-data/master/data/events/{match_id}.json\")\n", + " con.execute(\"INSERT INTO files values(?, ?, ?)\", (match_id, 'events', events_data.content))\n", + " lineup_data = requests.get(f\"https://raw.githubusercontent.com/statsbomb/open-data/master/data/lineups/{match_id}.json\")\n", + " con.execute(\"INSERT INTO files values(?, ?, ?)\", (match_id, 'lineup', lineup_data.content))\n", + " con.commit()\n", + "\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "f6a44223", + "metadata": {}, + "source": [ + "Next load the data from two matches into the database" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "f0cb4ec8", + "metadata": {}, + "outputs": [], + "source": [ + "load_into_table(9636)\n", + "load_into_table(9609)" + ] + }, + { + "cell_type": "markdown", + "id": "03820ca5", + "metadata": {}, + "source": [ + "Define our database adapter. The adapter supports all urls starting with `db://`" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "id": "f9642105", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import BinaryIO\n", + "\n", + "from kloppy.infra.io.adapters import Adapter, adapters\n", + "\n", + "class DBAdapter(Adapter):\n", + " def __init__(self, con):\n", + " self.con = con\n", + " \n", + " def supports(self, url: str):\n", + " return url.startswith(\"db://\")\n", + " \n", + " def read_to_stream(self, url: str, output: BinaryIO):\n", + " match_id, file_type = url[5:].split(\"/\")\n", + " cursor = con.cursor()\n", + " cursor.execute(\"SELECT data FROM files WHERE match_id = ? AND file_type = ?\", (match_id, file_type))\n", + " result = cursor.fetchone()\n", + " output.write(result[0])\n", + "\n", + "db_adapter = DBAdapter(con)\n", + "\n", + "# When you rerun this code you might need to run: adapters.clear()\n", + "adapters.append(db_adapter)\n", + "\n", + "set_config('cache', None)\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "fdeffe42", + "metadata": {}, + "source": [ + "Use the adapter!" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "id": "3694c553", + "metadata": {}, + "outputs": [], + "source": [ + "dataset = statsbomb.load(\n", + " event_data=\"db://9636/events\",\n", + " lineup_data=\"db://9636/lineup\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "id": "cb4a8a3b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded from database: Barcelona - Las Palmas\n" + ] + } + ], + "source": [ + "home_team, away_team = dataset.metadata.teams\n", + "print(f\"Loaded from database: {home_team} - {away_team}\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.9" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/examples/broadcast_tracking_data.ipynb b/docs/tutorials/examples/broadcast_tracking_data.ipynb new file mode 100644 index 00000000..09aaaa55 --- /dev/null +++ b/docs/tutorials/examples/broadcast_tracking_data.ipynb @@ -0,0 +1,432 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Broadcast Tracking Data\n", + "\n", + "Unlike tracking data from permanently installed camera systems in a stadium, broadcast tracking captures player locations directly from video feeds. \n", + "As these broadcast feeds usually show only part of the pitch, this tracking data contains only part of the players information for most frames and may be missing data for some frames entirely due to playbacks or close-up camera views. \n", + "For more information about this data please go to https://github.com/SkillCorner/opendata.\n", + "\n", + "A note from Skillcorner: \n", + "\"if you use the data, we kindly ask that you credit SkillCorner and hope you'll notify us on Twitter so we can follow the great work being done with this data.\"\n", + "\n", + "Available Matches in the Skillcorner Opendata Repository: \n", + "\n", + " \"ID: 4039 - Manchester City vs Liverpool on 2020-07-02\" \n", + " \"ID: 3749 - Dortmund vs Bayern Munchen on 2020-05-26\" \n", + " \"ID: 3518 - Juventus vs Inter on 2020-03-08\" \n", + " \"ID: 3442 - Real Madrid vs FC Barcelona on 2020-03-01\" \n", + " \"ID: 2841 - FC Barcelona vs Real Madrid on 2019-12-18\" \n", + " \"ID: 2440 - Liverpool vs Manchester City on 2019-11-10\" \n", + " \"ID: 2417 - Bayern Munchen vs Dortmund on 2019-11-09\" \n", + " \"ID: 2269 - Paris vs Marseille on 2019-10-27\" \n", + " \"ID: 2068 - Inter vs Juventus on 2019-10-06\"\n", + "\n", + "Metadata is available for this data.\n", + "\n", + "## Loading Skillcorner data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from kloppy import skillcorner\n", + "\n", + "# there is one example match for testing purposes in kloppy that we use here\n", + "# for other matches change the filenames to the location of your downloaded skillcorner opendata files\n", + "matchdata_file = '../../kloppy/tests/files/skillcorner_match_data.json'\n", + "tracking_file = '../../kloppy/tests/files/skillcorner_structured_data.json'\n", + "\n", + "dataset = skillcorner.load(meta_data=matchdata_file,\n", + " raw_data=tracking_file,\n", + " limit=100)\n", + "\n", + "df = dataset.to_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exploring the data\n", + "\n", + "When you want to show the name of a player you are advised to use `str(player)`. This will call the magic `__str__` method that handles fallbacks for missing data. By default it will return `full_name`, and fallback to 1) `first_name last_name` 2) `player_id`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Thiago Alcantara (6)',\n", + " 'Lukas Mai (33)',\n", + " 'David Alaba (27)',\n", + " 'Manuel Neuer (1)',\n", + " 'Mickael Cuisance (11)',\n", + " 'Corentin Tolisso (24)',\n", + " 'Thomas Müller (25)',\n", + " 'Joshua Kimmich (32)',\n", + " 'Sven Ulreich (26)',\n", + " 'Robert Lewandowski (9)',\n", + " 'Kingsley Coman (29)',\n", + " 'Leon Goretzka (18)',\n", + " 'Alphonso Davies (19)',\n", + " 'Javier Martinez (8)',\n", + " 'Benjamin Pavard (5)',\n", + " 'Philippe Coutinho (10)',\n", + " 'Ivan Perisic (14)',\n", + " 'Serge Gnabry (22)']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metadata = dataset.metadata\n", + "home_team, away_team = metadata.teams\n", + "\n", + "[f\"{player} ({player.jersey_no})\" for player in home_team.players]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "home - FC Bayern Munchen\n", + "away - Borussia Dortmund\n" + ] + } + ], + "source": [ + "print(f\"{home_team.ground} - {home_team}\")\n", + "print(f\"{away_team.ground} - {away_team}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Working with tracking data\n", + "\n", + "The actual tracking data is available at `dataset.frames`. This list holds all frames. Each frame has a `players_coordinates` dictionary that is indexed by `Player` entities and has values of the `Point` type.\n", + "\n", + "Identities of players are not always specified. In that case only the team affiliation is known and a track_id that is part of the player_id is used to identify the same (unknown) player across multiple frames." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of players in the frame: 18\n", + "List home team players coordinates\n", + "[('home_anon_75', Point(x=0.651772292307619, y=0.058380954035294086)),\n", + " ('home_22', Point(x=0.5811316475035238, y=0.010450905444117642)),\n", + " ('home_5', Point(x=0.6586107308438095, y=0.028452003889705924)),\n", + " ('home_29', Point(x=0.5017706101333714, y=0.6315331512472059)),\n", + " ('home_18', Point(x=0.5846745990571428, y=0.09758438650000001)),\n", + " ('home_25', Point(x=0.5817775484115238, y=0.35925220307852945)),\n", + " ('home_8', Point(x=0.7467174002371428, y=0.13082327161470597)),\n", + " ('home_27', Point(x=0.7323288673171429, y=0.3728190142901471)),\n", + " ('home_19', Point(x=0.7071614695780952, y=0.507377931049897)),\n", + " ('home_32', Point(x=0.635410294564762, y=0.11935376853823532))]\n" + ] + } + ], + "source": [ + "first_frame = dataset.frames[88]\n", + "print(f\"Number of players in the frame: {len(first_frame.players_coordinates)}\")\n", + "\n", + "from pprint import pprint\n", + "print(\"List home team players coordinates\")\n", + "pprint([\n", + " (player.player_id, player_coordinates) \n", + " for player, player_coordinates\n", + " in first_frame.players_coordinates.items()\n", + " if player.team == home_team\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zaway_23_xaway_23_y...away_14_daway_14_shome_9_xhome_9_yhome_9_dhome_9_shome_anon_75_xhome_anon_75_yhome_anon_75_dhome_anon_75_s
0111.21523NoneNaNNaNNaNNaN0.7474890.098509...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
1111.31524NoneNaN0.791347-0.0200332.2437120.7453230.099367...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
2111.41525NoneNaN0.772630-0.0094692.5347990.7429560.099743...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
3111.51526NoneNaN0.7546250.0016122.6598130.7403860.099638...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
4111.61527NoneNaN0.7373300.0132102.6187550.7378750.096646...NaNNaNNaNNaNNaNNaNNaNNaNNaNNaN
\n", + "

5 rows × 92 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \\\n", + "0 1 11.2 1523 None NaN NaN \n", + "1 1 11.3 1524 None NaN 0.791347 \n", + "2 1 11.4 1525 None NaN 0.772630 \n", + "3 1 11.5 1526 None NaN 0.754625 \n", + "4 1 11.6 1527 None NaN 0.737330 \n", + "\n", + " ball_y ball_z away_23_x away_23_y ... away_14_d away_14_s \\\n", + "0 NaN NaN 0.747489 0.098509 ... NaN NaN \n", + "1 -0.020033 2.243712 0.745323 0.099367 ... NaN NaN \n", + "2 -0.009469 2.534799 0.742956 0.099743 ... NaN NaN \n", + "3 0.001612 2.659813 0.740386 0.099638 ... NaN NaN \n", + "4 0.013210 2.618755 0.737875 0.096646 ... NaN NaN \n", + "\n", + " home_9_x home_9_y home_9_d home_9_s home_anon_75_x home_anon_75_y \\\n", + "0 NaN NaN NaN NaN NaN NaN \n", + "1 NaN NaN NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN NaN NaN \n", + "4 NaN NaN NaN NaN NaN NaN \n", + "\n", + " home_anon_75_d home_anon_75_s \n", + "0 NaN NaN \n", + "1 NaN NaN \n", + "2 NaN NaN \n", + "3 NaN NaN \n", + "4 NaN NaN \n", + "\n", + "[5 rows x 92 columns]" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/examples/code_data.ipynb b/docs/tutorials/examples/code_data.ipynb new file mode 100644 index 00000000..a0b42f9c --- /dev/null +++ b/docs/tutorials/examples/code_data.ipynb @@ -0,0 +1,1628 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Code data\n", + "\n", + "Apart from event- and tracking data analytics often use code data. This type of data can be collected by hand using tools like SportsCode. \n", + "\n", + "Kloppy allows easy read AND write functionallity for the codes in SportsCode XML format. " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Reading XML file" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from kloppy import sportscode" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"file.xml\", \"w\") as fp:\n", + " fp.write(\"\"\"\n", + "\n", + " \n", + " \n", + " P1\n", + " 3.6\n", + " 9.7\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " P2\n", + " 68.3\n", + " 74.5\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " P3\n", + " 103.6\n", + " 109.6\n", + " SHOT\n", + " \n", + " \n", + " \n", + " \n", + "\"\"\")\n", + " \n", + "code_dataset = sportscode.load(\"file.xml\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_idperiod_idtimestampend_timestampcodeTeamPacking.ValueReceiverExpected.Goal.Value
0P113.69.7PASSHenkie1.0Klaas NømeNaN
1P2168.374.5PASSHenkie3.0PietNaN
2P31103.6109.6SHOTHenkieNaNNone0.13
\n", + "
" + ], + "text/plain": [ + " code_id period_id timestamp end_timestamp code Team Packing.Value \n", + "0 P1 1 3.6 9.7 PASS Henkie 1.0 \\\n", + "1 P2 1 68.3 74.5 PASS Henkie 3.0 \n", + "2 P3 1 103.6 109.6 SHOT Henkie NaN \n", + "\n", + " Receiver Expected.Goal.Value \n", + "0 Klaas Nøme NaN \n", + "1 Piet NaN \n", + "2 None 0.13 " + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "code_dataset.to_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The code dataset also allows filtering the codes" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_idperiod_idtimestampend_timestampcodeTeamPacking.ValueReceiver
0P113.69.7PASSHenkie1Klaas Nøme
1P2168.374.5PASSHenkie3Piet
\n", + "
" + ], + "text/plain": [ + " code_id period_id timestamp end_timestamp code Team Packing.Value \n", + "0 P1 1 3.6 9.7 PASS Henkie 1 \\\n", + "1 P2 1 68.3 74.5 PASS Henkie 3 \n", + "\n", + " Receiver \n", + "0 Klaas Nøme \n", + "1 Piet " + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "passes = code_dataset.filter(lambda code: code.code == 'PASS')\n", + "passes.to_df()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Writing XML file" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + " \n", + " \n", + " P1\n", + " 3.6\n", + " 9.7\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " P2\n", + " 68.3\n", + " 74.5\n", + " PASS\n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n" + ] + } + ], + "source": [ + "sportscode.save(passes, \"file.xml\")\n", + "\n", + "with open(\"file.xml\", \"r\") as fp:\n", + " print(fp.read())" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Converting event dataset into XML dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.10.2-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load_open_data()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "from kloppy.domain import Code, CodeDataset, EventType\n", + "\n", + "dataset_shots = dataset.filter(\n", + " lambda event: event.event_type == EventType.SHOT\n", + ")\n", + "\n", + "code_dataset = (\n", + " CodeDataset\n", + " .from_dataset(\n", + " dataset_shots,\n", + " lambda event: Code(\n", + " code_id=None, # make it auto increment on write\n", + " code=event.event_name,\n", + " period=event.period,\n", + " timestamp=max(0, event.timestamp - 7),\n", + " end_timestamp=event.timestamp + 5,\n", + " labels={\n", + " 'Player': str(event.player),\n", + " 'Team': str(event.team)\n", + " },\n", + " \n", + " # In the future next two won't be needed anymore\n", + " ball_owning_team=None,\n", + " ball_state=None\n", + " )\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_idperiod_idtimestampend_timestampcodePlayerTeam
0None1142.094154.094shotLionel Andrés Messi CuccittiniBarcelona
1None1332.239344.239shotJordi Alba RamosBarcelona
2None1921.625933.625shotLionel Andrés Messi CuccittiniBarcelona
3None1972.616984.616shotRubén Sobrino PozueloDeportivo Alavés
4None11088.9141100.914shotLuis Alberto Suárez DíazBarcelona
5None11835.2871847.287shotOusmane DembéléBarcelona
6None12097.8612109.861shotIvan RakitićBarcelona
7None12241.1682253.168shotLionel Andrés Messi CuccittiniBarcelona
8None12243.9892255.989shotGerard Piqué BernabéuBarcelona
9None12301.0832313.083shotOusmane DembéléBarcelona
10None12427.5922439.592shotLuis Alberto Suárez DíazBarcelona
11None12603.6122615.612shotOusmane DembéléBarcelona
12None2152.524164.524shotJordi Alba RamosBarcelona
13None2360.400372.400shotMubarak WakasoDeportivo Alavés
14None2527.355539.355shotLuis Alberto Suárez DíazBarcelona
15None2589.388601.388shotPhilippe Coutinho CorreiaBarcelona
16None2627.490639.490shotJordi Alba RamosBarcelona
17None2733.847745.847shotLionel Andrés Messi CuccittiniBarcelona
18None2929.156941.156shotPhilippe Coutinho CorreiaBarcelona
19None21084.9541096.954shotLionel Andrés Messi CuccittiniBarcelona
20None21236.5881248.588shotLionel Andrés Messi CuccittiniBarcelona
21None21406.3751418.375shotIvan RakitićBarcelona
22None21640.4921652.492shotLuis Alberto Suárez DíazBarcelona
23None21990.6792002.679shotAdrián Marín GómezDeportivo Alavés
24None22184.6062196.606shotPhilippe Coutinho CorreiaBarcelona
25None22254.5782266.578shotPhilippe Coutinho CorreiaBarcelona
26None22655.6382667.638shotLionel Andrés Messi CuccittiniBarcelona
27None22795.7702807.770shotLionel Andrés Messi CuccittiniBarcelona
\n", + "
" + ], + "text/plain": [ + " code_id period_id timestamp end_timestamp code \n", + "0 None 1 142.094 154.094 shot \\\n", + "1 None 1 332.239 344.239 shot \n", + "2 None 1 921.625 933.625 shot \n", + "3 None 1 972.616 984.616 shot \n", + "4 None 1 1088.914 1100.914 shot \n", + "5 None 1 1835.287 1847.287 shot \n", + "6 None 1 2097.861 2109.861 shot \n", + "7 None 1 2241.168 2253.168 shot \n", + "8 None 1 2243.989 2255.989 shot \n", + "9 None 1 2301.083 2313.083 shot \n", + "10 None 1 2427.592 2439.592 shot \n", + "11 None 1 2603.612 2615.612 shot \n", + "12 None 2 152.524 164.524 shot \n", + "13 None 2 360.400 372.400 shot \n", + "14 None 2 527.355 539.355 shot \n", + "15 None 2 589.388 601.388 shot \n", + "16 None 2 627.490 639.490 shot \n", + "17 None 2 733.847 745.847 shot \n", + "18 None 2 929.156 941.156 shot \n", + "19 None 2 1084.954 1096.954 shot \n", + "20 None 2 1236.588 1248.588 shot \n", + "21 None 2 1406.375 1418.375 shot \n", + "22 None 2 1640.492 1652.492 shot \n", + "23 None 2 1990.679 2002.679 shot \n", + "24 None 2 2184.606 2196.606 shot \n", + "25 None 2 2254.578 2266.578 shot \n", + "26 None 2 2655.638 2667.638 shot \n", + "27 None 2 2795.770 2807.770 shot \n", + "\n", + " Player Team \n", + "0 Lionel Andrés Messi Cuccittini Barcelona \n", + "1 Jordi Alba Ramos Barcelona \n", + "2 Lionel Andrés Messi Cuccittini Barcelona \n", + "3 Rubén Sobrino Pozuelo Deportivo Alavés \n", + "4 Luis Alberto Suárez Díaz Barcelona \n", + "5 Ousmane Dembélé Barcelona \n", + "6 Ivan Rakitić Barcelona \n", + "7 Lionel Andrés Messi Cuccittini Barcelona \n", + "8 Gerard Piqué Bernabéu Barcelona \n", + "9 Ousmane Dembélé Barcelona \n", + "10 Luis Alberto Suárez Díaz Barcelona \n", + "11 Ousmane Dembélé Barcelona \n", + "12 Jordi Alba Ramos Barcelona \n", + "13 Mubarak Wakaso Deportivo Alavés \n", + "14 Luis Alberto Suárez Díaz Barcelona \n", + "15 Philippe Coutinho Correia Barcelona \n", + "16 Jordi Alba Ramos Barcelona \n", + "17 Lionel Andrés Messi Cuccittini Barcelona \n", + "18 Philippe Coutinho Correia Barcelona \n", + "19 Lionel Andrés Messi Cuccittini Barcelona \n", + "20 Lionel Andrés Messi Cuccittini Barcelona \n", + "21 Ivan Rakitić Barcelona \n", + "22 Luis Alberto Suárez Díaz Barcelona \n", + "23 Adrián Marín Gómez Deportivo Alavés \n", + "24 Philippe Coutinho Correia Barcelona \n", + "25 Philippe Coutinho Correia Barcelona \n", + "26 Lionel Andrés Messi Cuccittini Barcelona \n", + "27 Lionel Andrés Messi Cuccittini Barcelona " + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "code_dataset.to_df()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + " \n", + " \n", + " 1\n", + " 142.094\n", + " 154.094\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 2\n", + " 332.239\n", + " 344.239\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 3\n", + " 921.625\n", + " 933.625\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 4\n", + " 972.616\n", + " 984.616\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 5\n", + " 1088.914\n", + " 1100.914\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 6\n", + " 1835.287\n", + " 1847.287\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 7\n", + " 2097.861\n", + " 2109.861\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 8\n", + " 2241.168\n", + " 2253.168\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 9\n", + " 2243.989\n", + " 2255.989\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 10\n", + " 2301.083\n", + " 2313.083\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 11\n", + " 2427.592\n", + " 2439.592\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 12\n", + " 2603.612\n", + " 2615.612\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 13\n", + " 2857.7909999999997\n", + " 2869.7909999999997\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 14\n", + " 3065.667\n", + " 3077.667\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 15\n", + " 3232.622\n", + " 3244.622\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 16\n", + " 3294.6549999999997\n", + " 3306.6549999999997\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 17\n", + " 3332.7569999999996\n", + " 3344.7569999999996\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 18\n", + " 3439.1139999999996\n", + " 3451.1139999999996\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 19\n", + " 3634.423\n", + " 3646.423\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 20\n", + " 3790.2209999999995\n", + " 3802.2209999999995\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 21\n", + " 3941.8549999999996\n", + " 3953.8549999999996\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 22\n", + " 4111.642\n", + " 4123.642\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 23\n", + " 4345.759\n", + " 4357.759\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 24\n", + " 4695.946\n", + " 4707.946\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 25\n", + " 4889.873\n", + " 4901.873\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 26\n", + " 4959.844999999999\n", + " 4971.844999999999\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 27\n", + " 5360.905\n", + " 5372.905\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + " 28\n", + " 5501.037\n", + " 5513.037\n", + " shot\n", + " \n", + " \n", + " \n", + " \n", + "\n", + "\n" + ] + } + ], + "source": [ + "sportscode.save(code_dataset, \"file.xml\")\n", + "\n", + "with open(\"file.xml\", \"r\") as fp:\n", + " print(fp.read())" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "import os\n", + "os.unlink(\"file.xml\")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
code_idperiod_idtimestampend_timestampcodePlayerTeam
0None1142.094154.094Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
1None1332.239344.239Schot BarcelonaJordi Alba RamosBarcelona
2None1921.625933.625Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
3None11088.9141100.914Schot BarcelonaLuis Alberto Suárez DíazBarcelona
4None11835.2871847.287Schot BarcelonaOusmane DembéléBarcelona
5None12097.8612109.861Schot BarcelonaIvan RakitićBarcelona
6None12241.1682253.168Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
7None12243.9892255.989Schot BarcelonaGerard Piqué BernabéuBarcelona
8None12301.0832313.083Schot BarcelonaOusmane DembéléBarcelona
9None12427.5922439.592Schot BarcelonaLuis Alberto Suárez DíazBarcelona
10None12603.6122615.612Schot BarcelonaOusmane DembéléBarcelona
11None2152.524164.524Schot BarcelonaJordi Alba RamosBarcelona
12None2527.355539.355Schot BarcelonaLuis Alberto Suárez DíazBarcelona
13None2589.388601.388Schot BarcelonaPhilippe Coutinho CorreiaBarcelona
14None2627.490639.490Schot BarcelonaJordi Alba RamosBarcelona
15None2733.847745.847Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
16None2929.156941.156Schot BarcelonaPhilippe Coutinho CorreiaBarcelona
17None21084.9541096.954Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
18None21236.5881248.588Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
19None21406.3751418.375Schot BarcelonaIvan RakitićBarcelona
20None21640.4921652.492Schot BarcelonaLuis Alberto Suárez DíazBarcelona
21None22184.6062196.606Schot BarcelonaPhilippe Coutinho CorreiaBarcelona
22None22254.5782266.578Schot BarcelonaPhilippe Coutinho CorreiaBarcelona
23None22655.6382667.638Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
24None22795.7702807.770Schot BarcelonaLionel Andrés Messi CuccittiniBarcelona
\n", + "
" + ], + "text/plain": [ + " code_id period_id timestamp end_timestamp code \n", + "0 None 1 142.094 154.094 Schot Barcelona \\\n", + "1 None 1 332.239 344.239 Schot Barcelona \n", + "2 None 1 921.625 933.625 Schot Barcelona \n", + "3 None 1 1088.914 1100.914 Schot Barcelona \n", + "4 None 1 1835.287 1847.287 Schot Barcelona \n", + "5 None 1 2097.861 2109.861 Schot Barcelona \n", + "6 None 1 2241.168 2253.168 Schot Barcelona \n", + "7 None 1 2243.989 2255.989 Schot Barcelona \n", + "8 None 1 2301.083 2313.083 Schot Barcelona \n", + "9 None 1 2427.592 2439.592 Schot Barcelona \n", + "10 None 1 2603.612 2615.612 Schot Barcelona \n", + "11 None 2 152.524 164.524 Schot Barcelona \n", + "12 None 2 527.355 539.355 Schot Barcelona \n", + "13 None 2 589.388 601.388 Schot Barcelona \n", + "14 None 2 627.490 639.490 Schot Barcelona \n", + "15 None 2 733.847 745.847 Schot Barcelona \n", + "16 None 2 929.156 941.156 Schot Barcelona \n", + "17 None 2 1084.954 1096.954 Schot Barcelona \n", + "18 None 2 1236.588 1248.588 Schot Barcelona \n", + "19 None 2 1406.375 1418.375 Schot Barcelona \n", + "20 None 2 1640.492 1652.492 Schot Barcelona \n", + "21 None 2 2184.606 2196.606 Schot Barcelona \n", + "22 None 2 2254.578 2266.578 Schot Barcelona \n", + "23 None 2 2655.638 2667.638 Schot Barcelona \n", + "24 None 2 2795.770 2807.770 Schot Barcelona \n", + "\n", + " Player Team \n", + "0 Lionel Andrés Messi Cuccittini Barcelona \n", + "1 Jordi Alba Ramos Barcelona \n", + "2 Lionel Andrés Messi Cuccittini Barcelona \n", + "3 Luis Alberto Suárez Díaz Barcelona \n", + "4 Ousmane Dembélé Barcelona \n", + "5 Ivan Rakitić Barcelona \n", + "6 Lionel Andrés Messi Cuccittini Barcelona \n", + "7 Gerard Piqué Bernabéu Barcelona \n", + "8 Ousmane Dembélé Barcelona \n", + "9 Luis Alberto Suárez Díaz Barcelona \n", + "10 Ousmane Dembélé Barcelona \n", + "11 Jordi Alba Ramos Barcelona \n", + "12 Luis Alberto Suárez Díaz Barcelona \n", + "13 Philippe Coutinho Correia Barcelona \n", + "14 Jordi Alba Ramos Barcelona \n", + "15 Lionel Andrés Messi Cuccittini Barcelona \n", + "16 Philippe Coutinho Correia Barcelona \n", + "17 Lionel Andrés Messi Cuccittini Barcelona \n", + "18 Lionel Andrés Messi Cuccittini Barcelona \n", + "19 Ivan Rakitić Barcelona \n", + "20 Luis Alberto Suárez Díaz Barcelona \n", + "21 Philippe Coutinho Correia Barcelona \n", + "22 Philippe Coutinho Correia Barcelona \n", + "23 Lionel Andrés Messi Cuccittini Barcelona \n", + "24 Lionel Andrés Messi Cuccittini Barcelona " + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Chain filter and map operators\n", + "new_dataset = (\n", + " code_dataset\n", + " .filter(lambda record: record.labels['Team'] == 'Barcelona')\n", + " .map(lambda record: record.replace(code='Schot Barcelona'))\n", + ")\n", + "new_dataset.to_df()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/examples/config.ipynb b/docs/tutorials/examples/config.ipynb new file mode 100644 index 00000000..6f78ed7a --- /dev/null +++ b/docs/tutorials/examples/config.ipynb @@ -0,0 +1,211 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "20bb72b7", + "metadata": {}, + "source": [ + "# Config\n", + "\n", + "Kloppy 3.3 adds support for setting global configuration. This makes it possible to set the coordinate system for all `load` calls, setting the cache directory (or disabling the cache) and passing settings to adapters.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e8979b51", + "metadata": {}, + "outputs": [], + "source": [ + "from kloppy.config import get_config, set_config, config_context" + ] + }, + { + "cell_type": "markdown", + "id": "85c33f79", + "metadata": {}, + "source": [ + "## Get config\n", + "\n", + "You can get all configuration variable or just a single one using `get_config`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "b70b22c9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'cache': '/Users/koen/kloppy_cache',\n", + " 'coordinate_system': 'kloppy',\n", + " 'event_factory': None,\n", + " 'adapters.http.basic_authentication': None,\n", + " 'adapters.s3.s3fs': None,\n", + " 'dataframe.engine': 'pandas'}" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_config()" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9d75b69b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'kloppy'" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "get_config(\"coordinate_system\")" + ] + }, + { + "cell_type": "markdown", + "id": "07f1ce9b", + "metadata": {}, + "source": [ + "## Set config\n", + "\n", + "Using `set_config` you can set the value for a single config variable. This value will be used for all calls to kloppy." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1ba6227a", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/plain": [ + "OptaCoordinateSystem(normalized=False, length=120, width=80)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "set_config(\"coordinate_system\", \"opta\")\n", + "\n", + "dataset = statsbomb.load_open_data()\n", + "\n", + "dataset.metadata.coordinate_system" + ] + }, + { + "cell_type": "markdown", + "id": "471839e3", + "metadata": {}, + "source": [ + "## Config context\n", + "\n", + "Inspired by pandas kloppy allows you to set config variables for a context (using a context manager). Config set using `config_context` will be reverted to to original values when python exists the context" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6fc5b89c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Before context: opta\n", + "Within context: statsbomb\n", + "After context: opta\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/plain": [ + "StatsBombCoordinateSystem(normalized=False, length=120, width=80)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(f\"Before context: {get_config('coordinate_system')}\")\n", + "with config_context(\"coordinate_system\", \"statsbomb\"):\n", + " print(f\"Within context: {get_config('coordinate_system')}\")\n", + " dataset = statsbomb.load_open_data()\n", + " \n", + "print(f\"After context: {get_config('coordinate_system')}\")\n", + "dataset.metadata.coordinate_system" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/examples/event_data.ipynb b/docs/tutorials/examples/event_data.ipynb new file mode 100644 index 00000000..191f8c01 --- /dev/null +++ b/docs/tutorials/examples/event_data.ipynb @@ -0,0 +1,1903 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Event data\n", + "\n", + "One of the main benefits of working with kloppy is that it loads metadata with the event data. This metadata includes teams (name, ground and provider id) and players (name, jersey number, optional position and provider id). Using this metadata, it becomes very easy to create an analysis that is usable by humans, because it includes names instead of only numbers.\n", + "\n", + "This section shows how metadata is organized and some use-cases." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Loading statsbomb data\n", + "\n", + "The datasets module of kloppy makes it trivial to load statsbomb data. Keep in mind that by using the data you accept the license of the open-data project.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 55, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load_open_data(event_types=[\"pass\", \"shot\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exploring metadata\n", + "\n", + "kloppy always loads the metadata for you and makes it available at the `metadata` property. " + ] + }, + { + "cell_type": "code", + "execution_count": 56, + "metadata": {}, + "outputs": [], + "source": [ + "metadata = dataset.metadata\n", + "home_team, away_team = metadata.teams" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "After loading the data, the metadata can be used to iterate over teams and players. By default `metadata.teams` contain `[HomeTeam, AwayTeam]`. `Team` and `Player` entities have the `__str__` magic method implemented to help you cast it to a string. When you want to " + ] + }, + { + "cell_type": "code", + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "home - Barcelona\n", + "away - Deportivo Alavés\n" + ] + } + ], + "source": [ + "print(f\"{home_team.ground} - {home_team}\")\n", + "print(f\"{away_team.ground} - {away_team}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Malcom Filipe Silva de Oliveira (14)',\n", + " 'Philippe Coutinho Correia (7)',\n", + " 'Sergio Busquets i Burgos (5)',\n", + " 'Jordi Alba Ramos (18)',\n", + " 'Gerard Piqué Bernabéu (3)',\n", + " 'Luis Alberto Suárez Díaz (9)',\n", + " 'Ivan Rakitić (4)',\n", + " 'Ousmane Dembélé (11)',\n", + " 'Samuel Yves Umtiti (23)',\n", + " 'Lionel Andrés Messi Cuccittini (10)',\n", + " 'Nélson Cabral Semedo (2)',\n", + " 'Sergi Roberto Carnicer (20)',\n", + " 'Clément Lenglet (15)',\n", + " 'Rafael Alcântara do Nascimento (12)',\n", + " 'Arturo Erasmo Vidal Pardo (22)',\n", + " 'Jasper Cillessen (13)',\n", + " 'Arthur Henrique Ramos de Oliveira Melo (8)',\n", + " 'Marc-André ter Stegen (1)']" + ] + }, + "execution_count": 58, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "[f\"{player} ({player.jersey_no})\" for player in home_team.players]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'statsbomb team id: 217 - 206'" + ] + }, + "execution_count": 59, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# get provider id for team\n", + "f\"statsbomb team id: {home_team.team_id} - {away_team.team_id}\"" + ] + }, + { + "cell_type": "code", + "execution_count": 60, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Malcom Filipe Silva de Oliveira id=3109',\n", + " 'Philippe Coutinho Correia id=3501',\n", + " 'Sergio Busquets i Burgos id=5203',\n", + " 'Jordi Alba Ramos id=5211',\n", + " 'Gerard Piqué Bernabéu id=5213',\n", + " 'Luis Alberto Suárez Díaz id=5246',\n", + " 'Ivan Rakitić id=5470',\n", + " 'Ousmane Dembélé id=5477',\n", + " 'Samuel Yves Umtiti id=5492',\n", + " 'Lionel Andrés Messi Cuccittini id=5503',\n", + " 'Nélson Cabral Semedo id=6374',\n", + " 'Sergi Roberto Carnicer id=6379',\n", + " 'Clément Lenglet id=6826',\n", + " 'Rafael Alcântara do Nascimento id=6998',\n", + " 'Arturo Erasmo Vidal Pardo id=8206',\n", + " 'Jasper Cillessen id=8652',\n", + " 'Arthur Henrique Ramos de Oliveira Melo id=11392',\n", + " 'Marc-André ter Stegen id=20055']" + ] + }, + "execution_count": 60, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# same for the players\n", + "[f\"{player} id={player.player_id}\" for player in metadata.teams[0].players]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 61, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jonathan Rodríguez Menéndez\n", + "Deportivo Alavés\n", + "Teams are comparable? True\n" + ] + } + ], + "source": [ + "# get player from first event\n", + "player = dataset.events[0].player\n", + "print(player)\n", + "print(player.team)\n", + "print(f\"Teams are comparable? {player.team == away_team}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `Team` and `Player` entities also contain the magic methods to use those keys in dictionaries or use them in sets. This makes it easy to do some calculations, and show the results without mapping the player_id to a name." + ] + }, + { + "cell_type": "code", + "execution_count": 62, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jonathan Rodríguez Menéndez has 14 passes\n", + "Guillermo Alfonso Maripán Loaysa has 18 passes\n", + "Sergio Busquets i Burgos has 79 passes\n", + "Ivan Rakitić has 138 passes\n", + "Ousmane Dembélé has 65 passes\n", + "Jordi Alba Ramos has 121 passes\n", + "Víctor Laguardia Cisneros has 11 passes\n", + "Marc-André ter Stegen has 23 passes\n", + "Gerard Piqué Bernabéu has 79 passes\n", + "Nélson Cabral Semedo has 31 passes\n", + "Sergi Roberto Carnicer has 85 passes\n", + "Samuel Yves Umtiti has 63 passes\n", + "Lionel Andrés Messi Cuccittini has 92 passes\n", + "Rubén Duarte Sánchez has 25 passes\n", + "Ibai Gómez Pérez has 35 passes\n", + "Mubarak Wakaso has 23 passes\n", + "Manuel Alejandro García Sánchez has 23 passes\n", + "Rubén Sobrino Pozuelo has 17 passes\n", + "Luis Alberto Suárez Díaz has 38 passes\n", + "Fernando Pacheco Flores has 16 passes\n", + "Martín Aguirregabiria Padilla has 20 passes\n", + "Daniel Alejandro Torres Rojas has 16 passes\n", + "Philippe Coutinho Correia has 51 passes\n", + "Jorge Franco Alviz has 11 passes\n", + "Adrián Marín Gómez has 6 passes\n", + "Arthur Henrique Ramos de Oliveira Melo has 18 passes\n", + "Borja González Tomás has 7 passes\n", + "Arturo Erasmo Vidal Pardo has 7 passes\n" + ] + } + ], + "source": [ + "from collections import defaultdict\n", + "\n", + "passes_per_player = defaultdict(list)\n", + "for event in dataset.events:\n", + " if event.event_name == \"pass\":\n", + " passes_per_player[event.player].append(event)\n", + " \n", + "for player, passes in passes_per_player.items():\n", + " print(f\"{player} has {len(passes)} passes\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's filter on home_team." + ] + }, + { + "cell_type": "code", + "execution_count": 63, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sergio Busquets i Burgos has 79 passes\n", + "Ivan Rakitić has 138 passes\n", + "Ousmane Dembélé has 65 passes\n", + "Jordi Alba Ramos has 121 passes\n", + "Marc-André ter Stegen has 23 passes\n", + "Gerard Piqué Bernabéu has 79 passes\n", + "Nélson Cabral Semedo has 31 passes\n", + "Sergi Roberto Carnicer has 85 passes\n", + "Samuel Yves Umtiti has 63 passes\n", + "Lionel Andrés Messi Cuccittini has 92 passes\n", + "Luis Alberto Suárez Díaz has 38 passes\n", + "Philippe Coutinho Correia has 51 passes\n", + "Arthur Henrique Ramos de Oliveira Melo has 18 passes\n", + "Arturo Erasmo Vidal Pardo has 7 passes\n" + ] + } + ], + "source": [ + "for player, passes in passes_per_player.items():\n", + " if player.team == home_team:\n", + " print(f\"{player} has {len(passes)} passes\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Use metadata when transforming to pandas dataframe\n", + "\n", + "The metadata can also be used when transforming a dataset to a pandas dataframe. Using keyword argument additional columns can be created." + ] + }, + { + "cell_type": "code", + "execution_count": 64, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresulttimestampplayer_idplayer_nameteam_name
034208ade-2af4-45c3-970e-655937cad938PASSCOMPLETE0.0986581Jonathan Rodríguez MenéndezDeportivo Alavés
1d1cccb73-c7ef-4b02-8267-ebd7f149904bPASSINCOMPLETE3.4976855Guillermo Alfonso Maripán LoaysaDeportivo Alavés
2f1cc47d6-4b19-45a6-beb9-33d67fc83f4bPASSCOMPLETE6.7855203Sergio Busquets i BurgosBarcelona
3f774571f-4b65-43a0-9bfc-6384948d1b82PASSCOMPLETE8.4315470Ivan RakitićBarcelona
446f0e871-3e72-4817-9a53-af27583ba6c1PASSCOMPLETE10.4335477Ousmane DembéléBarcelona
\n", + "
" + ], + "text/plain": [ + " event_id event_type result timestamp \\\n", + "0 34208ade-2af4-45c3-970e-655937cad938 PASS COMPLETE 0.098 \n", + "1 d1cccb73-c7ef-4b02-8267-ebd7f149904b PASS INCOMPLETE 3.497 \n", + "2 f1cc47d6-4b19-45a6-beb9-33d67fc83f4b PASS COMPLETE 6.785 \n", + "3 f774571f-4b65-43a0-9bfc-6384948d1b82 PASS COMPLETE 8.431 \n", + "4 46f0e871-3e72-4817-9a53-af27583ba6c1 PASS COMPLETE 10.433 \n", + "\n", + " player_id player_name team_name \n", + "0 6581 Jonathan Rodríguez Menéndez Deportivo Alavés \n", + "1 6855 Guillermo Alfonso Maripán Loaysa Deportivo Alavés \n", + "2 5203 Sergio Busquets i Burgos Barcelona \n", + "3 5470 Ivan Rakitić Barcelona \n", + "4 5477 Ousmane Dembélé Barcelona " + ] + }, + "execution_count": 64, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "\n", + "dataframe = dataset.to_df(\n", + " \"*\", # Get all default columns\n", + " player_name=lambda event: str(event.player),\n", + " team_name=lambda event: str(event.player.team)\n", + ")\n", + "\n", + "dataframe[[\n", + " 'event_id', 'event_type', 'result', 'timestamp', 'player_id', \n", + " 'player_name', 'team_name'\n", + "]].head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Attribute transformers\n", + "\n", + "Attribute transformer make it possible to add predefined attributes to a dataset. The attributes are calculated during export to a pandas DataFrame. Kloppy does provide some Transformers like one to calculate the angle to the goal, and one to calculate the distance to the goal. When you need additional Transformers you can write your one by providing a `Callable` to `to_df`. " + ] + }, + { + "cell_type": "code", + "execution_count": 65, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
angle_to_goaldistance_to_goal
090.48146659.502101
182.24996485.278954
269.18735491.468574
377.00538386.720816
466.56201394.278842
.........
1155121.57816563.972650
1156104.39359358.330952
115739.55966844.749302
115871.09542438.581083
115955.9012689.721368
\n", + "

1160 rows × 2 columns

\n", + "
" + ], + "text/plain": [ + " angle_to_goal distance_to_goal\n", + "0 90.481466 59.502101\n", + "1 82.249964 85.278954\n", + "2 69.187354 91.468574\n", + "3 77.005383 86.720816\n", + "4 66.562013 94.278842\n", + "... ... ...\n", + "1155 121.578165 63.972650\n", + "1156 104.393593 58.330952\n", + "1157 39.559668 44.749302\n", + "1158 71.095424 38.581083\n", + "1159 55.901268 9.721368\n", + "\n", + "[1160 rows x 2 columns]" + ] + }, + "execution_count": 65, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "from kloppy.domain.services.transformers.attribute import (\n", + " BodyPartTransformer, AngleToGoalTransformer, DistanceToGoalTransformer\n", + ")\n", + "\n", + "dataset = statsbomb.load_open_data(\n", + " event_types=[\"pass\", \"shot\"], \n", + " coordinates=\"statsbomb\"\n", + ")\n", + "\n", + "dataset.to_df(\n", + " AngleToGoalTransformer(),\n", + " DistanceToGoalTransformer()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 66, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "{'is_body_part_right_foot': False, 'is_body_part_left_foot': True, 'is_body_part_head': False, 'is_body_part_both_hands': False, 'is_body_part_chest': False, 'is_body_part_left_hand': False, 'is_body_part_right_hand': False, 'is_body_part_drop_kick': False, 'is_body_part_keeper_arm': False, 'is_body_part_other': False, 'is_body_part_no_touch': False}\n" + ] + }, + { + "data": { + "text/plain": [ + "{'angle_to_goal': 90.48146580583835}" + ] + }, + "execution_count": 66, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "event = dataset.events[0]\n", + "\n", + "transformer = BodyPartTransformer(encoding=\"one-hot\")\n", + "print(transformer(event))\n", + "\n", + "\n", + "transformer = AngleToGoalTransformer()\n", + "transformer(event)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Wildcard\n", + "\n", + "When you want to export a set of attributes you can specify a wildcard pattern. This pattern is matched against all default (exported by the `Default` Transformer) attributes." + ] + }, + { + "cell_type": "code", + "execution_count": 67, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampcoordinates_xcoordinates_yend_coordinates_xend_coordinates_y
010.09860.5040.5035.525.5
113.49735.5028.5085.572.5
216.78534.507.5034.520.5
318.43135.5020.5035.51.5
4110.43333.502.5025.51.5
.....................
115522787.91465.5073.5059.560.5
115622791.39563.5054.5089.55.5
115722795.12791.505.5090.526.5
115822798.90683.5027.50106.544.5
115922802.770111.9534.55NaNNaN
\n", + "

1160 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp coordinates_x coordinates_y end_coordinates_x \\\n", + "0 1 0.098 60.50 40.50 35.5 \n", + "1 1 3.497 35.50 28.50 85.5 \n", + "2 1 6.785 34.50 7.50 34.5 \n", + "3 1 8.431 35.50 20.50 35.5 \n", + "4 1 10.433 33.50 2.50 25.5 \n", + "... ... ... ... ... ... \n", + "1155 2 2787.914 65.50 73.50 59.5 \n", + "1156 2 2791.395 63.50 54.50 89.5 \n", + "1157 2 2795.127 91.50 5.50 90.5 \n", + "1158 2 2798.906 83.50 27.50 106.5 \n", + "1159 2 2802.770 111.95 34.55 NaN \n", + "\n", + " end_coordinates_y \n", + "0 25.5 \n", + "1 72.5 \n", + "2 20.5 \n", + "3 1.5 \n", + "4 1.5 \n", + "... ... \n", + "1155 60.5 \n", + "1156 5.5 \n", + "1157 26.5 \n", + "1158 44.5 \n", + "1159 NaN \n", + "\n", + "[1160 rows x 6 columns]" + ] + }, + "execution_count": 67, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.to_df(\n", + " 'period_id',\n", + " 'timestamp',\n", + " '*coordinates*',\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## User-defined Transformers\n", + "\n", + "Transformers are nothing more than a function which accepts a `Event` and returns `Dict` (`Callable[[Event], Dict])`). The Transformers provided by kloppy are actually classes that define a `__call__` method. You can also use a `lambda` function or any other function to transform attributes.\n", + "\n", + "When you use named attributes (specified using a keyword argument) the returned value can be any type (`Callable[[Event], Any]`). " + ] + }, + { + "cell_type": "code", + "execution_count": 68, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
periodtimestampsome_columnsother_column
010.098123462
113.4971234252
216.7851234194
318.4311234121
4110.4331234161
...............
115522787.9141234230
115622791.3951234153
115722795.1271234151
115822798.9061234160
115922802.7701234242
\n", + "

1160 rows × 4 columns

\n", + "
" + ], + "text/plain": [ + " period timestamp some_columns other_column\n", + "0 1 0.098 1234 62\n", + "1 1 3.497 1234 252\n", + "2 1 6.785 1234 194\n", + "3 1 8.431 1234 121\n", + "4 1 10.433 1234 161\n", + "... ... ... ... ...\n", + "1155 2 2787.914 1234 230\n", + "1156 2 2791.395 1234 153\n", + "1157 2 2795.127 1234 151\n", + "1158 2 2798.906 1234 160\n", + "1159 2 2802.770 1234 242\n", + "\n", + "[1160 rows x 4 columns]" + ] + }, + "execution_count": 68, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "import random\n", + "\n", + "dataset.to_df(\n", + " # Unnamed transformer must always be defined as a Callable. The function must return a Dictionary\n", + " lambda event: {'period': event.period.id, 'timestamp': event.timestamp},\n", + " \n", + " # Named transformer can be specified as a constant\n", + " some_columns=1234,\n", + " \n", + " # Or as a callable\n", + " other_column=lambda x: random.randint(0, 255)\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Polars\n", + "\n", + "Since version 3.8.0 it's possible to export a `Dataset` to a [Polars](https://www.pola.rs/) dataframe." + ] + }, + { + "cell_type": "code", + "execution_count": 78, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "shape: (5, 19)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "event_id\n", + "\n", + "event_type\n", + "\n", + "result\n", + "\n", + "success\n", + "\n", + "period_id\n", + "\n", + "timestamp\n", + "\n", + "end_timestamp\n", + "\n", + "ball_state\n", + "\n", + "ball_owning_team\n", + "\n", + "team_id\n", + "\n", + "player_id\n", + "\n", + "coordinates_x\n", + "\n", + "coordinates_y\n", + "\n", + "end_coordinates_x\n", + "\n", + "end_coordinates_y\n", + "\n", + "receiver_player_id\n", + "\n", + "set_piece_type\n", + "\n", + "body_part_type\n", + "\n", + "pass_type\n", + "
\n", + "str\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "bool\n", + "\n", + "i64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "str\n", + "\n", + "str\n", + "
\n", + ""19edeac2-e63f-...\n", + "\n", + ""GENERIC:Starti...\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "1\n", + "\n", + "0.0\n", + "\n", + "null\n", + "\n", + ""alive"\n", + "\n", + ""909"\n", + "\n", + ""909"\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + ""89072e2e-b64f-...\n", + "\n", + ""GENERIC:Starti...\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "1\n", + "\n", + "0.0\n", + "\n", + "null\n", + "\n", + ""alive"\n", + "\n", + ""909"\n", + "\n", + ""914"\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + ""46c6901e-3b12-...\n", + "\n", + ""GENERIC:Half S...\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "1\n", + "\n", + "0.0\n", + "\n", + "null\n", + "\n", + ""alive"\n", + "\n", + ""909"\n", + "\n", + ""914"\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + ""9e5b0646-91cc-...\n", + "\n", + ""GENERIC:Half S...\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "1\n", + "\n", + "0.0\n", + "\n", + "null\n", + "\n", + ""alive"\n", + "\n", + ""909"\n", + "\n", + ""909"\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + ""bbc398f7-c784-...\n", + "\n", + ""PASS"\n", + "\n", + ""COMPLETE"\n", + "\n", + "true\n", + "\n", + "1\n", + "\n", + "0.878\n", + "\n", + "2.788504\n", + "\n", + ""alive"\n", + "\n", + ""909"\n", + "\n", + ""909"\n", + "\n", + ""11086"\n", + "\n", + "59.95\n", + "\n", + "39.95\n", + "\n", + "32.45\n", + "\n", + "28.75\n", + "\n", + ""8963"\n", + "\n", + ""KICK_OFF"\n", + "\n", + ""RIGHT_FOOT"\n", + "\n", + "null\n", + "
\n", + "
" + ], + "text/plain": [ + "shape: (5, 19)\n", + "┌─────────┬──────────┬──────────┬─────────┬─────┬────────────┬────────────┬────────────┬───────────┐\n", + "│ event_i ┆ event_ty ┆ result ┆ success ┆ ... ┆ receiver_p ┆ set_piece_ ┆ body_part_ ┆ pass_type │\n", + "│ d ┆ pe ┆ --- ┆ --- ┆ ┆ layer_id ┆ type ┆ type ┆ --- │\n", + "│ --- ┆ --- ┆ str ┆ bool ┆ ┆ --- ┆ --- ┆ --- ┆ str │\n", + "│ str ┆ str ┆ ┆ ┆ ┆ str ┆ str ┆ str ┆ │\n", + "╞═════════╪══════════╪══════════╪═════════╪═════╪════════════╪════════════╪════════════╪═══════════╡\n", + "│ 19edeac ┆ GENERIC: ┆ null ┆ null ┆ ... ┆ null ┆ null ┆ null ┆ null │\n", + "│ 2-e63f- ┆ Starting ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 4795-8a ┆ XI ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 8b-17a6 ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ e9fd... ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 89072e2 ┆ GENERIC: ┆ null ┆ null ┆ ... ┆ null ┆ null ┆ null ┆ null │\n", + "│ e-b64f- ┆ Starting ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 4099-84 ┆ XI ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 6b-b22c ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ f000... ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 46c6901 ┆ GENERIC: ┆ null ┆ null ┆ ... ┆ null ┆ null ┆ null ┆ null │\n", + "│ e-3b12- ┆ Half ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 495a-b6 ┆ Start ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 8a-19ca ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 1579... ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 9e5b064 ┆ GENERIC: ┆ null ┆ null ┆ ... ┆ null ┆ null ┆ null ┆ null │\n", + "│ 6-91cc- ┆ Half ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 49a1-bf ┆ Start ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 88-39bd ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ e773... ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ bbc398f ┆ PASS ┆ COMPLETE ┆ true ┆ ... ┆ 8963 ┆ KICK_OFF ┆ RIGHT_FOOT ┆ null │\n", + "│ 7-c784- ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 4958-a5 ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 04-37b5 ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "│ 83ca... ┆ ┆ ┆ ┆ ┆ ┆ ┆ ┆ │\n", + "└─────────┴──────────┴──────────┴─────────┴─────┴────────────┴────────────┴────────────┴───────────┘" + ] + }, + "execution_count": 78, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# %pip install polars\n", + "# you might need to install polars\n", + "\n", + "df = dataset.to_df(\n", + " engine=\"polars\"\n", + ")\n", + "df.head()" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## to_records\n", + "\n", + "Under the hood the `to_df` method uses the `to_records` method. " + ] + }, + { + "cell_type": "code", + "execution_count": 69, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'period': 1, 'timestamp': 0.098, 'some_columns': 1234, 'other_column': 42},\n", + " {'period': 1, 'timestamp': 3.497, 'some_columns': 1234, 'other_column': 72},\n", + " {'period': 1, 'timestamp': 6.785, 'some_columns': 1234, 'other_column': 135},\n", + " {'period': 1, 'timestamp': 8.431, 'some_columns': 1234, 'other_column': 100},\n", + " {'period': 1, 'timestamp': 10.433, 'some_columns': 1234, 'other_column': 193},\n", + " {'period': 1, 'timestamp': 11.15, 'some_columns': 1234, 'other_column': 64},\n", + " {'period': 1, 'timestamp': 24.687, 'some_columns': 1234, 'other_column': 22},\n", + " {'period': 1, 'timestamp': 30.008, 'some_columns': 1234, 'other_column': 157},\n", + " {'period': 1, 'timestamp': 34.738, 'some_columns': 1234, 'other_column': 73},\n", + " {'period': 1, 'timestamp': 37.467, 'some_columns': 1234, 'other_column': 226}]" + ] + }, + "execution_count": 69, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "records = dataset.to_records(\n", + " # Unnamed transformer must always be defined as a Callable. The function must return a Dictionary\n", + " lambda event: {'period': event.period.id, 'timestamp': event.timestamp},\n", + " \n", + " # Named transformer can be specified as a constant\n", + " some_columns=1234,\n", + " \n", + " # Or as a callable\n", + " other_column=lambda x: random.randint(0, 255)\n", + ")\n", + "records[:10]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## EventFactory\n", + "\n", + "In some cases like to use your own `Event` classes. This can be useful when you need certain data that isn't stored in the regular `Event` classes" + ] + }, + { + "cell_type": "code", + "execution_count": 70, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
statsbomb_xgplayertimestamp
00.075164Lionel Andrés Messi Cuccittini149.094
10.062892Jordi Alba Ramos339.239
20.020535Lionel Andrés Messi Cuccittini928.625
30.096234Rubén Sobrino Pozuelo979.616
40.035420Luis Alberto Suárez Díaz1095.914
50.089920Ousmane Dembélé1842.287
60.071365Ivan Rakitić2104.861
70.078886Lionel Andrés Messi Cuccittini2248.168
80.171218Gerard Piqué Bernabéu2250.989
90.226095Ousmane Dembélé2308.083
100.257290Luis Alberto Suárez Díaz2434.592
110.145402Ousmane Dembélé2610.612
120.143644Jordi Alba Ramos2864.792
130.034266Mubarak Wakaso3072.668
140.018334Luis Alberto Suárez Díaz3239.623
150.014615Philippe Coutinho Correia3301.656
160.039418Jordi Alba Ramos3339.758
170.026228Lionel Andrés Messi Cuccittini3446.115
180.031532Philippe Coutinho Correia3641.424
190.137812Lionel Andrés Messi Cuccittini3797.222
200.081403Lionel Andrés Messi Cuccittini3948.856
210.009953Ivan Rakitić4118.643
220.337188Luis Alberto Suárez Díaz4352.760
230.137859Adrián Marín Gómez4702.947
240.379632Philippe Coutinho Correia4896.874
250.086874Philippe Coutinho Correia4966.846
260.262502Lionel Andrés Messi Cuccittini5367.906
270.289481Lionel Andrés Messi Cuccittini5508.038
\n", + "
" + ], + "text/plain": [ + " statsbomb_xg player timestamp\n", + "0 0.075164 Lionel Andrés Messi Cuccittini 149.094\n", + "1 0.062892 Jordi Alba Ramos 339.239\n", + "2 0.020535 Lionel Andrés Messi Cuccittini 928.625\n", + "3 0.096234 Rubén Sobrino Pozuelo 979.616\n", + "4 0.035420 Luis Alberto Suárez Díaz 1095.914\n", + "5 0.089920 Ousmane Dembélé 1842.287\n", + "6 0.071365 Ivan Rakitić 2104.861\n", + "7 0.078886 Lionel Andrés Messi Cuccittini 2248.168\n", + "8 0.171218 Gerard Piqué Bernabéu 2250.989\n", + "9 0.226095 Ousmane Dembélé 2308.083\n", + "10 0.257290 Luis Alberto Suárez Díaz 2434.592\n", + "11 0.145402 Ousmane Dembélé 2610.612\n", + "12 0.143644 Jordi Alba Ramos 2864.792\n", + "13 0.034266 Mubarak Wakaso 3072.668\n", + "14 0.018334 Luis Alberto Suárez Díaz 3239.623\n", + "15 0.014615 Philippe Coutinho Correia 3301.656\n", + "16 0.039418 Jordi Alba Ramos 3339.758\n", + "17 0.026228 Lionel Andrés Messi Cuccittini 3446.115\n", + "18 0.031532 Philippe Coutinho Correia 3641.424\n", + "19 0.137812 Lionel Andrés Messi Cuccittini 3797.222\n", + "20 0.081403 Lionel Andrés Messi Cuccittini 3948.856\n", + "21 0.009953 Ivan Rakitić 4118.643\n", + "22 0.337188 Luis Alberto Suárez Díaz 4352.760\n", + "23 0.137859 Adrián Marín Gómez 4702.947\n", + "24 0.379632 Philippe Coutinho Correia 4896.874\n", + "25 0.086874 Philippe Coutinho Correia 4966.846\n", + "26 0.262502 Lionel Andrés Messi Cuccittini 5367.906\n", + "27 0.289481 Lionel Andrés Messi Cuccittini 5508.038" + ] + }, + "execution_count": 70, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from dataclasses import dataclass\n", + "\n", + "from kloppy.domain import EventFactory, create_event, ShotEvent\n", + "from kloppy import statsbomb\n", + "\n", + "\n", + "@dataclass(repr=False)\n", + "class StatsBombShotEvent(ShotEvent):\n", + " statsbomb_xg: float = None\n", + " \n", + " \n", + "class StatsBombEventFactory(EventFactory):\n", + " def build_shot(self, **kwargs) -> ShotEvent:\n", + " kwargs['statsbomb_xg'] = kwargs['raw_event']['shot']['statsbomb_xg']\n", + " return create_event(StatsBombShotEvent, **kwargs)\n", + " \n", + " \n", + "event_factory = StatsBombEventFactory()\n", + "\n", + "dataset = statsbomb.load_open_data(event_factory=event_factory)\n", + "\n", + "dataset.filter(\"shot\").to_df(\n", + " \"statsbomb_xg\",\n", + " \"player\",\n", + " timestamp=lambda event: event.period.start_timestamp + event.timestamp,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Freeze frame\n", + "\n", + "For event data it's very useful to have additional context about the event. This can be a metric like packing or xG, but also the frame of tracking data. This freeze frame contains player coordinates. Some providers retrieve the information from broadcast video feeds and therefore only the coordinates of players visible in the feed are known.\n", + "Furthermore the vendor might include only the team of the player, and not a player identifier." + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "metadata": {}, + "outputs": [], + "source": [ + "dataset = statsbomb.load_open_data(\n", + " match_id='3788741',\n", + " coordinates=\"statsbomb\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "metadata": {}, + "outputs": [], + "source": [ + "event = dataset.find(\"shot\")" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# You might need to install mplsoccer package\n", + "# %pip install mplsoccer\n", + "\n", + "from mplsoccer.pitch import Pitch\n", + "\n", + "home_team = dataset.metadata.teams[0]\n", + "\n", + "pitch = Pitch(pitch_type='statsbomb',\n", + " pitch_color='white', line_color='#c7d5cc')\n", + "fig, ax = pitch.draw()\n", + "\n", + "def get_color(player):\n", + " if player == event.player:\n", + " return \"blue\"\n", + " elif player.team == event.player.team:\n", + " return \"green\"\n", + " elif player.position.position_id == '1':\n", + " return \"black\"\n", + " else:\n", + " return \"grey\"\n", + "\n", + "x, y, color = zip(*[\n", + " (coordinates.x, coordinates.y, get_color(player))\n", + " for player, coordinates in event.freeze_frame.players_coordinates.items()\n", + "])\n", + "\n", + "_ = pitch.scatter(x, y, color=color, ax=ax)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/examples/navigating.ipynb b/docs/tutorials/examples/navigating.ipynb new file mode 100644 index 00000000..d4186e92 --- /dev/null +++ b/docs/tutorials/examples/navigating.ipynb @@ -0,0 +1,525 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "d5e58e18", + "metadata": {}, + "source": [ + "# Navigating\n", + "\n", + "Kloppy 3.2 adds some powerfull tools to navigate through you event data. In this tutorial you will learn how to use them.\n", + "\n", + "\n", + "## Dataset\n", + "\n", + "On dataset level it's possible to use `filter`, `find` and `find_all`. All these functions access the same argument for finding the right events.\n", + "You can pass a string or a function. In case of a string is must be either 'event_type', 'event_type.result' or '.result'. Some examples: 'shot.goal', 'pass' or '.complete'.\n", + "\n", + "Lets have a look at how these work.\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c910dd35", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_ybody_part_type
04c7c4ab1-6b9f-4504-a237-249c2e0c549fSHOTGOALTrue21091.954Nonealive21721755030.8004170.563125NoneNoneLEFT_FOOT
1683c6752-13bc-4892-94ed-22e1c938f1f7SHOTGOALTrue22261.578Nonealive21721735010.8754170.388125NoneNoneRIGHT_FOOT
255d71847-9511-4417-aea9-6f415e279011SHOTGOALTrue22802.770Nonealive21721755030.9329170.431875NoneNoneLEFT_FOOT
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success period_id \\\n", + "0 4c7c4ab1-6b9f-4504-a237-249c2e0c549f SHOT GOAL True 2 \n", + "1 683c6752-13bc-4892-94ed-22e1c938f1f7 SHOT GOAL True 2 \n", + "2 55d71847-9511-4417-aea9-6f415e279011 SHOT GOAL True 2 \n", + "\n", + " timestamp end_timestamp ball_state ball_owning_team team_id player_id \\\n", + "0 1091.954 None alive 217 217 5503 \n", + "1 2261.578 None alive 217 217 3501 \n", + "2 2802.770 None alive 217 217 5503 \n", + "\n", + " coordinates_x coordinates_y end_coordinates_x end_coordinates_y \\\n", + "0 0.800417 0.563125 None None \n", + "1 0.875417 0.388125 None None \n", + "2 0.932917 0.431875 None None \n", + "\n", + " body_part_type \n", + "0 LEFT_FOOT \n", + "1 RIGHT_FOOT \n", + "2 LEFT_FOOT " + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from kloppy import statsbomb\n", + "\n", + "# Load a Statsbomb open data dataset\n", + "dataset = statsbomb.load_open_data()\n", + "\n", + "# Create a new dataset which contains all goals\n", + "filtered_dataset = dataset.filter('shot.goal')\n", + "\n", + "# Show the results\n", + "filtered_dataset.to_df()" + ] + }, + { + "cell_type": "markdown", + "id": "6fe919e7", + "metadata": {}, + "source": [ + "The filtered dataset doesn't contain any events other than goals. Lets validate that. When we try to find all passes we should get an empty list" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "57aac560", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "0" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "passes = filtered_dataset.find_all('pass')\n", + "len(passes)" + ] + }, + { + "cell_type": "markdown", + "id": "4cd3edb8", + "metadata": {}, + "source": [ + "The original dataset does contain passes, right?" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "45de2f06", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1132" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "passes = dataset.find_all('pass')\n", + "len(passes)" + ] + }, + { + "cell_type": "markdown", + "id": "3239a684", + "metadata": {}, + "source": [ + "Now we already touched the `find_all` method. This method accepts the same argument. The difference is that `find_all` returns a list of events, where `filter` returns a new Dataset. The `find` method return the first matching event or None when it cannot find one." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2df283f7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset.find('shot')" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "7c432ca0", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "print(filtered_dataset.find('pass'))" + ] + }, + { + "cell_type": "markdown", + "id": "4b5120a9", + "metadata": {}, + "source": [ + "## Event\n", + "\n", + "On Event level there are also some new methods for navigating. The `prev` and `next` methods are added. These allow you to quickly find previous or next events. But those two methods also accept the filter argument like the Dataset methods do. This makes useful to find a certain type of event instead of just the one before/after.\n", + "\n", + "Lets have look at how this works" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "31fd93a8", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Load a Statsbomb open data dataset\n", + "dataset = statsbomb.load_open_data()\n", + "\n", + "first_goal = dataset.find('shot.goal')\n", + "first_goal" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "98dda35c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "\n", + "\n" + ] + } + ], + "source": [ + "# Lets previous and next events\n", + "print(first_goal.prev())\n", + "print(first_goal)\n", + "print(first_goal.next())" + ] + }, + { + "cell_type": "markdown", + "id": "9be4a6a1", + "metadata": {}, + "source": [ + "But what if we want to find the last complete pass before the goal?" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "841fa959", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first_goal.prev('pass.complete')" + ] + }, + { + "cell_type": "markdown", + "id": "b7026192", + "metadata": {}, + "source": [ + "Or when we don't care about the event type, but want to make sure it's complete.." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "16aad417", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "first_goal.prev('.complete')\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "9b5514bd", + "metadata": {}, + "source": [ + "## Related events\n", + "\n", + "Some vendors include `related_events` in their data. The related events can be accessed via `get_related_events` method, or by `related_pass`, `related_carry`, etc for each event type.\n", + "\n", + "The `get_related_events` returns a list which can be empty. The `related_pass` methods return an Event or None when that type is not specified." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "963e687d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[,\n", + " ,\n", + " ,\n", + " ]" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "carry_event = first_goal.prev('carry')\n", + "carry_event.get_related_events()" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "7d2c3377", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "None\n" + ] + } + ], + "source": [ + "print(carry_event.related_pass())" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3e53ee99", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "print(carry_event.related_take_on())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2ed402bd", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/docs/tutorials/examples/plotting.ipynb b/docs/tutorials/examples/plotting.ipynb new file mode 100644 index 00000000..3a65f6b5 --- /dev/null +++ b/docs/tutorials/examples/plotting.ipynb @@ -0,0 +1,165 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Plotting\n", + "\n", + "In this quickstart, we'll show all kind of examples. As PySport encourages OpenSource projects, all examples use other sports analytics packages in combination with kloppy. You can find more packages at https://opensource.pysport.org/\n", + "\n", + "\n", + "## Plotting events using mplsoccer\n", + "\n", + "In this example the [mplsoccer](https://github.com/andrewRowlinson/mplsoccer) package by [Andy Rowlinson](https://twitter.com/numberstorm) is used.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Requirement already satisfied: mplsoccer in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (1.1.10)\n", + "Requirement already satisfied: matplotlib in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (3.7.1)\n", + "Requirement already satisfied: seaborn in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (0.12.2)\n", + "Requirement already satisfied: scipy in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (1.10.1)\n", + "Requirement already satisfied: pillow in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (9.4.0)\n", + "Requirement already satisfied: requests in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/requests-2.28.2-py3.10.egg (from mplsoccer) (2.28.2)\n", + "Requirement already satisfied: numpy in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (1.24.2)\n", + "Requirement already satisfied: pandas in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (1.5.3)\n", + "Requirement already satisfied: fonttools>=4.22.0 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (4.38.0)\n", + "Requirement already satisfied: packaging>=20.0 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (23.0)\n", + "Requirement already satisfied: pyparsing>=2.3.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (3.0.9)\n", + "Requirement already satisfied: python-dateutil>=2.7 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/python_dateutil-2.8.2-py3.10.egg (from matplotlib) (2.8.2)\n", + "Requirement already satisfied: cycler>=0.10 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (0.11.0)\n", + "Requirement already satisfied: contourpy>=1.0.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (1.0.7)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (1.4.4)\n", + "Requirement already satisfied: pytz>=2020.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/pytz-2022.7.1-py3.10.egg (from pandas->mplsoccer) (2022.7.1)\n", + "Requirement already satisfied: six>=1.5 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/six-1.16.0-py3.10.egg (from python-dateutil>=2.7->matplotlib) (1.16.0)\n", + "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/charset_normalizer-3.0.1-py3.10-macosx-12-arm64.egg (from requests->mplsoccer) (3.0.1)\n", + "Requirement already satisfied: idna<4,>=2.5 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/idna-3.4-py3.10.egg (from requests->mplsoccer) (3.4)\n", + "Requirement already satisfied: urllib3<1.27,>=1.21.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/urllib3-1.26.14-py3.10.egg (from requests->mplsoccer) (1.26.14)\n", + "Requirement already satisfied: certifi>=2017.4.17 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/certifi-2022.12.7-py3.10.egg (from requests->mplsoccer) (2022.12.7)\n", + "\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m A new release of pip available: \u001b[0m\u001b[31;49m22.3.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m23.0.1\u001b[0m\n", + "\u001b[1m[\u001b[0m\u001b[34;49mnotice\u001b[0m\u001b[1;39;49m]\u001b[0m\u001b[39;49m To update, run: \u001b[0m\u001b[32;49mpip install --upgrade pip\u001b[0m\n" + ] + } + ], + "source": [ + "import sys\n", + "!{sys.executable} -m pip install mplsoccer matplotlib seaborn" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Going to show passes of: Lionel Andrés Messi Cuccittini\n" + ] + } + ], + "source": [ + "## Load data\n", + "\n", + "from mplsoccer.pitch import Pitch\n", + "from kloppy import statsbomb\n", + "\n", + "dataset = statsbomb.load_open_data(\n", + " event_types=[\"pass\"], \n", + " coordinates=\"statsbomb\"\n", + ")\n", + "\n", + "home_team, away_team = dataset.metadata.teams\n", + "\n", + "messi = home_team.players[9]\n", + "print(f\"Going to show passes of: {messi}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "df = (\n", + " dataset\n", + " .filter(lambda event: event.player == messi)\n", + " .to_df()\n", + ")\n", + "\n", + "\n", + "pitch = Pitch(pitch_color='#e7f1fa', line_zorder=1, line_color='black', pitch_type=\"statsbomb\")\n", + "fig, ax = pitch.draw()\n", + "plot = pitch.kdeplot(\n", + " df[\"coordinates_x\"],\n", + " df[\"coordinates_y\"],\n", + " ax=ax,\n", + " fill=True,\n", + " n_levels=50,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/examples/state.ipynb b/docs/tutorials/examples/state.ipynb new file mode 100644 index 00000000..32226afe --- /dev/null +++ b/docs/tutorials/examples/state.ipynb @@ -0,0 +1,1427 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# State\n", + "\n", + "While working with event data it can be convenient to also use the current score. This can be used to determine if a team is winning, losing or drawing. A more generic name for the score is 'state'. \n", + "\n", + "In this quickstart we'll look at how to use the `add_state` method of kloppy to add state to the events for later use.\n", + "\n", + "## Loading some statsbomb data\n", + "\n", + "First we'll load Barcelona - Deportivo Alaves from the statsbomb open-data project." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "['Barcelona', 'Deportivo Alavés']\n", + "{}\n" + ] + } + ], + "source": [ + "from kloppy import statsbomb\n", + "from kloppy.domain import EventType\n", + "\n", + "dataset = statsbomb.load_open_data()\n", + "print([team.name for team in dataset.metadata.teams])\n", + "print(dataset.events[0].state)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add state - score\n", + "\n", + "kloppy contains some default state builders: score, lineup and sequence. Let's have a look at the `score` state builder. " + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "{'score': Score(home=0, away=0)}" + ] + }, + "execution_count": 32, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset = dataset.add_state('score')\n", + "\n", + "dataset.events[0].state" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "As you can see `state` is now filled with a score object. The object contains two attributes: `home` and `away`. Every event contains a score object which is automatically updated when a goal is scored.\n", + "\n", + "Now lets have a look at how we can use the score state. First we filter on only shots." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "28" + ] + }, + "execution_count": 33, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataset = dataset.filter(lambda event: event.event_type == EventType.SHOT)\n", + "shots = dataset.events\n", + "len(shots)" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0-0 Barcelona - Lionel Andrés Messi Cuccittini - OFF_TARGET\n", + "0-0 Barcelona - Jordi Alba Ramos - OFF_TARGET\n", + "0-0 Barcelona - Lionel Andrés Messi Cuccittini - SAVED\n", + "0-0 Deportivo Alavés - Rubén Sobrino Pozuelo - OFF_TARGET\n", + "0-0 Barcelona - Luis Alberto Suárez Díaz - OFF_TARGET\n", + "0-0 Barcelona - Ousmane Dembélé - OFF_TARGET\n", + "0-0 Barcelona - Ivan Rakitić - OFF_TARGET\n", + "0-0 Barcelona - Lionel Andrés Messi Cuccittini - POST\n", + "0-0 Barcelona - Gerard Piqué Bernabéu - OFF_TARGET\n", + "0-0 Barcelona - Ousmane Dembélé - SAVED\n", + "0-0 Barcelona - Luis Alberto Suárez Díaz - OFF_TARGET\n", + "0-0 Barcelona - Ousmane Dembélé - OFF_TARGET\n", + "0-0 Barcelona - Jordi Alba Ramos - SAVED\n", + "0-0 Deportivo Alavés - Mubarak Wakaso - BLOCKED\n", + "0-0 Barcelona - Luis Alberto Suárez Díaz - BLOCKED\n", + "0-0 Barcelona - Philippe Coutinho Correia - BLOCKED\n", + "0-0 Barcelona - Jordi Alba Ramos - BLOCKED\n", + "0-0 Barcelona - Lionel Andrés Messi Cuccittini - OFF_TARGET\n", + "0-0 Barcelona - Philippe Coutinho Correia - BLOCKED\n", + "0-0 Barcelona - Lionel Andrés Messi Cuccittini - GOAL\n", + "1-0 Barcelona - Lionel Andrés Messi Cuccittini - POST\n", + "1-0 Barcelona - Ivan Rakitić - BLOCKED\n", + "1-0 Barcelona - Luis Alberto Suárez Díaz - SAVED\n", + "1-0 Deportivo Alavés - Adrián Marín Gómez - OFF_TARGET\n", + "1-0 Barcelona - Philippe Coutinho Correia - SAVED\n", + "1-0 Barcelona - Philippe Coutinho Correia - GOAL\n", + "2-0 Barcelona - Lionel Andrés Messi Cuccittini - SAVED\n", + "2-0 Barcelona - Lionel Andrés Messi Cuccittini - GOAL\n" + ] + } + ], + "source": [ + "for shot in shots:\n", + " print(shot.state['score'], shot.player.team, '-', shot.player, '-', shot.result)" + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_ybody_part_typehome_scoreaway_score
065f16e50-7c5d-4293-b2fc-d20887a772f9SHOTOFF_TARGETFalse1149.094Nonealive21721755030.9304170.645625NoneNoneRIGHT_FOOT00
1b0f73423-3990-45ae-9dda-3512c2d1aff3SHOTOFF_TARGETFalse1339.239Nonealive21721752110.9495830.336875NoneNoneLEFT_FOOT00
213b1ddab-d22e-43d9-bfe4-12632fea1a27SHOTSAVEDFalse1928.625Nonealive21721755030.7662500.430625NoneNoneLEFT_FOOT00
3391bfb74-07a6-4afe-9568-02a9b23f5bd4SHOTOFF_TARGETFalse1979.616Nonealive20620666130.9087500.483125NoneNoneHEAD00
45e55f5a5-954f-4cc4-ba6e-a9cf6d6e249eSHOTOFF_TARGETFalse11095.914Nonealive21721752460.8912500.311875NoneNoneRIGHT_FOOT00
51c0347cd-14dc-4aa8-91eb-520672a6cfe1SHOTOFF_TARGETFalse11842.287Nonealive21721754770.9004170.341875NoneNoneLEFT_FOOT00
67c3182af-c8a8-4c7c-934e-5c41c7b93c6aSHOTOFF_TARGETFalse12104.861Nonealive21721754700.9329170.545625NoneNoneHEAD00
739f231e5-0072-461c-beb0-a9bedb420f83SHOTPOSTFalse12248.168Nonealive21721755030.8079170.674375NoneNoneLEFT_FOOT00
8062cdd08-8773-424f-8fc5-2e3d441c3c5cSHOTOFF_TARGETFalse12250.989Nonealive21721752130.9354170.516875NoneNoneHEAD00
9c09e904d-6c8e-479d-af2e-c2c5863aca71SHOTSAVEDFalse12308.083Nonealive21721754770.8537500.364375NoneNoneRIGHT_FOOT00
109eda2385-88bb-4c37-b039-a738c4cd8d4bSHOTOFF_TARGETFalse12434.592Nonealive21721752460.9104170.370625NoneNoneRIGHT_FOOT00
110b56df83-958e-4a31-9e5a-d741b4bf95cfSHOTOFF_TARGETFalse12610.612Nonealive21721754770.8870830.418125NoneNoneRIGHT_FOOT00
1279851101-0f47-48e2-bdac-2de2b2bc89cbSHOTSAVEDFalse2159.524Nonealive21721752110.9737500.389375NoneNoneLEFT_FOOT00
13d37ddbf4-05b6-4127-8ca6-287a069bd912SHOTBLOCKEDFalse2367.400Nonealive20620666260.8829170.286875NoneNoneLEFT_FOOT00
14b8f9567a-b33c-4013-91a4-dd5c088dd14aSHOTBLOCKEDFalse2534.355Nonealive21721752460.9254170.300625NoneNoneRIGHT_FOOT00
152e2b7445-812c-4162-a5a2-4a99290359aeSHOTBLOCKEDFalse2596.388Nonealive21721735010.7862500.305625NoneNoneRIGHT_FOOT00
16480a2132-0c0e-4a0b-95e7-eb994a1542ffSHOTBLOCKEDFalse2634.490Nonealive21721752110.8945830.323125NoneNoneLEFT_FOOT00
17dfb55124-1e6a-475c-a460-3d9bf920bf04SHOTOFF_TARGETFalse2740.847Nonealive21721755030.8337500.725625NoneNoneLEFT_FOOT00
18ff334a7e-6ec2-4c71-9957-6d8accaf48dfSHOTBLOCKEDFalse2936.156Nonealive21721735010.8345830.345625NoneNoneRIGHT_FOOT00
194c7c4ab1-6b9f-4504-a237-249c2e0c549fSHOTGOALTrue21091.954Nonealive21721755030.8004170.563125NoneNoneLEFT_FOOT00
2028175830-bbf0-4be9-baef-75c1de731809SHOTPOSTFalse21243.588Nonealive21721755030.8837500.588125NoneNoneLEFT_FOOT10
2189cef214-dec8-43c7-8222-3ea8942ae868SHOTBLOCKEDFalse21413.375Nonealive21721754700.7387500.534375NoneNoneRIGHT_FOOT10
220a78095f-dde1-4cba-8639-3ac2750d3a4dSHOTSAVEDFalse21647.492Nonealive21721752460.9087500.375625NoneNoneLEFT_FOOT10
236e7d482d-30cc-4f5c-9b65-84c52ef8247cSHOTOFF_TARGETFalse21997.679Nonealive20620669350.9445830.418125NoneNoneRIGHT_FOOT10
245f8f624e-eae9-4e6b-a086-d864bc8f4140SHOTSAVEDFalse22191.606Nonealive21721735010.9445830.388125NoneNoneRIGHT_FOOT10
25683c6752-13bc-4892-94ed-22e1c938f1f7SHOTGOALTrue22261.578Nonealive21721735010.8754170.388125NoneNoneRIGHT_FOOT10
26252b3061-7d3f-4922-b04f-0b37a44c6300SHOTSAVEDFalse22662.638Nonealive21721755030.8837500.575625NoneNoneLEFT_FOOT20
2755d71847-9511-4417-aea9-6f415e279011SHOTGOALTrue22802.770Nonealive21721755030.9329170.431875NoneNoneLEFT_FOOT20
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success \\\n", + "0 65f16e50-7c5d-4293-b2fc-d20887a772f9 SHOT OFF_TARGET False \n", + "1 b0f73423-3990-45ae-9dda-3512c2d1aff3 SHOT OFF_TARGET False \n", + "2 13b1ddab-d22e-43d9-bfe4-12632fea1a27 SHOT SAVED False \n", + "3 391bfb74-07a6-4afe-9568-02a9b23f5bd4 SHOT OFF_TARGET False \n", + "4 5e55f5a5-954f-4cc4-ba6e-a9cf6d6e249e SHOT OFF_TARGET False \n", + "5 1c0347cd-14dc-4aa8-91eb-520672a6cfe1 SHOT OFF_TARGET False \n", + "6 7c3182af-c8a8-4c7c-934e-5c41c7b93c6a SHOT OFF_TARGET False \n", + "7 39f231e5-0072-461c-beb0-a9bedb420f83 SHOT POST False \n", + "8 062cdd08-8773-424f-8fc5-2e3d441c3c5c SHOT OFF_TARGET False \n", + "9 c09e904d-6c8e-479d-af2e-c2c5863aca71 SHOT SAVED False \n", + "10 9eda2385-88bb-4c37-b039-a738c4cd8d4b SHOT OFF_TARGET False \n", + "11 0b56df83-958e-4a31-9e5a-d741b4bf95cf SHOT OFF_TARGET False \n", + "12 79851101-0f47-48e2-bdac-2de2b2bc89cb SHOT SAVED False \n", + "13 d37ddbf4-05b6-4127-8ca6-287a069bd912 SHOT BLOCKED False \n", + "14 b8f9567a-b33c-4013-91a4-dd5c088dd14a SHOT BLOCKED False \n", + "15 2e2b7445-812c-4162-a5a2-4a99290359ae SHOT BLOCKED False \n", + "16 480a2132-0c0e-4a0b-95e7-eb994a1542ff SHOT BLOCKED False \n", + "17 dfb55124-1e6a-475c-a460-3d9bf920bf04 SHOT OFF_TARGET False \n", + "18 ff334a7e-6ec2-4c71-9957-6d8accaf48df SHOT BLOCKED False \n", + "19 4c7c4ab1-6b9f-4504-a237-249c2e0c549f SHOT GOAL True \n", + "20 28175830-bbf0-4be9-baef-75c1de731809 SHOT POST False \n", + "21 89cef214-dec8-43c7-8222-3ea8942ae868 SHOT BLOCKED False \n", + "22 0a78095f-dde1-4cba-8639-3ac2750d3a4d SHOT SAVED False \n", + "23 6e7d482d-30cc-4f5c-9b65-84c52ef8247c SHOT OFF_TARGET False \n", + "24 5f8f624e-eae9-4e6b-a086-d864bc8f4140 SHOT SAVED False \n", + "25 683c6752-13bc-4892-94ed-22e1c938f1f7 SHOT GOAL True \n", + "26 252b3061-7d3f-4922-b04f-0b37a44c6300 SHOT SAVED False \n", + "27 55d71847-9511-4417-aea9-6f415e279011 SHOT GOAL True \n", + "\n", + " period_id timestamp end_timestamp ball_state ball_owning_team team_id \\\n", + "0 1 149.094 None alive 217 217 \n", + "1 1 339.239 None alive 217 217 \n", + "2 1 928.625 None alive 217 217 \n", + "3 1 979.616 None alive 206 206 \n", + "4 1 1095.914 None alive 217 217 \n", + "5 1 1842.287 None alive 217 217 \n", + "6 1 2104.861 None alive 217 217 \n", + "7 1 2248.168 None alive 217 217 \n", + "8 1 2250.989 None alive 217 217 \n", + "9 1 2308.083 None alive 217 217 \n", + "10 1 2434.592 None alive 217 217 \n", + "11 1 2610.612 None alive 217 217 \n", + "12 2 159.524 None alive 217 217 \n", + "13 2 367.400 None alive 206 206 \n", + "14 2 534.355 None alive 217 217 \n", + "15 2 596.388 None alive 217 217 \n", + "16 2 634.490 None alive 217 217 \n", + "17 2 740.847 None alive 217 217 \n", + "18 2 936.156 None alive 217 217 \n", + "19 2 1091.954 None alive 217 217 \n", + "20 2 1243.588 None alive 217 217 \n", + "21 2 1413.375 None alive 217 217 \n", + "22 2 1647.492 None alive 217 217 \n", + "23 2 1997.679 None alive 206 206 \n", + "24 2 2191.606 None alive 217 217 \n", + "25 2 2261.578 None alive 217 217 \n", + "26 2 2662.638 None alive 217 217 \n", + "27 2 2802.770 None alive 217 217 \n", + "\n", + " player_id coordinates_x coordinates_y end_coordinates_x \\\n", + "0 5503 0.930417 0.645625 None \n", + "1 5211 0.949583 0.336875 None \n", + "2 5503 0.766250 0.430625 None \n", + "3 6613 0.908750 0.483125 None \n", + "4 5246 0.891250 0.311875 None \n", + "5 5477 0.900417 0.341875 None \n", + "6 5470 0.932917 0.545625 None \n", + "7 5503 0.807917 0.674375 None \n", + "8 5213 0.935417 0.516875 None \n", + "9 5477 0.853750 0.364375 None \n", + "10 5246 0.910417 0.370625 None \n", + "11 5477 0.887083 0.418125 None \n", + "12 5211 0.973750 0.389375 None \n", + "13 6626 0.882917 0.286875 None \n", + "14 5246 0.925417 0.300625 None \n", + "15 3501 0.786250 0.305625 None \n", + "16 5211 0.894583 0.323125 None \n", + "17 5503 0.833750 0.725625 None \n", + "18 3501 0.834583 0.345625 None \n", + "19 5503 0.800417 0.563125 None \n", + "20 5503 0.883750 0.588125 None \n", + "21 5470 0.738750 0.534375 None \n", + "22 5246 0.908750 0.375625 None \n", + "23 6935 0.944583 0.418125 None \n", + "24 3501 0.944583 0.388125 None \n", + "25 3501 0.875417 0.388125 None \n", + "26 5503 0.883750 0.575625 None \n", + "27 5503 0.932917 0.431875 None \n", + "\n", + " end_coordinates_y body_part_type home_score away_score \n", + "0 None RIGHT_FOOT 0 0 \n", + "1 None LEFT_FOOT 0 0 \n", + "2 None LEFT_FOOT 0 0 \n", + "3 None HEAD 0 0 \n", + "4 None RIGHT_FOOT 0 0 \n", + "5 None LEFT_FOOT 0 0 \n", + "6 None HEAD 0 0 \n", + "7 None LEFT_FOOT 0 0 \n", + "8 None HEAD 0 0 \n", + "9 None RIGHT_FOOT 0 0 \n", + "10 None RIGHT_FOOT 0 0 \n", + "11 None RIGHT_FOOT 0 0 \n", + "12 None LEFT_FOOT 0 0 \n", + "13 None LEFT_FOOT 0 0 \n", + "14 None RIGHT_FOOT 0 0 \n", + "15 None RIGHT_FOOT 0 0 \n", + "16 None LEFT_FOOT 0 0 \n", + "17 None LEFT_FOOT 0 0 \n", + "18 None RIGHT_FOOT 0 0 \n", + "19 None LEFT_FOOT 0 0 \n", + "20 None LEFT_FOOT 1 0 \n", + "21 None RIGHT_FOOT 1 0 \n", + "22 None LEFT_FOOT 1 0 \n", + "23 None RIGHT_FOOT 1 0 \n", + "24 None RIGHT_FOOT 1 0 \n", + "25 None RIGHT_FOOT 1 0 \n", + "26 None LEFT_FOOT 2 0 \n", + "27 None LEFT_FOOT 2 0 " + ] + }, + "execution_count": 35, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataframe = dataset.to_df(\n", + " \"*\",\n", + " home_score=lambda event: event.state['score'].home,\n", + " away_score=lambda event: event.state['score'].away\n", + ")\n", + "dataframe" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now filter the dataframe. We only want to see shots when we are winning by at least two goals difference." + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
event_idevent_typeresultsuccessperiod_idtimestampend_timestampball_stateball_owning_teamteam_idplayer_idcoordinates_xcoordinates_yend_coordinates_xend_coordinates_ybody_part_typehome_scoreaway_score
26252b3061-7d3f-4922-b04f-0b37a44c6300SHOTSAVEDFalse22662.638Nonealive21721755030.8837500.575625NoneNoneLEFT_FOOT20
2755d71847-9511-4417-aea9-6f415e279011SHOTGOALTrue22802.770Nonealive21721755030.9329170.431875NoneNoneLEFT_FOOT20
\n", + "
" + ], + "text/plain": [ + " event_id event_type result success \\\n", + "26 252b3061-7d3f-4922-b04f-0b37a44c6300 SHOT SAVED False \n", + "27 55d71847-9511-4417-aea9-6f415e279011 SHOT GOAL True \n", + "\n", + " period_id timestamp end_timestamp ball_state ball_owning_team team_id \\\n", + "26 2 2662.638 None alive 217 217 \n", + "27 2 2802.770 None alive 217 217 \n", + "\n", + " player_id coordinates_x coordinates_y end_coordinates_x \\\n", + "26 5503 0.883750 0.575625 None \n", + "27 5503 0.932917 0.431875 None \n", + "\n", + " end_coordinates_y body_part_type home_score away_score \n", + "26 None LEFT_FOOT 2 0 \n", + "27 None LEFT_FOOT 2 0 " + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataframe[dataframe['home_score'] - dataframe['away_score'] >= 2]" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add state - lineup\n", + "\n", + "We are able to add more state. In this example we'll look at adding lineup state." + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from kloppy import statsbomb\n", + "from kloppy.domain import EventType\n", + "\n", + "dataset = statsbomb.load_open_data()\n", + "\n", + "home_team, away_team = dataset.metadata.teams" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Arturo Vidal is a substitute on the side of Barcelona. We add lineup to all events so we are able to filter out events where Arturo Vidal is on the pitch." + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "metadata": {}, + "outputs": [], + "source": [ + "arturo_vidal = home_team.get_player_by_id(8206)" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "metadata": {}, + "outputs": [], + "source": [ + "dataframe = (\n", + " dataset\n", + " .add_state('lineup')\n", + " .filter(lambda event: arturo_vidal in event.state['lineup'].players)\n", + " .to_df()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "time on pitch: 490.6479999999997 seconds\n" + ] + } + ], + "source": [ + "print(f\"time on pitch: {dataframe['timestamp'].max() - dataframe['timestamp'].min()} seconds\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "metadata": {}, + "outputs": [], + "source": [ + "dataframe = (\n", + " dataset\n", + " .add_state('lineup')\n", + " .filter(lambda event: event.event_type == EventType.PASS and event.team == home_team)\n", + " .to_df(\n", + " \"*\",\n", + " vidal_on_pitch=lambda event: arturo_vidal in event.state['lineup'].players\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
sumcountpercentage
vidal_on_pitch
False70979888.847118
True838894.318182
\n", + "
" + ], + "text/plain": [ + " sum count percentage\n", + "vidal_on_pitch \n", + "False 709 798 88.847118\n", + "True 83 88 94.318182" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataframe = dataframe.groupby(['vidal_on_pitch'])['success'].agg(['sum', 'count'])\n", + "dataframe['percentage'] = dataframe['sum'] / dataframe['count'] * 100\n", + "dataframe" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add state - formation\n", + "\n", + "In this example we'll look at adding formation state to all shots." + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages/kloppy-3.7.1-py3.10.egg/kloppy/_providers/statsbomb.py:67: UserWarning: \n", + "\n", + "You are about to use StatsBomb public data.\n", + "By using this data, you are agreeing to the user agreement. \n", + "The user agreement can be found here: https://github.com/statsbomb/open-data/blob/master/LICENSE.pdf\n", + "\n", + " warnings.warn(\n" + ] + } + ], + "source": [ + "from kloppy import statsbomb\n", + "from kloppy.domain import EventType\n", + "\n", + "\n", + "dataset = statsbomb.load_open_data()\n", + "\n", + "dataframe = (\n", + " dataset\n", + " .add_state('formation')\n", + " .filter(\n", + " lambda event: event.event_type == EventType.SHOT\n", + " )\n", + " .to_df(\n", + " \"*\",\n", + " Team=lambda event: str(event.team),\n", + " Formation=lambda event: str(\n", + " event.state['formation'].home \n", + " if event.team == dataset.metadata.teams[0] \n", + " else event.state['formation'].away\n", + " )\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
GoalsShotsPercentage
TeamFormation
Barcelona4-3-331421.428571
4-4-20110.000000
Deportivo Alavés4-1-4-1020.000000
4-4-2010.000000
\n", + "
" + ], + "text/plain": [ + " Goals Shots Percentage\n", + "Team Formation \n", + "Barcelona 4-3-3 3 14 21.428571\n", + " 4-4-2 0 11 0.000000\n", + "Deportivo Alavés 4-1-4-1 0 2 0.000000\n", + " 4-4-2 0 1 0.000000" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "dataframe_stats = (\n", + " dataframe\n", + " .groupby(['Team', 'Formation'])['success']\n", + " .agg(['sum', 'count'])\n", + ")\n", + "dataframe_stats['Percentage'] = (\n", + " dataframe_stats['sum'] \n", + " / dataframe_stats['count'] \n", + " * 100\n", + ")\n", + "dataframe_stats.rename(\n", + " columns={\n", + " 'sum': 'Goals',\n", + " 'count': 'Shots'\n", + " }\n", + ")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "kloppy-venv", + "language": "python", + "name": "kloppy-venv" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/docs/tutorials/examples/tracking_data.ipynb b/docs/tutorials/examples/tracking_data.ipynb new file mode 100644 index 00000000..2b613be3 --- /dev/null +++ b/docs/tutorials/examples/tracking_data.ipynb @@ -0,0 +1,3373 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Tracking data\n", + "\n", + "Metadata is available on tracking data. TRACAB and Metrica does not include any extra metadata besides team and jersey number. [EPTS](https://football-technology.fifa.com/en/media-tiles/research-development-epts-standard-data-format/) is the only format right now that contains additional information that will be loaded as metadata\n", + "\n", + "\n", + "## Loading Metrica data\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "scrolled": true + }, + "outputs": [], + "source": [ + "from kloppy import metrica\n", + "\n", + "dataset = metrica.load_open_data(limit=10)\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exploring the data\n", + "\n", + "When you want to show the name of a player you are advised to use `str(player)`. This will call the magic `__str__` method that handles fallbacks for missing data. By default it will return `full_name`, and fallback to 1) `first_name last_name` 2) `player_id`." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['home_11 (11)',\n", + " 'home_1 (1)',\n", + " 'home_2 (2)',\n", + " 'home_3 (3)',\n", + " 'home_4 (4)',\n", + " 'home_5 (5)',\n", + " 'home_6 (6)',\n", + " 'home_7 (7)',\n", + " 'home_8 (8)',\n", + " 'home_9 (9)',\n", + " 'home_10 (10)',\n", + " 'home_12 (12)',\n", + " 'home_13 (13)',\n", + " 'home_14 (14)']" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "metadata = dataset.metadata\n", + "home_team, away_team = metadata.teams\n", + "\n", + "[f\"{player} ({player.jersey_no})\" for player in home_team.players]\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "home - Home\n", + "away - Away\n" + ] + } + ], + "source": [ + "print(f\"{home_team.ground} - {home_team}\")\n", + "print(f\"{away_team.ground} - {away_team}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Working with tracking data\n", + "\n", + "The actual tracking data is available at `dataset.frames`. This list holds all frames. Each frame has a `players_coordinates` dictionary that is indexed by `Player` entities and has values of the `Point` type.\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Number of players in the frame: 22\n", + "List home team players coordinates\n", + "[Point(x=0.00082, y=0.51762),\n", + " Point(x=0.32648, y=0.34678),\n", + " Point(x=0.33701, y=0.51137),\n", + " Point(x=0.30927, y=0.64471),\n", + " Point(x=0.32137, y=0.78738),\n", + " Point(x=0.41094, y=0.27410999999999996),\n", + " Point(x=0.41698, y=0.52157),\n", + " Point(x=0.39125, y=0.6745),\n", + " Point(x=0.45388, y=0.78826),\n", + " Point(x=0.52697, y=0.6202),\n", + " Point(x=0.55243, y=0.56731)]\n" + ] + } + ], + "source": [ + "first_frame = dataset.frames[0]\n", + "print(f\"Number of players in the frame: {len(first_frame.players_coordinates)}\")\n", + "\n", + "from pprint import pprint\n", + "print(\"List home team players coordinates\")\n", + "pprint([\n", + " player_coordinates \n", + " for player, player_coordinates\n", + " in first_frame.players_coordinates.items()\n", + " if player.team == home_team\n", + "])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Exporting to a dataframe\n", + "\n", + "kloppy allows you to export a `dataset` to a `DataFrame`. Both polars and pandas are supported. You can use the following engines: `polars`, `pandas`, `pandas[pyarrow]`. The pyarrow engine requires python 3.8 or higher." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "period_id int64\n", + "timestamp float64\n", + "frame_id int64\n", + "ball_state object\n", + "ball_owning_team_id object\n", + " ... \n", + "away_23_s object\n", + "away_24_x float64\n", + "away_24_y float64\n", + "away_24_d object\n", + "away_24_s object\n", + "Length: 96, dtype: object" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = dataset.to_df(engine=\"pandas\") # default behaviour\n", + "df.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "period_id int64[pyarrow]\n", + "timestamp double[pyarrow]\n", + "frame_id int64[pyarrow]\n", + "ball_state null[pyarrow]\n", + "ball_owning_team_id null[pyarrow]\n", + " ... \n", + "away_23_s null[pyarrow]\n", + "away_24_x double[pyarrow]\n", + "away_24_y double[pyarrow]\n", + "away_24_d null[pyarrow]\n", + "away_24_s null[pyarrow]\n", + "Length: 96, dtype: object" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = dataset.to_df(engine=\"pandas[pyarrow]\")\n", + "df.dtypes" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
period_idtimestampframe_idball_stateball_owning_team_idball_xball_yball_zhome_11_xhome_11_y...away_22_daway_22_saway_23_xaway_23_yaway_23_daway_23_saway_24_xaway_24_yaway_24_daway_24_s
010.001<NA><NA>0.454720.61291<NA>0.000820.51762...<NA><NA>0.436930.94998<NA><NA>0.378330.72617<NA><NA>
110.042<NA><NA>0.496450.59344<NA>0.000960.51762...<NA><NA>0.436930.94998<NA><NA>0.378330.72617<NA><NA>
210.083<NA><NA>0.537160.57444<NA>0.001140.51762...<NA><NA>0.436930.94998<NA><NA>0.378330.72617<NA><NA>
310.124<NA><NA>0.553460.57769<NA>0.001210.51762...<NA><NA>0.436440.94962<NA><NA>0.377560.72527<NA><NA>
410.165<NA><NA>0.555120.59430<NA>0.001290.51762...<NA><NA>0.435800.95023<NA><NA>0.376630.72457<NA><NA>
510.206<NA><NA>0.556770.61091<NA>0.001400.51762...<NA><NA>0.435040.95116<NA><NA>0.375570.72327<NA><NA>
610.247<NA><NA>0.558420.62752<NA>0.001400.51762...<NA><NA>0.434230.95195<NA><NA>0.374390.72249<NA><NA>
710.288<NA><NA>0.560040.64371<NA>0.001400.51762...<NA><NA>0.433470.95343<NA><NA>0.373200.72152<NA><NA>
810.329<NA><NA>0.561730.66074<NA>0.001200.51807...<NA><NA>0.432730.95422<NA><NA>0.371960.72073<NA><NA>
910.3610<NA><NA>0.563380.67735<NA>0.001000.51853...<NA><NA>0.431990.95566<NA><NA>0.370680.71990<NA><NA>
\n", + "

10 rows × 96 columns

\n", + "
" + ], + "text/plain": [ + " period_id timestamp frame_id ball_state ball_owning_team_id ball_x \n", + "0 1 0.00 1 0.45472 \\\n", + "1 1 0.04 2 0.49645 \n", + "2 1 0.08 3 0.53716 \n", + "3 1 0.12 4 0.55346 \n", + "4 1 0.16 5 0.55512 \n", + "5 1 0.20 6 0.55677 \n", + "6 1 0.24 7 0.55842 \n", + "7 1 0.28 8 0.56004 \n", + "8 1 0.32 9 0.56173 \n", + "9 1 0.36 10 0.56338 \n", + "\n", + " ball_y ball_z home_11_x home_11_y ... away_22_d away_22_s away_23_x \n", + "0 0.61291 0.00082 0.51762 ... 0.43693 \\\n", + "1 0.59344 0.00096 0.51762 ... 0.43693 \n", + "2 0.57444 0.00114 0.51762 ... 0.43693 \n", + "3 0.57769 0.00121 0.51762 ... 0.43644 \n", + "4 0.59430 0.00129 0.51762 ... 0.43580 \n", + "5 0.61091 0.00140 0.51762 ... 0.43504 \n", + "6 0.62752 0.00140 0.51762 ... 0.43423 \n", + "7 0.64371 0.00140 0.51762 ... 0.43347 \n", + "8 0.66074 0.00120 0.51807 ... 0.43273 \n", + "9 0.67735 0.00100 0.51853 ... 0.43199 \n", + "\n", + " away_23_y away_23_d away_23_s away_24_x away_24_y away_24_d away_24_s \n", + "0 0.94998 0.37833 0.72617 \n", + "1 0.94998 0.37833 0.72617 \n", + "2 0.94998 0.37833 0.72617 \n", + "3 0.94962 0.37756 0.72527 \n", + "4 0.95023 0.37663 0.72457 \n", + "5 0.95116 0.37557 0.72327 \n", + "6 0.95195 0.37439 0.72249 \n", + "7 0.95343 0.37320 0.72152 \n", + "8 0.95422 0.37196 0.72073 \n", + "9 0.95566 0.37068 0.71990 \n", + "\n", + "[10 rows x 96 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "shape: (10, 96)\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "period_id\n", + "\n", + "timestamp\n", + "\n", + "frame_id\n", + "\n", + "ball_state\n", + "\n", + "ball_owning_team_id\n", + "\n", + "ball_x\n", + "\n", + "ball_y\n", + "\n", + "ball_z\n", + "\n", + "home_11_x\n", + "\n", + "home_11_y\n", + "\n", + "home_11_d\n", + "\n", + "home_11_s\n", + "\n", + "home_1_x\n", + "\n", + "home_1_y\n", + "\n", + "home_1_d\n", + "\n", + "home_1_s\n", + "\n", + "home_2_x\n", + "\n", + "home_2_y\n", + "\n", + "home_2_d\n", + "\n", + "home_2_s\n", + "\n", + "home_3_x\n", + "\n", + "home_3_y\n", + "\n", + "home_3_d\n", + "\n", + "home_3_s\n", + "\n", + "home_4_x\n", + "\n", + "home_4_y\n", + "\n", + "home_4_d\n", + "\n", + "home_4_s\n", + "\n", + "home_5_x\n", + "\n", + "home_5_y\n", + "\n", + "home_5_d\n", + "\n", + "home_5_s\n", + "\n", + "home_6_x\n", + "\n", + "home_6_y\n", + "\n", + "home_6_d\n", + "\n", + "home_6_s\n", + "\n", + "home_7_x\n", + "\n", + "...\n", + "\n", + "away_15_s\n", + "\n", + "away_16_x\n", + "\n", + "away_16_y\n", + "\n", + "away_16_d\n", + "\n", + "away_16_s\n", + "\n", + "away_17_x\n", + "\n", + "away_17_y\n", + "\n", + "away_17_d\n", + "\n", + "away_17_s\n", + "\n", + "away_18_x\n", + "\n", + "away_18_y\n", + "\n", + "away_18_d\n", + "\n", + "away_18_s\n", + "\n", + "away_19_x\n", + "\n", + "away_19_y\n", + "\n", + "away_19_d\n", + "\n", + "away_19_s\n", + "\n", + "away_20_x\n", + "\n", + "away_20_y\n", + "\n", + "away_20_d\n", + "\n", + "away_20_s\n", + "\n", + "away_21_x\n", + "\n", + "away_21_y\n", + "\n", + "away_21_d\n", + "\n", + "away_21_s\n", + "\n", + "away_22_x\n", + "\n", + "away_22_y\n", + "\n", + "away_22_d\n", + "\n", + "away_22_s\n", + "\n", + "away_23_x\n", + "\n", + "away_23_y\n", + "\n", + "away_23_d\n", + "\n", + "away_23_s\n", + "\n", + "away_24_x\n", + "\n", + "away_24_y\n", + "\n", + "away_24_d\n", + "\n", + "away_24_s\n", + "
\n", + "i64\n", + "\n", + "f64\n", + "\n", + "i64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "...\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "\n", + "f64\n", + "
\n", + "1\n", + "\n", + "0.0\n", + "\n", + "1\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45472\n", + "\n", + "0.61291\n", + "\n", + "null\n", + "\n", + "0.00082\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32648\n", + "\n", + "0.34678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33701\n", + "\n", + "0.51137\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30927\n", + "\n", + "0.64471\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32137\n", + "\n", + "0.78738\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41094\n", + "\n", + "0.27411\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41698\n", + "\n", + "0.52157\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39125\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67658\n", + "\n", + "0.5329\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.6731\n", + "\n", + "0.23524\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40783\n", + "\n", + "0.38475\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45472\n", + "\n", + "0.61291\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.5596\n", + "\n", + "0.32225\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55243\n", + "\n", + "0.56731\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.50067\n", + "\n", + "0.05678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43693\n", + "\n", + "0.94998\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37833\n", + "\n", + "0.72617\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.04\n", + "\n", + "2\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.49645\n", + "\n", + "0.59344\n", + "\n", + "null\n", + "\n", + "0.00096\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32648\n", + "\n", + "0.34678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33701\n", + "\n", + "0.51137\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30927\n", + "\n", + "0.64471\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32137\n", + "\n", + "0.78738\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41094\n", + "\n", + "0.27411\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41698\n", + "\n", + "0.52157\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39125\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67658\n", + "\n", + "0.5329\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.6731\n", + "\n", + "0.23524\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40783\n", + "\n", + "0.38475\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45472\n", + "\n", + "0.61291\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.5596\n", + "\n", + "0.32225\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55243\n", + "\n", + "0.56731\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.50067\n", + "\n", + "0.05678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43693\n", + "\n", + "0.94998\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37833\n", + "\n", + "0.72617\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.08\n", + "\n", + "3\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.53716\n", + "\n", + "0.57444\n", + "\n", + "null\n", + "\n", + "0.00114\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32648\n", + "\n", + "0.34678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33701\n", + "\n", + "0.51137\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30927\n", + "\n", + "0.64471\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32137\n", + "\n", + "0.78738\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41094\n", + "\n", + "0.27411\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41698\n", + "\n", + "0.52157\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39125\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67658\n", + "\n", + "0.5329\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.6731\n", + "\n", + "0.23524\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40783\n", + "\n", + "0.38475\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45472\n", + "\n", + "0.61291\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.5596\n", + "\n", + "0.32225\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55243\n", + "\n", + "0.56731\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.50067\n", + "\n", + "0.05678\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43693\n", + "\n", + "0.94998\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37833\n", + "\n", + "0.72617\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.12\n", + "\n", + "4\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55346\n", + "\n", + "0.57769\n", + "\n", + "null\n", + "\n", + "0.00121\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32622\n", + "\n", + "0.34683\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33687\n", + "\n", + "0.51012\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30944\n", + "\n", + "0.64446\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32142\n", + "\n", + "0.78759\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41067\n", + "\n", + "0.27435\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41703\n", + "\n", + "0.52035\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39134\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.6764\n", + "\n", + "0.53238\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.67279\n", + "\n", + "0.23458\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40771\n", + "\n", + "0.38495\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45454\n", + "\n", + "0.61182\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55974\n", + "\n", + "0.32224\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55236\n", + "\n", + "0.56687\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.50034\n", + "\n", + "0.05609\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43644\n", + "\n", + "0.94962\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37756\n", + "\n", + "0.72527\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.16\n", + "\n", + "5\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55512\n", + "\n", + "0.5943\n", + "\n", + "null\n", + "\n", + "0.00129\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32597\n", + "\n", + "0.34731\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33664\n", + "\n", + "0.50982\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30948\n", + "\n", + "0.64472\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32136\n", + "\n", + "0.78841\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41065\n", + "\n", + "0.2749\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41682\n", + "\n", + "0.52048\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39141\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67599\n", + "\n", + "0.53231\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.67253\n", + "\n", + "0.23436\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40736\n", + "\n", + "0.38529\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45426\n", + "\n", + "0.61275\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55992\n", + "\n", + "0.32209\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55202\n", + "\n", + "0.56689\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.50017\n", + "\n", + "0.05566\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.4358\n", + "\n", + "0.95023\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37663\n", + "\n", + "0.72457\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.2\n", + "\n", + "6\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55677\n", + "\n", + "0.61091\n", + "\n", + "null\n", + "\n", + "0.0014\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32581\n", + "\n", + "0.34841\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33637\n", + "\n", + "0.51034\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.30978\n", + "\n", + "0.64521\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32159\n", + "\n", + "0.78949\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41082\n", + "\n", + "0.27561\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41663\n", + "\n", + "0.5218\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.3919\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67537\n", + "\n", + "0.53273\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.67194\n", + "\n", + "0.23481\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40697\n", + "\n", + "0.38691\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45397\n", + "\n", + "0.61379\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56017\n", + "\n", + "0.32324\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55157\n", + "\n", + "0.56743\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.49989\n", + "\n", + "0.05568\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43504\n", + "\n", + "0.95116\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37557\n", + "\n", + "0.72327\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.24\n", + "\n", + "7\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55842\n", + "\n", + "0.62752\n", + "\n", + "null\n", + "\n", + "0.0014\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.3257\n", + "\n", + "0.34963\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33624\n", + "\n", + "0.51081\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.31015\n", + "\n", + "0.64561\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32198\n", + "\n", + "0.79079\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41094\n", + "\n", + "0.27703\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41641\n", + "\n", + "0.52318\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39235\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67471\n", + "\n", + "0.53342\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.67133\n", + "\n", + "0.23545\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40653\n", + "\n", + "0.38889\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45365\n", + "\n", + "0.61524\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56049\n", + "\n", + "0.32509\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55095\n", + "\n", + "0.568\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.49952\n", + "\n", + "0.05609\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43423\n", + "\n", + "0.95195\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37439\n", + "\n", + "0.72249\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.28\n", + "\n", + "8\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56004\n", + "\n", + "0.64371\n", + "\n", + "null\n", + "\n", + "0.0014\n", + "\n", + "0.51762\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32564\n", + "\n", + "0.35084\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33608\n", + "\n", + "0.51131\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.3105\n", + "\n", + "0.64627\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32243\n", + "\n", + "0.79227\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.411\n", + "\n", + "0.27849\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41618\n", + "\n", + "0.52467\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39281\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67406\n", + "\n", + "0.5345\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.67063\n", + "\n", + "0.23644\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.4061\n", + "\n", + "0.39067\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45323\n", + "\n", + "0.61675\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56084\n", + "\n", + "0.32694\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.55034\n", + "\n", + "0.56908\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.4992\n", + "\n", + "0.05658\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43347\n", + "\n", + "0.95343\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.3732\n", + "\n", + "0.72152\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.32\n", + "\n", + "9\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56173\n", + "\n", + "0.66074\n", + "\n", + "null\n", + "\n", + "0.0012\n", + "\n", + "0.51807\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32557\n", + "\n", + "0.35222\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33593\n", + "\n", + "0.5119\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.31089\n", + "\n", + "0.64675\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32282\n", + "\n", + "0.79394\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41109\n", + "\n", + "0.28002\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41592\n", + "\n", + "0.52596\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39319\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67335\n", + "\n", + "0.53554\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.66985\n", + "\n", + "0.23731\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40562\n", + "\n", + "0.39252\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.4528\n", + "\n", + "0.61873\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56123\n", + "\n", + "0.32896\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.54961\n", + "\n", + "0.56997\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.49885\n", + "\n", + "0.05704\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43273\n", + "\n", + "0.95422\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37196\n", + "\n", + "0.72073\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "1\n", + "\n", + "0.36\n", + "\n", + "10\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56338\n", + "\n", + "0.67735\n", + "\n", + "null\n", + "\n", + "0.001\n", + "\n", + "0.51853\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32551\n", + "\n", + "0.35363\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.33581\n", + "\n", + "0.51244\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.31133\n", + "\n", + "0.64735\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.32312\n", + "\n", + "0.79555\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41119\n", + "\n", + "0.28163\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.41563\n", + "\n", + "0.5276\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.39362\n", + "\n", + "...\n", + "\n", + "null\n", + "\n", + "0.67265\n", + "\n", + "0.53646\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.66911\n", + "\n", + "0.23839\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.40513\n", + "\n", + "0.39424\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.45237\n", + "\n", + "0.62088\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.56163\n", + "\n", + "0.33123\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.5488\n", + "\n", + "0.5711\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.49848\n", + "\n", + "0.05756\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.43199\n", + "\n", + "0.95566\n", + "\n", + "null\n", + "\n", + "null\n", + "\n", + "0.37068\n", + "\n", + "0.7199\n", + "\n", + "null\n", + "\n", + "null\n", + "
\n", + "
" + ], + "text/plain": [ + "shape: (10, 96)\n", + "┌─────────┬────────────┬──────────┬──────────┬─────┬───────────┬───────────┬───────────┬───────────┐\n", + "│ period_ ┆ timestamp ┆ frame_id ┆ ball_sta ┆ ... ┆ away_24_x ┆ away_24_y ┆ away_24_d ┆ away_24_s │\n", + "│ id ┆ --- ┆ --- ┆ te ┆ ┆ --- ┆ --- ┆ --- ┆ --- │\n", + "│ --- ┆ f64 ┆ i64 ┆ --- ┆ ┆ f64 ┆ f64 ┆ f64 ┆ f64 │\n", + "│ i64 ┆ ┆ ┆ f64 ┆ ┆ ┆ ┆ ┆ │\n", + "╞═════════╪════════════╪══════════╪══════════╪═════╪═══════════╪═══════════╪═══════════╪═══════════╡\n", + "│ 1 ┆ 0.0 ┆ 1 ┆ null ┆ ... ┆ 0.37833 ┆ 0.72617 ┆ null ┆ null │\n", + "│ 1 ┆ 0.04 ┆ 2 ┆ null ┆ ... ┆ 0.37833 ┆ 0.72617 ┆ null ┆ null │\n", + "│ 1 ┆ 0.08 ┆ 3 ┆ null ┆ ... ┆ 0.37833 ┆ 0.72617 ┆ null ┆ null │\n", + "│ 1 ┆ 0.12 ┆ 4 ┆ null ┆ ... ┆ 0.37756 ┆ 0.72527 ┆ null ┆ null │\n", + "│ ... ┆ ... ┆ ... ┆ ... ┆ ... ┆ ... ┆ ... ┆ ... ┆ ... │\n", + "│ 1 ┆ 0.24 ┆ 7 ┆ null ┆ ... ┆ 0.37439 ┆ 0.72249 ┆ null ┆ null │\n", + "│ 1 ┆ 0.28 ┆ 8 ┆ null ┆ ... ┆ 0.3732 ┆ 0.72152 ┆ null ┆ null │\n", + "│ 1 ┆ 0.32 ┆ 9 ┆ null ┆ ... ┆ 0.37196 ┆ 0.72073 ┆ null ┆ null │\n", + "│ 1 ┆ 0.36 ┆ 10 ┆ null ┆ ... ┆ 0.37068 ┆ 0.7199 ┆ null ┆ null │\n", + "└─────────┴────────────┴──────────┴──────────┴─────┴───────────┴───────────┴───────────┴───────────┘" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = dataset.to_df(engine=\"polars\")\n", + "df" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.6" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": false, + "title_cell": "Table of Contents", + "title_sidebar": "Contents", + "toc_cell": false, + "toc_position": {}, + "toc_section_display": true, + "toc_window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/mkdocs.yml b/mkdocs.yml index 5a44d7bd..15523dcb 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -31,16 +31,16 @@ nav: - SkillCorner: tutorials/getting-started/tracking-data/skillcorner.ipynb - StatsPerform: tutorials/getting-started/tracking-data/statsperform.ipynb - TRACAB: tutorials/getting-started/tracking-data/tracab.ipynb - - Examples: - - Event Data: examples/event_data.ipynb - - Tracking Data: examples/tracking_data.ipynb - - Broadcast Tracking Data: examples/broadcast_tracking_data.ipynb - - Code data: examples/code_data.ipynb - - State: examples/state.ipynb - - Navigating: examples/navigating.ipynb - - Plotting: examples/plotting.ipynb - - Config: examples/config.ipynb - - Adapters: examples/adapter.ipynb + - Examples: + - Event Data: tutorials/examples/event_data.ipynb + - Tracking Data: tutorials/examples/tracking_data.ipynb + - Broadcast Tracking Data: tutorials/examples/broadcast_tracking_data.ipynb + - Code data: tutorials/examples/code_data.ipynb + - State: tutorials/examples/state.ipynb + - Navigating: tutorials/examples/navigating.ipynb + - Plotting: tutorials/examples/plotting.ipynb + - Config: tutorials/examples/config.ipynb + - Adapters: tutorials/examples/adapter.ipynb # - API Reference: # - Domain: # - Common: api/domain/common.md From bc493995a764e41a429ccad0d24d2ed4114534fc Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Wed, 16 Aug 2023 08:54:10 +0200 Subject: [PATCH 04/12] missing qualifiers key --- kloppy/domain/services/event_factory.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/kloppy/domain/services/event_factory.py b/kloppy/domain/services/event_factory.py index 0f550ba5..01d6d991 100644 --- a/kloppy/domain/services/event_factory.py +++ b/kloppy/domain/services/event_factory.py @@ -44,6 +44,9 @@ def create_event(event_cls: Type[T], **kwargs) -> T: if "freeze_frame" not in kwargs: kwargs["freeze_frame"] = None + if 'qualifiers' not in kwargs: + kwargs['qualifiers'] = None + all_kwargs = dict(**kwargs, **extra_kwargs) relevant_kwargs = { @@ -55,7 +58,7 @@ def create_event(event_cls: Type[T], **kwargs) -> T: and field.name not in all_kwargs ) } - + if len(relevant_kwargs) < len(all_kwargs): skipped_kwargs = set(all_kwargs.keys()) - set(relevant_kwargs.keys()) warnings.warn( From 7d62607d8c003dfc6f5877684c01f4084e6fffd1 Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Wed, 16 Aug 2023 09:14:07 +0200 Subject: [PATCH 05/12] dnno --- .gitignore | 2 ++ kloppy/domain/services/event_factory.py | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index d7354f77..85685c95 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,5 @@ examples/pattern_matching/repository/*.json *.code-workspace .* + +testing.py \ No newline at end of file diff --git a/kloppy/domain/services/event_factory.py b/kloppy/domain/services/event_factory.py index 01d6d991..d5a14e2c 100644 --- a/kloppy/domain/services/event_factory.py +++ b/kloppy/domain/services/event_factory.py @@ -44,9 +44,6 @@ def create_event(event_cls: Type[T], **kwargs) -> T: if "freeze_frame" not in kwargs: kwargs["freeze_frame"] = None - if 'qualifiers' not in kwargs: - kwargs['qualifiers'] = None - all_kwargs = dict(**kwargs, **extra_kwargs) relevant_kwargs = { From 2910b0bf05cc3c035dbd7c936dcf02ecb9c5d41f Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Wed, 16 Aug 2023 13:58:03 +0200 Subject: [PATCH 06/12] bla --- kloppy/infra/serializers/tracking/skillcorner.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kloppy/infra/serializers/tracking/skillcorner.py b/kloppy/infra/serializers/tracking/skillcorner.py index ebdbe1e8..2ed726f8 100644 --- a/kloppy/infra/serializers/tracking/skillcorner.py +++ b/kloppy/infra/serializers/tracking/skillcorner.py @@ -4,6 +4,7 @@ from collections import Counter import numpy as np import json +from pathlib import Path from kloppy.domain import ( attacking_direction_from_frame, @@ -182,7 +183,14 @@ def _set_skillcorner_attacking_directions(cls, frames, periods): ].attacking_direction = AttackingDirection.NOT_SET def __load_json(self, file): - return json.load(file) + if Path(file.name).suffix == '.jsonl': + data = [] + with open(file.name, 'r') as f: + for line in f: + data.append(json.loads(line)) + return data + else: + return json.load(file) @classmethod def __get_periods(cls, tracking): From 9e18f4cbbe295cf405f064e38c9f40765211e8c2 Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Thu, 17 Aug 2023 15:09:15 +0200 Subject: [PATCH 07/12] skillcorner extrapolated_data support --- .../infra/serializers/tracking/skillcorner.py | 30 ++++++++++++++----- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/kloppy/infra/serializers/tracking/skillcorner.py b/kloppy/infra/serializers/tracking/skillcorner.py index 2ed726f8..24355655 100644 --- a/kloppy/infra/serializers/tracking/skillcorner.py +++ b/kloppy/infra/serializers/tracking/skillcorner.py @@ -137,7 +137,7 @@ def _get_frame_data( return Frame( frame_id=frame_id, - timestamp=frame_time, + timestamp=frame_time - periods[frame_period].start_timestamp, ball_coordinates=ball_coordinates, players_data=players_data, period=periods[frame_period], @@ -148,8 +148,20 @@ def _get_frame_data( @classmethod def _timestamp_from_timestring(cls, timestring): - m, s = timestring.split(":") - return 60 * float(m) + float(s) + # Split the timestring into hours, minutes, and seconds + parts = timestring.split(":") + + # If there are only two parts, it's in the old format (MM:SS.ss) + if len(parts) == 2: # "92:37.60" + m, s = parts + return 60 * float(m) + float(s) + # If there are three parts, it's in the new format (HH:MM:SS.ss) + elif len(parts) == 3: # "01:29:56.40" + h, m, s = parts + return 3600 * float(h) + 60 * float(m) + float(s) + else: + # Invalid format + raise ValueError("Invalid timestring format") @classmethod def _set_skillcorner_attacking_directions(cls, frames, periods): @@ -183,11 +195,15 @@ def _set_skillcorner_attacking_directions(cls, frames, periods): ].attacking_direction = AttackingDirection.NOT_SET def __load_json(self, file): - if Path(file.name).suffix == '.jsonl': + if Path(file.name).suffix == ".jsonl": data = [] - with open(file.name, 'r') as f: - for line in f: - data.append(json.loads(line)) + with open(file.name, "r") as file: + for line in file: + obj = json.loads(line) + # for each line rename timestamp to time to make it compatible with existing loader + if "timestamp" in obj: + obj["time"] = obj.pop("timestamp") + data.append(obj) return data else: return json.load(file) From 6ecbf549b07b2ada340fa9028e94da142c6541d4 Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Thu, 17 Aug 2023 15:36:47 +0200 Subject: [PATCH 08/12] gitignore --- .gitignore | 2 -- 1 file changed, 2 deletions(-) diff --git a/.gitignore b/.gitignore index 85685c95..d7354f77 100644 --- a/.gitignore +++ b/.gitignore @@ -148,5 +148,3 @@ examples/pattern_matching/repository/*.json *.code-workspace .* - -testing.py \ No newline at end of file From 87c00de257d544e6c5581ed733971d3c24c40796 Mon Sep 17 00:00:00 2001 From: Joris Bekkers Date: Thu, 17 Aug 2023 15:43:26 +0200 Subject: [PATCH 09/12] removed comments --- kloppy/infra/serializers/tracking/skillcorner.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/kloppy/infra/serializers/tracking/skillcorner.py b/kloppy/infra/serializers/tracking/skillcorner.py index 24355655..6e5cac7f 100644 --- a/kloppy/infra/serializers/tracking/skillcorner.py +++ b/kloppy/infra/serializers/tracking/skillcorner.py @@ -148,19 +148,15 @@ def _get_frame_data( @classmethod def _timestamp_from_timestring(cls, timestring): - # Split the timestring into hours, minutes, and seconds parts = timestring.split(":") - # If there are only two parts, it's in the old format (MM:SS.ss) - if len(parts) == 2: # "92:37.60" + if len(parts) == 2: m, s = parts return 60 * float(m) + float(s) - # If there are three parts, it's in the new format (HH:MM:SS.ss) - elif len(parts) == 3: # "01:29:56.40" + elif len(parts) == 3: h, m, s = parts return 3600 * float(h) + 60 * float(m) + float(s) else: - # Invalid format raise ValueError("Invalid timestring format") @classmethod From 5b016890da68dac9f8b99dbfea69c4f490cfb63f Mon Sep 17 00:00:00 2001 From: UnravelSports <64530306+UnravelSports@users.noreply.github.com> Date: Fri, 5 Jan 2024 15:59:23 +0100 Subject: [PATCH 10/12] Update skillcorner.py With open removed --- kloppy/infra/serializers/tracking/skillcorner.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/kloppy/infra/serializers/tracking/skillcorner.py b/kloppy/infra/serializers/tracking/skillcorner.py index 6e5cac7f..0fb81e16 100644 --- a/kloppy/infra/serializers/tracking/skillcorner.py +++ b/kloppy/infra/serializers/tracking/skillcorner.py @@ -193,13 +193,12 @@ def _set_skillcorner_attacking_directions(cls, frames, periods): def __load_json(self, file): if Path(file.name).suffix == ".jsonl": data = [] - with open(file.name, "r") as file: - for line in file: - obj = json.loads(line) - # for each line rename timestamp to time to make it compatible with existing loader - if "timestamp" in obj: - obj["time"] = obj.pop("timestamp") - data.append(obj) + for line in file: + obj = json.loads(line) + # for each line rename timestamp to time to make it compatible with existing loader + if "timestamp" in obj: + obj["time"] = obj.pop("timestamp") + data.append(obj) return data else: return json.load(file) From a39daf49f7e6f7d1bb4f5b2679edce3910e572b0 Mon Sep 17 00:00:00 2001 From: koenvo Date: Fri, 5 Jan 2024 16:02:23 +0100 Subject: [PATCH 11/12] Update event_factory.py --- kloppy/domain/services/event_factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kloppy/domain/services/event_factory.py b/kloppy/domain/services/event_factory.py index d5a14e2c..0f550ba5 100644 --- a/kloppy/domain/services/event_factory.py +++ b/kloppy/domain/services/event_factory.py @@ -55,7 +55,7 @@ def create_event(event_cls: Type[T], **kwargs) -> T: and field.name not in all_kwargs ) } - + if len(relevant_kwargs) < len(all_kwargs): skipped_kwargs = set(all_kwargs.keys()) - set(relevant_kwargs.keys()) warnings.warn( From ea7b26306471d21d29bf87635bd35ac1605d2b33 Mon Sep 17 00:00:00 2001 From: UnravelSports <64530306+UnravelSports@users.noreply.github.com> Date: Wed, 10 Jan 2024 16:21:11 +0000 Subject: [PATCH 12/12] Dries minor change to use SkillCorner ID as player ID --- kloppy/infra/serializers/tracking/skillcorner.py | 11 +++++------ kloppy/tests/test_skillcorner.py | 3 +++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/kloppy/infra/serializers/tracking/skillcorner.py b/kloppy/infra/serializers/tracking/skillcorner.py index 0fb81e16..a255e501 100644 --- a/kloppy/infra/serializers/tracking/skillcorner.py +++ b/kloppy/infra/serializers/tracking/skillcorner.py @@ -280,7 +280,7 @@ def deserialize(self, inputs: SkillCornerInputs) -> TrackingDataset: metadata["away_team"].get("id"): "away_team", } - player_id_to_team_dict = { + player_to_team_dict = { player["trackable_object"]: player["team_id"] for player in metadata["players"] } @@ -321,8 +321,7 @@ def deserialize(self, inputs: SkillCornerInputs) -> TrackingDataset: ) teams = [home_team, away_team] - for player_id in player_dict.keys(): - player = player_dict.get(player_id) + for player_track_obj_id, player in player_dict.items(): team_id = player["team_id"] if team_id == home_team_id: @@ -332,8 +331,8 @@ def deserialize(self, inputs: SkillCornerInputs) -> TrackingDataset: team_string = "AWAY" team = away_team - players[team_string][player_id] = Player( - player_id=f"{team.ground}_{player['number']}", + players[team_string][player_track_obj_id] = Player( + player_id=f"{player['id']}", team=team, jersey_no=player["number"], name=f"{player['first_name']} {player['last_name']}", @@ -378,7 +377,7 @@ def _iter(): teams, teamdict, players, - player_id_to_team_dict, + player_to_team_dict, periods, player_dict, anon_players, diff --git a/kloppy/tests/test_skillcorner.py b/kloppy/tests/test_skillcorner.py index 67741ad4..0688f033 100644 --- a/kloppy/tests/test_skillcorner.py +++ b/kloppy/tests/test_skillcorner.py @@ -50,6 +50,9 @@ def test_correct_deserialization(self, raw_data: Path, meta_data: Path): # are frames with wrong camera views and pregame skipped? assert dataset.records[0].timestamp == 11.2 + # make sure skillcorner ID is used as player ID + assert dataset.metadata.teams[0].players[0].player_id == "10247" + # make sure data is loaded correctly home_player = dataset.metadata.teams[0].players[2] assert dataset.records[0].players_data[