Repository: aeye-lab/pymovements
Pull request #194 (2023-02-23 11:19:40)

Events: Add method to clear event dataframes
## Description

This adds the possibility to manually clear the event dataframe.
Additionally, a `clear` flag is added to the `detect_events()` method to clear the dataframe instead of merging new detections.

## Implemented changes

- [x] Add `Dataset.clear_events()`
- [x] Add argument `clear: bool = False` to `Dataset.detect_events()`

## Type of change

- [x] New feature (non-breaking change which adds functionality)
- [x] This change is or requires a documentation update

## How Has This Been Tested?
Only the `clear()` method has been tested. Testing `detect_events()` is a separate part on my agenda.

- [x] Empty dataframes stay empty dataframes
- [x] list of a single event dataframe is cleared to a list of a single empty dataframe
- [x] list of a two event dataframes is cleared to a list of two empty dataframes

## Checklist:

- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have made corresponding changes to the documentation
- [x] My changes generate no new warnings
- [x] I have added tests that prove my fix is effective or that my feature works
- [x] New and existing unit tests pass locally with my changes
- [x] Any dependent changes have been merged and published in downstream modules
- [x] I have checked my code and corrected any misspellings


codecov-commenter: # [Codecov](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) Report
Base: **60.12**% // Head: **60.62**% // Increases project coverage by **`+0.49%`** :tada:
> Coverage data is based on head [(`1f3f7c3`)](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) compared to base [(`f498066`)](https://codecov.io/gh/aeye-lab/pymovements/commit/f4980668900ea8bd4bef3e63657df31f038fdb56?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).
> Patch coverage: 85.71% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #194      +/-   ##
==========================================
+ Coverage   60.12%   60.62%   +0.49%     
==========================================
  Files          21       21              
  Lines         790      800      +10     
  Branches      183      187       +4     
==========================================
+ Hits          475      485      +10     
  Misses        304      304              
  Partials       11       11              
```


| [Impacted Files](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) | Coverage Δ | |
|---|---|---|
| [src/pymovements/datasets/dataset.py](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXRzL2RhdGFzZXQucHk=) | `25.36% <85.71%> (+2.25%)` | :arrow_up: |
| [src/pymovements/synthetic/step\_function.py](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL3N5bnRoZXRpYy9zdGVwX2Z1bmN0aW9uLnB5) | `100.00% <0.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/aeye-lab/pymovements/pull/194?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).


Patch:
diff --git a/src/pymovements/datasets/dataset.py b/src/pymovements/datasets/dataset.py
index b5d675a..f9d8529 100644
--- a/src/pymovements/datasets/dataset.py
+++ b/src/pymovements/datasets/dataset.py
@@ -28,6 +28,7 @@ import polars as pl
 from tqdm.auto import tqdm
 
 from pymovements.base import Experiment
+from pymovements.events.events import Event
 from pymovements.events.events import EventDetectionCallable
 from pymovements.utils.paths import get_filepaths
 
@@ -394,6 +395,7 @@ class Dataset:
             self,
             method: EventDetectionCallable,
             eye: str = 'auto',
+            clear: bool = False,
             verbose: bool = True,
             **kwargs,
     ) -> None:
@@ -407,6 +409,9 @@ class Dataset:
             Select which eye to choose. Valid options are ``auto``, ``left`` or ``right``. If
             ``auto`` is passed, ``left`` will only be chosen if the right eye is not available in
             the gaze data frame.
+        clear : bool
+            If ``True``, event DataFrame will be overwritten with new DataFrame instead of being
+             merged into the existing one.
         verbose : bool
             If ``True``, show progress bar.
         **kwargs :
@@ -447,7 +452,7 @@ class Dataset:
             events = method(positions=positions, velocities=velocities, **kwargs)
             event_dfs.append(events)
 
-        if not self.events:
+        if not self.events or clear:
             self.events = event_dfs
             return
 
@@ -457,6 +462,14 @@ class Dataset:
                 how='diagonal',
             )
 
+    def clear_events(self) -> None:
+        """Clear event DataFrame."""
+        if len(self.events) == 0:
+            return
+
+        for file_id, _ in enumerate(self.events):
+            self.events[file_id] = pl.DataFrame(schema=Event.schema)
+
     def save(self, verbose: int = 1):
         """Save preprocessed gaze and event files.
 


Test patch:
diff --git a/tests/datasets/test_dataset.py b/tests/datasets/test_dataset.py
new file mode 100644
index 0000000..87aa9f5
--- /dev/null
+++ b/tests/datasets/test_dataset.py
@@ -0,0 +1,74 @@
+# Copyright (c) 2023 The pymovements Project Authors
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+"""Test all functionality in pymovements.datasets.dataset."""
+import polars as pl
+import pytest
+from polars.testing import assert_frame_equal
+
+from pymovements.datasets.dataset import Dataset
+from pymovements.events.events import Event
+
+
+@pytest.mark.parametrize(
+    'events_init, events_expected',
+    [
+        pytest.param(
+            [],
+            [],
+            id='empty_list_stays_empty_list',
+        ),
+        pytest.param(
+            [pl.DataFrame(schema=Event.schema)],
+            [pl.DataFrame(schema=Event.schema)],
+            id='empty_df_stays_empty_df',
+        ),
+        pytest.param(
+            [pl.DataFrame({'type': ['event'], 'onset': [0], 'offset': [99]}, schema=Event.schema)],
+            [pl.DataFrame(schema=Event.schema)],
+            id='single_instance_filled_df_gets_cleared_to_empty_df',
+        ),
+        pytest.param(
+            [
+                pl.DataFrame(
+                    {'type': ['event'], 'onset': [0], 'offset': [99]},
+                    schema=Event.schema,
+                ),
+                pl.DataFrame(
+                    {'type': ['event'], 'onset': [0], 'offset': [99]},
+                    schema=Event.schema,
+                ),
+            ],
+            [pl.DataFrame(schema=Event.schema), pl.DataFrame(schema=Event.schema)],
+            id='two_instance_filled_df_gets_cleared_to_two_empty_dfs',
+        ),
+    ],
+)
+def test_clear_events(events_init, events_expected):
+    """Test if idt detects fixations."""
+    dataset = Dataset(root='data')
+    dataset.events = events_init
+    dataset.clear_events()
+
+    if isinstance(events_init, list) and not events_init:
+        assert dataset.events == events_expected
+
+    else:
+        for events_df_result, events_df_expected in zip(dataset.events, events_expected):
+            assert_frame_equal(events_df_result, events_df_expected)