Repository: aeye-lab/pymovements
Pull request #553 (2023-09-14 07:52:14)

feat: Add events to GazeDataFrame init
## Description

Add events keyword argument to GazeDataFrame initializer.
For a start I would like to keep this rather simple.

#552 must be merged first

## Implemented changes

- [x] If events is None: create empty EventDataFrame
- [x] Else: create copy of passed EventDataFrame

## Type of change

- [x] New feature (non-breaking change which adds functionality)

## How Has This Been Tested?

- [x] Tested attributes after initialization for a few dataframe combinations

## 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[bot]: ## [Codecov](https://app.codecov.io/gh/aeye-lab/pymovements/pull/553?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 [(`7e33adf`)](https://app.codecov.io/gh/aeye-lab/pymovements/commit/7e33adfa1037fb3b7b234fa3c2815e0cd9498f3e?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00% compared to head [(`33ba497`)](https://app.codecov.io/gh/aeye-lab/pymovements/pull/553?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00%.

> :exclamation: Current head 33ba497 differs from pull request most recent head e0a5d71. Consider uploading reports for the commit e0a5d71 to get more accurate results

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


```diff
@@            Coverage Diff            @@
##              main      #553   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           48        48           
  Lines         2137      2141    +4     
  Branches       521       475   -46     
=========================================
+ Hits          2137      2141    +4     
```


| [Files Changed](https://app.codecov.io/gh/aeye-lab/pymovements/pull/553?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) | Coverage Δ | |
|---|---|---|
| [src/pymovements/gaze/gaze\_dataframe.py](https://app.codecov.io/gh/aeye-lab/pymovements/pull/553?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab#diff-c3JjL3B5bW92ZW1lbnRzL2dhemUvZ2F6ZV9kYXRhZnJhbWUucHk=) | `100.00% <100.00%> (ø)` | |


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aeye-lab/pymovements/pull/553?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab).   
:loudspeaker: Have feedback on the report? [Share it here](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/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py
index 5561b35..3cfb3a1 100644
--- a/src/pymovements/gaze/gaze_dataframe.py
+++ b/src/pymovements/gaze/gaze_dataframe.py
@@ -27,6 +27,7 @@ from typing import Any
 
 import polars as pl
 
+import pymovements as pm  # pylint: disable=cyclic-import
 from pymovements.gaze import transforms
 from pymovements.gaze.experiment import Experiment
 from pymovements.utils import checks
@@ -79,6 +80,7 @@ class GazeDataFrame:
             self,
             data: pl.DataFrame | None = None,
             experiment: Experiment | None = None,
+            events: pm.EventDataFrame | None = None,
             *,
             trial_columns: str | list[str] | None = None,
             time_column: str | None = None,
@@ -95,6 +97,8 @@ class GazeDataFrame:
             A dataframe to be transformed to a polars dataframe.
         experiment : Experiment
             The experiment definition.
+        events: EventDataFrame
+            A dataframe of events in the gaze signal.
         trial_columns:
             The name of the trial columns in the input data frame. If the list is empty or None,
             the input data frame is assumed to contain only one trial. If the list is not empty,
@@ -214,6 +218,11 @@ class GazeDataFrame:
         self.n_components = _infer_n_components(self.frame, column_specifiers)
         self.experiment = experiment
 
+        if events is None:
+            self.events = pm.EventDataFrame()
+        else:
+            self.events = events.copy()
+
     def transform(
             self,
             transform_method: str | Callable[..., pl.Expr],


Test patch:
diff --git a/tests/gaze/gaze_init_test.py b/tests/gaze/gaze_init_test.py
index f74b87c..b0b510e 100644
--- a/tests/gaze/gaze_init_test.py
+++ b/tests/gaze/gaze_init_test.py
@@ -23,7 +23,7 @@ import polars as pl
 import pytest
 from polars.testing import assert_frame_equal
 
-from pymovements.gaze.gaze_dataframe import GazeDataFrame
+import pymovements as pm
 
 
 @pytest.mark.parametrize(
@@ -704,7 +704,7 @@ from pymovements.gaze.gaze_dataframe import GazeDataFrame
     ],
 )
 def test_init_gaze_dataframe_has_expected_attrs(init_kwargs, expected_frame, expected_n_components):
-    gaze = GazeDataFrame(**init_kwargs)
+    gaze = pm.GazeDataFrame(**init_kwargs)
     assert_frame_equal(gaze.frame, expected_frame)
     assert gaze.n_components == expected_n_components
 
@@ -1175,7 +1175,7 @@ def test_init_gaze_dataframe_has_expected_attrs(init_kwargs, expected_frame, exp
 )
 def test_gaze_dataframe_init_exceptions(init_kwargs, exception, exception_msg):
     with pytest.raises(exception) as excinfo:
-        GazeDataFrame(**init_kwargs)
+        pm.GazeDataFrame(**init_kwargs)
 
     msg, = excinfo.value.args
     assert msg == exception_msg
@@ -1187,9 +1187,71 @@ def test_gaze_copy_init_has_same_n_components():
     Refers to issue #514.
     """
     df_orig = pl.from_numpy(np.zeros((2, 1000)), orient='col', schema=['x', 'y'])
-    gaze = GazeDataFrame(df_orig, position_columns=['x', 'y'])
+    gaze = pm.GazeDataFrame(df_orig, position_columns=['x', 'y'])
 
     df_copy = gaze.frame.clone()
-    gaze_copy = GazeDataFrame(df_copy)
+    gaze_copy = pm.GazeDataFrame(df_copy)
 
     assert gaze.n_components == gaze_copy.n_components
+
+
+@pytest.mark.parametrize(
+    ('events', 'init_kwargs'),
+    [
+        pytest.param(
+            None,
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23], 'y': [4.56]}, schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+            },
+            id='data_with_no_events',
+        ),
+
+        pytest.param(
+            pm.EventDataFrame(),
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23], 'y': [4.56]}, schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+            },
+            id='data_empty_events',
+        ),
+
+        pytest.param(
+            pm.EventDataFrame(),
+            {},
+            id='no_data_empty_events',
+        ),
+
+        pytest.param(
+            pm.EventDataFrame(name='saccade', onsets=[0], offsets=[10]),
+            {},
+            id='no_data_with_saccades',
+        ),
+
+        pytest.param(
+            pm.EventDataFrame(name='fixation', onsets=[100], offsets=[910]),
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23], 'y': [4.56]}, schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+            },
+            id='data_with_fixations',
+        ),
+    ],
+)
+def test_gaze_init_events(events, init_kwargs):
+    if events is None:
+        expected_events = pm.EventDataFrame().frame
+    else:
+        expected_events = events.frame
+
+    gaze = pm.GazeDataFrame(events=events, **init_kwargs)
+
+    assert_frame_equal(gaze.events.frame, expected_events)
+    # We don't want the events point to the same reference.
+    assert gaze.events.frame is not expected_events