Skip to content

Commit

Permalink
Merge branch 'master' into feature/opta-fix-deflected-pass-end-coordi…
Browse files Browse the repository at this point in the history
…nates
  • Loading branch information
DriesDeprest committed May 28, 2024
2 parents ddc0af1 + f6e6af5 commit 7456d8d
Show file tree
Hide file tree
Showing 10 changed files with 507 additions and 107 deletions.
9 changes: 1 addition & 8 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: [3.8, 3.9, "3.10", "3.11"]
include:
- os: macos-latest
python-version: "3.7.16"
- os: ubuntu-latest
python-version: "3.7"
- os: windows-latest
python-version: "3.7"
python-version: [3.8, 3.9, "3.10", "3.11", "3.12"]

steps:
- uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion docs/examples/event_data.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -1882,7 +1882,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
"version": "3.10.13"
},
"toc": {
"base_numbering": 1,
Expand Down
21 changes: 11 additions & 10 deletions docs/examples/plotting.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,27 @@
"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: pandas in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (2.0.3)\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: pillow in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from mplsoccer) (9.4.0)\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: 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: 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: 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: 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: packaging>=20.0 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from matplotlib) (23.0)\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: 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: tzdata>=2022.1 in /Users/koen/Developer/Projects/PySport/kloppy/.venv/lib/python3.10/site-packages (from pandas->mplsoccer) (2023.3)\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 A new release of pip is available: \u001b[0m\u001b[31;49m23.0.1\u001b[0m\u001b[39;49m -> \u001b[0m\u001b[32;49m24.0\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"
]
}
Expand Down Expand Up @@ -87,7 +88,7 @@
},
{
"cell_type": "code",
"execution_count": 4,
"execution_count": 3,
"metadata": {},
"outputs": [
{
Expand Down Expand Up @@ -144,7 +145,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
"version": "3.10.13"
},
"toc": {
"base_numbering": 1,
Expand Down
15 changes: 10 additions & 5 deletions kloppy/infra/serializers/event/opta/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,9 +734,11 @@ def deserialize(self, inputs: OptaInputs) -> EventDataset:
matchdata_path = objectify.ObjectPath(
"SoccerFeed.SoccerDocument.MatchData"
)
match_result_path = objectify.ObjectPath(
"SoccerFeed.SoccerDocument.MatchData.MatchInfo.Result"

result_elements = f7_root.xpath(
"/SoccerFeed/SoccerDocument/MatchData/MatchInfo/Result"
)

team_elms = list(
matchdata_path.find(f7_root).iterchildren("TeamData")
)
Expand All @@ -756,9 +758,12 @@ def deserialize(self, inputs: OptaInputs) -> EventDataset:
)
score = Score(home=home_score, away=away_score)
teams = [home_team, away_team]
match_result_type = list(match_result_path.find(f7_root))[
0
].attrib["Type"]

if result_elements and "Type" in result_elements[0].attrib:
match_result_type = result_elements[0].attrib["Type"]
else:
match_result_type = None

periods = _create_periods(match_result_type)

if len(home_team.players) == 0 or len(away_team.players) == 0:
Expand Down
72 changes: 52 additions & 20 deletions kloppy/infra/serializers/tracking/tracab/tracab_dat.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import logging
from datetime import timedelta
import warnings
from typing import Tuple, Dict, NamedTuple, IO, Optional, Union
from typing import Dict, Optional, Union
import html

from lxml import objectify

Expand Down Expand Up @@ -63,23 +64,23 @@ def _frame_from_line(cls, teams, period, line, frame_rate):
team = teams[0]
elif team_id == 0:
team = teams[1]
else:
# it's probably -1, but make sure it doesn't crash
elif team_id in (-1, 3, 4):
continue
else:
raise DeserializationError(
f"Unknown Player Team ID: {team_id}"
)

player = team.get_player_by_jersey_number(jersey_no)

if not player:
player = Player(
player_id=f"{team.ground}_{jersey_no}",
team=team,
jersey_no=int(jersey_no),
if player:
players_data[player] = PlayerData(
coordinates=Point(float(x), float(y)), speed=float(speed)
)
else:
# continue
raise DeserializationError(
f"Player not found for player jersey no {jersey_no} of team: {team.name}"
)
team.players.append(player)

players_data[player] = PlayerData(
coordinates=Point(float(x), float(y)), speed=float(speed)
)

(
ball_x,
Expand Down Expand Up @@ -129,14 +130,37 @@ def __validate_inputs(inputs: Dict[str, Readable]):
if "raw_data" not in inputs:
raise ValueError("Please specify a value for 'raw_data'")

def deserialize(self, inputs: TRACABInputs) -> TrackingDataset:
# TODO: also used in Metrica, extract to a method
home_team = Team(team_id="home", name="home", ground=Ground.HOME)
away_team = Team(team_id="away", name="away", ground=Ground.AWAY)
teams = [home_team, away_team]
@staticmethod
def create_team(team_data, ground, start_frame_id):
team = Team(
team_id=str(team_data["TeamId"]),
name=html.unescape(team_data["ShortName"]),
ground=ground,
)

team.players = [
Player(
player_id=str(player["PlayerId"]),
team=team,
first_name=html.unescape(player["FirstName"]),
last_name=html.unescape(player["LastName"]),
name=html.unescape(
player["FirstName"] + " " + player["LastName"]
),
jersey_no=int(player["JerseyNo"]),
starting=True
if player["StartFrameCount"] == start_frame_id
else False,
)
for player in team_data["Players"]["Player"]
]

return team

def deserialize(self, inputs: TRACABInputs) -> TrackingDataset:
with performance_logging("Loading metadata", logger=logger):
match = objectify.fromstring(inputs.meta_data.read()).match
meta_data = objectify.fromstring(inputs.meta_data.read())
match = meta_data.match
frame_rate = int(match.attrib["iFrameRateFps"])
pitch_size_width = float(match.attrib["fPitchXSizeMeters"])
pitch_size_height = float(match.attrib["fPitchYSizeMeters"])
Expand All @@ -158,6 +182,14 @@ def deserialize(self, inputs: TRACABInputs) -> TrackingDataset:
)
)

home_team = self.create_team(
meta_data["HomeTeam"], Ground.HOME, start_frame_id
)
away_team = self.create_team(
meta_data["AwayTeam"], Ground.AWAY, start_frame_id
)
teams = [home_team, away_team]

with performance_logging("Loading data", logger=logger):
transformer = self.get_transformer(
pitch_length=pitch_size_width, pitch_width=pitch_size_height
Expand Down
Loading

0 comments on commit 7456d8d

Please sign in to comment.