Repository: aeye-lab/pymovements
Pull request #438 (2023-06-05 16:37:22)

feat(events): Raise RuntimeError in compute_event_properties() if name not found

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) Report
Patch coverage: **`100.00`**% and no project coverage change.
> Comparison is base [(`dcd9f4a`)](https://app.codecov.io/gh/aeye-lab/pymovements/commit/dcd9f4ad9af5fe791a4e4eca52e770035184b2bf?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00% compared to head [(`f91c881`)](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00%.

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


```diff
@@            Coverage Diff            @@
##              main      #438   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           55        55           
  Lines         2021      2022    +1     
  Branches       460       461    +1     
=========================================
+ Hits          2021      2022    +1     
```


| [Impacted Files](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) | Coverage Δ | |
|---|---|---|
| [src/pymovements/dataset/dataset.py](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2RhdGFzZXQvZGF0YXNldC5weQ==) | `100.00% <ø> (ø)` | |
| [src/pymovements/events/event\_processing.py](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2V2ZW50cy9ldmVudF9wcm9jZXNzaW5nLnB5) | `100.00% <100.00%> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aeye-lab/pymovements/pull/438?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/dataset/dataset.py b/src/pymovements/dataset/dataset.py
index 0cce8fa..b2f98bc 100644
--- a/src/pymovements/dataset/dataset.py
+++ b/src/pymovements/dataset/dataset.py
@@ -525,6 +525,8 @@ class Dataset:
         InvalidProperty
             If ``property_name`` is not a valid property. See
             :py:mod:`pymovements.events.event_properties` for an overview of supported properties.
+        RuntimeError
+            If specified event name ``name`` is missing from ``events``.
 
         Returns
         -------
diff --git a/src/pymovements/events/event_processing.py b/src/pymovements/events/event_processing.py
index d706e5d..50f0f4e 100644
--- a/src/pymovements/events/event_processing.py
+++ b/src/pymovements/events/event_processing.py
@@ -170,6 +170,8 @@ class EventGazeProcessor:
         InvalidProperty
             If ``property_name`` is not a valid property. See
             :py:mod:`pymovements.events.event_properties` for an overview of supported properties.
+        RuntimeError
+            If specified event name ``name`` is missing from ``events``.
         """
         if isinstance(identifiers, str):
             trial_identifiers = [identifiers]
@@ -220,14 +222,13 @@ class EventGazeProcessor:
         # a name and its on- and offset.
         event_identifiers = [*trial_identifiers, 'name', 'onset', 'offset']
 
-        # Each event is uniquely defined by a list of trial identifiers,
-        # a name and its on- and offset.
-        event_identifiers = [*trial_identifiers, 'name', 'onset', 'offset']
-
         joined_frame = gaze.frame.join(events.frame, on=trial_identifiers)
         if name is not None:
             joined_frame = joined_frame.filter(pl.col('name').str.contains(f'^{name}$'))
 
+        if len(joined_frame) == 0:
+            raise RuntimeError(f'No events with name "{name}" found in data frame')
+
         result = (
             joined_frame
             .filter(pl.col('time').is_between(pl.col('onset'), pl.col('offset')))


Test patch:
diff --git a/tests/dataset/dataset_test.py b/tests/dataset/dataset_test.py
index eed081f..f51e6cb 100644
--- a/tests/dataset/dataset_test.py
+++ b/tests/dataset/dataset_test.py
@@ -1297,11 +1297,6 @@ def test_event_dataframe_add_property_has_expected_schema(
             ['peak_velocity'],
             id='single_event_peak_velocity_regex_name_sacc',
         ),
-        pytest.param(
-            {'event_properties': 'peak_velocity', 'name': 'acc'},
-            ['peak_velocity'],
-            id='single_event_peak_velocity_name_acc_all_null',
-        ),
     ],
 )
 def test_event_dataframe_add_property_effect_property_columns(
@@ -1316,6 +1311,29 @@ def test_event_dataframe_add_property_effect_property_columns(
         assert events_df.event_property_columns == expected_property_columns
 
 
+@pytest.mark.parametrize(
+    ('property_kwargs', 'exception', 'exception_msg'),
+    [
+        pytest.param(
+            {'event_properties': 'peak_velocity', 'name': 'taccade'},
+            RuntimeError, 'No events with name "taccade" found in data frame',
+            id='name_missing',
+        ),
+    ],
+)
+def test_event_dataframe_add_property_raises_exception(
+        dataset_configuration, property_kwargs, exception, exception_msg,
+):
+    dataset = pm.Dataset(**dataset_configuration['init_kwargs'])
+    dataset.load(preprocessed=True, events=True)
+
+    with pytest.raises(exception) as excinfo:
+        dataset.compute_event_properties(**property_kwargs)
+
+    msg, = excinfo.value.args
+    assert msg == exception_msg
+
+
 @pytest.mark.parametrize(
     'property_kwargs',
     [
diff --git a/tests/events/event_processing_test.py b/tests/events/event_processing_test.py
index af6b5c7..8d4ec32 100644
--- a/tests/events/event_processing_test.py
+++ b/tests/events/event_processing_test.py
@@ -415,40 +415,6 @@ def test_event_gaze_processor_init_exceptions(args, kwargs, exception, msg_subst
             ),
             id='two_events_peak_velocity_name_filter',
         ),
-        pytest.param(
-            pl.from_dict(
-                {'subject_id': [1, 1], 'name': 'abcdef', 'onset': [0, 80], 'offset': [10, 100]},
-                schema={
-                    'subject_id': pl.Int64, 'name': pl.Utf8, 'onset': pl.Int64, 'offset': pl.Int64,
-                },
-            ),
-            pl.from_dict(
-                {
-                    'subject_id': np.ones(100),
-                    'time': np.arange(100),
-                    'x_vel': np.concatenate([np.ones(10), np.zeros(70), 2 * np.ones(20)]),
-                    'y_vel': np.concatenate([np.ones(10), np.zeros(70), 2 * np.ones(20)]),
-                },
-                schema={
-                    'subject_id': pl.Int64,
-                    'time': pl.Int64,
-                    'x_vel': pl.Float64,
-                    'y_vel': pl.Float64,
-                },
-            ),
-            {'event_properties': 'peak_velocity'},
-            {'identifiers': 'subject_id', 'name': 'cde'},
-            pl.DataFrame(
-                schema={
-                    'subject_id': pl.Int64,
-                    'name': pl.Utf8,
-                    'onset': pl.Int64,
-                    'offset': pl.Int64,
-                    'peak_velocity': pl.Float64,
-                },
-            ),
-            id='two_events_peak_velocity_name_filter_no_match_all_empty_dataframe',
-        ),
         pytest.param(
             pl.from_dict(
                 {'subject_id': [1, 1], 'name': ['A', 'B'], 'onset': [0, 80], 'offset': [10, 100]},
@@ -613,6 +579,34 @@ def test_event_gaze_processor_process_correct_result(
             ('identifiers', 'list', 'must', 'not', 'empty'),
             id='empty_list',
         ),
+
+        pytest.param(
+            pl.from_dict(
+                {'subject_id': [1, 1], 'name': 'abcdef', 'onset': [0, 80], 'offset': [10, 100]},
+                schema={
+                    'subject_id': pl.Int64, 'name': pl.Utf8, 'onset': pl.Int64, 'offset': pl.Int64,
+                },
+            ),
+            pl.from_dict(
+                {
+                    'subject_id': np.ones(100),
+                    'time': np.arange(100),
+                    'x_vel': np.concatenate([np.ones(10), np.zeros(70), 2 * np.ones(20)]),
+                    'y_vel': np.concatenate([np.ones(10), np.zeros(70), 2 * np.ones(20)]),
+                },
+                schema={
+                    'subject_id': pl.Int64,
+                    'time': pl.Int64,
+                    'x_vel': pl.Float64,
+                    'y_vel': pl.Float64,
+                },
+            ),
+            {'event_properties': 'peak_velocity'},
+            {'identifiers': 'subject_id', 'name': 'cde'},
+            RuntimeError,
+            ('No events with name "cde" found in data frame',),
+            id='event_name_not_in_dataframe',
+        ),
     ],
 )
 def test_event_processor_process_exceptions(