diff --git a/changelog.md b/changelog.md index c923167f4..7c0ccefa8 100644 --- a/changelog.md +++ b/changelog.md @@ -9,6 +9,7 @@ * Two-channel visualizations can be plotted using `"rg"`, `"gb"`, or `"rb"`. * Added `duration` property to `KymoTrack` which returns the duration (in seconds) that the track was observed. * Added option to filter tracks with `lk.filter_tracks(tracks, minimum_duration=duration_in_seconds)` by track duration (in seconds). +* Added `KymoTrackGroup.filter()` to filter tracks by duration or length (number of identified track points) in-place. #### Improvements diff --git a/lumicks/pylake/kymotracker/kymotrack.py b/lumicks/pylake/kymotracker/kymotrack.py index c3d0bcf58..167dec65a 100644 --- a/lumicks/pylake/kymotracker/kymotrack.py +++ b/lumicks/pylake/kymotracker/kymotrack.py @@ -1276,6 +1276,22 @@ def remove_tracks_in_rect(self, rect, all_points=False): self._src = [track for track in self._src if not track.in_rect(rect, all_points)] + def filter(self, *, minimum_length=1, minimum_duration=0): + """Remove tracks shorter than a minimum number of time points from the list. + + Parameters + ---------- + minimum_length : int, optional + Minimum length for the track to be accepted (default: 1). + minimum_duration : seconds, optional + Minimum duration for a track to be accepted (default: 0). + """ + from .kymotracker import filter_tracks # local import to avoid circular import + + self._src = filter_tracks( + self, minimum_length=minimum_length, minimum_duration=minimum_duration + )._src + def __repr__(self): return f"{self.__class__.__name__}(N={len(self)})" diff --git a/lumicks/pylake/kymotracker/tests/test_refinement.py b/lumicks/pylake/kymotracker/tests/test_refinement.py index 4b532469e..1c2e6eca6 100644 --- a/lumicks/pylake/kymotracker/tests/test_refinement.py +++ b/lumicks/pylake/kymotracker/tests/test_refinement.py @@ -505,9 +505,17 @@ def test_filter_by_duration( tracks = KymoTrackGroup([k1, k2, k3, k4, k5]) filtered = filter_tracks(tracks, minimum_length=0, minimum_duration=minimum_duration) - assert len(filtered) == ref_length - np.testing.assert_allclose(filtered[0]._minimum_observable_duration, ref_minimum) - np.testing.assert_allclose(filtered[-1]._minimum_observable_duration, ref_minimum_dt2) + assert len(tracks) == 5 # return new instance + + tracks.filter(minimum_length=0, minimum_duration=minimum_duration) + assert len(tracks) == ref_length # in-place modification + + for filtered_tracks in (filtered, tracks): + assert len(filtered_tracks) == ref_length + np.testing.assert_allclose(filtered_tracks[0]._minimum_observable_duration, ref_minimum) + np.testing.assert_allclose( + filtered_tracks[-1]._minimum_observable_duration, ref_minimum_dt2 + ) def test_empty_group():