Skip to content

Commit

Permalink
Notes:
Browse files Browse the repository at this point in the history
1.	Remained with Pieter’s initial proposal of only adding: AERIAL, GROUND, LOOSE_BALL & SLIDING_TACKLE
2.	StatsBomb: Checked qualifiers with “name” instead of id, since ids are not consistent in StatsBomb open data. As per StatsBomb helpdesk.
3.	Added a method: .get_qualifier_values() . Which returns a list of Qualifiers instead of .get_qualifier_value(), that returns the first Qualifier.
4.	Also Added NEUTRAL as outcome, since this is provided in wyscout_v2
  • Loading branch information
MKlaasman committed Jul 5, 2023
1 parent da95d6a commit 00da3ff
Show file tree
Hide file tree
Showing 28 changed files with 222 additions and 170 deletions.
1 change: 0 additions & 1 deletion kloppy/_providers/datafactory.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ def load(
event_factory=event_factory or get_config("event_factory"),
)
with open_as_file(event_data) as event_data_fp:

return deserializer.deserialize(
inputs=DatafactoryInputs(event_data=event_data_fp),
)
1 change: 0 additions & 1 deletion kloppy/_providers/opta.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def load(
with open_as_file(f7_data) as f7_data_fp, open_as_file(
f24_data
) as f24_data_fp:

return deserializer.deserialize(
inputs=OptaInputs(f7_data=f7_data_fp, f24_data=f24_data_fp),
)
1 change: 0 additions & 1 deletion kloppy/_providers/statsbomb.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ def load(
) as lineup_data_fp, open_as_file(
Source.create(three_sixty_data, optional=True)
) as three_sixty_data_fp:

return deserializer.deserialize(
inputs=StatsBombInputs(
event_data=event_data_fp,
Expand Down
4 changes: 0 additions & 4 deletions kloppy/domain/models/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,6 @@ def vertical_orientation(self) -> VerticalOrientation:

@property
def pitch_dimensions(self) -> PitchDimensions:

if self.length is not None and self.width is not None:
return PitchDimensions(
x_dim=Dimension(0, 1),
Expand Down Expand Up @@ -656,7 +655,6 @@ def pitch_dimensions(self) -> PitchDimensions:


def build_coordinate_system(provider: Provider, **kwargs):

if provider == Provider.TRACAB:
return TracabCoordinateSystem(normalized=False, **kwargs)

Expand Down Expand Up @@ -966,7 +964,6 @@ def to_records(
as_list: bool = True,
**named_columns: "Column",
) -> Union[List[Dict[str, Any]], Iterable[Dict[str, Any]]]:

from ..services.transformers.data_record import get_transformer_cls

transformer = get_transformer_cls(self.dataset_type)(
Expand All @@ -984,7 +981,6 @@ def to_dict(
orient: Literal["list"] = "list",
**named_columns: "Column",
) -> Dict[str, List[Any]]:

if orient == "list":
from ..services.transformers.data_record import get_transformer_cls

Expand Down
5 changes: 2 additions & 3 deletions kloppy/domain/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,12 @@ class DuelResult(ResultType):
Attributes:
WON (DuelResult): When winning the duel (player touching the ball first)
LOST (DuelResult): When losing the duel (opponent touches the ball first)
NEUTRAL (DuelResult): When neither player wins duel [Mainly for WyScout v2]
"""

WON = "WON"
LOST = "LOST"
NEUTRAL = "NEUTRAL"

@property
def is_success(self):
Expand Down Expand Up @@ -385,14 +387,12 @@ class DuelType(Enum):
GROUND (DuelType): A duel when the ball is on the ground.
LOOSE_BALL (DuelType): When the ball is not under the control of any particular player or team.
SLIDING_TACKLE (DuelType): A duel where the player slides on the ground to kick the ball away from an opponent.
STANDING_TACKLE (DuelType): A duel where the player makes a standing tackle.
"""

AERIAL = "AERIAL"
GROUND = "GROUND"
LOOSE_BALL = "LOOSE_BALL"
SLIDING_TACKLE = "SLIDING_TACKLE"
STANDING_TACKLE = "STANDING_TACKLE"


@dataclass
Expand Down Expand Up @@ -469,7 +469,6 @@ def get_qualifier_value(self, qualifier_type: Type[Qualifier]):
return qualifier.value
return None


def get_qualifier_values(self, qualifier_type: Type[Qualifier]):
"""
Returns all Qualifiers of a certain type, or None if qualifier is not present.
Expand Down
1 change: 0 additions & 1 deletion kloppy/domain/services/state_builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def add_state(dataset: EventDataset, *builder_keys: List[str]) -> EventDataset:

events = []
for event in dataset.events:

state = {
builder_key: builder.reduce_before(state[builder_key], event)
for builder_key, builder in builders.items()
Expand Down
1 change: 0 additions & 1 deletion kloppy/domain/services/state_builder/builders/sequence.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ def reduce_before(self, state: Sequence, event: Event) -> Sequence:
return state

def reduce_after(self, state: Sequence, event: Event) -> Sequence:

if isinstance(event, CLOSE_SEQUENCE):
state = replace(
state, sequence_id=state.sequence_id + 1, team=None
Expand Down
1 change: 0 additions & 1 deletion kloppy/domain/services/transformers/attribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,6 @@ def __call__(self, frame: Frame) -> Dict[str, Any]:
else None,
)
for player, player_data in frame.players_data.items():

row.update(
{
f"{player.player_id}_x": player_data.coordinates.x
Expand Down
1 change: 0 additions & 1 deletion kloppy/domain/services/transformers/data_record.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ def __init__(
**named_columns: Union[str, Callable[[T], Any]],
):
if not columns and not named_columns:

converter = self.default_transformer()
else:
default = self.default_transformer()
Expand Down
13 changes: 0 additions & 13 deletions kloppy/domain/services/transformers/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ def __init__(
to_pitch_dimensions: Optional[PitchDimensions] = None,
to_orientation: Optional[Orientation] = None,
):

if (
from_pitch_dimensions
and from_coordinate_system
Expand Down Expand Up @@ -90,7 +89,6 @@ def _needs_pitch_dimensions_change(self):
def change_point_dimensions(
self, point: Union[Point, Point3D, None]
) -> Union[Point, Point3D, None]:

if point is None:
return None

Expand All @@ -108,7 +106,6 @@ def change_point_dimensions(
def flip_point(
self, point: Union[Point, Point3D, None]
) -> Union[Point, Point3D, None]:

if not point:
return None

Expand Down Expand Up @@ -160,7 +157,6 @@ def __needs_flip(
return flip

def transform_frame(self, frame: Frame) -> Frame:

# Change coordinate system
if self._needs_coordinate_system_change:
frame = self.__change_frame_coordinate_system(frame)
Expand All @@ -178,7 +174,6 @@ def transform_frame(self, frame: Frame) -> Frame:
return frame

def __change_frame_coordinate_system(self, frame: Frame):

return Frame(
# doesn't change
timestamp=frame.timestamp,
Expand All @@ -205,7 +200,6 @@ def __change_frame_coordinate_system(self, frame: Frame):
)

def __change_frame_dimensions(self, frame: Frame):

return Frame(
# doesn't change
timestamp=frame.timestamp,
Expand Down Expand Up @@ -234,7 +228,6 @@ def __change_frame_dimensions(self, frame: Frame):
def __change_point_coordinate_system(
self, point: Union[Point, Point3D, None]
) -> Union[Point, Point3D, None]:

if not point:
return None

Expand All @@ -257,7 +250,6 @@ def __change_point_coordinate_system(
return Point(x=x, y=y)

def __flip_frame(self, frame: Frame):

players_data = {}
for player, data in frame.players_data.items():
players_data[player] = PlayerData(
Expand All @@ -281,7 +273,6 @@ def __flip_frame(self, frame: Frame):
)

def transform_event(self, event: Event) -> Event:

# Change coordinate system
if self._needs_coordinate_system_change:
event = self.__change_event_coordinate_system(event)
Expand All @@ -303,7 +294,6 @@ def transform_event(self, event: Event) -> Event:
return event

def __change_event_coordinate_system(self, event: Event):

position_changes = {
field.name: self.__change_point_coordinate_system(
getattr(event, field.name)
Expand All @@ -316,7 +306,6 @@ def __change_event_coordinate_system(self, event: Event):
return replace(event, **position_changes)

def __change_event_dimensions(self, event: Event):

position_changes = {
field.name: self.change_point_dimensions(
getattr(event, field.name)
Expand All @@ -329,7 +318,6 @@ def __change_event_dimensions(self, event: Event):
return replace(event, **position_changes)

def __flip_event(self, event: Event):

position_changes = {
field.name: self.flip_point(getattr(event, field.name))
for field in fields(event)
Expand All @@ -350,7 +338,6 @@ def transform_dataset(
to_orientation: Optional[Orientation] = None,
to_coordinate_system: Optional[CoordinateSystem] = None,
) -> Dataset:

if (
to_pitch_dimensions is None
and to_orientation is None
Expand Down
1 change: 0 additions & 1 deletion kloppy/infra/serializers/event/datafactory/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,7 +353,6 @@ def provider(self) -> Provider:
return Provider.DATAFACTORY

def deserialize(self, inputs: DatafactoryInputs) -> EventDataset:

transformer = self.get_transformer(length=2, width=2)

with performance_logging("load data", logger=logger):
Expand Down
5 changes: 0 additions & 5 deletions kloppy/infra/serializers/event/metrica/json_deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def _parse_subtypes(event: dict) -> List:
def _parse_pass(
event: Dict, previous_event: Dict, subtypes: List, team: Team
) -> Dict:

event_type_id = event["type"]["id"]

if event_type_id == MS_PASS_OUTCOME_COMPLETE:
Expand Down Expand Up @@ -157,7 +156,6 @@ def _parse_pass(
def _get_event_qualifiers(
event: Dict, previous_event: Dict, subtypes: List
) -> List[Qualifier]:

qualifiers = []

qualifiers.extend(_get_event_setpiece_qualifiers(previous_event, subtypes))
Expand All @@ -169,7 +167,6 @@ def _get_event_qualifiers(
def _get_event_setpiece_qualifiers(
previous_event: Dict, subtypes: List
) -> List[Qualifier]:

qualifiers = []
previous_event_type_id = previous_event["type"]["id"]
if previous_event_type_id == MS_SET_PIECE:
Expand All @@ -193,7 +190,6 @@ def _get_event_setpiece_qualifiers(


def _get_event_bodypart_qualifiers(subtypes: List) -> List[Qualifier]:

qualifiers = []
if subtypes and MS_BODY_PART_HEAD in subtypes:
qualifiers.append(BodyPartQualifier(value=BodyPart.HEAD))
Expand Down Expand Up @@ -274,7 +270,6 @@ def deserialize(self, inputs: MetricaJsonEventDataInputs) -> EventDataset:
with performance_logging("parse data", logger=logger):
events = []
for i, raw_event in enumerate(raw_events["data"]):

if raw_event["team"]["id"] == metadata.teams[0].team_id:
team = metadata.teams[0]
elif raw_event["team"]["id"] == metadata.teams[1].team_id:
Expand Down
39 changes: 23 additions & 16 deletions kloppy/infra/serializers/event/opta/deserializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
ShotResult,
TakeOnResult,
DuelResult,
DuelType,
DuelQualifier,
Ground,
Score,
Provider,
Expand All @@ -41,8 +43,6 @@
BodyPart,
PassType,
PassQualifier,
DuelType,
DuelQualifier
)
from kloppy.exceptions import DeserializationError
from kloppy.infra.serializers.event.deserializer import EventDataDeserializer
Expand Down Expand Up @@ -319,8 +319,22 @@ def _parse_shot(

def _parse_duel(raw_qualifiers: List, type_id: int, outcome: int) -> Dict:
qualifiers = _get_event_qualifiers(raw_qualifiers)
duel_qualifiers = _get_duel_qualifiers(type_id)
qualifiers.extend(duel_qualifiers)
if type_id == EVENT_TYPE_TACKLE:
qualifiers.extend([DuelQualifier(value=DuelType.GROUND)])
elif type_id == EVENT_TYPE_AERIAL:
qualifiers.extend(
[
DuelQualifier(value=DuelType.LOOSE_BALL),
DuelQualifier(value=DuelType.AERIAL),
]
)
elif type_id == EVENT_TYPE_50_50:
qualifiers.extend(
[
DuelQualifier(value=DuelType.LOOSE_BALL),
DuelQualifier(value=DuelType.GROUND),
]
)

if outcome:
result = DuelResult.WON
Expand Down Expand Up @@ -475,17 +489,6 @@ def _get_event_card_qualifiers(raw_qualifiers: List) -> List[Qualifier]:
return qualifiers


def _get_duel_qualifiers(type_id: int) -> List[Qualifier]:
if type_id == EVENT_TYPE_TACKLE:
duel_qualifiers = [DuelQualifier(value=DuelType.GROUND), DuelQualifier(value=DuelType.STANDING_TACKLE)]
elif type_id == EVENT_TYPE_AERIAL:
duel_qualifiers = [DuelQualifier(value=DuelType.LOOSE_BALL), DuelQualifier(value=DuelType.AERIAL)]
elif type_id == EVENT_TYPE_50_50:
duel_qualifiers = [DuelQualifier(value=DuelType.LOOSE_BALL), DuelQualifier(value=DuelType.GROUND)]

return duel_qualifiers


def _get_event_type_name(type_id: int) -> str:
return event_type_names.get(type_id, "unknown")

Expand Down Expand Up @@ -676,12 +679,16 @@ def deserialize(self, inputs: OptaInputs) -> EventDataset:
qualifiers=None,
**generic_event_kwargs,
)

elif type_id in DUEL_EVENTS:
duel_event_kwargs = _parse_duel(raw_qualifiers, type_id, outcome)
duel_event_kwargs = _parse_duel(
raw_qualifiers, type_id, outcome
)
event = self.event_factory.build_duel(
**duel_event_kwargs,
**generic_event_kwargs,
)

elif type_id == EVENT_TYPE_FOUL_COMMITTED:
event = self.event_factory.build_foul_committed(
result=None,
Expand Down
Loading

0 comments on commit 00da3ff

Please sign in to comment.