From bcbf93e5442ea86243e8468ab72f37cf50d26c69 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Fri, 8 Sep 2023 20:08:33 +0200 Subject: [PATCH 01/11] feat: Add support for explicit column passing in gaze.from_numpy() --- src/pymovements/gaze/integration.py | 88 ++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 13 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index e61c9025e..8725dac63 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -28,18 +28,24 @@ from pymovements.gaze.experiment import Experiment from pymovements.gaze.gaze_dataframe import GazeDataFrame +from pymovements.utils import checks def from_numpy( - data: np.ndarray, - schema: list[str], + data: np.ndarray = None, + time: np.ndarray | None = None, + pixel: np.ndarray | list[np.ndarray] | None = None, + position: np.ndarray | list[np.ndarray] | None = None, + velocity: np.ndarray | list[np.ndarray] | None = None, + acceleration: np.ndarray | list[np.ndarray] | None = None, + schema: list[str] = 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, + pixel_columns: list[str] | list[int] | None = None, + position_columns: list[str] | list[int] | None = None, + velocity_columns: list[str] | list[int] | None = None, + acceleration_columns: list[str] | list[int] | None = None, ) -> GazeDataFrame: """Construct a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a numpy array. @@ -48,6 +54,16 @@ def from_numpy( ---------- data: Two-dimensional data represented as a numpy ndarray. + time: + Array of timestamps. + pixel: + Array of gaze pixel positions. + position: + Array of gaze positions in degrees of visual angle. + velocity: + Array of gaze velocities in degrees of visual angle per second. + acceleration: + Array of gaze accelerations in degrees of visual angle per square second. schema: A list of column names. orient: @@ -70,16 +86,62 @@ def from_numpy( ------- py:class:`~pymovements.GazeDataFrame` """ - df = pl.from_numpy(data=data, schema=schema, orient=orient) - return GazeDataFrame( + + if data is not None: + checks.check_is_mutual_exclusive(data=data, time=time) + checks.check_is_mutual_exclusive(data=data, pixel=pixel) + checks.check_is_mutual_exclusive(data=data, position=position) + checks.check_is_mutual_exclusive(data=data, velocity=velocity) + checks.check_is_mutual_exclusive(data=data, acceleration=acceleration) + + df = pl.from_numpy(data=data, schema=schema, orient=orient) + return GazeDataFrame( + data=df, + experiment=experiment, + time_column=time_column, + pixel_columns=pixel_columns, + position_columns=position_columns, + velocity_columns=velocity_columns, + acceleration_columns=acceleration_columns, + ) + + n_components: int = 0 + columns: pl.Series = [] + + if time is not None: + column = pl.from_numpy(data=time, schema=['time'], orient=orient)['time'] + columns.append(column) + + if pixel is not None: + column = pl.from_numpy(data=pixel, schema=['pixel'], orient=orient)['pixel'] + columns.append(column) + n_components = column.list.lengths()[0] + + if position is not None: + column = pl.from_numpy(data=position, schema=['position'], orient=orient)['position'] + columns.append(column) + n_components = column.list.lengths()[0] + + if velocity is not None: + column = pl.from_numpy(data=velocity, schema=['velocity'], orient=orient)['velocity'] + columns.append(column) + n_components = column.list.lengths()[0] + + if acceleration is not None: + column = pl.from_numpy( + data=acceleration, schema=['acceleration'], orient=orient, + )['acceleration'] + columns.append(column) + n_components = column.list.lengths()[0] + + df = pl.DataFrame(columns) + gaze = GazeDataFrame( data=df, experiment=experiment, - time_column=time_column, - pixel_columns=pixel_columns, - position_columns=position_columns, - velocity_columns=velocity_columns, - acceleration_columns=acceleration_columns, ) + gaze.n_components = n_components + + return gaze def from_pandas( From 4da4a28ec0185bf50da938e6e3e2fe7830a7797f Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Fri, 8 Sep 2023 20:52:55 +0200 Subject: [PATCH 02/11] add tests --- src/pymovements/gaze/integration.py | 60 ++++++++++++++++------------ tests/gaze/integration_numpy_test.py | 53 +++++++++++++++++++++++- 2 files changed, 85 insertions(+), 28 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index 8725dac63..72d72793f 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -32,20 +32,20 @@ def from_numpy( - data: np.ndarray = None, + data: np.ndarray | None = None, time: np.ndarray | None = None, pixel: np.ndarray | list[np.ndarray] | None = None, position: np.ndarray | list[np.ndarray] | None = None, velocity: np.ndarray | list[np.ndarray] | None = None, acceleration: np.ndarray | list[np.ndarray] | None = None, - schema: list[str] = None, + schema: list[str] | None = None, experiment: Experiment | None = None, orient: Literal['col', 'row'] = 'col', time_column: str | None = None, - pixel_columns: list[str] | list[int] | None = None, - position_columns: list[str] | list[int] | None = None, - velocity_columns: list[str] | list[int] | None = None, - acceleration_columns: list[str] | list[int] | 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: """Construct a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a numpy array. @@ -105,41 +105,49 @@ def from_numpy( acceleration_columns=acceleration_columns, ) - n_components: int = 0 - columns: pl.Series = [] + dfs: pl.Series = [] + time_column = None if time is not None: - column = pl.from_numpy(data=time, schema=['time'], orient=orient)['time'] - columns.append(column) + df = pl.from_numpy(data=time, schema=['time'], orient=orient) + dfs.append(df) + time_column = 'time' + pixel_columns = None if pixel is not None: - column = pl.from_numpy(data=pixel, schema=['pixel'], orient=orient)['pixel'] - columns.append(column) - n_components = column.list.lengths()[0] + df = pl.from_numpy(data=pixel, orient=orient).select(pl.all().prefix('pixel_')) + dfs.append(df) + pixel_columns = df.columns + position_columns = None if position is not None: - column = pl.from_numpy(data=position, schema=['position'], orient=orient)['position'] - columns.append(column) - n_components = column.list.lengths()[0] + df = pl.from_numpy(data=position, orient=orient).select(pl.all().prefix('position_')) + dfs.append(df) + position_columns = df.columns + velocity_columns = None if velocity is not None: - column = pl.from_numpy(data=velocity, schema=['velocity'], orient=orient)['velocity'] - columns.append(column) - n_components = column.list.lengths()[0] + df = pl.from_numpy(data=velocity, orient=orient).select(pl.all().prefix('velocity_')) + dfs.append(df) + velocity_columns = df.columns + acceleration_columns = None if acceleration is not None: - column = pl.from_numpy( - data=acceleration, schema=['acceleration'], orient=orient, - )['acceleration'] - columns.append(column) - n_components = column.list.lengths()[0] + df = pl.from_numpy(data=acceleration, orient=orient) + df = df.select(pl.all().prefix('acceleration_')) + dfs.append(df) + acceleration_columns = df.columns - df = pl.DataFrame(columns) + df = pl.concat(dfs, how='horizontal') gaze = GazeDataFrame( data=df, experiment=experiment, + time_column=time_column, + pixel_columns=pixel_columns, + position_columns=position_columns, + velocity_columns=velocity_columns, + acceleration_columns=acceleration_columns, ) - gaze.n_components = n_components return gaze diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index 3e5b2c3da..c2fdfe2ae 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -57,18 +57,22 @@ def test_from_numpy(): assert gaze.columns == schema -def test_from_pandas_explicit_columns(): +def test_from_numpy_with_schema(): array = np.array( [ [0, 1, 2, 3], [4, 5, 6, 7], [9, 8, 7, 6], [5, 4, 3, 2], + [1, 2, 3, 4], + [5, 6, 7, 8], + [2, 3, 4, 5], + [6, 7, 8, 9], ], dtype=np.int64, ) - schema = ['x_pix', 'y_pix', 'x_pos', 'y_pos'] + schema = ['x_pix', 'y_pix', 'x_pos', 'y_pos', 'x_vel', 'y_vel', 'x_acc', 'y_acc'] experiment = pm.Experiment( screen_width_px=1280, @@ -86,11 +90,56 @@ def test_from_pandas_explicit_columns(): experiment=experiment, pixel_columns=['x_pix', 'y_pix'], position_columns=['x_pos', 'y_pos'], + velocity_columns=['x_vel', 'y_vel'], + acceleration_columns=['x_acc', 'y_acc'], + ) + + expected = pl.DataFrame({ + 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], + 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], + 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], + 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], + }) + + assert_frame_equal(gaze.frame, expected) + + assert gaze.n_components == 2 + + +def test_from_numpy_explicit_columns(): + time = np.array([101, 102, 103, 104]) + pixel = np.array([[0, 1, 2, 3], [4, 5, 6, 7]], dtype=np.int64) + position = np.array([[9, 8, 7, 6], [5, 4, 3, 2]], dtype=np.int64) + velocity = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.int64) + acceleration = np.array([[2, 3, 4, 5], [6, 7, 8, 9]], dtype=np.int64) + + 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, + ) + + gaze = pm.gaze.from_numpy( + time=time, + pixel=pixel, + position=position, + velocity=velocity, + acceleration=acceleration, + experiment=experiment, ) expected = pl.DataFrame({ + 'time': [101, 102, 103, 104], 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], + 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], + 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], }) assert_frame_equal(gaze.frame, expected) + + assert gaze.n_components == 2 From a341a1c82f54aea58b82b1f68214bb0472499230 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 13:09:24 +0200 Subject: [PATCH 03/11] add test for all none in init --- src/pymovements/gaze/integration.py | 3 ++- tests/gaze/integration_numpy_test.py | 30 +++++++++++++++++++++++++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index 72d72793f..afcfdfad1 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -105,7 +105,8 @@ def from_numpy( acceleration_columns=acceleration_columns, ) - dfs: pl.Series = [] + # Initialize with an empty DataFrame, as every column specifier could be None. + dfs: list[pl.DataFrame] = [pl.DataFrame()] time_column = None if time is not None: diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index c2fdfe2ae..d47d19034 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -60,6 +60,7 @@ def test_from_numpy(): def test_from_numpy_with_schema(): array = np.array( [ + [101, 102, 103, 104], [0, 1, 2, 3], [4, 5, 6, 7], [9, 8, 7, 6], @@ -72,7 +73,7 @@ def test_from_numpy_with_schema(): dtype=np.int64, ) - schema = ['x_pix', 'y_pix', 'x_pos', 'y_pos', 'x_vel', 'y_vel', 'x_acc', 'y_acc'] + schema = ['t', 'x_pix', 'y_pix', 'x_pos', 'y_pos', 'x_vel', 'y_vel', 'x_acc', 'y_acc'] experiment = pm.Experiment( screen_width_px=1280, @@ -88,6 +89,7 @@ def test_from_numpy_with_schema(): data=array, schema=schema, experiment=experiment, + time_column='t', pixel_columns=['x_pix', 'y_pix'], position_columns=['x_pos', 'y_pos'], velocity_columns=['x_vel', 'y_vel'], @@ -95,6 +97,7 @@ def test_from_numpy_with_schema(): ) expected = pl.DataFrame({ + 'time': [101, 102, 103, 104], 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], @@ -102,7 +105,6 @@ def test_from_numpy_with_schema(): }) assert_frame_equal(gaze.frame, expected) - assert gaze.n_components == 2 @@ -141,5 +143,27 @@ def test_from_numpy_explicit_columns(): }) assert_frame_equal(gaze.frame, expected) - assert gaze.n_components == 2 + + +def test_init_all_none(): + gaze = pm.gaze.from_numpy( + data=None, + schema=None, + experiment=None, + time=None, + pixel=None, + position=None, + velocity=None, + acceleration=None, + time_column=None, + pixel_columns=None, + position_columns=None, + velocity_columns=None, + acceleration_columns=None, + ) + + expected = pl.DataFrame() + + assert_frame_equal(gaze.frame, expected) + assert gaze.n_components is None From c8eed529462dbcbeee64f5f3ec114ee982f4a941 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 13:27:23 +0200 Subject: [PATCH 04/11] simplify signature --- src/pymovements/gaze/integration.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index da8e1abed..c66beda8f 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -34,10 +34,10 @@ def from_numpy( data: np.ndarray | None = None, time: np.ndarray | None = None, - pixel: np.ndarray | list[np.ndarray] | None = None, - position: np.ndarray | list[np.ndarray] | None = None, - velocity: np.ndarray | list[np.ndarray] | None = None, - acceleration: np.ndarray | list[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', From fb3793554c16946d732b959f540000063b33ed64 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 13:36:41 +0200 Subject: [PATCH 05/11] fix types in test --- tests/gaze/integration_numpy_test.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index d47d19034..8b6760b8d 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -70,7 +70,7 @@ def test_from_numpy_with_schema(): [2, 3, 4, 5], [6, 7, 8, 9], ], - dtype=np.int64, + dtype=np.float64, ) schema = ['t', 'x_pix', 'y_pix', 'x_pos', 'y_pos', 'x_vel', 'y_vel', 'x_acc', 'y_acc'] @@ -96,13 +96,22 @@ def test_from_numpy_with_schema(): acceleration_columns=['x_acc', 'y_acc'], ) - expected = pl.DataFrame({ - 'time': [101, 102, 103, 104], - 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], - 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], - 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], - 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], - }) + expected = pl.DataFrame( + { + 'time': [101, 102, 103, 104], + 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], + 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], + 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], + 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], + }, + schema={ + 'time': pl.Float64, + 'pixel': pl.List(pl.Float64), + 'position': pl.List(pl.Float64), + 'velocity': pl.List(pl.Float64), + 'acceleration': pl.List(pl.Float64), + }, + ) assert_frame_equal(gaze.frame, expected) assert gaze.n_components == 2 From be6d2017e7d6eb8a123dc6f5374cd784713cd332 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 13:38:18 +0200 Subject: [PATCH 06/11] make pydocstyle happy --- src/pymovements/gaze/integration.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index c66beda8f..42ce681af 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -85,7 +85,6 @@ def from_numpy( ------- py:class:`~pymovements.GazeDataFrame` """ - if data is not None: checks.check_is_mutual_exclusive(data=data, time=time) checks.check_is_mutual_exclusive(data=data, pixel=pixel) From f5a90acab1e62bdb45c5ba5bc18c5e8acae6eee5 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 13:48:59 +0200 Subject: [PATCH 07/11] fix numpy types in test --- tests/gaze/integration_numpy_test.py | 31 ++++++++++++++++++---------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/tests/gaze/integration_numpy_test.py b/tests/gaze/integration_numpy_test.py index 8b6760b8d..ae39855d2 100644 --- a/tests/gaze/integration_numpy_test.py +++ b/tests/gaze/integration_numpy_test.py @@ -118,11 +118,11 @@ def test_from_numpy_with_schema(): def test_from_numpy_explicit_columns(): - time = np.array([101, 102, 103, 104]) + time = np.array([101, 102, 103, 104], dtype=np.int64) pixel = np.array([[0, 1, 2, 3], [4, 5, 6, 7]], dtype=np.int64) - position = np.array([[9, 8, 7, 6], [5, 4, 3, 2]], dtype=np.int64) - velocity = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.int64) - acceleration = np.array([[2, 3, 4, 5], [6, 7, 8, 9]], dtype=np.int64) + position = np.array([[9, 8, 7, 6], [5, 4, 3, 2]], dtype=np.float64) + velocity = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.float64) + acceleration = np.array([[2, 3, 4, 5], [6, 7, 8, 9]], dtype=np.float64) experiment = pm.Experiment( screen_width_px=1280, @@ -143,13 +143,22 @@ def test_from_numpy_explicit_columns(): experiment=experiment, ) - expected = pl.DataFrame({ - 'time': [101, 102, 103, 104], - 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], - 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], - 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], - 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], - }) + expected = pl.DataFrame( + { + 'time': [101, 102, 103, 104], + 'pixel': [[0, 4], [1, 5], [2, 6], [3, 7]], + 'position': [[9, 5], [8, 4], [7, 3], [6, 2]], + 'velocity': [[1, 5], [2, 6], [3, 7], [4, 8]], + 'acceleration': [[2, 6], [3, 7], [4, 8], [5, 9]], + }, + schema={ + 'time': pl.Int64, + 'pixel': pl.List(pl.Int64), + 'position': pl.List(pl.Float64), + 'velocity': pl.List(pl.Float64), + 'acceleration': pl.List(pl.Float64), + }, + ) assert_frame_equal(gaze.frame, expected) assert gaze.n_components == 2 From 85bddf55e1be5b967978a72b0431432fccd42ef5 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 14:15:08 +0200 Subject: [PATCH 08/11] formatting --- src/pymovements/gaze/integration.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index 42ce681af..adaaadd9b 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -138,7 +138,7 @@ def from_numpy( acceleration_columns = df.columns df = pl.concat(dfs, how='horizontal') - gaze = GazeDataFrame( + return GazeDataFrame( data=df, experiment=experiment, time_column=time_column, @@ -148,8 +148,6 @@ def from_numpy( acceleration_columns=acceleration_columns, ) - return gaze - def from_pandas( data: pd.DataFrame, From a1da5e185d15971203d0cbb68190169e005a4352 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Sat, 9 Sep 2023 20:23:16 +0200 Subject: [PATCH 09/11] improve documentation --- src/pymovements/gaze/integration.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index adaaadd9b..224284444 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -49,6 +49,14 @@ def from_numpy( ) -> GazeDataFrame: """Get a :py:class:`~pymovements.gaze.gaze_dataframe.GazeDataFrame` from a numpy array. + There are two mutually exclusive ways of conversion. + + Case 1: Pass a single numpy array via `data` and specify its schema and orientation. + You can then additionally pass column specifiers, e.g. `time_column` and `position_columns`. + + Case 2: For each type of signal, you can pass the numpy array explicitly, e.g. `position` or + `velocity`. You don't need to + Parameters ---------- data: @@ -84,6 +92,10 @@ def from_numpy( Returns ------- py:class:`~pymovements.GazeDataFrame` + + Examples + -------- + """ if data is not None: checks.check_is_mutual_exclusive(data=data, time=time) From da21e96d31fbf8fa1ba95483e84c069a889c73d8 Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Mon, 11 Sep 2023 21:30:32 +0200 Subject: [PATCH 10/11] add more documentation --- src/pymovements/gaze/integration.py | 111 +++++++++++++++++++++++++--- 1 file changed, 101 insertions(+), 10 deletions(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index 224284444..cae22e8be 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -51,11 +51,12 @@ def from_numpy( There are two mutually exclusive ways of conversion. - Case 1: Pass a single numpy array via `data` and specify its schema and orientation. - You can then additionally pass column specifiers, e.g. `time_column` and `position_columns`. + **Single data array**: Pass a single numpy array via `data` and specify its schema and + orientation. You can then additionally pass column specifiers, e.g. `time_column` and + `position_columns`. - Case 2: For each type of signal, you can pass the numpy array explicitly, e.g. `position` or - `velocity`. You don't need to + **Column specific arrays**: For each type of signal, you can pass the numpy array explicitly, + e.g. `position` or `velocity`. You must not pass `data` or any column specifiers. Parameters ---------- @@ -95,15 +96,105 @@ def from_numpy( Examples -------- + Creating an example numpy array with 4 columns and 100 rows. We call this layout column + orientation. + >>> import numpy as np + >>> import pymovements as pm + >>> + >>> arr = np.zeros((3, 100)) + >>> arr.shape + (3, 100) + Specifying the underlying schema: + >>> schema = ['t', 'x', 'y'] + + Pass the array as ``data`` to ``pm.gaze.from_numpy()``, by specifying schema and components. + >>> gaze = pm.gaze.from_numpy( + ... arr, + ... schema=schema, + ... time_column='t', + ... position_columns=['x', 'y'], + ... orient='col', + ... ) + >>> gaze.frame + shape: (100, 2) + ┌──────┬────────────┐ + │ time ┆ position │ + │ --- ┆ --- │ + │ f64 ┆ list[f64] │ + ╞══════╪════════════╡ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ … ┆ … │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + └──────┴────────────┘ + + Use the ``orient`` keyword argument to specify the layout of your array. + >>> arr.T.shape + (100, 3) + + >>> gaze = pm.gaze.from_numpy( + ... arr.T, + ... schema=schema, + ... time_column='t', + ... position_columns=['x', 'y'], + ... orient='row', + ... ) + >>> gaze.frame + shape: (100, 2) + ┌──────┬────────────┐ + │ time ┆ position │ + │ --- ┆ --- │ + │ f64 ┆ list[f64] │ + ╞══════╪════════════╡ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ … ┆ … │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + └──────┴────────────┘ + + Pass the data explicitly via the specific keyword arguments, without having to specify a schema. + >>> gaze = pm.gaze.from_numpy( + ... time=arr[0], + ... position=arr[[1, 2]], + ... orient='col', + ... ) + >>> gaze.frame + shape: (100, 2) + ┌──────┬────────────┐ + │ time ┆ position │ + │ --- ┆ --- │ + │ f64 ┆ list[f64] │ + ╞══════╪════════════╡ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ … ┆ … │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + │ 0.0 ┆ [0.0, 0.0] │ + └──────┴────────────┘ """ - if data is not None: - checks.check_is_mutual_exclusive(data=data, time=time) - checks.check_is_mutual_exclusive(data=data, pixel=pixel) - checks.check_is_mutual_exclusive(data=data, position=position) - checks.check_is_mutual_exclusive(data=data, velocity=velocity) - checks.check_is_mutual_exclusive(data=data, acceleration=acceleration) + # Either data or {time, pixel, position, velocity, acceleration} must be None. + checks.check_is_mutual_exclusive(data=data, time=time) + checks.check_is_mutual_exclusive(data=data, pixel=pixel) + checks.check_is_mutual_exclusive(data=data, position=position) + checks.check_is_mutual_exclusive(data=data, velocity=velocity) + checks.check_is_mutual_exclusive(data=data, acceleration=acceleration) + if data is not None: df = pl.from_numpy(data=data, schema=schema, orient=orient) return GazeDataFrame( data=df, From 217b1f08f0a82821f39df69b8998ccc9b5d4fc9e Mon Sep 17 00:00:00 2001 From: "Daniel G, Krakowczyk" Date: Mon, 11 Sep 2023 21:32:09 +0200 Subject: [PATCH 11/11] imporve docs --- src/pymovements/gaze/integration.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pymovements/gaze/integration.py b/src/pymovements/gaze/integration.py index cae22e8be..ec7b5f70a 100644 --- a/src/pymovements/gaze/integration.py +++ b/src/pymovements/gaze/integration.py @@ -56,7 +56,8 @@ def from_numpy( `position_columns`. **Column specific arrays**: For each type of signal, you can pass the numpy array explicitly, - e.g. `position` or `velocity`. You must not pass `data` or any column specifiers. + e.g. `position` or `velocity`. You must not pass `data` or any column list specifiers using this + method. Parameters ----------