Repository: aeye-lab/pymovements
Pull request #654 (2024-01-10 18:12:19)

feat!: add time column if missing during gaze initialization
## Description

During development of #382 I encountered potential bugs if there is no time column present.
For a robust implementation we need to guarantee that a time column exists.

Depends on #650 for pydocstyle ignores

## Implemented changes

During initialization, there's an if clause that checks for a passed time column. If there is none, one will be created with `pl.arange(0, len(self.frame))`

## How Has This Been Tested?

- [x] Updated tests to always have a time column, even in case of zero rows and no data

## 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/654?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Comparison is base [(`4fd1b18`)](https://app.codecov.io/gh/aeye-lab/pymovements/commit/4fd1b18320bd401495779307e5895a80f9cf1502?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 100.00% compared to head [(`5db82e1`)](https://app.codecov.io/gh/aeye-lab/pymovements/pull/654?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=aeye-lab) 99.91%.

> :exclamation: Current head 5db82e1 differs from pull request most recent head 28f64f6. Consider uploading reports for the commit 28f64f6 to get more accurate results


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


```diff
@@             Coverage Diff             @@
##              main     #654      +/-   ##
===========================================
- Coverage   100.00%   99.91%   -0.09%     
===========================================
  Files           54       54              
  Lines         2356     2358       +2     
  Branches       588      536      -52     
===========================================
  Hits          2356     2356              
- Partials         0        2       +2     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/aeye-lab/pymovements/pull/654?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).

SiQube: merge conflict
dkrako: I have improved the timesteps creation by taking account of the experiment sampling rate to create timesteps in millisecond unit.
SiQube: merge conflict

Patch:
diff --git a/src/pymovements/dataset/dataset_files.py b/src/pymovements/dataset/dataset_files.py
index 7930565..48059e0 100644
--- a/src/pymovements/dataset/dataset_files.py
+++ b/src/pymovements/dataset/dataset_files.py
@@ -238,8 +238,8 @@ def load_gaze_files(
             time_column = None
             distance_column = None
 
-            if 'time' in gaze_data.columns:
-                time_column = 'time'
+            # Time column is always available in every initialized GazeDataFrame.
+            time_column = 'time'
 
             if 'distance' in gaze_data.columns:
                 distance_column = 'distance'
diff --git a/src/pymovements/gaze/gaze_dataframe.py b/src/pymovements/gaze/gaze_dataframe.py
index 358c2df..19ed593 100644
--- a/src/pymovements/gaze/gaze_dataframe.py
+++ b/src/pymovements/gaze/gaze_dataframe.py
@@ -119,14 +119,14 @@ class GazeDataFrame:
     │ 1002 ┆ 0.3 ┆ 0.3 │
     └──────┴─────┴─────┘
 
-    We can now initialize our ``GazeDataFrame`` by specyfing the names of the pixel position
-    columns.
+    We can now initialize our ``GazeDataFrame`` by specyfing the names of the time and pixel
+    position columns.
 
-    >>> gaze = GazeDataFrame(data=df, pixel_columns=['x', 'y'])
+    >>> gaze = GazeDataFrame(data=df, time_column='t', pixel_columns=['x', 'y'])
     >>> gaze.frame
     shape: (3, 2)
     ┌──────┬────────────┐
-    │ t    ┆ pixel      │
+    │ time ┆ pixel      │
     │ ---  ┆ ---        │
     │ i64  ┆ list[f64]  │
     ╞══════╪════════════╡
@@ -135,6 +135,51 @@ class GazeDataFrame:
     │ 1002 ┆ [0.3, 0.3] │
     └──────┴────────────┘
 
+    In case your data has no time column available, you can pass an :py:class:``Experiment`` to
+    create a time column with the correct sampling rate during initialization. The time column will
+    be represented in millisecond units.
+
+    >>> df_no_time = df.select(pl.exclude('t'))
+    >>> df_no_time
+    shape: (3, 2)
+    ┌─────┬─────┐
+    │ x   ┆ y   │
+    │ --- ┆ --- │
+    │ f64 ┆ f64 │
+    ╞═════╪═════╡
+    │ 0.1 ┆ 0.1 │
+    │ 0.2 ┆ 0.2 │
+    │ 0.3 ┆ 0.3 │
+    └─────┴─────┘
+
+    >>> experiment = Experiment(1024, 768, 38, 30, 60, 'center', sampling_rate=100)
+    >>> gaze = GazeDataFrame(data=df_no_time, experiment=experiment, pixel_columns=['x', 'y'])
+    >>> gaze.frame
+    shape: (3, 2)
+    ┌──────┬────────────┐
+    │ time ┆ pixel      │
+    │ ---  ┆ ---        │
+    │ f64  ┆ list[f64]  │
+    ╞══════╪════════════╡
+    │ 0.0  ┆ [0.1, 0.1] │
+    │ 10.0 ┆ [0.2, 0.2] │
+    │ 20.0 ┆ [0.3, 0.3] │
+    └──────┴────────────┘
+
+    Leaving out the experiment definition will create a continuous integer column in step units.
+    >>> gaze = GazeDataFrame(data=df_no_time, pixel_columns=['x', 'y'])
+    >>> gaze.frame
+    shape: (3, 2)
+    ┌──────┬────────────┐
+    │ time ┆ pixel      │
+    │ ---  ┆ ---        │
+    │ i64  ┆ list[f64]  │
+    ╞══════╪════════════╡
+    │ 0    ┆ [0.1, 0.1] │
+    │ 1    ┆ [0.2, 0.2] │
+    │ 2    ┆ [0.3, 0.3] │
+    └──────┴────────────┘
+
     """
 
     valid_pixel_position_columns = [
@@ -195,6 +240,18 @@ class GazeDataFrame:
 
         self.trial_columns = trial_columns
 
+        # In case the 'time' column is already present we don't need to do anything.
+        # Otherwise, create a new time column starting with zero.
+        if time_column is None and 'time' not in self.frame.columns:
+            timesteps = pl.arange(0, len(self.frame))
+
+            # In case we have an experiment with sampling rate given, we convert to milliseconds.
+            if experiment is not None and experiment.sampling_rate is not None:
+                timesteps = timesteps * (1000 / experiment.sampling_rate)
+
+            self.frame = self.frame.with_columns(time=timesteps)
+
+        # This if clause is mutually exclusive with the previous one.
         if time_column is not None:
             self.frame = self.frame.rename({time_column: 'time'})
 


Test patch:
diff --git a/tests/unit/gaze/apply_test.py b/tests/unit/gaze/apply_test.py
index a4cdf3e..747623a 100644
--- a/tests/unit/gaze/apply_test.py
+++ b/tests/unit/gaze/apply_test.py
@@ -42,14 +42,14 @@ from pymovements.synthetic import step_function
                     length=100, steps=[0, 10], values=[(1, 1, 1, 1, 0, 0), (0, 0, 0, 0, 0, 0)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.gaze.from_numpy(
                 velocity=step_function(
                     length=100, steps=[0, 10], values=[(1, 1, 1, 1, 0, 0), (0, 0, 0, 0, 0, 0)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
                 events=pm.events.EventDataFrame(
                     name='fixation',
                     onsets=[0],
@@ -72,7 +72,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.gaze.from_numpy(
                 velocity=step_function(
@@ -82,7 +82,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
                 events=pm.EventDataFrame(
                     name='saccade',
                     onsets=[20, 70],
diff --git a/tests/unit/gaze/detect_test.py b/tests/unit/gaze/detect_test.py
index 3f429f7..6dff0dc 100644
--- a/tests/unit/gaze/detect_test.py
+++ b/tests/unit/gaze/detect_test.py
@@ -54,7 +54,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 position=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(name='fixation', onsets=[0], offsets=[99]),
             id='idt_constant_position_single_fixation',
@@ -70,7 +70,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 position=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(name='custom_fixation', onsets=[0], offsets=[99]),
             id='idt_constant_position_single_fixation_custom_name',
@@ -87,7 +87,7 @@ from pymovements.synthetic import step_function
                     length=100, steps=[49, 50], values=[(9, 9), (1, 1)], start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0, 50], offsets=[49, 99]),
             id='idt_three_steps_two_fixations',
@@ -105,7 +105,7 @@ from pymovements.synthetic import step_function
                     values=[(np.nan, np.nan), (0, 0), (np.nan, np.nan)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0, 20], offsets=[9, 89]),
             id='idt_two_fixations_interrupted_by_nan',
@@ -124,7 +124,7 @@ from pymovements.synthetic import step_function
                     values=[(np.nan, np.nan), (0, 0), (np.nan, np.nan)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0], offsets=[89]),
             id='idt_one_fixation_including_nan',
@@ -140,7 +140,7 @@ from pymovements.synthetic import step_function
                 time=np.arange(1000, 1100, dtype=int),
                 position=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -160,7 +160,7 @@ from pymovements.synthetic import step_function
                 time=np.arange(1000, 1010, 0.1, dtype=float),
                 position=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -181,7 +181,7 @@ from pymovements.synthetic import step_function
                 time=np.reshape(np.arange(1000, 1100, dtype=int), (100, 1)),
                 position=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -200,7 +200,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 time=np.arange(0, 100, 1),
                 velocity=np.ones((2, 100)) * 20,
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(),
             id='ivt_constant_velocity_no_fixation',
@@ -214,7 +214,7 @@ from pymovements.synthetic import step_function
             },
             pm.gaze.from_numpy(
                 velocity=np.zeros((2, 100)),
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(name='fixation', onsets=[0], offsets=[99]),
             id='ivt_constant_position_single_fixation',
@@ -229,7 +229,7 @@ from pymovements.synthetic import step_function
             },
             pm.gaze.from_numpy(
                 velocity=np.zeros((2, 100)),
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(name='custom_fixation', onsets=[0], offsets=[99]),
             id='ivt_constant_position_single_fixation_custom_name',
@@ -246,7 +246,7 @@ from pymovements.synthetic import step_function
                     length=100, steps=[49, 51], values=[(90, 90), (0, 0)], start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0, 51], offsets=[48, 99]),
             id='ivt_three_steps_two_fixations',
@@ -264,7 +264,7 @@ from pymovements.synthetic import step_function
                     values=[(np.nan, np.nan), (0, 0), (np.nan, np.nan)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0, 20], offsets=[9, 89]),
             id='ivt_two_fixations_interrupted_by_nan',
@@ -283,7 +283,7 @@ from pymovements.synthetic import step_function
                     values=[(np.nan, np.nan), (0, 0), (np.nan, np.nan)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(name='fixation', onsets=[0], offsets=[89]),
             id='ivt_one_fixation_including_nan',
@@ -299,7 +299,7 @@ from pymovements.synthetic import step_function
                 time=np.arange(1000, 1100, dtype=int),
                 velocity=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -319,7 +319,7 @@ from pymovements.synthetic import step_function
                 time=np.arange(1000, 1010, 0.1, dtype=float),
                 velocity=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -340,7 +340,7 @@ from pymovements.synthetic import step_function
                 time=np.reshape(np.arange(1000, 1100, dtype=int), (100, 1)),
                 velocity=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -360,7 +360,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 velocity=step_function(length=100, steps=[0], values=[(0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -380,7 +380,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 velocity=step_function(length=100, steps=[0], values=[(0, 0, 0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -400,7 +400,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 velocity=step_function(length=100, steps=[0], values=[(0, 0, 0, 0, 0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -422,7 +422,7 @@ from pymovements.synthetic import step_function
                     length=100, steps=[0, 10], values=[(0, 0, 1, 1, 1, 1), (0, 0, 0, 0, 0, 0)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -444,7 +444,7 @@ from pymovements.synthetic import step_function
                     length=100, steps=[0, 10], values=[(1, 1, 0, 0, 1, 1), (0, 0, 0, 0, 0, 0)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -466,7 +466,7 @@ from pymovements.synthetic import step_function
                     length=100, steps=[0, 10], values=[(1, 1, 1, 1, 0, 0), (0, 0, 0, 0, 0, 0)],
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.events.EventDataFrame(
                 name='fixation',
@@ -485,7 +485,7 @@ from pymovements.synthetic import step_function
                 time=np.reshape(np.arange(1000, 1100, dtype=int), (100, 1)),
                 velocity=step_function(length=100, steps=[40, 50], values=[(9, 9), (0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(),
             id='microsaccades_two_steps_one_saccade_high_threshold_no_events',
@@ -499,7 +499,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 velocity=step_function(length=100, steps=[40, 50], values=[(9, 9), (0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='saccade',
@@ -518,7 +518,7 @@ from pymovements.synthetic import step_function
             pm.gaze.from_numpy(
                 velocity=step_function(length=100, steps=[40, 50], values=[(9, 9), (0, 0)]),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='custom_saccade',
@@ -541,7 +541,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='saccade',
@@ -565,7 +565,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='saccade',
@@ -589,7 +589,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='saccade',
@@ -614,7 +614,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(
                 name='saccade',
@@ -638,7 +638,7 @@ from pymovements.synthetic import step_function
                     start_value=(0, 0),
                 ),
                 orient='row',
-                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 10),
+                experiment=pm.Experiment(1024, 768, 38, 30, 60, 'center', 1000),
             ),
             pm.EventDataFrame(),
             id='microsaccades_two_steps_one_saccade_timesteps',
@@ -723,6 +723,18 @@ def test_gaze_detect(method, kwargs, gaze, expected):
     assert_frame_equal(gaze.events.frame, expected.frame)
 
 
+def test_gaze_detect_custom_method_no_arguments():
+    def custom_method():
+        return pm.EventDataFrame()
+
+    gaze = pm.gaze.from_numpy()
+
+    expected = pm.EventDataFrame()
+
+    gaze.detect(custom_method)
+    assert_frame_equal(gaze.events.frame, expected.frame)
+
+
 @pytest.mark.parametrize(
     ('method', 'kwargs', 'gaze', 'exception', 'exception_msg'),
     [
@@ -735,7 +747,7 @@ def test_gaze_detect(method, kwargs, gaze, expected):
             },
             pm.gaze.GazeDataFrame(None, pm.Experiment(1024, 768, 38, 30, 60, 'center', 10)),
             pl.exceptions.ColumnNotFoundError,
-            "Column 'velocity' not found. Available columns are: []",
+            "Column 'velocity' not found. Available columns are: ['time']",
             id='ivt_no_velocity_raises_column_not_found_error',
         ),
 
diff --git a/tests/unit/gaze/gaze_dataframe_unnest_test.py b/tests/unit/gaze/gaze_dataframe_unnest_test.py
index b9c895c..95fac71 100644
--- a/tests/unit/gaze/gaze_dataframe_unnest_test.py
+++ b/tests/unit/gaze/gaze_dataframe_unnest_test.py
@@ -73,35 +73,37 @@ import pymovements as pm
         pytest.param(
             pl.DataFrame({'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel', 'output_columns': ['x', 'y']},
-            pl.DataFrame({'x': [1.23], 'y': [4.56]}),
+            pl.DataFrame({'time': [0], 'x': [1.23], 'y': [4.56]}),
             id='df_single_row_two_pixel_columns',
         ),
 
         pytest.param(
             pl.DataFrame({'abc': [1], 'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel', 'output_columns': ['x', 'y']},
-            pl.DataFrame({'abc': [1], 'x': [1.23], 'y': [4.56]}),
+            pl.DataFrame({'abc': [1], 'time': [0], 'x': [1.23], 'y': [4.56]}),
             id='df_single_row_three_columns_two_pixel_columns',
         ),
 
         pytest.param(
             pl.DataFrame({'pixel': [[1.2, 3.4, 5.6, 7.8]]}),
             {'input_columns': 'pixel', 'output_columns': ['xl', 'yl', 'xr', 'yr']},
-            pl.DataFrame({'xl': [1.2], 'yl': [3.4], 'xr': [5.6], 'yr': [7.8]}),
+            pl.DataFrame({'time': [0], 'xl': [1.2], 'yl': [3.4], 'xr': [5.6], 'yr': [7.8]}),
             id='df_single_row_four_pixel_columns',
         ),
 
         pytest.param(
             pl.DataFrame({'pixel': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]}),
             {'input_columns': 'pixel', 'output_columns': ['xl', 'yl', 'xr', 'yr', 'xa', 'ya']},
-            pl.DataFrame({'xl': [.1], 'yl': [.2], 'xr': [.3], 'yr': [.4], 'xa': [.5], 'ya': [.6]}),
+            pl.DataFrame({
+                'time': [0], 'xl': [.1], 'yl': [.2], 'xr': [.3], 'yr': [.4], 'xa': [.5], 'ya': [.6],
+            }),
             id='df_single_row_six_pixel_columns',
         ),
 
         pytest.param(
             pl.DataFrame(schema={'pixel': pl.List(pl.Float64)}),
             {'input_columns': 'pixel', 'output_suffixes': ['_x', '_y'], 'output_columns': None},
-            pl.DataFrame(schema={'pixel_x': pl.Float64, 'pixel_y': pl.Float64}),
+            pl.DataFrame(schema={'time': pl.Int64, 'pixel_x': pl.Float64, 'pixel_y': pl.Float64}),
             id='empty_df_with_schema_two_pixel_suffixes_columns_none',
             marks=pytest.mark.xfail(reason='#522'),
         ),
@@ -109,7 +111,11 @@ import pymovements as pm
         pytest.param(
             pl.DataFrame(schema={'abc': pl.Int64, 'pixel': pl.List(pl.Float64)}),
             {'input_columns': 'pixel', 'output_suffixes': ['_x', '_y'], 'output_columns': None},
-            pl.DataFrame(schema={'abc': pl.Int64, 'pixel_x': pl.Float64, 'pixel_y': pl.Float64}),
+            pl.DataFrame(
+                schema={
+                    'abc': pl.Int64, 'time': pl.Int64, 'pixel_x': pl.Float64, 'pixel_y': pl.Float64,
+                },
+            ),
             id='empty_df_with_three_column_schema_two_pixel_suffixes_columns_none',
             marks=pytest.mark.xfail(reason='#522'),
         ),
@@ -151,14 +157,14 @@ import pymovements as pm
         pytest.param(
             pl.DataFrame({'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel', 'output_suffixes': ['_x', '_y']},
-            pl.DataFrame({'pixel_x': [1.23], 'pixel_y': [4.56]}),
+            pl.DataFrame({'time': [0], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
             id='df_single_row_two_pixel_suffixes',
         ),
 
         pytest.param(
             pl.DataFrame({'abc': [1], 'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel', 'output_suffixes': ['_x', '_y']},
-            pl.DataFrame({'abc': [1], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
+            pl.DataFrame({'abc': [1], 'time': [0], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
             id='df_single_row_three_columns_two_pixel_suffixes',
         ),
 
@@ -166,6 +172,7 @@ import pymovements as pm
             pl.DataFrame({'pixel': [[1.2, 3.4, 5.6, 7.8]]}),
             {'input_columns': 'pixel', 'output_suffixes': ['_xl', '_yl', '_xr', '_yr']},
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [1.2], 'pixel_yl': [3.4],
                 'pixel_xr': [5.6], 'pixel_yr': [7.8],
             }),
@@ -179,6 +186,7 @@ import pymovements as pm
                 'output_suffixes': ['_xl', '_yl', '_xr', '_yr', '_xa', '_ya'],
             },
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [.1], 'pixel_yl': [.2],
                 'pixel_xr': [.3], 'pixel_yr': [.4],
                 'pixel_xa': [.5], 'pixel_ya': [.6],
@@ -189,14 +197,14 @@ import pymovements as pm
         pytest.param(
             pl.DataFrame({'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel'},
-            pl.DataFrame({'pixel_x': [1.23], 'pixel_y': [4.56]}),
+            pl.DataFrame({'time': [0], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
             id='df_single_row_two_pixel_suffixes_default_values',
         ),
 
         pytest.param(
             pl.DataFrame({'abc': [1], 'pixel': [[1.23, 4.56]]}),
             {'input_columns': 'pixel'},
-            pl.DataFrame({'abc': [1], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
+            pl.DataFrame({'abc': [1], 'time': [0], 'pixel_x': [1.23], 'pixel_y': [4.56]}),
             id='df_single_row_three_columns_two_pixel_suffixes_default_values',
         ),
 
@@ -204,6 +212,7 @@ import pymovements as pm
             pl.DataFrame({'pixel': [[1.2, 3.4, 5.6, 7.8]]}),
             {'input_columns': 'pixel'},
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [1.2], 'pixel_yl': [3.4],
                 'pixel_xr': [5.6], 'pixel_yr': [7.8],
             }),
@@ -214,6 +223,7 @@ import pymovements as pm
             pl.DataFrame({'pixel': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]}),
             {'input_columns': 'pixel'},
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [.1], 'pixel_yl': [.2],
                 'pixel_xr': [.3], 'pixel_yr': [.4],
                 'pixel_xa': [.5], 'pixel_ya': [.6],
@@ -244,6 +254,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
                 'position': [[1.23, 4.56]],
                 'velocity': [[1.23, 4.56]],
                 'acceleration': [[1.23, 4.56]],
+                'time': [0],
                 'x': [1.23],
                 'y': [4.56],
             }),
@@ -258,6 +269,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
             }),
             {},
             pl.DataFrame({
+                'time': [0],
                 'pixel_x': [1.23], 'pixel_y': [4.56],
                 'position_x': [1.23], 'position_y': [4.56],
                 'velocity_x': [1.23], 'velocity_y': [4.56],
@@ -274,6 +286,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
             }),
             {},
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [1.23], 'pixel_yl': [4.56],
                 'pixel_xr': [7.89], 'pixel_yr': [10.11],
                 'position_xl': [1.23], 'position_yl': [4.56],
@@ -294,6 +307,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
             }),
             {},
             pl.DataFrame({
+                'time': [0],
                 'pixel_xl': [1.23], 'pixel_yl': [4.56],
                 'pixel_xr': [7.89], 'pixel_yr': [10.11],
                 'pixel_xa': [12.13], 'pixel_ya': [14.15],
@@ -322,6 +336,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
                 'pixel': [[1.23, 4.56]],
                 'velocity': [[1.23, 4.56]],
                 'acceleration': [[1.23, 4.56]],
+                'time': [0],
                 'position_x': [1.23], 'position_y': [4.56],
             }),
             id='df_single_row_two_components_unnest_one_multiple_columns_suffix_specified',
@@ -338,6 +353,7 @@ def test_gaze_dataframe_unnest_has_expected_frame(init_data, unnest_kwargs, expe
             pl.DataFrame({
                 'pixel': [[1.23, 4.56]],
                 'velocity': [[1.23, 4.56]],
+                'time': [0],
                 'position_x': [1.23], 'position_y': [4.56],
                 'acceleration_x': [1.23], 'acceleration_y': [4.56],
             }),
diff --git a/tests/unit/gaze/gaze_init_test.py b/tests/unit/gaze/gaze_init_test.py
index 5353d8a..0aef889 100644
--- a/tests/unit/gaze/gaze_init_test.py
+++ b/tests/unit/gaze/gaze_init_test.py
@@ -33,7 +33,7 @@ import pymovements as pm
             {
                 'data': pl.DataFrame(),
             },
-            pl.DataFrame(),
+            pl.DataFrame(schema={'time': pl.Int64}),
             None,
             id='empty_df_no_schema',
         ),
@@ -42,7 +42,7 @@ import pymovements as pm
             {
                 'data': pl.DataFrame(schema={'abc': pl.Int64}),
             },
-            pl.DataFrame(schema={'abc': pl.Int64}),
+            pl.DataFrame(schema={'abc': pl.Int64, 'time': pl.Int64}),
             None,
             id='empty_df_with_schema_no_component_columns',
         ),
@@ -55,7 +55,7 @@ import pymovements as pm
                 'velocity_columns': [],
                 'acceleration_columns': [],
             },
-            pl.DataFrame(schema={'abc': pl.Int64}),
+            pl.DataFrame(schema={'abc': pl.Int64, 'time': pl.Int64}),
             None,
             id='empty_df_with_schema_all_component_columns_empty_lists',
         ),
@@ -65,7 +65,7 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'x': pl.Float64, 'y': pl.Float64}),
                 'pixel_columns': ['x', 'y'],
             },
-            pl.DataFrame(schema={'pixel': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)}),
             2,
             id='empty_df_with_schema_two_pixel_columns',
         ),
@@ -75,7 +75,7 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'abc': pl.Int64, 'x': pl.Float64, 'y': pl.Float64}),
                 'pixel_columns': ['x', 'y'],
             },
-            pl.DataFrame(schema={'abc': pl.Int64, 'pixel': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'abc': pl.Int64, 'time': pl.Int64, 'pixel': pl.List(pl.Float64)}),
             2,
             id='empty_df_with_three_column_schema_two_pixel_columns',
         ),
@@ -89,7 +89,7 @@ import pymovements as pm
                 ),
                 'pixel_columns': ['xr', 'yr', 'xl', 'yl'],
             },
-            pl.DataFrame(schema={'pixel': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)}),
             4,
             id='empty_df_with_schema_four_pixel_columns',
         ),
@@ -107,7 +107,7 @@ import pymovements as pm
                     'x_right', 'y_right', 'x_left', 'y_left', 'x_avg', 'y_avg',
                 ],
             },
-            pl.DataFrame(schema={'pixel': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)}),
             6,
             id='empty_df_with_schema_six_pixel_columns',
         ),
@@ -120,8 +120,8 @@ import pymovements as pm
                 'pixel_columns': ['x', 'y'],
             },
             pl.from_dict(
-                {'pixel': [[1.23, 4.56]]},
-                schema={'pixel': pl.List(pl.Float64)},
+                {'time': [0], 'pixel': [[1.23, 4.56]]},
+                schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_two_pixel_columns',
@@ -136,8 +136,8 @@ import pymovements as pm
                 'pixel_columns': ['x', 'y'],
             },
             pl.from_dict(
-                {'abc': [1], 'pixel': [[1.23, 4.56]]},
-                schema={'abc': pl.Int64, 'pixel': pl.List(pl.Float64)},
+                {'time': [0], 'abc': [1], 'pixel': [[1.23, 4.56]]},
+                schema={'abc': pl.Int64, 'time': pl.Int64, 'pixel': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_three_columns_two_pixel_columns',
@@ -152,8 +152,8 @@ import pymovements as pm
                 'pixel_columns': ['xl', 'yl', 'xr', 'yr'],
             },
             pl.from_dict(
-                {'pixel': [[1.2, 3.4, 5.6, 7.8]]},
-                schema={'pixel': pl.List(pl.Float64)},
+                {'time': [0], 'pixel': [[1.2, 3.4, 5.6, 7.8]]},
+                schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)},
             ),
             4,
             id='df_single_row_four_pixel_columns',
@@ -178,8 +178,8 @@ import pymovements as pm
                 ],
             },
             pl.from_dict(
-                {'pixel': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
-                schema={'pixel': pl.List(pl.Float64)},
+                {'time': [0], 'pixel': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
+                schema={'time': pl.Int64, 'pixel': pl.List(pl.Float64)},
             ),
             6,
             id='df_single_row_six_pixel_columns',
@@ -190,7 +190,7 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'x': pl.Float64, 'y': pl.Float64}),
                 'position_columns': ['x', 'y'],
             },
-            pl.DataFrame(schema={'position': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'position': pl.List(pl.Float64)}),
             2,
             id='empty_df_with_schema_two_position_columns',
         ),
@@ -200,7 +200,11 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'abc': pl.Int64, 'x': pl.Float64, 'y': pl.Float64}),
                 'position_columns': ['x', 'y'],
             },
-            pl.DataFrame(schema={'abc': pl.Int64, 'position': pl.List(pl.Float64)}),
+            pl.DataFrame(
+                schema={
+                    'abc': pl.Int64, 'time': pl.Int64, 'position': pl.List(pl.Float64),
+                },
+            ),
             2,
             id='empty_df_with_three_column_schema_two_position_columns',
         ),
@@ -214,7 +218,7 @@ import pymovements as pm
                 ),
                 'position_columns': ['xr', 'yr', 'xl', 'yl'],
             },
-            pl.DataFrame(schema={'position': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'position': pl.List(pl.Float64)}),
             4,
             id='empty_df_with_schema_four_position_columns',
         ),
@@ -232,7 +236,7 @@ import pymovements as pm
                     'x_right', 'y_right', 'x_left', 'y_left', 'x_avg', 'y_avg',
                 ],
             },
-            pl.DataFrame(schema={'position': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'position': pl.List(pl.Float64)}),
             6,
             id='empty_df_with_schema_six_position_columns',
         ),
@@ -245,8 +249,8 @@ import pymovements as pm
                 'position_columns': ['x', 'y'],
             },
             pl.from_dict(
-                {'position': [[1.23, 4.56]]},
-                schema={'position': pl.List(pl.Float64)},
+                {'time': [0], 'position': [[1.23, 4.56]]},
+                schema={'time': pl.Int64, 'position': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_two_position_columns',
@@ -261,8 +265,8 @@ import pymovements as pm
                 'position_columns': ['x', 'y'],
             },
             pl.from_dict(
-                {'abc': [1], 'position': [[1.23, 4.56]]},
-                schema={'abc': pl.Int64, 'position': pl.List(pl.Float64)},
+                {'time': [0], 'abc': [1], 'position': [[1.23, 4.56]]},
+                schema={'abc': pl.Int64, 'time': pl.Int64, 'position': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_three_columns_two_position_columns',
@@ -277,8 +281,8 @@ import pymovements as pm
                 'position_columns': ['xl', 'yl', 'xr', 'yr'],
             },
             pl.from_dict(
-                {'position': [[1.2, 3.4, 5.6, 7.8]]},
-                schema={'position': pl.List(pl.Float64)},
+                {'time': [0], 'position': [[1.2, 3.4, 5.6, 7.8]]},
+                schema={'time': pl.Int64, 'position': pl.List(pl.Float64)},
             ),
             4,
             id='df_single_row_four_position_columns',
@@ -303,8 +307,8 @@ import pymovements as pm
                 ],
             },
             pl.from_dict(
-                {'position': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
-                schema={'position': pl.List(pl.Float64)},
+                {'time': [0], 'position': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
+                schema={'time': pl.Int64, 'position': pl.List(pl.Float64)},
             ),
             6,
             id='df_single_row_six_position_columns',
@@ -315,7 +319,7 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'x_vel': pl.Float64, 'y_vel': pl.Float64}),
                 'velocity_columns': ['x_vel', 'y_vel'],
             },
-            pl.DataFrame(schema={'velocity': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)}),
             2,
             id='empty_df_with_schema_two_velocity_columns',
         ),
@@ -329,7 +333,11 @@ import pymovements as pm
                 ),
                 'velocity_columns': ['x_vel', 'y_vel'],
             },
-            pl.DataFrame(schema={'abc': pl.Int64, 'velocity': pl.List(pl.Float64)}),
+            pl.DataFrame(
+                schema={
+                    'abc': pl.Int64, 'time': pl.Int64, 'velocity': pl.List(pl.Float64),
+                },
+            ),
             2,
             id='empty_df_with_three_column_schema_two_velocity_columns',
         ),
@@ -344,7 +352,7 @@ import pymovements as pm
                 ),
                 'velocity_columns': ['xr_vel', 'yr_vel', 'xl_vel', 'yl_vel'],
             },
-            pl.DataFrame(schema={'velocity': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)}),
             4,
             id='empty_df_with_schema_four_velocity_columns',
         ),
@@ -364,7 +372,7 @@ import pymovements as pm
                     'x_avg_vel', 'y_avg_vel',
                 ],
             },
-            pl.DataFrame(schema={'velocity': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)}),
             6,
             id='empty_df_with_schema_six_velocity_columns',
         ),
@@ -378,8 +386,8 @@ import pymovements as pm
                 'velocity_columns': ['x_vel', 'y_vel'],
             },
             pl.from_dict(
-                {'velocity': [[1.23, 4.56]]},
-                schema={'velocity': pl.List(pl.Float64)},
+                {'time': [0], 'velocity': [[1.23, 4.56]]},
+                schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_two_velocity_columns',
@@ -394,8 +402,8 @@ import pymovements as pm
                 'velocity_columns': ['x_vel', 'y_vel'],
             },
             pl.from_dict(
-                {'abc': [1], 'velocity': [[1.23, 4.56]]},
-                schema={'abc': pl.Int64, 'velocity': pl.List(pl.Float64)},
+                {'time': [0], 'abc': [1], 'velocity': [[1.23, 4.56]]},
+                schema={'abc': pl.Int64, 'time': pl.Int64, 'velocity': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_three_columns_two_velocity_columns',
@@ -416,8 +424,8 @@ import pymovements as pm
                 'velocity_columns': ['xl_vel', 'yl_vel', 'xr_vel', 'yr_vel'],
             },
             pl.from_dict(
-                {'velocity': [[1.2, 3.4, 5.6, 7.8]]},
-                schema={'velocity': pl.List(pl.Float64)},
+                {'time': [0], 'velocity': [[1.2, 3.4, 5.6, 7.8]]},
+                schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)},
             ),
             4,
             id='df_single_row_four_velocity_columns',
@@ -444,8 +452,8 @@ import pymovements as pm
                 ],
             },
             pl.from_dict(
-                {'velocity': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
-                schema={'velocity': pl.List(pl.Float64)},
+                {'time': [0], 'velocity': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
+                schema={'time': pl.Int64, 'velocity': pl.List(pl.Float64)},
             ),
             6,
             id='df_single_row_six_velocity_columns',
@@ -456,7 +464,7 @@ import pymovements as pm
                 'data': pl.DataFrame(schema={'x_acc': pl.Float64, 'y_acc': pl.Float64}),
                 'acceleration_columns': ['x_acc', 'y_acc'],
             },
-            pl.DataFrame(schema={'acceleration': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)}),
             2,
             id='empty_df_with_schema_two_acceleration_columns',
         ),
@@ -470,7 +478,11 @@ import pymovements as pm
                 ),
                 'acceleration_columns': ['x_acc', 'y_acc'],
             },
-            pl.DataFrame(schema={'abc': pl.Int64, 'acceleration': pl.List(pl.Float64)}),
+            pl.DataFrame(
+                schema={
+                    'abc': pl.Int64, 'time': pl.Int64, 'acceleration': pl.List(pl.Float64),
+                },
+            ),
             2,
             id='empty_df_with_three_column_schema_two_acceleration_columns',
         ),
@@ -485,7 +497,7 @@ import pymovements as pm
                 ),
                 'acceleration_columns': ['xr_acc', 'yr_acc', 'xl_acc', 'yl_acc'],
             },
-            pl.DataFrame(schema={'acceleration': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)}),
             4,
             id='empty_df_with_schema_four_acceleration_columns',
         ),
@@ -505,7 +517,7 @@ import pymovements as pm
                     'x_avg_acc', 'y_avg_acc',
                 ],
             },
-            pl.DataFrame(schema={'acceleration': pl.List(pl.Float64)}),
+            pl.DataFrame(schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)}),
             6,
             id='empty_df_with_schema_six_acceleration_columns',
         ),
@@ -519,8 +531,8 @@ import pymovements as pm
                 'acceleration_columns': ['x_acc', 'y_acc'],
             },
             pl.from_dict(
-                {'acceleration': [[1.23, 4.56]]},
-                schema={'acceleration': pl.List(pl.Float64)},
+                {'time': [0], 'acceleration': [[1.23, 4.56]]},
+                schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_two_acceleration_columns',
@@ -535,8 +547,8 @@ import pymovements as pm
                 'acceleration_columns': ['x_acc', 'y_acc'],
             },
             pl.from_dict(
-                {'abc': [1], 'acceleration': [[1.23, 4.56]]},
-                schema={'abc': pl.Int64, 'acceleration': pl.List(pl.Float64)},
+                {'time': [0], 'abc': [1], 'acceleration': [[1.23, 4.56]]},
+                schema={'abc': pl.Int64, 'time': pl.Int64, 'acceleration': pl.List(pl.Float64)},
             ),
             2,
             id='df_single_row_three_columns_two_acceleration_columns',
@@ -557,8 +569,8 @@ import pymovements as pm
                 'acceleration_columns': ['xl_acc', 'yl_acc', 'xr_acc', 'yr_acc'],
             },
             pl.from_dict(
-                {'acceleration': [[1.2, 3.4, 5.6, 7.8]]},
-                schema={'acceleration': pl.List(pl.Float64)},
+                {'time': [0], 'acceleration': [[1.2, 3.4, 5.6, 7.8]]},
+                schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)},
             ),
             4,
             id='df_single_row_four_acceleration_columns',
@@ -585,8 +597,8 @@ import pymovements as pm
                 ],
             },
             pl.from_dict(
-                {'acceleration': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
-                schema={'acceleration': pl.List(pl.Float64)},
+                {'time': [0], 'acceleration': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]]},
+                schema={'time': pl.Int64, 'acceleration': pl.List(pl.Float64)},
             ),
             6,
             id='df_single_row_six_acceleration_columns',
@@ -615,12 +627,14 @@ import pymovements as pm
             },
             pl.from_dict(
                 {
+                    'time': [0],
                     'pixel': [[0.1, 0.2]],
                     'position': [[1.1, 1.2]],
                     'velocity': [[3.1, 3.2]],
                     'acceleration': [[5.1, 5.2]],
                 },
                 schema={
+                    'time': pl.Int64,
                     'pixel': pl.List(pl.Float64),
                     'position': pl.List(pl.Float64),
                     'velocity': pl.List(pl.Float64),
@@ -686,12 +700,14 @@ import pymovements as pm
             },
             pl.from_dict(
                 {
+                    'time': [0],
                     'pixel': [[0.1, 0.2, 0.3, 0.4, 0.5, 0.6]],
                     'position': [[1.1, 1.2, 1.3, 1.4, 1.5, 1.6]],
                     'velocity': [[3.1, 3.2, 3.3, 3.4, 3.5, 3.6]],
                     'acceleration': [[5.1, 5.2, 5.3, 5.4, 5.5, 5.6]],
                 },
                 schema={
+                    'time': pl.Int64,
                     'pixel': pl.List(pl.Float64),
                     'position': pl.List(pl.Float64),
                     'velocity': pl.List(pl.Float64),
@@ -701,6 +717,57 @@ import pymovements as pm
             6,
             id='df_single_row_all_types_six_columns',
         ),
+
+        pytest.param(
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23, 2.34, 3.45], 'y': [4.56, 5.67, 6.78]},
+                    schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+            },
+            pl.from_dict(
+                {'time': [0, 1, 2], 'position': [[1.23, 4.56], [2.34, 5.67], [3.45, 6.78]]},
+                schema={'time': pl.Int64, 'position': pl.List(pl.Float64)},
+            ),
+            2,
+            id='df_three_rows_two_position_columns_no_time_no_experiment',
+        ),
+
+        pytest.param(
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23, 2.34, 3.45], 'y': [4.56, 5.67, 6.78]},
+                    schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+                'experiment': pm.Experiment(1024, 768, 38, 30, None, 'center', 100),
+            },
+            pl.from_dict(
+                {'time': [0.0, 10.0, 20.0], 'position': [[1.23, 4.56], [2.34, 5.67], [3.45, 6.78]]},
+                schema={'time': pl.Float64, 'position': pl.List(pl.Float64)},
+            ),
+            2,
+            id='df_three_rows_two_position_columns_no_time_100_hz',
+        ),
+
+        pytest.param(
+            {
+                'data': pl.from_dict(
+                    {'x': [1.23, 2.34, 3.45], 'y': [4.56, 5.67, 6.78]},
+                    schema={'x': pl.Float64, 'y': pl.Float64},
+                ),
+                'position_columns': ['x', 'y'],
+                'experiment': pm.Experiment(1024, 768, 38, 30, None, 'center', 1000),
+            },
+            pl.from_dict(
+                {'time': [0.0, 1.0, 2.0], 'position': [[1.23, 4.56], [2.34, 5.67], [3.45, 6.78]]},
+                schema={'time': pl.Float64, 'position': pl.List(pl.Float64)},
+            ),
+            2,
+            id='df_three_rows_two_position_columns_no_time_1000_hz',
+        ),
+
     ],
 )
 def test_init_gaze_dataframe_has_expected_attrs(init_kwargs, expected_frame, expected_n_components):
@@ -709,7 +776,7 @@ def test_init_gaze_dataframe_has_expected_attrs(init_kwargs, expected_frame, exp
     assert gaze.n_components == expected_n_components
 
 
-@pytest.mark.parametrize(
+@ pytest.mark.parametrize(
     ('init_kwargs', 'exception', 'exception_msg'),
     [
         pytest.param(
@@ -1195,7 +1262,7 @@ def test_gaze_copy_init_has_same_n_components():
     assert gaze.n_components == gaze_copy.n_components
 
 
-@pytest.mark.parametrize(
+@ pytest.mark.parametrize(
     ('events', 'init_kwargs'),
     [
         pytest.param(
diff --git a/tests/unit/gaze/gaze_transform_test.py b/tests/unit/gaze/gaze_transform_test.py
index 4343738..c5919bb 100644
--- a/tests/unit/gaze/gaze_transform_test.py
+++ b/tests/unit/gaze/gaze_transform_test.py
@@ -799,7 +799,7 @@ def test_gaze_dataframe_pix2deg_creates_position_column(data, experiment, pixel_
                 'nor is a pixel column explicitly specified. '
                 'You can specify the pixel column via: '
                 'pix2deg(pixel_column="name_of_your_pixel_column"). '
-                "Available dataframe columns are: ['acceleration']"
+                "Available dataframe columns are: ['time', 'acceleration']"
             ),
             id='no_pixel_column',
         ),
@@ -936,7 +936,7 @@ def test_gaze_dataframe_pos2acc_creates_acceleration_column(data, experiment, po
                 "Since the dataframe has a 'pixel' column, "
                 'consider running pix2deg() before pos2acc(). If you want to run transformations '
                 "in pixel units, you can do so by using pos2acc(position_column='pixel'). "
-                "Available dataframe columns are: ['pixel']"
+                "Available dataframe columns are: ['time', 'pixel']"
             ),
             id='no_position_column_but_has_pixel_column',
         ),
@@ -952,7 +952,7 @@ def test_gaze_dataframe_pos2acc_creates_acceleration_column(data, experiment, po
                 'nor is a position column explicitly specified. '
                 'You can specify the position column via: '
                 'pos2acc(position_column="your_position_column"). '
-                "Available dataframe columns are: ['acceleration']"
+                "Available dataframe columns are: ['time', 'acceleration']"
             ),
             id='no_position_column',
         ),
@@ -1037,7 +1037,7 @@ def test_gaze_dataframe_pos2vel_creates_velocity_column(data, experiment, positi
                 "Since the dataframe has a 'pixel' column, "
                 'consider running pix2deg() before pos2vel(). If you want to run transformations '
                 "in pixel units, you can do so by using pos2vel(position_column='pixel'). "
-                "Available dataframe columns are: ['pixel']"
+                "Available dataframe columns are: ['time', 'pixel']"
             ),
             id='no_position_column_but_has_pixel_column',
         ),
@@ -1053,7 +1053,7 @@ def test_gaze_dataframe_pos2vel_creates_velocity_column(data, experiment, positi
                 'nor is a position column explicitly specified. '
                 'You can specify the position column via: '
                 'pos2vel(position_column="your_position_column"). '
-                "Available dataframe columns are: ['acceleration']"
+                "Available dataframe columns are: ['time', 'acceleration']"
             ),
             id='no_position_column',
         ),
diff --git a/tests/unit/gaze/integration_numpy_test.py b/tests/unit/gaze/integration_numpy_test.py
index 707b1db..fac979e 100644
--- a/tests/unit/gaze/integration_numpy_test.py
+++ b/tests/unit/gaze/integration_numpy_test.py
@@ -54,8 +54,8 @@ def test_from_numpy():
         experiment=experiment,
     )
 
-    assert gaze.frame.shape == (4, 4)
-    assert gaze.columns == schema
+    assert gaze.frame.shape == (4, 5)
+    assert gaze.columns == schema + ['time']  # expected schema includes additional time column
 
 
 def test_from_numpy_with_schema():
@@ -269,7 +269,7 @@ def test_from_numpy_all_none():
         acceleration_columns=None,
     )
 
-    expected = pl.DataFrame()
+    expected = pl.DataFrame(schema={'time': pl.Int64})
 
     assert_frame_equal(gaze.frame, expected)
     assert gaze.n_components is None
diff --git a/tests/unit/gaze/integration_pandas_test.py b/tests/unit/gaze/integration_pandas_test.py
index e10abdf..506a77c 100644
--- a/tests/unit/gaze/integration_pandas_test.py
+++ b/tests/unit/gaze/integration_pandas_test.py
@@ -26,7 +26,7 @@ from polars.testing import assert_frame_equal
 import pymovements as pm
 
 
-def test_from_pandas():
+def test_from_pandas_additional_time_column():
     pandas_df = pd.DataFrame(
         {
             'x_pix': [0, 1, 2, 3],
@@ -43,8 +43,8 @@ def test_from_pandas():
         experiment=experiment,
     )
 
-    assert gaze.frame.shape == (4, 4)
-    assert all(gaze.columns == pandas_df.columns)
+    assert gaze.frame.shape == (4, 5)
+    assert gaze.columns == list(pandas_df.columns) + ['time']
 
 
 def test_from_pandas_explicit_columns():
diff --git a/tests/unit/gaze/io/csv_test.py b/tests/unit/gaze/io/csv_test.py
index 195b5ed..f349954 100644
--- a/tests/unit/gaze/io/csv_test.py
+++ b/tests/unit/gaze/io/csv_test.py
@@ -81,7 +81,7 @@ import pymovements as pm
                 'pixel_columns': pm.datasets.GazeOnFaces().pixel_columns,
                 **pm.datasets.GazeOnFaces().custom_read_kwargs,
             },
-            (10, 1),
+            (10, 2),
             id='gaze_on_faces_dataset_example',
         ),
         pytest.param(