Skip to content

Commit

Permalink
Merge pull request #88 from PySport/add-wyscout-dataset
Browse files Browse the repository at this point in the history
Add Wyscout dataset
  • Loading branch information
koenvo authored Mar 12, 2021
2 parents 34ce674 + 53d4706 commit dd93271
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 45 deletions.
7 changes: 7 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

Find out all changes between different kloppy versions

## 1.5.2 (2021-03-12)

Pull request merged:

- Add Wyscout dataset ([#88](https://github.com/PySport/kloppy/pull/88))


## 1.5.1 (2020-12-23):

Pull request merged:
Expand Down
2 changes: 1 addition & 1 deletion kloppy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@
)
from .domain.services.state_builder import add_state

__version__ = "1.5.1"
__version__ = "1.5.2"
1 change: 1 addition & 0 deletions kloppy/infra/datasets/event/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .statsbomb import Statsbomb
from .wyscout import Wyscout
15 changes: 15 additions & 0 deletions kloppy/infra/datasets/event/wyscout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import Dict, Type

from ..core.builder import DatasetBuilder
from ...serializers.event import EventDataSerializer, WyscoutSerializer


class Wyscout(DatasetBuilder):
def get_dataset_urls(self, **kwargs) -> Dict[str, str]:
match_id = kwargs.get("match_id", "2499841")
return {
"event_data": f"https://raw.githubusercontent.com/koenvo/wyscout-soccer-match-event-dataset/main/processed/files/{match_id}.json",
}

def get_serializer_cls(self) -> Type[EventDataSerializer]:
return WyscoutSerializer
83 changes: 40 additions & 43 deletions kloppy/infra/serializers/event/wyscout/serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
INVALID_PLAYER = "0"


def _parse_team(raw_events: List[Dict], wyId: str, ground: Ground) -> Team:
def _parse_team(raw_events, wyId: str, ground: Ground) -> Team:
team = Team(
team_id=wyId,
name=raw_events["teams"][wyId]["officialName"],
Expand Down Expand Up @@ -103,12 +103,12 @@ def _parse_shot(raw_event: Dict, next_event: Dict) -> Dict:
elif any((_has_tag(raw_event, tag) for tag in wyscout_tags.SHOT_ON_GOAL)):
result = ShotResult.SAVED

if next_event["eventName"] == wyscout_events.SAVE.EVENT:
if next_event["subEventName"] == wyscout_events.SAVE.REFLEXES:
if next_event["eventId"] == wyscout_events.SAVE.EVENT:
if next_event["subEventId"] == wyscout_events.SAVE.REFLEXES:
qualifiers.append(
GoalkeeperActionQualifier(GoalkeeperAction.REFLEX)
)
if next_event["subEventName"] == wyscout_events.SAVE.SAVE_ATTEMPT:
if next_event["subEventId"] == wyscout_events.SAVE.SAVE_ATTEMPT:
qualifiers.append(
GoalkeeperActionQualifier(GoalkeeperAction.SAVE_ATTEMPT)
)
Expand All @@ -126,19 +126,19 @@ def _parse_shot(raw_event: Dict, next_event: Dict) -> Dict:
def _pass_qualifiers(raw_event) -> List[Qualifier]:
qualifiers = _generic_qualifiers(raw_event)

if raw_event["subEventName"] == wyscout_events.PASS.CROSS:
if raw_event["subEventId"] == wyscout_events.PASS.CROSS:
qualifiers.append(PassQualifier(PassType.CROSS))
elif raw_event["subEventName"] == wyscout_events.PASS.HAND:
elif raw_event["subEventId"] == wyscout_events.PASS.HAND:
qualifiers.append(PassQualifier(PassType.HAND_PASS))
elif raw_event["subEventName"] == wyscout_events.PASS.HEAD:
elif raw_event["subEventId"] == wyscout_events.PASS.HEAD:
qualifiers.append(PassQualifier(PassType.HEAD_PASS))
elif raw_event["subEventName"] == wyscout_events.PASS.HIGH:
elif raw_event["subEventId"] == wyscout_events.PASS.HIGH:
qualifiers.append(PassQualifier(PassType.HIGH_PASS))
elif raw_event["subEventName"] == wyscout_events.PASS.LAUNCH:
elif raw_event["subEventId"] == wyscout_events.PASS.LAUNCH:
qualifiers.append(PassQualifier(PassType.LAUNCH))
elif raw_event["subEventName"] == wyscout_events.PASS.SIMPLE:
elif raw_event["subEventId"] == wyscout_events.PASS.SIMPLE:
qualifiers.append(PassQualifier(PassType.SIMPLE_PASS))
elif raw_event["subEventName"] == wyscout_events.PASS.SMART:
elif raw_event["subEventId"] == wyscout_events.PASS.SMART:
qualifiers.append(PassQualifier(PassType.SMART_PASS))

if _has_tag(raw_event, wyscout_tags.LEFT_FOOT):
Expand All @@ -158,11 +158,11 @@ def _parse_pass(raw_event: Dict, next_event: Dict) -> Dict:
pass_result = PassResult.INCOMPLETE

if next_event:
if next_event["eventName"] == wyscout_events.OFFSIDE.EVENT:
if next_event["eventId"] == wyscout_events.OFFSIDE.EVENT:
pass_result = PassResult.OFFSIDE
if next_event["eventName"] == wyscout_events.INTERRUPTION.EVENT:
if next_event["eventId"] == wyscout_events.INTERRUPTION.EVENT:
if (
next_event["subEventName"]
next_event["subEventId"]
== wyscout_events.INTERRUPTION.BALL_OUT
):
pass_result = PassResult.OUT
Expand Down Expand Up @@ -218,28 +218,25 @@ def _parse_set_piece(raw_event: Dict, next_event: Dict) -> Dict:

result = {}

if raw_event["subEventName"] in wyscout_events.FREE_KICK.PASS_TYPES:
if raw_event["subEventId"] in wyscout_events.FREE_KICK.PASS_TYPES:
result = _parse_pass(raw_event, next_event)
if raw_event["subEventName"] == wyscout_events.FREE_KICK.GOAL_KICK:
if raw_event["subEventId"] == wyscout_events.FREE_KICK.GOAL_KICK:
qualifiers.append(SetPieceQualifier(SetPieceType.GOAL_KICK))
elif raw_event["subEventName"] == wyscout_events.FREE_KICK.THROW_IN:
elif raw_event["subEventId"] == wyscout_events.FREE_KICK.THROW_IN:
qualifiers.append(SetPieceQualifier(SetPieceType.THROW_IN))
qualifiers.append(PassQualifier(PassType.HAND_PASS))
elif raw_event["subEventName"] in [
elif raw_event["subEventId"] in [
wyscout_events.FREE_KICK.FREE_KICK,
wyscout_events.FREE_KICK.FREE_KICK_CROSS,
]:
qualifiers.append(SetPieceQualifier(SetPieceType.FREE_KICK))
elif raw_event["subEventName"] == wyscout_events.FREE_KICK.CORNER:
elif raw_event["subEventId"] == wyscout_events.FREE_KICK.CORNER:
qualifiers.append(SetPieceQualifier(SetPieceType.CORNER_KICK))
elif raw_event["subEventName"] in wyscout_events.FREE_KICK.SHOT_TYPES:
elif raw_event["subEventId"] in wyscout_events.FREE_KICK.SHOT_TYPES:
result = _parse_shot(raw_event, next_event)
if (
raw_event["subEventName"]
== wyscout_events.FREE_KICK.FREE_KICK_SHOT
):
if raw_event["subEventId"] == wyscout_events.FREE_KICK.FREE_KICK_SHOT:
qualifiers.append(SetPieceQualifier(SetPieceType.FREE_KICK))
elif raw_event["subEventName"] == wyscout_events.FREE_KICK.PENALTY:
elif raw_event["subEventId"] == wyscout_events.FREE_KICK.PENALTY:
qualifiers.append(SetPieceQualifier(SetPieceType.PENALTY))

result["qualifiers"] = qualifiers
Expand Down Expand Up @@ -285,6 +282,11 @@ def deserialize(

with performance_logging("load data", logger=logger):
raw_events = json.load(inputs["event_data"])
for event in raw_events["events"]:
if "eventId" not in event:
event["eventId"] = event["eventName"]
if "subEventId" not in event:
event["subEventId"] = event.get("subEventName")

periods = []

Expand All @@ -309,14 +311,12 @@ def deserialize(

team_id = str(raw_event["teamId"])
player_id = str(raw_event["playerId"])
period_id = int(raw_event["matchPeriod"].replace("H", ""))

if (
len(periods) == 0
or periods[-1].id != raw_event["matchPeriod"]
):
if len(periods) == 0 or periods[-1].id != period_id:
periods.append(
Period(
id=raw_event["matchPeriod"],
id=period_id,
start_timestamp=0,
end_timestamp=0,
)
Expand All @@ -340,17 +340,17 @@ def deserialize(
}

event = None
if raw_event["eventName"] == wyscout_events.SHOT.EVENT:
if raw_event["eventId"] == wyscout_events.SHOT.EVENT:
shot_event_args = _parse_shot(raw_event, next_event)
event = ShotEvent.create(
**shot_event_args, **generic_event_args
)
elif raw_event["eventName"] == wyscout_events.PASS.EVENT:
elif raw_event["eventId"] == wyscout_events.PASS.EVENT:
pass_event_args = _parse_pass(raw_event, next_event)
event = PassEvent.create(
**pass_event_args, **generic_event_args
)
elif raw_event["eventName"] == wyscout_events.FOUL.EVENT:
elif raw_event["eventId"] == wyscout_events.FOUL.EVENT:
foul_event_args = _parse_foul(raw_event)
event = FoulCommittedEvent.create(
**foul_event_args, **generic_event_args
Expand All @@ -362,46 +362,43 @@ def deserialize(
event = CardEvent.create(
**card_event_args, **generic_event_args
)
elif (
raw_event["eventName"] == wyscout_events.INTERRUPTION.EVENT
):
elif raw_event["eventId"] == wyscout_events.INTERRUPTION.EVENT:
ball_out_event_args = _parse_ball_out(raw_event)
event = BallOutEvent.create(
**ball_out_event_args, **generic_event_args
)
elif raw_event["eventName"] == wyscout_events.FREE_KICK.EVENT:
elif raw_event["eventId"] == wyscout_events.FREE_KICK.EVENT:
set_piece_event_args = _parse_set_piece(
raw_event, next_event
)
if (
raw_event["subEventName"]
raw_event["subEventId"]
in wyscout_events.FREE_KICK.PASS_TYPES
):
event = PassEvent.create(
**set_piece_event_args, **generic_event_args
)
elif (
raw_event["subEventName"]
raw_event["subEventId"]
in wyscout_events.FREE_KICK.SHOT_TYPES
):
event = ShotEvent.create(
**set_piece_event_args, **generic_event_args
)

elif (
raw_event["eventName"]
== wyscout_events.OTHERS_ON_BALL.EVENT
raw_event["eventId"] == wyscout_events.OTHERS_ON_BALL.EVENT
):
recovery_event_args = _parse_recovery(raw_event)
event = RecoveryEvent.create(
**recovery_event_args, **generic_event_args
)
elif raw_event["eventName"] == wyscout_events.DUEL.EVENT:
elif raw_event["eventId"] == wyscout_events.DUEL.EVENT:
takeon_event_args = _parse_takeon(raw_event)
event = TakeOnEvent.create(
**takeon_event_args, **generic_event_args
)
elif raw_event["eventName"] not in [
elif raw_event["eventId"] not in [
wyscout_events.SAVE.EVENT,
wyscout_events.OFFSIDE.EVENT,
]:
Expand Down
2 changes: 1 addition & 1 deletion mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
site_name: kloppy 1.5.1
site_name: kloppy 1.5.2
site_url: https://kloppy.pysport.org
repo_url: https://github.com/PySport/kloppy
repo_name: 'GitHub'
Expand Down

0 comments on commit dd93271

Please sign in to comment.