From 33ba497a535aa9ea7c6b5f8b257318675bcbe620 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 09:39:00 +0200 Subject: [PATCH 01/15] feat: Add events to GazeDataFrame init --- src/pymovements/gaze/gaze_dataframe.py | 9 ++++ tests/gaze/gaze_init_test.py | 74 ++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 5 deletions(-) diff --git a/src/pymovements/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py index 5561b358c..88321e4ed 100644 --- a/src/pymovements/gaze/gaze_dataframe.py +++ b/src/pymovements/gaze/gaze_dataframe.py @@ -27,6 +27,7 @@ import polars as pl +import pymovements as pm from pymovements.gaze import transforms from pymovements.gaze.experiment import Experiment from pymovements.utils import checks @@ -79,6 +80,7 @@ def __init__( 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 @@ def __init__( 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 @@ def __init__( 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.clone() + def transform( self, transform_method: str | Callable[..., pl.Expr], diff --git a/tests/gaze/gaze_init_test.py b/tests/gaze/gaze_init_test.py index f74b87c89..b30897f29 100644 --- a/tests/gaze/gaze_init_test.py +++ b/tests/gaze/gaze_init_test.py @@ -23,7 +23,7 @@ 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 @@ ], ) 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,73 @@ 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 From ef5898b1d49706a9233dcebc9c40d09a29401060 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 14 Sep 2023 10:29:46 +0000 Subject: [PATCH 02/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/gaze/gaze_init_test.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/gaze/gaze_init_test.py b/tests/gaze/gaze_init_test.py index b30897f29..b0b510ee3 100644 --- a/tests/gaze/gaze_init_test.py +++ b/tests/gaze/gaze_init_test.py @@ -1195,8 +1195,6 @@ def test_gaze_copy_init_has_same_n_components(): assert gaze.n_components == gaze_copy.n_components - - @pytest.mark.parametrize( ('events', 'init_kwargs'), [ From 9ff3ba8d18b8e7c6243b16a09f5b27a06f4039dc Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 09:44:13 +0200 Subject: [PATCH 03/15] feat: Add EventDataFrame.clone() --- src/pymovements/events/frame.py | 10 ++++++++++ tests/events/frame_test.py | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/pymovements/events/frame.py b/src/pymovements/events/frame.py index 6ce425bb4..1c8921a79 100644 --- a/src/pymovements/events/frame.py +++ b/src/pymovements/events/frame.py @@ -164,6 +164,16 @@ def event_property_columns(self) -> list[str]: event_property_columns -= set(self._additional_columns) return list(event_property_columns) + def copy(self) -> EventDataFrame: + """Return a copy of the EventDataFrame. + + Returns + ------- + EventDataFrame + A copy of the EventDataFrame. + """ + return EventDataFrame(data=self.frame.clone()) + def _add_minimal_schema_columns(self, df: pl.DataFrame) -> pl.DataFrame: """Add minimal schema columns to :py:class:`polars.DataFrame` if they are missing.""" if len(df) == 0: diff --git a/tests/events/frame_test.py b/tests/events/frame_test.py index 4c616ec75..4cf7d55b0 100644 --- a/tests/events/frame_test.py +++ b/tests/events/frame_test.py @@ -186,3 +186,13 @@ def test_event_dataframe_columns_same_as_frame(): event_df = pm.EventDataFrame(**init_kwargs) assert event_df.columns == event_df.frame.columns + + +def test_event_dataframe_copy(): + events = pm.EventDataFrame(name='saccade', onsets=[0], offsets=[123]) + events_copy = events.copy() + + # We want to have separate dataframes but with the exact same data. + assert events is not events_copy + assert events.frame is not events_copy.frame + assert_frame_equal(events.frame, events_copy.frame) From 26bf825efd245ca21d8f421013dd550a52d29d83 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 13:09:39 +0200 Subject: [PATCH 04/15] . --- src/pymovements/gaze/gaze_dataframe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pymovements/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py index 88321e4ed..5df0f8835 100644 --- a/src/pymovements/gaze/gaze_dataframe.py +++ b/src/pymovements/gaze/gaze_dataframe.py @@ -221,7 +221,7 @@ def __init__( if events is None: self.events = pm.EventDataFrame() else: - self.events = events.clone() + self.events = events.copy() def transform( self, From 01b8c0389107fa21bccf151ed32e3bff21481263 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 13:14:53 +0200 Subject: [PATCH 05/15] make me happy by ignoring a pylint line --- src/pymovements/gaze/gaze_dataframe.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pymovements/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py index 5df0f8835..3cfb3a11c 100644 --- a/src/pymovements/gaze/gaze_dataframe.py +++ b/src/pymovements/gaze/gaze_dataframe.py @@ -27,7 +27,7 @@ import polars as pl -import pymovements as pm +import pymovements as pm # pylint: disable=cyclic-import from pymovements.gaze import transforms from pymovements.gaze.experiment import Experiment from pymovements.utils import checks From a5166b3dd2b565bbbafc26f94b497e550dff337a Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 13:30:42 +0200 Subject: [PATCH 06/15] . --- src/pymovements/gaze/integration.py | 17 ++++++++--------- tests/gaze/integration_pandas_test.py | 20 ++------------------ 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index ec7b5f70a..db6dc8966 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -26,8 +26,7 @@ import pandas as pd import polars as pl -from pymovements.gaze.experiment import Experiment -from pymovements.gaze.gaze_dataframe import GazeDataFrame +import pymovements as pm from pymovements.utils import checks @@ -39,14 +38,14 @@ def from_numpy( velocity: np.ndarray | None = None, acceleration: np.ndarray | None = None, schema: list[str] | None = None, - experiment: Experiment | None = None, + experiment: pm.Experiment | None = None, orient: Literal['col', 'row'] = 'col', time_column: str | None = None, pixel_columns: list[str] | None = None, position_columns: list[str] | None = None, velocity_columns: list[str] | None = None, acceleration_columns: list[str] | None = None, -) -> GazeDataFrame: +) -> pm.GazeDataFrame: """Get a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a numpy array. There are two mutually exclusive ways of conversion. @@ -197,7 +196,7 @@ def from_numpy( if data is not None: df = pl.from_numpy(data=data, schema=schema, orient=orient) - return GazeDataFrame( + return pm.GazeDataFrame( data=df, experiment=experiment, time_column=time_column, @@ -242,7 +241,7 @@ def from_numpy( acceleration_columns = df.columns df = pl.concat(dfs, how='horizontal') - return GazeDataFrame( + return pm.GazeDataFrame( data=df, experiment=experiment, time_column=time_column, @@ -255,13 +254,13 @@ def from_numpy( def from_pandas( data: pd.DataFrame, - experiment: Experiment | None = None, + experiment: pm.Experiment | None = None, time_column: str | None = None, pixel_columns: list[str] | None = None, position_columns: list[str] | None = None, velocity_columns: list[str] | None = None, acceleration_columns: list[str] | None = None, -) -> GazeDataFrame: +) -> pm.GazeDataFrame: """Get a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a pandas DataFrame. Parameters @@ -286,7 +285,7 @@ def from_pandas( py:class:`~pymovements.GazeDataFrame` """ df = pl.from_pandas(data=data) - return GazeDataFrame( + return pm.GazeDataFrame( data=df, experiment=experiment, time_column=time_column, diff --git a/tests/gaze/integration_pandas_test.py b/tests/gaze/integration_pandas_test.py index 2d1dc2808..ae7f34bde 100644 --- a/tests/gaze/integration_pandas_test.py +++ b/tests/gaze/integration_pandas_test.py @@ -35,15 +35,7 @@ def test_from_pandas(): }, ) - experiment = pm.Experiment( - screen_width_px=1280, - screen_height_px=1024, - screen_width_cm=38, - screen_height_cm=30, - distance_cm=68, - origin='lower left', - sampling_rate=1000.0, - ) + experiment = pm.Experiment(1280, 1024, 38, 30, 68, 'lower left', 1000.0) gaze = pm.gaze.from_pandas( data=pandas_df, @@ -64,15 +56,7 @@ def test_from_pandas_explicit_columns(): }, ) - experiment = pm.Experiment( - screen_width_px=1280, - screen_height_px=1024, - screen_width_cm=38, - screen_height_cm=30, - distance_cm=68, - origin='lower left', - sampling_rate=1000.0, - ) + experiment = pm.Experiment(1280, 1024, 38, 30, 68, 'lower left', 1000.0) gaze = pm.gaze.from_pandas( data=pandas_df, From e642f2c5a4347a7d7175c5fde62ad7d9d9149452 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 11:52:55 +0200 Subject: [PATCH 07/15] fix imports and finally make pylint happy --- src/pymovements/__init__.py | 6 +++--- src/pymovements/dataset/dataset.py | 2 +- src/pymovements/gaze/integration.py | 17 +++++++++-------- src/pymovements/plotting/heatmap.py | 2 +- src/pymovements/plotting/main_sequence_plot.py | 2 +- src/pymovements/plotting/traceplot.py | 1 + src/pymovements/plotting/tsplot.py | 2 +- 7 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/pymovements/__init__.py b/src/pymovements/__init__.py index fd52b4827..7abdc84f3 100644 --- a/src/pymovements/__init__.py +++ b/src/pymovements/__init__.py @@ -33,9 +33,9 @@ from pymovements.events import EventDataFrame from pymovements.events import EventGazeProcessor from pymovements.events import EventProcessor -from pymovements.gaze import Experiment -from pymovements.gaze import GazeDataFrame -from pymovements.gaze import Screen +from pymovements.gaze.experiment import Experiment +from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.gaze.screen import Screen __all__ = [ diff --git a/src/pymovements/dataset/dataset.py b/src/pymovements/dataset/dataset.py index 7eea58629..62ef9616d 100644 --- a/src/pymovements/dataset/dataset.py +++ b/src/pymovements/dataset/dataset.py @@ -37,7 +37,7 @@ from pymovements.events.detection import EventDetectionLibrary from pymovements.events.frame import EventDataFrame from pymovements.events.processing import EventGazeProcessor -from pymovements.gaze import GazeDataFrame +from pymovements.gaze.gaze_dataframe import GazeDataFrame class Dataset: diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index db6dc8966..ec7b5f70a 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -26,7 +26,8 @@ import pandas as pd import polars as pl -import pymovements as pm +from pymovements.gaze.experiment import Experiment +from pymovements.gaze.gaze_dataframe import GazeDataFrame from pymovements.utils import checks @@ -38,14 +39,14 @@ def from_numpy( velocity: np.ndarray | None = None, acceleration: np.ndarray | None = None, schema: list[str] | None = None, - experiment: pm.Experiment | None = None, + experiment: Experiment | None = None, orient: Literal['col', 'row'] = 'col', time_column: str | None = None, pixel_columns: list[str] | None = None, position_columns: list[str] | None = None, velocity_columns: list[str] | None = None, acceleration_columns: list[str] | None = None, -) -> pm.GazeDataFrame: +) -> GazeDataFrame: """Get a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a numpy array. There are two mutually exclusive ways of conversion. @@ -196,7 +197,7 @@ def from_numpy( if data is not None: df = pl.from_numpy(data=data, schema=schema, orient=orient) - return pm.GazeDataFrame( + return GazeDataFrame( data=df, experiment=experiment, time_column=time_column, @@ -241,7 +242,7 @@ def from_numpy( acceleration_columns = df.columns df = pl.concat(dfs, how='horizontal') - return pm.GazeDataFrame( + return GazeDataFrame( data=df, experiment=experiment, time_column=time_column, @@ -254,13 +255,13 @@ def from_numpy( def from_pandas( data: pd.DataFrame, - experiment: pm.Experiment | None = None, + experiment: Experiment | None = None, time_column: str | None = None, pixel_columns: list[str] | None = None, position_columns: list[str] | None = None, velocity_columns: list[str] | None = None, acceleration_columns: list[str] | None = None, -) -> pm.GazeDataFrame: +) -> GazeDataFrame: """Get a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a pandas DataFrame. Parameters @@ -285,7 +286,7 @@ def from_pandas( py:class:`~pymovements.GazeDataFrame` """ df = pl.from_pandas(data=data) - return pm.GazeDataFrame( + return GazeDataFrame( data=df, experiment=experiment, time_column=time_column, diff --git a/src/pymovements/plotting/heatmap.py b/src/pymovements/plotting/heatmap.py index 44da8953a..30574efe7 100644 --- a/src/pymovements/plotting/heatmap.py +++ b/src/pymovements/plotting/heatmap.py @@ -24,7 +24,7 @@ import numpy as np from matplotlib import colors -from pymovements.gaze import GazeDataFrame +from pymovements.gaze.gaze_dataframe import GazeDataFrame def heatmap( diff --git a/src/pymovements/plotting/main_sequence_plot.py b/src/pymovements/plotting/main_sequence_plot.py index 551e73152..f19aae71e 100644 --- a/src/pymovements/plotting/main_sequence_plot.py +++ b/src/pymovements/plotting/main_sequence_plot.py @@ -25,7 +25,7 @@ from matplotlib.collections import Collection from polars import ColumnNotFoundError -from pymovements.events import EventDataFrame +from pymovements.events.frame import EventDataFrame def main_sequence_plot( diff --git a/src/pymovements/plotting/traceplot.py b/src/pymovements/plotting/traceplot.py index 4bacb1fe8..9b6b62988 100644 --- a/src/pymovements/plotting/traceplot.py +++ b/src/pymovements/plotting/traceplot.py @@ -30,6 +30,7 @@ from pymovements.gaze.gaze_dataframe import GazeDataFrame + # This is really a dirty workaround to use the Agg backend if runnning pytest. # This is needed as Windows workers on GitHub fail randomly with other backends. # Unfortunately the Agg module cannot show plots in jupyter notebooks. diff --git a/src/pymovements/plotting/tsplot.py b/src/pymovements/plotting/tsplot.py index 8207ea715..767545bc0 100644 --- a/src/pymovements/plotting/tsplot.py +++ b/src/pymovements/plotting/tsplot.py @@ -26,7 +26,7 @@ import numpy as np import polars as pl -from pymovements.gaze import GazeDataFrame +from pymovements.gaze.gaze_dataframe import GazeDataFrame def tsplot( From a8868fcda84af5f3c0241f19b11f7152152854d7 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 11:58:35 +0200 Subject: [PATCH 08/15] fix import error --- src/pymovements/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pymovements/__init__.py b/src/pymovements/__init__.py index 7abdc84f3..e3fff1d48 100644 --- a/src/pymovements/__init__.py +++ b/src/pymovements/__init__.py @@ -21,6 +21,7 @@ from pymovements import _version from pymovements import datasets from pymovements import events +from pymovements import exceptions from pymovements import gaze from pymovements import plotting from pymovements import synthetic @@ -56,6 +57,7 @@ 'Screen', 'GazeDataFrame', + 'exceptions', 'plotting', 'synthetic', 'utils', From 5e4ff0818b29972316d864864ff696fe94dc7190 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 10:03:39 +0200 Subject: [PATCH 09/15] feat: Add events to gaze integration functions --- src/pymovements/gaze/integration.py | 14 +++++++--- tests/gaze/integration_numpy_test.py | 41 +++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index ec7b5f70a..87671e335 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -26,6 +26,7 @@ import pandas as pd import polars as pl +import pymovements as pm from pymovements.gaze.experiment import Experiment from pymovements.gaze.gaze_dataframe import GazeDataFrame from pymovements.utils import checks @@ -33,13 +34,15 @@ def from_numpy( data: np.ndarray | None = None, + experiment: Experiment | None = None, + events: pm.EventDataFrame | None = None, + *, time: np.ndarray | None = None, pixel: np.ndarray | None = None, position: np.ndarray | None = None, velocity: np.ndarray | None = None, acceleration: np.ndarray | None = None, schema: list[str] | None = None, - experiment: Experiment | None = None, orient: Literal['col', 'row'] = 'col', time_column: str | None = None, pixel_columns: list[str] | None = None, @@ -63,6 +66,10 @@ def from_numpy( ---------- data: Two-dimensional data represented as a numpy ndarray. + experiment : Experiment + The experiment definition. + events: EventDataFrame + A dataframe of events in the gaze signal. time: Array of timestamps. pixel: @@ -77,9 +84,6 @@ def from_numpy( A list of column names. orient: Whether to interpret the two-dimensional data as columns or as rows. - experiment : Experiment - The experiment definition. - time_column: str | None = None, time_column: The name of the timestamp column in the input data frame. pixel_columns: @@ -200,6 +204,7 @@ def from_numpy( return GazeDataFrame( data=df, experiment=experiment, + events=events, time_column=time_column, pixel_columns=pixel_columns, position_columns=position_columns, @@ -245,6 +250,7 @@ def from_numpy( return GazeDataFrame( data=df, experiment=experiment, + events=events, time_column=time_column, pixel_columns=pixel_columns, position_columns=position_columns, diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index ae39855d2..4fea99e43 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -20,6 +20,7 @@ """Test from gaze.from_numpy.""" import numpy as np import polars as pl +import pytest from polars.testing import assert_frame_equal import pymovements as pm @@ -164,7 +165,7 @@ def test_from_numpy_explicit_columns(): assert gaze.n_components == 2 -def test_init_all_none(): +def test_from_numpy_all_none(): gaze = pm.gaze.from_numpy( data=None, schema=None, @@ -185,3 +186,41 @@ def test_init_all_none(): assert_frame_equal(gaze.frame, expected) assert gaze.n_components is None + + +@pytest.mark.parametrize( + ('events'), + [ + pytest.param( + None, + id='events_none', + ), + + pytest.param( + pm.EventDataFrame(), + id='events_empty', + ), + + pytest.param( + pm.EventDataFrame(name='fixation', onsets=[123], offsets=[345]), + id='fixation', + ), + + pytest.param( + pm.EventDataFrame(name='saccade', onsets=[34123], offsets=[67345]), + id='saccade', + ), + + ] +) +def test_from_numpy_events(events): + if events is None: + expected_events = pm.EventDataFrame().frame + else: + expected_events = events.frame + + gaze = pm.gaze.from_numpy(events=events) + + 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 From 74f4238d12c00240326a77bfaea5b780ad81a0f2 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 10:04:15 +0200 Subject: [PATCH 10/15] . --- tests/gaze/integration_numpy_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index 4fea99e43..c94ab66f6 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -211,7 +211,7 @@ def test_from_numpy_all_none(): id='saccade', ), - ] + ], ) def test_from_numpy_events(events): if events is None: From ae8bb233123dbe1b70c70043e94f66ba2aa0ae60 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 10:11:42 +0200 Subject: [PATCH 11/15] feat: Add events to gaze integration functions --- src/pymovements/gaze/integration.py | 5 ++++ tests/gaze/integration_numpy_test.py | 2 +- tests/gaze/integration_pandas_test.py | 43 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index 87671e335..d075739d7 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -262,6 +262,8 @@ def from_numpy( def from_pandas( data: pd.DataFrame, experiment: Experiment | None = None, + events: pm.EventDataFrame | None = None, + *, time_column: str | None = None, pixel_columns: list[str] | None = None, position_columns: list[str] | None = None, @@ -276,6 +278,8 @@ def from_pandas( Data represented as a pandas DataFrame. experiment : Experiment The experiment definition. + events: EventDataFrame + A dataframe of events in the gaze signal. time_column: The name of the timestamp column in the input data frame. pixel_columns: @@ -295,6 +299,7 @@ def from_pandas( return GazeDataFrame( data=df, experiment=experiment, + events=events, time_column=time_column, pixel_columns=pixel_columns, position_columns=position_columns, diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index c94ab66f6..ed61afedf 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -189,7 +189,7 @@ def test_from_numpy_all_none(): @pytest.mark.parametrize( - ('events'), + 'events', [ pytest.param( None, diff --git a/tests/gaze/integration_pandas_test.py b/tests/gaze/integration_pandas_test.py index ae7f34bde..082c3b85b 100644 --- a/tests/gaze/integration_pandas_test.py +++ b/tests/gaze/integration_pandas_test.py @@ -20,6 +20,7 @@ """Test from gaze.from_pandas.""" import pandas as pd import polars as pl +import pytest from polars.testing import assert_frame_equal import pymovements as pm @@ -71,3 +72,45 @@ def test_from_pandas_explicit_columns(): }) assert_frame_equal(gaze.frame, expected) + + +@pytest.mark.parametrize( + ('df', 'events'), + [ + pytest.param( + pd.DataFrame(), + None, + id='events_none', + ), + + pytest.param( + pd.DataFrame(), + pm.EventDataFrame(), + id='events_empty', + ), + + pytest.param( + pd.DataFrame(), + pm.EventDataFrame(name='fixation', onsets=[123], offsets=[345]), + id='fixation', + ), + + pytest.param( + pd.DataFrame(), + pm.EventDataFrame(name='saccade', onsets=[34123], offsets=[67345]), + id='saccade', + ), + + ], +) +def test_from_pandas_events(df, events): + if events is None: + expected_events = pm.EventDataFrame().frame + else: + expected_events = events.frame + + gaze = pm.gaze.from_pandas(data=df, events=events) + + 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 From 04dd31897060adae186c53c0e4e76f7ba591b637 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 12:14:17 +0200 Subject: [PATCH 12/15] fixed python circular import error, pylint still unhappy --- src/pymovements/__init__.py | 6 +++--- src/pymovements/events/processing.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pymovements/__init__.py b/src/pymovements/__init__.py index e3fff1d48..d7591775a 100644 --- a/src/pymovements/__init__.py +++ b/src/pymovements/__init__.py @@ -34,9 +34,9 @@ from pymovements.events import EventDataFrame from pymovements.events import EventGazeProcessor from pymovements.events import EventProcessor -from pymovements.gaze.experiment import Experiment -from pymovements.gaze.gaze_dataframe import GazeDataFrame -from pymovements.gaze.screen import Screen +from pymovements.gaze import Experiment +from pymovements.gaze import GazeDataFrame +from pymovements.gaze import Screen __all__ = [ diff --git a/src/pymovements/events/processing.py b/src/pymovements/events/processing.py index 15a51bf86..3abf2c9f8 100644 --- a/src/pymovements/events/processing.py +++ b/src/pymovements/events/processing.py @@ -26,10 +26,10 @@ import polars as pl +import pymovements as pm from pymovements.events.frame import EventDataFrame from pymovements.events.properties import EVENT_PROPERTIES from pymovements.exceptions import InvalidProperty -from pymovements.gaze.gaze_dataframe import GazeDataFrame class EventProcessor: @@ -140,7 +140,7 @@ def __init__( def process( self, events: EventDataFrame, - gaze: GazeDataFrame, + gaze: pm.GazeDataFrame, identifiers: str | list[str], name: str | None = None, ) -> pl.DataFrame: From 24de5e2fe5f850096357e9cb140d96802aa0a346 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 12:18:23 +0200 Subject: [PATCH 13/15] make pylint happy by ignoreing a line --- src/pymovements/events/processing.py | 2 +- src/pymovements/gaze/integration.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pymovements/events/processing.py b/src/pymovements/events/processing.py index 3abf2c9f8..5f8b57970 100644 --- a/src/pymovements/events/processing.py +++ b/src/pymovements/events/processing.py @@ -26,7 +26,7 @@ import polars as pl -import pymovements as pm +import pymovements as pm # pylint: disable=cyclic-import from pymovements.events.frame import EventDataFrame from pymovements.events.properties import EVENT_PROPERTIES from pymovements.exceptions import InvalidProperty diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index d075739d7..0d3554ccf 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -26,7 +26,7 @@ import pandas as pd import polars as pl -import pymovements as pm +from pymovements.events.frame import EventDataFrame from pymovements.gaze.experiment import Experiment from pymovements.gaze.gaze_dataframe import GazeDataFrame from pymovements.utils import checks @@ -35,7 +35,7 @@ def from_numpy( data: np.ndarray | None = None, experiment: Experiment | None = None, - events: pm.EventDataFrame | None = None, + events: EventDataFrame | None = None, *, time: np.ndarray | None = None, pixel: np.ndarray | None = None, @@ -262,7 +262,7 @@ def from_numpy( def from_pandas( data: pd.DataFrame, experiment: Experiment | None = None, - events: pm.EventDataFrame | None = None, + events: EventDataFrame | None = None, *, time_column: str | None = None, pixel_columns: list[str] | None = None, From cbe2c53dddad555d08c6be0d72de84849a91a21a Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 16:54:21 +0200 Subject: [PATCH 14/15] revert import changes --- src/pymovements/plotting/heatmap.py | 2 +- src/pymovements/plotting/main_sequence_plot.py | 2 +- src/pymovements/plotting/traceplot.py | 2 +- src/pymovements/plotting/tsplot.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pymovements/plotting/heatmap.py b/src/pymovements/plotting/heatmap.py index 30574efe7..44da8953a 100644 --- a/src/pymovements/plotting/heatmap.py +++ b/src/pymovements/plotting/heatmap.py @@ -24,7 +24,7 @@ import numpy as np from matplotlib import colors -from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.gaze import GazeDataFrame def heatmap( diff --git a/src/pymovements/plotting/main_sequence_plot.py b/src/pymovements/plotting/main_sequence_plot.py index f19aae71e..551e73152 100644 --- a/src/pymovements/plotting/main_sequence_plot.py +++ b/src/pymovements/plotting/main_sequence_plot.py @@ -25,7 +25,7 @@ from matplotlib.collections import Collection from polars import ColumnNotFoundError -from pymovements.events.frame import EventDataFrame +from pymovements.events import EventDataFrame def main_sequence_plot( diff --git a/src/pymovements/plotting/traceplot.py b/src/pymovements/plotting/traceplot.py index 9b6b62988..e82e9fff1 100644 --- a/src/pymovements/plotting/traceplot.py +++ b/src/pymovements/plotting/traceplot.py @@ -28,7 +28,7 @@ from matplotlib import colors from matplotlib.collections import LineCollection -from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.gaze import GazeDataFrame # This is really a dirty workaround to use the Agg backend if runnning pytest. diff --git a/src/pymovements/plotting/tsplot.py b/src/pymovements/plotting/tsplot.py index 767545bc0..8207ea715 100644 --- a/src/pymovements/plotting/tsplot.py +++ b/src/pymovements/plotting/tsplot.py @@ -26,7 +26,7 @@ import numpy as np import polars as pl -from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.gaze import GazeDataFrame def tsplot( From c7c91862dd01d355c64cfd7ee48dbcd38eb6d8e6 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Thu, 14 Sep 2023 16:56:04 +0200 Subject: [PATCH 15/15] revert import changes --- src/pymovements/dataset/dataset.py | 2 +- src/pymovements/plotting/traceplot.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/pymovements/dataset/dataset.py b/src/pymovements/dataset/dataset.py index 62ef9616d..7eea58629 100644 --- a/src/pymovements/dataset/dataset.py +++ b/src/pymovements/dataset/dataset.py @@ -37,7 +37,7 @@ from pymovements.events.detection import EventDetectionLibrary from pymovements.events.frame import EventDataFrame from pymovements.events.processing import EventGazeProcessor -from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.gaze import GazeDataFrame class Dataset: diff --git a/src/pymovements/plotting/traceplot.py b/src/pymovements/plotting/traceplot.py index e82e9fff1..4bacb1fe8 100644 --- a/src/pymovements/plotting/traceplot.py +++ b/src/pymovements/plotting/traceplot.py @@ -28,8 +28,7 @@ from matplotlib import colors from matplotlib.collections import LineCollection -from pymovements.gaze import GazeDataFrame - +from pymovements.gaze.gaze_dataframe import GazeDataFrame # This is really a dirty workaround to use the Agg backend if runnning pytest. # This is needed as Windows workers on GitHub fail randomly with other backends.