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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions kloppy/_providers/pff.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ def load_tracking(
limit: Optional[int] = None,
coordinates: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load and deserialize tracking data from the provided metadata, roster metadata, and raw data files.
Expand All @@ -26,6 +27,9 @@ def load_tracking(
limit (Optional[int], optional): The maximum number of records to process. If None, all records are processed. Defaults to None.
coordinates (Optional[str], optional): The coordinate system to use for the tracking data (e.g., "pff"). Defaults to None.
only_alive (Optional[bool], optional): Whether to include only sequences when the ball is in play. Defaults to False.
exclude_missing_ball_frames (Optional[bool], optional): Whether to
exclude frames in which the ball coordinates are missing (e.g.
not tracked). Defaults to False.

Returns:
TrackingDataset: A deserialized TrackingDataset object containing the processed tracking data.
Expand All @@ -35,6 +39,7 @@ def load_tracking(
limit=limit,
coordinate_system=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(meta_data) as meta_data_fp,
Expand Down
4 changes: 4 additions & 0 deletions kloppy/_providers/secondspectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def load(
limit: Optional[int] = None,
coordinates: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load SecondSpectrum tracking data.
Expand All @@ -30,6 +31,8 @@ def load(
limit: Limit the number of frames to load to the first `limit` frames.
coordinates: The coordinate system to use.
only_alive: Only include frames in which the game is not paused.
exclude_missing_ball_frames: Exclude frames in which the ball
coordinates are missing (e.g. not tracked).

Returns:
The parsed tracking data.
Expand All @@ -39,6 +42,7 @@ def load(
limit=limit,
coordinate_system=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(meta_data) as meta_data_fp,
Expand Down
4 changes: 4 additions & 0 deletions kloppy/_providers/skillcorner.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def load(
include_empty_frames: Optional[bool] = False,
data_version: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load SkillCorner broadcast tracking data.
Expand All @@ -33,6 +34,8 @@ def load(
include_empty_frames: Include frames in which no objects were tracked.
only_alive: Only include frames in which the game is not paused.
data_version: Specify the input data version.
exclude_missing_ball_frames: Exclude frames in which the ball
coordinates are missing (e.g. not tracked).

Returns:
The parsed tracking data.
Expand All @@ -50,6 +53,7 @@ def load(
include_empty_frames=include_empty_frames,
data_version=data_version,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(meta_data) as meta_data_fp,
Expand Down
7 changes: 7 additions & 0 deletions kloppy/_providers/sportec.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def load_tracking(
limit: Optional[int] = None,
coordinates: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load Sportec Solutions tracking data.
Expand All @@ -68,6 +69,8 @@ def load_tracking(
limit: Limit the number of frames to load to the first `limit` frames.
coordinates: The coordinate system to use.
only_alive: Only include frames in which the game is not paused.
exclude_missing_ball_frames: Exclude frames in which the ball
coordinates are missing (e.g. not tracked).

Returns:
The parsed tracking data.
Expand All @@ -77,6 +80,7 @@ def load_tracking(
limit=limit,
coordinate_system=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(meta_data) as meta_data_fp,
Expand Down Expand Up @@ -194,6 +198,7 @@ def load_open_tracking_data(
limit: Optional[int] = None,
coordinates: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load tracking data for a game from the IDSSE dataset.
Expand All @@ -210,6 +215,7 @@ def load_open_tracking_data(
limit:
coordinates:
only_alive:
exclude_missing_ball_frames:

Notes:
The dataset contains seven full matches of raw event and position data
Expand Down Expand Up @@ -242,4 +248,5 @@ def load_open_tracking_data(
limit=limit,
coordinates=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
4 changes: 4 additions & 0 deletions kloppy/_providers/statsperform.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def load_tracking(
limit: Optional[int] = None,
coordinates: Optional[str] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load Stats Perform tracking data.
Expand All @@ -119,6 +120,8 @@ def load_tracking(
limit: Limit the number of frames to load to the first `limit` frames.
coordinates: The coordinate system to use.
only_alive: Only include frames in which the game is not paused.
exclude_missing_ball_frames: Exclude frames in which the ball
coordinates are missing (e.g. not tracked).

Returns:
The parsed tracking data.
Expand All @@ -129,6 +132,7 @@ def load_tracking(
limit=limit,
coordinate_system=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(ma1_data) as ma1_data_fp,
Expand Down
4 changes: 4 additions & 0 deletions kloppy/_providers/tracab.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ def load(
coordinates: Optional[str] = None,
only_alive: bool = False,
file_format: Optional[str] = None,
exclude_missing_ball_frames: Optional[bool] = False,
) -> TrackingDataset:
"""
Load TRACAB tracking data.
Expand All @@ -29,6 +30,8 @@ def load(
coordinates: The coordinate system to use.
only_alive: Only include frames in which the game is not paused.
file_format: Deprecated. The format will be inferred based on the file extensions.
exclude_missing_ball_frames: Exclude frames in which the ball
coordinates are missing (e.g. not tracked).

Returns:
The parsed tracking data.
Expand Down Expand Up @@ -79,6 +82,7 @@ def load(
limit=limit,
coordinate_system=coordinates,
only_alive=only_alive,
exclude_missing_ball_frames=exclude_missing_ball_frames,
)
with (
open_as_file(meta_data) as meta_data_fp,
Expand Down
5 changes: 5 additions & 0 deletions kloppy/infra/serializers/tracking/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(
limit: Optional[int] = None,
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
exclude_missing_ball_frames: Optional[bool] = False,
):
if not limit:
limit = 0
Expand All @@ -29,6 +30,10 @@ def __init__(

self.transformer_builder = DatasetTransformerBuilder(coordinate_system)

if not exclude_missing_ball_frames:
exclude_missing_ball_frames = False
self.exclude_missing_ball_frames = exclude_missing_ball_frames

def get_transformer(
self,
pitch_length: Optional[float] = None,
Expand Down
11 changes: 10 additions & 1 deletion kloppy/infra/serializers/tracking/pff.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,11 @@ def __init__(
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.only_alive = only_alive

self._ball_owning_team = None
Expand Down Expand Up @@ -369,6 +372,12 @@ def _iter():
)
)

if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue

# if Regular Time
if _frame_period in {1, 2}:
frames.append(frame)
Expand Down
27 changes: 22 additions & 5 deletions kloppy/infra/serializers/tracking/secondspectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,11 @@ def __init__(
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.only_alive = only_alive

@property
Expand All @@ -88,10 +91,17 @@ def _frame_from_framedata(cls, teams, period, frame_data):

if frame_data["ball"]["xyz"]:
ball_x, ball_y, ball_z = frame_data["ball"]["xyz"]
ball_coordinates = Point3D(
float(ball_x), float(ball_y), float(ball_z)
)
ball_speed = frame_data["ball"]["speed"]
if float(ball_z) == -10:
# Second Spectrum uses a z-coordinate of -10 as a sentinel
# value to indicate that the ball position is unknown (e.g.
# dead ball frames where the ball is not tracked).
ball_coordinates = None
ball_speed = None
else:
ball_coordinates = Point3D(
float(ball_x), float(ball_y), float(ball_z)
)
ball_speed = frame_data["ball"]["speed"]
else:
ball_coordinates = None
ball_speed = None
Expand Down Expand Up @@ -302,6 +312,13 @@ def _iter():

frame = self._frame_from_framedata(teams, period, frame_data)
frame = transformer.transform_frame(frame)

if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue

frames.append(frame)

n_frames += 1
Expand Down
11 changes: 10 additions & 1 deletion kloppy/infra/serializers/tracking/skillcorner.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,11 @@ def __init__(
include_empty_frames: Optional[bool] = False,
data_version: Optional[str] = None,
only_alive: bool = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.include_empty_frames = include_empty_frames
self.data_version = data_version
self.only_alive = only_alive
Expand Down Expand Up @@ -566,6 +569,12 @@ def _iter():
continue
frame = transformer.transform_frame(frame)

if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue

frames.append(frame)
n_frames += 1

Expand Down
15 changes: 13 additions & 2 deletions kloppy/infra/serializers/tracking/sportec/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -308,8 +308,11 @@ def __init__(
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
only_alive: bool = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.only_alive = only_alive

def deserialize(self, inputs: SportecTrackingDataInputs) -> TrackingDataset:
Expand Down Expand Up @@ -423,7 +426,15 @@ def deserialize(self, inputs: SportecTrackingDataInputs) -> TrackingDataset:
ball_speed=float(ball_data.get("S", 0)),
other_data={},
)
frames.append(transformer.transform_frame(frame))
frame = transformer.transform_frame(frame)

if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue

frames.append(frame)
frame_count += 1
except KeyError as e:
logger.warning(
Expand Down
10 changes: 9 additions & 1 deletion kloppy/infra/serializers/tracking/statsperform.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,11 @@ def __init__(
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
only_alive: Optional[bool] = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.only_alive = only_alive
self._provider = provider

Expand Down Expand Up @@ -190,6 +193,11 @@ def _iter():
self.only_alive and frame.ball_state == BallState.DEAD
):
continue
if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue
frames.append(frame)

n_frames += 1
Expand Down
12 changes: 11 additions & 1 deletion kloppy/infra/serializers/tracking/tracab/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,11 @@ def __init__(
sample_rate: Optional[float] = None,
coordinate_system: Optional[Union[str, Provider]] = None,
only_alive: bool = False,
exclude_missing_ball_frames: Optional[bool] = False,
):
super().__init__(limit, sample_rate, coordinate_system)
super().__init__(
limit, sample_rate, coordinate_system, exclude_missing_ball_frames
)
self.only_alive = only_alive

@property
Expand Down Expand Up @@ -68,6 +71,13 @@ def deserialize(self, inputs: TRACABInputs) -> TrackingDataset:
)
):
frame = transformer.transform_frame(frame)

if (
self.exclude_missing_ball_frames
and frame.ball_coordinates is None
):
continue

frames.append(frame)

if self.limit and n + 1 >= (self.limit / self.sample_rate):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{"period": 1, "frameIdx": 0, "gameClock": 0.0, "wallClock": 0, "homePlayers": [{"playerId": "20grw", "number": 18, "xyz": [47.08390519972812, -23.939657073124085, 0.0], "speed": 4.159728710616509}, {"playerId": "6isrbi", "number": 19, "xyz": [19.17951616720707, -18.48668548635993, 0.0], "speed": 2.970267598952689}, {"playerId": "um288", "number": 4, "xyz": [-30.509158016824763, 33.65864992667011, 0.0], "speed": 6.348061414609102}, {"playerId": "4g3xq", "number": 10, "xyz": [30.17646822220036, -23.553471074622724, 0.0], "speed": 7.198769564560433}, {"playerId": "8xwx2", "number": 2, "xyz": [-8.943903672572427, -28.171654132650364, 0.0], "speed": 6.578958220040129}, {"playerId": "92vl5", "number": 13, "xyz": [-18.92639312241409, -19.33210269741475, 0.0], "speed": 1.8135661713032674}, {"playerId": "bvgtb", "number": 16, "xyz": [-13.924452254475682, 23.13785420227054, 0.0], "speed": 7.226293244793556}, {"playerId": "1lxq7v", "number": 14, "xyz": [-9.914449514605563, 9.328319654492567, 0.0], "speed": 1.9584318294446863}, {"playerId": "45w3q", "number": 3, "xyz": [-7.022877875154813, 23.023272240950618, 0.0], "speed": 7.109345942137604}, {"playerId": "qyk3c", "number": 15, "xyz": [-4.987389181079115, 30.22372922092669, 0.0], "speed": 5.178095998697229}, {"playerId": "o8x4i", "number": 8, "xyz": [-3.064965005231983, -13.660815735415385, 0.0], "speed": 7.6112618403084875}], "awayPlayers": [{"playerId": "r63sch", "number": 16, "xyz": [-8.665538194381213, 5.617606910595072, 0], "speed": 5.760820874469998}, {"playerId": "rtwi56", "number": 9, "xyz": [-19.81448388759739, -22.918811216735563, 0], "speed": 6.481607157019558}, {"playerId": "v7whr2", "number": 19, "xyz": [9.202983907546757, -19.60341782134609, 0], "speed": 0.878302509243686}, {"playerId": "j4mao7", "number": 4, "xyz": [-10.735280832860404, 30.31540452260084, 0], "speed": 3.731662373613796}, {"playerId": "rifq85", "number": 5, "xyz": [-21.39492284025012, -5.9462944058408, 0], "speed": 4.0277387540324705}, {"playerId": "vopck", "number": 18, "xyz": [22.97612688473503, -2.0016430713183926, 0], "speed": 5.9327388004505215}, {"playerId": "2q0uv", "number": 3, "xyz": [-45.11871334915762, -20.06459030559596, 0], "speed": 6.013778909583734}, {"playerId": "9bgzhy", "number": 0, "xyz": [-14.093654281648053, 33.55791756142988, 0], "speed": 0.3998524714346736}, {"playerId": "56zeu", "number": 12, "xyz": [-47.577496222772595, 17.454360144081754, 0], "speed": 3.138109659801027}, {"playerId": "27cl51", "number": 17, "xyz": [-26.504869268227615, 27.962809235078748, 0], "speed": 5.601974076542458}, {"playerId": "eh90mu", "number": 15, "xyz": [27.448466335973844, 8.19488340273177, 0], "speed": 6.810978214682541}], "ball": {"xyz": [35.677209254343666, 7.89935190646589, 0.0], "speed": [2.7858965434718073]}, "live": false, "lastTouch": "home"}
{"period": 1, "frameIdx": 400, "gameClock": 16.0, "wallClock": 16000, "homePlayers": [{"playerId": "20grw", "number": 18, "xyz": [25.268790982431074, 22.20366326500799, 0.0], "speed": 3.399643389984491}, {"playerId": "6isrbi", "number": 19, "xyz": [30.58807809399908, -6.795431589960108, 0.0], "speed": 2.089086482831614}, {"playerId": "um288", "number": 4, "xyz": [12.384734186892985, 29.872026327916622, 0.0], "speed": 1.281519916600737}, {"playerId": "4g3xq", "number": 10, "xyz": [11.289462273006825, -21.286392309162064, 0.0], "speed": 7.481216812137205}, {"playerId": "8xwx2", "number": 2, "xyz": [31.18022590708573, -20.300594162860495, 0.0], "speed": 6.258715381588318}, {"playerId": "92vl5", "number": 13, "xyz": [-8.862631236841855, 1.7995532080610457, 0.0], "speed": 4.454909450993342}, {"playerId": "bvgtb", "number": 16, "xyz": [25.914033752537225, 7.577602665341962, 0.0], "speed": 2.269060713765395}, {"playerId": "1lxq7v", "number": 14, "xyz": [-4.53962458264393, 18.18907904228159, 0.0], "speed": 7.113934776712516}, {"playerId": "45w3q", "number": 3, "xyz": [31.14073482829733, -32.98296716567569, 0.0], "speed": 3.506944273542337}, {"playerId": "qyk3c", "number": 15, "xyz": [41.536043363743346, -11.869768643616899, 0.0], "speed": 4.446668017766199}, {"playerId": "o8x4i", "number": 8, "xyz": [-34.144823948253084, 13.020788077473325, 0.0], "speed": 2.2582109522856735}], "awayPlayers": [{"playerId": "r63sch", "number": 16, "xyz": [42.503700344829156, 8.449738887757828, 0], "speed": 6.842748604269784}, {"playerId": "rtwi56", "number": 9, "xyz": [-45.2791551642064, 32.827281801557355, 0], "speed": 4.4133759698368555}, {"playerId": "v7whr2", "number": 19, "xyz": [23.87036121406162, 29.37794190783506, 0], "speed": 1.4989383465177823}, {"playerId": "j4mao7", "number": 4, "xyz": [47.537378317299336, -2.800366493529472, 0], "speed": 5.93025565914477}, {"playerId": "rifq85", "number": 5, "xyz": [43.505059125325836, -17.11545218922354, 0], "speed": 0.2551582847421887}, {"playerId": "vopck", "number": 18, "xyz": [47.97213745552502, -15.844698779340803, 0], "speed": 1.1468915263706894}, {"playerId": "2q0uv", "number": 3, "xyz": [18.6206553724836, -22.520512965631585, 0], "speed": 1.6840667945456342}, {"playerId": "9bgzhy", "number": 0, "xyz": [-20.080274662004204, -4.428060178117001, 0], "speed": 6.175830486845053}, {"playerId": "56zeu", "number": 12, "xyz": [-18.339986168931866, 8.46679062167231, 0], "speed": 5.4975572149978}, {"playerId": "27cl51", "number": 17, "xyz": [2.3443601968923917, -3.673330114344593, 0], "speed": 1.4473853671749843}, {"playerId": "eh90mu", "number": 15, "xyz": [33.969400496582615, 1.560273112097392, 0], "speed": 0.3955959287690938}], "ball": {"xyz": [-23.147073918432426, 13.69367399756424, -10], "speed": 0.0}, "live": false, "lastTouch": "away"}
Loading