From a56249fd92194902f85549af7988ebd7707d9f98 Mon Sep 17 00:00:00 2001 From: Ricardo Vieira Date: Thu, 16 Jan 2025 13:16:49 +0000 Subject: [PATCH 1/2] Add start and end times to sequence --- .../state_builder/builders/sequence.py | 39 +++++++++++++++---- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/kloppy/domain/services/state_builder/builders/sequence.py b/kloppy/domain/services/state_builder/builders/sequence.py index e851f4309..d8d16618a 100644 --- a/kloppy/domain/services/state_builder/builders/sequence.py +++ b/kloppy/domain/services/state_builder/builders/sequence.py @@ -3,6 +3,7 @@ from kloppy.domain import ( Event, Team, + Time, EventDataset, PassEvent, CarryEvent, @@ -22,30 +23,52 @@ class Sequence: sequence_id: int team: Team + start: Time + end: Time class SequenceStateBuilder(StateBuilder): + current_sequence: Sequence + def initial_state(self, dataset: EventDataset) -> Sequence: - for event in dataset.events: - if isinstance(event, OPEN_SEQUENCE): - return Sequence(sequence_id=0, team=event.team) - return Sequence(sequence_id=0, team=None) + self.current_sequence = Sequence( + sequence_id=0, team=None, start=None, end=None + ) + return self.current_sequence def reduce_before(self, state: Sequence, event: Event) -> Sequence: if isinstance(event, OPEN_SEQUENCE) and ( state.team != event.team or event.get_qualifier_value(SetPieceQualifier) ): - state = replace( - state, sequence_id=state.sequence_id + 1, team=event.team + # Finalize the current sequence + self.current_sequence.end = event.time + + # Start a new sequence + self.current_sequence = replace( + state, + sequence_id=state.sequence_id + 1, + team=event.team, + start=event.time, + end=None, ) + state = self.current_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 + # Finalize the current sequence + self.current_sequence.end = event.time + + # Start a new sequence + self.current_sequence = replace( + state, + sequence_id=state.sequence_id + 1, + team=None, + start=event.time, + end=None, ) + state = self.current_sequence return state From af800a03f1696726cd9ddcef9925f95aabc0de48 Mon Sep 17 00:00:00 2001 From: Ricardo Vieira Date: Thu, 16 Jan 2025 15:49:24 +0000 Subject: [PATCH 2/2] Add tests and fix times --- .../services/state_builder/builders/sequence.py | 17 ++++++++++------- kloppy/tests/test_state_builder.py | 6 ++++++ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/kloppy/domain/services/state_builder/builders/sequence.py b/kloppy/domain/services/state_builder/builders/sequence.py index d8d16618a..ecae12719 100644 --- a/kloppy/domain/services/state_builder/builders/sequence.py +++ b/kloppy/domain/services/state_builder/builders/sequence.py @@ -28,6 +28,7 @@ class Sequence: class SequenceStateBuilder(StateBuilder): + # current_sequence is mutable by design so every event in the sequence can be updated with the correct times current_sequence: Sequence def initial_state(self, dataset: EventDataset) -> Sequence: @@ -37,13 +38,14 @@ def initial_state(self, dataset: EventDataset) -> Sequence: return self.current_sequence def reduce_before(self, state: Sequence, event: Event) -> Sequence: + # Set the start time of the sequence if it is not set yet + if self.current_sequence.start is None: + self.current_sequence.start = event.time + if isinstance(event, OPEN_SEQUENCE) and ( state.team != event.team or event.get_qualifier_value(SetPieceQualifier) ): - # Finalize the current sequence - self.current_sequence.end = event.time - # Start a new sequence self.current_sequence = replace( state, @@ -57,16 +59,17 @@ 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): - # Finalize the current sequence - self.current_sequence.end = event.time + # Always update the end time of the sequence + # This ensures sequences without CLOSE_SEQUENCE events still have the correct time + self.current_sequence.end = event.time + if isinstance(event, CLOSE_SEQUENCE): # Start a new sequence self.current_sequence = replace( state, sequence_id=state.sequence_id + 1, team=None, - start=event.time, + start=None, end=None, ) state = self.current_sequence diff --git a/kloppy/tests/test_state_builder.py b/kloppy/tests/test_state_builder.py index d27f6190a..eab868089 100644 --- a/kloppy/tests/test_state_builder.py +++ b/kloppy/tests/test_state_builder.py @@ -50,6 +50,12 @@ def test_sequence_state_builder(self, base_dir): events = list(events) events_per_sequence[sequence_id] = len(events) + # Check if the sequence start and end times match the first and last event + sequence_start = events[0].state["sequence"].start + sequence_end = events[-1].state["sequence"].end + assert events[0].time == sequence_start + assert events[-1].time == sequence_end + assert events_per_sequence[0] == 4 assert events_per_sequence[51] == 7