def deserialize( self, inputs: Dict[str, Readable], options: Dict = None ) -> EventDataset: """ Deserialize StatsBomb event data into a `EventDataset`. Parameters ---------- inputs : dict input `event_data` should point to a `Readable` object containing the 'json' formatted event data. input `lineup_data` should point to a `Readable` object containing the 'json' formatted lineup data. options : dict Options for deserialization of the StatsBomb file. Possible options are `event_types` (list of event types) to specify the event types that should be returned. Valid types: "shot", "pass", "carry", "take_on" and "generic". Generic is everything other than the first 4. Those events are barely parsed. This type of event can be used to do the parsing yourself. Every event has a 'raw_event' attribute which contains the original dictionary. Returns ------- dataset : EventDataset Raises ------ See Also -------- Examples -------- >>> serializer = StatsBombSerializer() >>> with open("events/12312312.json", "rb") as event_data, \ >>> open("lineups/123123123.json", "rb") as lineup_data: >>> >>> dataset = serializer.deserialize( >>> inputs={ >>> 'event_data': event_data, >>> 'lineup_data': lineup_data >>> }, >>> options={ >>> 'event_types': ["pass", "take_on", "carry", "shot"] >>> } >>> ) """ self.__validate_inputs(inputs) if not options: options = {} with performance_logging("load data", logger=logger): raw_events = json.load(inputs["event_data"]) home_lineup, away_lineup = json.load(inputs["lineup_data"]) ( shot_fidelity_version, xy_fidelity_version, ) = _determine_xy_fidelity_versions(raw_events) logger.info( f"Determined Fidelity versions: shot v{shot_fidelity_version} / XY v{xy_fidelity_version}" ) with performance_logging("parse data", logger=logger): home_team = Team( team_id=str(home_lineup["team_id"]), name=home_lineup["team_name"], ground=Ground.HOME, ) home_team.players = [ Player( player_id=str(player["player_id"]), team=home_team, name=player["player_name"], jersey_no=int(player["jersey_number"]), ) for player in home_lineup["lineup"] ] away_team = Team( team_id=str(away_lineup["team_id"]), name=away_lineup["team_name"], ground=Ground.AWAY, ) away_team.players = [ Player( player_id=str(player["player_id"]), team=away_team, name=player["player_name"], jersey_no=int(player["jersey_number"]), ) for player in away_lineup["lineup"] ] teams = [home_team, away_team] wanted_event_types = [ EventType[event_type.upper()] for event_type in options.get("event_types", []) ] periods = [] period = None events = [] for raw_event in raw_events: if raw_event["team"]["id"] == home_lineup["team_id"]: team = teams[0] elif raw_event["team"]["id"] == away_lineup["team_id"]: team = teams[1] else: raise Exception( f"Unknown team_id {raw_event['team']['id']}" ) if ( raw_event["possession_team"]["id"] == home_lineup["team_id"] ): possession_team = teams[0] elif ( raw_event["possession_team"]["id"] == away_lineup["team_id"] ): possession_team = teams[1] else: raise Exception( f"Unknown possession_team_id: {raw_event['possession_team']}" ) timestamp = parse_str_ts(raw_event["timestamp"]) period_id = int(raw_event["period"]) if not period or period.id != period_id: period = Period( id=period_id, start_timestamp=( timestamp if not period # period = [start, end], add millisecond to prevent overlapping else timestamp + period.end_timestamp + 0.001 ), end_timestamp=None, ) periods.append(period) else: period.end_timestamp = period.start_timestamp + timestamp player = None if "player" in raw_event: player = team.get_player_by_id(raw_event["player"]["id"]) event_type = raw_event["type"]["id"] if event_type == SB_EVENT_TYPE_SHOT: fidelity_version = shot_fidelity_version elif event_type in ( SB_EVENT_TYPE_CARRY, SB_EVENT_TYPE_DRIBBLE, SB_EVENT_TYPE_PASS, ): fidelity_version = xy_fidelity_version else: # TODO: Uh ohhhh.. don't know which one to pick fidelity_version = xy_fidelity_version generic_event_kwargs = dict( # from DataRecord period=period, timestamp=timestamp, ball_owning_team=possession_team, ball_state=BallState.ALIVE, # from Event event_id=raw_event["id"], team=team, player=player, coordinates=( _parse_coordinates( raw_event.get("location"), fidelity_version ) if "location" in raw_event else None ), raw_event=raw_event, ) if event_type == SB_EVENT_TYPE_PASS: pass_event_kwargs = _parse_pass( pass_dict=raw_event["pass"], team=team, fidelity_version=fidelity_version, ) event = PassEvent( # TODO: Consider moving this to _parse_pass receive_timestamp=timestamp + raw_event["duration"], **pass_event_kwargs, **generic_event_kwargs, ) elif event_type == SB_EVENT_TYPE_SHOT: shot_event_kwargs = _parse_shot( shot_dict=raw_event["shot"] ) event = ShotEvent( **shot_event_kwargs, **generic_event_kwargs ) # For dribble and carry the definitions # are flipped between Statsbomb and kloppy elif event_type == SB_EVENT_TYPE_DRIBBLE: take_on_event_kwargs = _parse_take_on( take_on_dict=raw_event["dribble"] ) event = TakeOnEvent( **take_on_event_kwargs, **generic_event_kwargs ) elif event_type == SB_EVENT_TYPE_CARRY: carry_event_kwargs = _parse_carry( carry_dict=raw_event["carry"], fidelity_version=fidelity_version, ) event = CarryEvent( # TODO: Consider moving this to _parse_carry end_timestamp=timestamp + raw_event["duration"], **carry_event_kwargs, **generic_event_kwargs, ) else: event = GenericEvent( result=None, event_name=raw_event["type"]["name"], **generic_event_kwargs, ) if ( not wanted_event_types or event.event_type in wanted_event_types ): events.append(event) metadata = Metadata( teams=teams, periods=periods, pitch_dimensions=PitchDimensions( x_dim=Dimension(0, 120), y_dim=Dimension(0, 80) ), frame_rate=None, orientation=Orientation.ACTION_EXECUTING_TEAM, flags=DatasetFlag.BALL_OWNING_TEAM, score=None, ) return EventDataset(metadata=metadata, records=events,)
def deserialize(self, inputs: Dict[str, Readable], options: Dict = None) -> EventDataset: """ Deserialize StatsBomb event data into a `EventDataset`. Parameters ---------- inputs : dict input `event_data` should point to a `Readable` object containing the 'json' formatted event data. input `lineup_data` should point to a `Readable` object containing the 'json' formatted lineup data. options : dict Options for deserialization of the StatsBomb file. Possible options are `event_types` (list of event types) to specify the event types that should be returned. Valid types: "shot", "pass", "carry", "take_on" and "generic". Generic is everything other than the first 4. Those events are barely parsed. This type of event can be used to do the parsing yourself. Every event has a 'raw_event' attribute which contains the original dictionary. Returns ------- dataset : EventDataset Raises ------ See Also -------- Examples -------- >>> serializer = StatsBombSerializer() >>> with open("events/12312312.json", "rb") as event_data, \ >>> open("lineups/123123123.json", "rb") as lineup_data: >>> >>> dataset = serializer.deserialize( >>> inputs={ >>> 'event_data': event_data, >>> 'lineup_data': lineup_data >>> }, >>> options={ >>> 'event_types': ["pass", "take_on", "carry", "shot"] >>> } >>> ) """ self.__validate_inputs(inputs) if not options: options = {} with performance_logging("load data", logger=logger): raw_events = json.load(inputs['event_data']) home_lineup, away_lineup = json.load(inputs['lineup_data']) shot_fidelity_version, xy_fidelity_version = _determine_xy_fidelity_versions( raw_events) logger.info( f"Determined Fidelity versions: shot v{shot_fidelity_version} / XY v{xy_fidelity_version}" ) with performance_logging("parse data", logger=logger): home_player_map = { player['player_id']: str(player['jersey_number']) for player in home_lineup['lineup'] } away_player_map = { player['player_id']: str(player['jersey_number']) for player in away_lineup['lineup'] } wanted_event_types = [ EventType[event_type.upper()] for event_type in options.get('event_types', []) ] periods = [] period = None events = [] for raw_event in raw_events: if raw_event['team']['id'] == home_lineup['team_id']: team = Team.HOME current_team_map = home_player_map elif raw_event['team']['id'] == away_lineup['team_id']: team = Team.AWAY current_team_map = away_player_map else: raise Exception( f"Unknown team_id {raw_event['team']['id']}") if raw_event['possession_team']['id'] == home_lineup[ 'team_id']: possession_team = Team.HOME elif raw_event['possession_team']['id'] == away_lineup[ 'team_id']: possession_team = Team.AWAY else: raise Exception( f"Unknown possession_team_id: {raw_event['possession_team']}" ) timestamp = parse_str_ts(raw_event['timestamp']) period_id = int(raw_event['period']) if not period or period.id != period_id: period = Period(id=period_id, start_timestamp=timestamp if not period else timestamp + period.end_timestamp, end_timestamp=None) periods.append(period) else: period.end_timestamp = period.start_timestamp + timestamp player_jersey_no = None if 'player' in raw_event: player_jersey_no = current_team_map[raw_event['player'] ['id']] event_type = raw_event['type']['id'] if event_type == SB_EVENT_TYPE_SHOT: fidelity_version = shot_fidelity_version elif event_type in (SB_EVENT_TYPE_CARRY, SB_EVENT_TYPE_DRIBBLE, SB_EVENT_TYPE_PASS): fidelity_version = xy_fidelity_version else: # TODO: Uh ohhhh.. don't know which one to pick fidelity_version = xy_fidelity_version generic_event_kwargs = dict( # from DataRecord period=period, timestamp=timestamp, ball_owning_team=possession_team, ball_state=BallState.ALIVE, # from Event event_id=raw_event['id'], team=team, player_jersey_no=player_jersey_no, position=(_parse_position(raw_event.get('location'), fidelity_version) if 'location' in raw_event else None), raw_event=raw_event) if event_type == SB_EVENT_TYPE_PASS: pass_event_kwargs = _parse_pass( pass_dict=raw_event['pass'], current_team_map=current_team_map, fidelity_version=fidelity_version) event = PassEvent( # TODO: Consider moving this to _parse_pass receive_timestamp=timestamp + raw_event['duration'], **pass_event_kwargs, **generic_event_kwargs) elif event_type == SB_EVENT_TYPE_SHOT: shot_event_kwargs = _parse_shot( shot_dict=raw_event['shot']) event = ShotEvent(**shot_event_kwargs, **generic_event_kwargs) # For dribble and carry the definitions # are flipped between Statsbomb and kloppy elif event_type == SB_EVENT_TYPE_DRIBBLE: take_on_event_kwargs = _parse_take_on( take_on_dict=raw_event['dribble']) event = TakeOnEvent(**take_on_event_kwargs, **generic_event_kwargs) elif event_type == SB_EVENT_TYPE_CARRY: carry_event_kwargs = _parse_carry( carry_dict=raw_event['carry'], fidelity_version=fidelity_version) event = CarryEvent( # TODO: Consider moving this to _parse_carry end_timestamp=timestamp + raw_event['duration'], **carry_event_kwargs, **generic_event_kwargs) else: event = GenericEvent(result=None, **generic_event_kwargs) if not wanted_event_types or event.event_type in wanted_event_types: events.append(event) return EventDataset(flags=DatasetFlag.BALL_OWNING_TEAM, orientation=Orientation.ACTION_EXECUTING_TEAM, pitch_dimensions=PitchDimensions( x_dim=Dimension(0, 120), y_dim=Dimension(0, 80)), periods=periods, records=events)
def deserialize(self, inputs: MetricaJsonEventDataInputs) -> EventDataset: with performance_logging("load data", logger=logger): raw_events = json.load(inputs.event_data) metadata = load_metadata(inputs.meta_data, provider=Provider.METRICA) transformer = self.get_transformer( length=metadata.pitch_dimensions.length, width=metadata.pitch_dimensions.width, ) 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: team = metadata.teams[1] else: raise DeserializationError( f"Unknown team_id {raw_event['team']['id']}") player = team.get_player_by_id(raw_event["from"]["id"]) event_type = raw_event["type"]["id"] subtypes = _parse_subtypes(raw_event) period = [ period for period in metadata.periods if period.id == raw_event["period"] ][0] previous_event = raw_events["data"][i - 1] generic_event_kwargs = dict( # from DataRecord period=period, timestamp=raw_event["start"]["time"], ball_owning_team=_parse_ball_owning_team(event_type, team), ball_state=BallState.ALIVE, # from Event event_id=None, team=team, player=player, coordinates=(_parse_coordinates(raw_event["start"])), raw_event=raw_event, ) iteration_events = [] if event_type in MS_PASS_TYPES: pass_event_kwargs = _parse_pass( event=raw_event, previous_event=previous_event, subtypes=subtypes, team=team, ) event = PassEvent.create( **pass_event_kwargs, **generic_event_kwargs, ) elif event_type == MS_EVENT_TYPE_SHOT: shot_event_kwargs = _parse_shot( event=raw_event, previous_event=previous_event, subtypes=subtypes, ) event = ShotEvent.create( **shot_event_kwargs, **generic_event_kwargs, ) elif subtypes and MS_EVENT_TYPE_DRIBBLE in subtypes: take_on_event_kwargs = _parse_take_on(subtypes=subtypes) event = TakeOnEvent.create( qualifiers=None, **take_on_event_kwargs, **generic_event_kwargs, ) elif event_type == MS_EVENT_TYPE_CARRY: carry_event_kwargs = _parse_carry(event=raw_event, ) event = CarryEvent.create( qualifiers=None, **carry_event_kwargs, **generic_event_kwargs, ) elif event_type == MS_EVENT_TYPE_RECOVERY: event = RecoveryEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) elif event_type == MS_EVENT_TYPE_FOUL_COMMITTED: event = FoulCommittedEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) else: event = GenericEvent.create( result=None, qualifiers=None, event_name=raw_event["type"]["name"], **generic_event_kwargs, ) if self.should_include_event(event): events.append(transformer.transform_event(event)) # Checks if the event ended out of the field and adds a synthetic out event if event.result in OUT_EVENT_RESULTS: generic_event_kwargs["ball_state"] = BallState.DEAD if raw_event["end"]["x"]: generic_event_kwargs[ "coordinates"] = _parse_coordinates( raw_event["end"]) generic_event_kwargs["timestamp"] = raw_event["end"][ "time"] event = BallOutEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) if self.should_include_event(event): events.append(transformer.transform_event(event)) return EventDataset( metadata=metadata, records=events, )
def deserialize(self, inputs: Dict[str, Readable], options: Dict = None) -> EventDataset: """ Deserialize Metrica Sports event data json format into a `EventDataset`. Parameters ---------- inputs : dict input `raw_data` should point to a `Readable` object containing the 'json' formatted event data. input `metadata` should point to a `Readable` object containing the `xml` metadata file. options : dict Options for deserialization of the Metrica Sports event json file. Possible options are `event_types` (list of event types) to specify the event types that should be returned. Valid types: "shot", "pass", "carry", "take_on" and "generic". Generic is everything other than the first 4. Those events are barely parsed. This type of event can be used to do the parsing yourself. Every event has a 'raw_event' attribute which contains the original dictionary. Returns ------- dataset : EventDataset Raises ------ See Also -------- Examples -------- >>> serializer = MetricaEventsJsonSerializer() >>> with open("events.json", "rb") as raw_data, \ >>> open("metadata.xml", "rb") as metadata: >>> >>> dataset = serializer.deserialize( >>> inputs={ >>> 'raw_data': raw_data, >>> 'metadata': metadata >>> }, >>> options={ >>> 'event_types': ["pass", "take_on", "carry", "shot"] >>> } >>> ) """ self.__validate_inputs(inputs) if not options: options = {} with performance_logging("load data", logger=logger): raw_events = json.load(inputs["raw_data"]) metadata = load_metadata(inputs["metadata"], provider=Provider.METRICA) with performance_logging("parse data", logger=logger): wanted_event_types = [ EventType[event_type.upper()] for event_type in options.get("event_types", []) ] events = [] for raw_event in 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: team = metadata.teams[1] else: raise Exception( f"Unknown team_id {raw_event['team']['id']}") player = team.get_player_by_id(raw_event["from"]["id"]) event_type = raw_event["type"]["id"] subtypes = _parse_subtypes(raw_event) period = [ period for period in metadata.periods if period.id == raw_event["period"] ][0] generic_event_kwargs = dict( # from DataRecord period=period, timestamp=raw_event["start"]["time"], ball_owning_team=_parse_ball_owning_team(event_type, team), ball_state=BallState.ALIVE, # from Event event_id=None, team=team, player=player, coordinates=(_parse_coordinates(raw_event["start"])), raw_event=raw_event, ) if event_type in MS_PASS_TYPES: pass_event_kwargs = _parse_pass( event=raw_event, subtypes=subtypes, team=team, ) event = PassEvent( **pass_event_kwargs, **generic_event_kwargs, ) elif event_type == MS_EVENT_TYPE_SHOT: shot_event_kwargs = _parse_shot(event=raw_event, subtypes=subtypes) event = ShotEvent(**shot_event_kwargs, **generic_event_kwargs) elif subtypes and MS_EVENT_TYPE_DRIBBLE in subtypes: take_on_event_kwargs = _parse_take_on(subtypes=subtypes) event = TakeOnEvent(**take_on_event_kwargs, **generic_event_kwargs) elif event_type == MS_EVENT_TYPE_CARRY: carry_event_kwargs = _parse_carry(event=raw_event, ) event = CarryEvent( **carry_event_kwargs, **generic_event_kwargs, ) else: event = GenericEvent( result=None, event_name=raw_event["type"]["name"], **generic_event_kwargs, ) if (not wanted_event_types or event.event_type in wanted_event_types): events.append(event) return EventDataset( metadata=metadata, records=events, )
def deserialize(self, inputs: StatsbombInputs) -> EventDataset: transformer = self.get_transformer(length=120, width=80) with performance_logging("load data", logger=logger): raw_events = json.load(inputs.event_data) home_lineup, away_lineup = json.load(inputs.lineup_data) ( shot_fidelity_version, xy_fidelity_version, ) = _determine_xy_fidelity_versions(raw_events) logger.info( f"Determined Fidelity versions: shot v{shot_fidelity_version} / XY v{xy_fidelity_version}" ) with performance_logging("parse data", logger=logger): starting_player_ids = { str(player["player"]["id"]) for raw_event in raw_events if raw_event["type"]["id"] == SB_EVENT_TYPE_STARTING_XI for player in raw_event["tactics"]["lineup"] } starting_formations = { raw_event["team"]["id"]: FormationType("-".join( list(str(raw_event["tactics"]["formation"])))) for raw_event in raw_events if raw_event["type"]["id"] == SB_EVENT_TYPE_STARTING_XI } home_team = Team( team_id=str(home_lineup["team_id"]), name=home_lineup["team_name"], ground=Ground.HOME, starting_formation=starting_formations[home_lineup["team_id"]], ) home_team.players = [ Player( player_id=str(player["player_id"]), team=home_team, name=player["player_name"], jersey_no=int(player["jersey_number"]), starting=str(player["player_id"]) in starting_player_ids, ) for player in home_lineup["lineup"] ] away_team = Team( team_id=str(away_lineup["team_id"]), name=away_lineup["team_name"], ground=Ground.AWAY, starting_formation=starting_formations[away_lineup["team_id"]], ) away_team.players = [ Player( player_id=str(player["player_id"]), team=away_team, name=player["player_name"], jersey_no=int(player["jersey_number"]), starting=str(player["player_id"]) in starting_player_ids, ) for player in away_lineup["lineup"] ] teams = [home_team, away_team] periods = [] period = None events = [] for raw_event in raw_events: if raw_event["team"]["id"] == home_lineup["team_id"]: team = home_team elif raw_event["team"]["id"] == away_lineup["team_id"]: team = away_team else: raise DeserializationError( f"Unknown team_id {raw_event['team']['id']}") if (raw_event["possession_team"]["id"] == home_lineup["team_id"]): possession_team = home_team elif (raw_event["possession_team"]["id"] == away_lineup["team_id"]): possession_team = away_team else: raise DeserializationError( f"Unknown possession_team_id: {raw_event['possession_team']}" ) timestamp = parse_str_ts(raw_event["timestamp"]) period_id = int(raw_event["period"]) if not period or period.id != period_id: period = Period( id=period_id, start_timestamp=( timestamp if not period # period = [start, end], add millisecond to prevent overlapping else timestamp + period.end_timestamp + 0.001), end_timestamp=None, ) periods.append(period) else: period.end_timestamp = period.start_timestamp + timestamp player = None if "player" in raw_event: player = team.get_player_by_id(raw_event["player"]["id"]) event_type = raw_event["type"]["id"] if event_type == SB_EVENT_TYPE_SHOT: fidelity_version = shot_fidelity_version elif event_type in ( SB_EVENT_TYPE_CARRY, SB_EVENT_TYPE_DRIBBLE, SB_EVENT_TYPE_PASS, ): fidelity_version = xy_fidelity_version else: # TODO: Uh ohhhh.. don't know which one to pick fidelity_version = xy_fidelity_version generic_event_kwargs = { # from DataRecord "period": period, "timestamp": timestamp, "ball_owning_team": possession_team, "ball_state": BallState.ALIVE, # from Event "event_id": raw_event["id"], "team": team, "player": player, "coordinates": (_parse_coordinates( raw_event.get("location"), fidelity_version, ) if "location" in raw_event else None), "related_event_ids": raw_event.get("related_events", []), "raw_event": raw_event, } new_events = [] if event_type == SB_EVENT_TYPE_PASS: pass_event_kwargs = _parse_pass( pass_dict=raw_event["pass"], team=team, fidelity_version=fidelity_version, ) pass_event = PassEvent.create( # TODO: Consider moving this to _parse_pass receive_timestamp=timestamp + raw_event["duration"], **pass_event_kwargs, **generic_event_kwargs, ) new_events.append(pass_event) elif event_type == SB_EVENT_TYPE_SHOT: shot_event_kwargs = _parse_shot( shot_dict=raw_event["shot"], ) shot_event = ShotEvent.create( **shot_event_kwargs, **generic_event_kwargs, ) new_events.append(shot_event) # For dribble and carry the definitions # are flipped between Statsbomb and kloppy elif event_type == SB_EVENT_TYPE_DRIBBLE: take_on_event_kwargs = _parse_take_on( take_on_dict=raw_event["dribble"], ) take_on_event = TakeOnEvent.create( qualifiers=None, **take_on_event_kwargs, **generic_event_kwargs, ) new_events.append(take_on_event) elif event_type == SB_EVENT_TYPE_CARRY: carry_event_kwargs = _parse_carry( carry_dict=raw_event["carry"], fidelity_version=fidelity_version, ) carry_event = CarryEvent.create( qualifiers=None, # TODO: Consider moving this to _parse_carry end_timestamp=timestamp + raw_event.get("duration", 0), **carry_event_kwargs, **generic_event_kwargs, ) new_events.append(carry_event) # lineup affecting events elif event_type == SB_EVENT_TYPE_SUBSTITUTION: substitution_event_kwargs = _parse_substitution( substitution_dict=raw_event["substitution"], team=team, ) substitution_event = SubstitutionEvent.create( result=None, qualifiers=None, **substitution_event_kwargs, **generic_event_kwargs, ) new_events.append(substitution_event) elif event_type == SB_EVENT_TYPE_BAD_BEHAVIOUR: bad_behaviour_kwargs = _parse_bad_behaviour( bad_behaviour_dict=raw_event.get("bad_behaviour", {}), ) if "card" in bad_behaviour_kwargs: card_kwargs = bad_behaviour_kwargs["card"] card_event = CardEvent.create( result=None, qualifiers=None, card_type=card_kwargs["card_type"], **generic_event_kwargs, ) new_events.append(card_event) elif event_type == SB_EVENT_TYPE_FOUL_COMMITTED: foul_committed_kwargs = _parse_foul_committed( foul_committed_dict=raw_event.get( "foul_committed", {}), ) foul_committed_event = FoulCommittedEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) new_events.append(foul_committed_event) if "card" in foul_committed_kwargs: card_kwargs = foul_committed_kwargs["card"] card_event = CardEvent.create( result=None, qualifiers=None, card_type=card_kwargs["card_type"], **generic_event_kwargs, ) new_events.append(card_event) elif event_type == SB_EVENT_TYPE_PLAYER_ON: player_on_event = PlayerOnEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) new_events.append(player_on_event) elif event_type == SB_EVENT_TYPE_PLAYER_OFF: player_off_event = PlayerOffEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) new_events.append(player_off_event) elif event_type == SB_EVENT_TYPE_RECOVERY: recovery_event = RecoveryEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) new_events.append(recovery_event) elif event_type == SB_EVENT_TYPE_FORMATION_CHANGE: formation_change_event_kwargs = _parse_formation_change( raw_event["tactics"]["formation"]) formation_change_event = FormationChangeEvent.create( result=None, qualifiers=None, **formation_change_event_kwargs, **generic_event_kwargs, ) new_events.append(formation_change_event) # rest: generic else: generic_event = GenericEvent.create( result=None, qualifiers=None, event_name=raw_event["type"]["name"], **generic_event_kwargs, ) new_events.append(generic_event) for event in new_events: if self.should_include_event(event): transformed_event = transformer.transform_event(event) events.append(transformed_event) # Checks if the event ended out of the field and adds a synthetic out event if event.result in OUT_EVENT_RESULTS: generic_event_kwargs["ball_state"] = BallState.DEAD if event.receiver_coordinates: generic_event_kwargs[ "coordinates"] = event.receiver_coordinates ball_out_event = BallOutEvent.create( result=None, qualifiers=None, **generic_event_kwargs, ) if self.should_include_event(ball_out_event): transformed_ball_out_event = ( transformer.transform_event(ball_out_event) ) events.append(transformed_ball_out_event) metadata = Metadata( teams=teams, periods=periods, pitch_dimensions=transformer.get_to_coordinate_system(). pitch_dimensions, frame_rate=None, orientation=Orientation.ACTION_EXECUTING_TEAM, flags=DatasetFlag.BALL_OWNING_TEAM, score=None, provider=Provider.STATSBOMB, coordinate_system=transformer.get_to_coordinate_system(), ) return EventDataset( metadata=metadata, records=events, )