From 73648e26b81d8f16a49fbca67de38c0422de91c1 Mon Sep 17 00:00:00 2001 From: manuba95 Date: Mon, 6 Jun 2022 17:13:30 +0200 Subject: [PATCH 1/2] fix: fixed calculation of differences --- floodlight/models/kinematics.py | 95 ++++++++++++++++++++++------ tests/test_models/test_kinematics.py | 20 +++--- 2 files changed, 86 insertions(+), 29 deletions(-) diff --git a/floodlight/models/kinematics.py b/floodlight/models/kinematics.py index b6fe8b2e..5eb340b7 100644 --- a/floodlight/models/kinematics.py +++ b/floodlight/models/kinematics.py @@ -3,6 +3,7 @@ from floodlight import XY from floodlight.core.property import PlayerProperty from floodlight.models.base import BaseModel, requires_fit +from floodlight.transforms.filter import _get_filterable_and_short_sequences class DistanceModel(BaseModel): @@ -83,28 +84,59 @@ def fit(self, xy: XY, difference: str = "central", axis: str = None): calculated in both dimensions. """ + # differentiate only sequences without NaNs + # shortest possible signal length to differentiate + min_signal_len = 2 + # pe-allocate array for differences if axis is None: - if difference == "central": - differences_xy = np.gradient(xy.xy, axis=0) - elif difference == "backward": - differences_xy = np.diff(xy.xy, axis=0, prepend=0) - else: - raise ValueError( - f"Expected axis to be one of (None, 'x', 'y'), got {axis}." - ) - distance_euclidean = np.hypot( - differences_xy[:, ::2], - differences_xy[:, 1::2], - ) + to_differentiate = xy.xy elif axis == "x": - distance_euclidean = np.gradient(xy.x, axis=0) + to_differentiate = xy.x elif axis == "y": - distance_euclidean = np.gradient(xy.y, axis=0) + to_differentiate = xy.y else: raise ValueError( f"Expected axis to be one of (None, 'x', 'y'), got {axis}." ) + differences_xy = np.full(to_differentiate.shape, np.NaN) + # loop through the xy-object columns + for i, column in enumerate(np.transpose(to_differentiate)): + # extract indices of filterable and short sequences + seqs_diff, seqs_short = _get_filterable_and_short_sequences( + column, min_signal_len + ) + # pre-allocate space for filtered column + col_diff = np.full(column.shape, np.nan) + + # loop through filterable sequences + for start, end in seqs_diff: + # apply filter to the sequence and enter filtered data to their + # respective indices in the data + if difference == "central": + col_diff[start:end] = np.gradient(column[start:end]) + elif difference == "backward": + col_diff[start:end] = np.diff( + column[start:end], prepend=column[start] + ) + else: + raise ValueError( + f"Expected differences to be one of ('central'," + f" 'backward', got {difference})" + ) + + # set single frames to 0 because differentiation is not possible + for start, end in seqs_short: + col_diff[start:end] = 0 + + # enter filtered data into respective column + differences_xy[:, i] = col_diff + + distance_euclidean = np.hypot( + differences_xy[:, ::2], + differences_xy[:, 1::2], + ) + self._distance_euclidean_ = PlayerProperty( property=distance_euclidean, name="distance_covered", framerate=xy.framerate ) @@ -323,12 +355,37 @@ def fit( velocity_model.fit(xy, difference=difference, axis=axis) velocity = velocity_model.velocity() - if difference == "central": - acceleration = np.gradient(velocity.property, axis=0) * velocity.framerate - else: - acceleration = ( - np.diff(velocity.property, axis=0, append=0) * velocity.framerate + min_signal_len = 2 + # pe-allocate array for differences + differences_vel = np.empty(velocity.property.shape) + # loop through the xy-object columns + for i, column in enumerate(np.transpose(velocity)): + # extract indices of filterable and short sequences + seqs_diff, seqs_short = _get_filterable_and_short_sequences( + column, min_signal_len ) + # pre-allocate space for filtered column + col_diff = np.full(column.shape, np.nan) + + # loop through filterable sequences + for start, end in seqs_diff: + # apply filter to the sequence and enter filtered data to their + # respective indices in the data + if difference == "central": + col_diff[start:end] = np.gradient(column[start:end]) + else: + col_diff[start:end] = np.diff( + column[start:end], prepend=column[start] + ) + + # set single frames to 0 because differentiation is not possible + for start, end in seqs_short: + col_diff[start:end] = 0 + + # enter filtered data into respective column + differences_vel[:, i] = col_diff + + acceleration = differences_vel * velocity.framerate self._acceleration_ = PlayerProperty( property=acceleration, diff --git a/tests/test_models/test_kinematics.py b/tests/test_models/test_kinematics.py index 3fa57843..2e18098f 100644 --- a/tests/test_models/test_kinematics.py +++ b/tests/test_models/test_kinematics.py @@ -24,7 +24,7 @@ def test_distance_model_fit_difference_central(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(distance_covered, 3), - np.array(((1, np.NaN), (1.118, 1.414), (1.414, np.NaN))), + np.array(((1, 0), (1.118, np.NaN), (1.414, 0))), equal_nan=True, ) @@ -42,7 +42,7 @@ def test_distance_model_fit_difference_backward(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(distance_covered, 3), - np.array(((0, 1.414), (1, np.NaN), (1.414, np.NaN))), + np.array(((0, 0), (1, np.NaN), (1.414, 0))), equal_nan=True, ) @@ -60,7 +60,7 @@ def test_distance_covered(example_xy_object_kinematics) -> None: # Assert assert np.array_equal( np.round(distance_covered, 3), - np.array(((1, np.NaN), (1.118, 1.414), (1.414, np.NaN))), + np.array(((1, 0), (1.118, np.NaN), (1.414, 0))), equal_nan=True, ) @@ -78,7 +78,7 @@ def test_cumulative_distance_covered(example_xy_object_kinematics) -> None: # Assert assert np.array_equal( np.round(distance_covered, 3), - np.array(((1, 0), (2.118, 1.414), (3.532, 1.414))), + np.array(((1, 0), (2.118, 0), (3.532, 0))), equal_nan=True, ) @@ -96,7 +96,7 @@ def test_velocity_model_fit_difference_central(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(velocity, 3), - np.array(((20, np.NaN), (22.361, 28.284), (28.284, np.NaN))), + np.array(((20, 0), (22.361, np.NaN), (28.284, 0))), equal_nan=True, ) @@ -114,7 +114,7 @@ def test_velocity_model_fit_difference_backward(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(velocity, 3), - np.array(((0, 28.284), (20, np.NaN), (28.284, np.NaN))), + np.array(((0, 0), (20, np.NaN), (28.284, 0))), equal_nan=True, ) @@ -132,7 +132,7 @@ def test_velocity(example_xy_object_kinematics) -> None: # Assert assert np.array_equal( np.round(velocity, 3), - np.array(((20, np.NaN), (22.361, 28.284), (28.284, np.NaN))), + np.array(((20, 0), (22.361, np.NaN), (28.284, 0))), equal_nan=True, ) @@ -150,7 +150,7 @@ def test_acceleration_model_difference_central(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(acceleration, 3), - np.array(((47.214, np.NaN), (82.843, np.NaN), (118.472, np.NaN))), + np.array(((47.214, 0), (82.843, np.NaN), (118.472, 0))), equal_nan=True, ) @@ -168,7 +168,7 @@ def test_acceleration_model_difference_backward(example_xy_object_kinematics) -> # Assert assert np.array_equal( np.round(acceleration, 3), - np.array(((400, np.NaN), (165.685, np.NaN), (-565.685, np.NaN))), + np.array(((0, 0), (400, np.NaN), (165.685, 0))), equal_nan=True, ) @@ -186,6 +186,6 @@ def test_acceleration(example_xy_object_kinematics) -> None: # Assert assert np.array_equal( np.round(acceleration, 3), - np.array(((47.214, np.NaN), (82.843, np.NaN), (118.472, np.NaN))), + np.array(((47.214, 0), (82.843, np.NaN), (118.472, 0))), equal_nan=True, ) From 6ba08370ff9c9913b019742c2e8d59500444dfa2 Mon Sep 17 00:00:00 2001 From: manuba95 Date: Mon, 6 Jun 2022 17:23:15 +0200 Subject: [PATCH 2/2] refactor: formatting --- floodlight/models/kinematics.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/floodlight/models/kinematics.py b/floodlight/models/kinematics.py index 5eb340b7..0ec7ac53 100644 --- a/floodlight/models/kinematics.py +++ b/floodlight/models/kinematics.py @@ -125,11 +125,11 @@ def fit(self, xy: XY, difference: str = "central", axis: str = None): f" 'backward', got {difference})" ) - # set single frames to 0 because differentiation is not possible + # set single frames to 0 because differentiation is not possible for start, end in seqs_short: col_diff[start:end] = 0 - # enter filtered data into respective column + # enter filtered data into respective column differences_xy[:, i] = col_diff distance_euclidean = np.hypot(