{ "cells": [ { "cell_type": "markdown", "id": "566a8637", "metadata": {}, "source": [ "# CSV Files\n", "\n", "CSV files are flexible but require explicit column definitions so that\n", "`pymovements` knows how to interpret the data. Below is a toy examples of eye-tracking data samples stored in a `csv` file." ] }, { "cell_type": "code", "execution_count": null, "id": "f3b5a975", "metadata": { "tags": [ "hide-cell" ] }, "outputs": [], "source": [ "import polars as pl\n", "\n", "import pymovements as pm\n", "from pymovements.gaze.experiment import Experiment\n", "\n", "csv_example = pl.read_csv(\n", " \"../../examples/gaze-toy-example.csv\",\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "8b96eb7e", "metadata": {}, "outputs": [], "source": [ "csv_example.head(5)" ] }, { "cell_type": "markdown", "id": "b0a70d32", "metadata": {}, "source": [ "### Time Information\n", "\n", "When loading gaze data with {py:func}`~pymovements.gaze.from_csv()`, the column containing timestamps must be specified via the `time_column` parameter if it is not already named `time`. This column will be internally standardized to `time` within the resulting {py:class}`~pymovements.Gaze` object to ensure a consistent temporal representation across datasets.\n", "\n", "If `time_unit` is not specified (`None`), timestamps are assumed to be in milliseconds. Supported units are:\n", "\n", "- `'ms'` — milliseconds (default) \n", "- `'s'` — seconds \n", "- `'step'` — sampling steps \n", "\n", "If `time_unit='step'`, an {py:class}`~pymovements.Experiment` definition with a sampling rate must be provided so that timestamps can be converted to milliseconds. If no `time_column` is provided, the data are assumed not to contain explicit timestamps. In that case, a time axis can be generated later based on the sampling rate defined in the experiment (see {doc}`Experiment Configuration <../experiment>`)." ] }, { "cell_type": "markdown", "id": "fa3db487", "metadata": {}, "source": [ "### Defining Gaze Components\n", "\n", "Gaze signals are typically stored in separate columns of the CSV file (e.g., `x_left`, `y_left`). \n", "The following component parameters specify how these flat columns should be grouped into structured gaze components inside the {py:class}`~pymovements.Gaze` object:\n", "\n", "| Parameter | Expects | Creates Nested Column | Unit |\n", "|------------|----------|------------------------|-------|\n", "| `pixel_columns` | List of pixel coordinate columns | `pixel` | pixels (`px`) |\n", "| `position_columns` | List of dva coordinate columns | `position` | `dva` (`°`) |\n", "| `velocity_columns` | List of velocity component columns | `velocity` | `dva/s` or `px/s` |\n", "| `acceleration_columns` | List of acceleration component columns | `acceleration` | `dva/s²` or `px/s²` |\n", "| `distance_column` | Single column name | `distance` | `cm` |\n", "\n", "If a non-empty list is passed to one of the component parameters, the specified columns are merged into a single nested list column in `samples`.\n", "\n", "The supported number of component columns with the expected order are:\n", "\n", "- **0 columns** → no nested column created \n", "- **2 columns** → monocular (`x`, `y`) \n", "- **4 columns** → binocular (`x_left`, `y_left`, `x_right`, `y_right`) \n", "- **6 columns** → binocular + cyclopian coordinates \n", "\n", "### Pixel vs. Position Coordinates\n", "\n", "You typically provide **either**:\n", "\n", "- `pixel_columns` — if your data are in screen pixels (common for raw exports)\n", "- `position_columns` — if your data are already converted to degrees of visual angle (dva)\n", "\n", "If both are provided, `pymovements` keeps both representations, allowing you to switch between coordinate systems without recomputing. Conversions between pixel and `dva` coordinates require a valid {py:class}`~pymovements.Experiment` with screen geometry and viewing distance.\n", "\n", "### Using an Experiment\n", "\n", "Providing an {py:class}`~pymovements.Experiment` connects gaze samples to screen geometry and sampling rate. This enables:\n", "\n", "- Pixel–dva transformations \n", "- Velocity and acceleration computation in physical units \n", "- Time-step conversion when `time_unit=\"step\"` \n", "\n", "If no experiment is provided, gaze data can still be loaded, but certain transformations will not be available.\n", "\n", "### Automatic Column Detection \n", "\n", "While `pymovements` provides functionality for automatic column detection, it is still under development. Currently, the naming schemes are:\n", "\n", "- column name prefixes define the type of data (e.g., `pixel`, `position`)\n", "- column name suffixes define the component (e.g. `x`, `y`, `xr`, `yl`)\n", "\n", "This means only column names like `pixel_x`, `position_xr` or `acceleration_xa` can be inferred. If the described schema fits your set-up, you can enable ``auto_column_detect=True``.\n", "\n", "### `from_csv()`\n", "\n", "Now putting all this together, we can load our toy example from above directly into {py:class}`~pymovements.Gaze`:" ] }, { "cell_type": "code", "execution_count": null, "id": "97c92fde", "metadata": {}, "outputs": [], "source": [ "experiment = Experiment(\n", " screen_width_px=1280,\n", " screen_height_px=1024,\n", " screen_width_cm=38,\n", " screen_height_cm=30.2,\n", " distance_cm=68,\n", " origin='upper left',\n", " sampling_rate=250.0,\n", ")\n", "\n", "gaze = pm.gaze.from_csv(\n", " '../../examples/gaze-toy-example.csv',\n", " experiment=experiment,\n", " time_column='time',\n", " pixel_columns=['x', 'y'],\n", " time_unit='ms'\n", ")\n", "\n", "gaze.samples.head(5)" ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 5 }