Working with Local Dataset#

In this tutorial, we will show how to use your own local dataset with the Dataset class. The Dataset class can help you to manage and process your eyetracking data.

Preparations#

We import pymovements as the alias pm for convenience.

[1]:
import pymovements as pm
/home/docs/checkouts/readthedocs.org/user_builds/pymovements/envs/v0.11.0/lib/python3.9/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
  from .autonotebook import tqdm as notebook_tqdm

For demonstration purposes, we will use the raw data provided by the Toy dataset, a sample dataset that comes with pymovements.

We will download the resources of this dataset the directory to simulate a local dataset for you. All downloaded archive files are automatically extracted and then removed. The directory of the dataset will be data/my_dataset.

After that we won’t use the python class anymore and delete the object (the files on your system will stay in place). Don’t worry if you’re confused about these lines as they are not relevant to your use case.

Just keep in mind that we now have some files with gaze data in the directory data/my_dataset.

[2]:
toy_dataset = pm.Dataset('ToyDataset', path='data/my_dataset')
toy_dataset.download(remove_finished=True)

del toy_dataset
Downloading http://github.com/aeye-lab/pymovements-toy-dataset/zipball/6cb5d663317bf418cec0c9abe1dde5085a8a8ebd/ to data/my_dataset/downloads/pymovements-toy-dataset.zip
pymovements-toy-dataset.zip: 100%|██████████| 3.06M/3.06M [00:00<00:00, 25.3MB/s]
Checking integrity of pymovements-toy-dataset.zip
Extracting pymovements-toy-dataset.zip to data/my_dataset/raw

Define your Experiment#

To use the Dataset class, we first need to create an Experiment instance. This class represents the properties of the experiment, such as the screen dimensions and sampling rate.

[3]:
experiment = pm.gaze.Experiment(
    screen_width_px=1280,
    screen_height_px=1024,
    screen_width_cm=38,
    screen_height_cm=30.2,
    distance_cm=68,
    origin='lower left',
    sampling_rate=1000,
)

Parameters for File Parsing#

We also define a filename_format which is a pattern expression used to match and extract values from filenames of data files in the dataset. For example, r'trial_{text_id:d}_{page_id:d}.csv' will match filenames that follow the pattern trial_{text_id}_{page_id}.csv and extract the values of text_id and page_id for each file.

[4]:
filename_format = r'trial_{text_id:d}_{page_id:d}.csv'

Both values of text_id and page_id are numeric. We can use a map to define the casting of these values.

[5]:
filename_format_dtypes = {
    'text_id': int,
    'page_id': int,
}

We can also adjust how the CSV files are read.

The column_map dictionary maps the original column names in the CSV files to the desired column names. Here the original column names are ‘timestamp’, ‘x’, and ‘y’, and the desired column names are ‘time’, ‘x_pix’, and ‘y_pix’, respectively.

[6]:
column_map = {
    'timestamp': 'time',
    'x': 'x_pix',
    'y': 'y_pix',
}

Here, we specify that the separator in the CSV files is a tab (‘:nbsphinx-math:`t’`).

[7]:
custom_read_kwargs = {
    'separator': '\t',
}

Define and load the Dataset#

Next we use all these definitions and create a DatasetDefinition by passing in the root directory, Experiment instance, and other optional parameters such as the filename regular expression and custom CSV reading parameters.

[8]:
dataset_definition = pm.DatasetDefinition(
    name='my_dataset',
    experiment=experiment,
    filename_format=filename_format,
    filename_format_dtypes=filename_format_dtypes,
    column_map=column_map,
    custom_read_kwargs=custom_read_kwargs,
)

Finally we create a Dataset instance by using the DatasetDefinition and specifying the directory path.

[9]:
dataset = pm.Dataset(
    definition=dataset_definition,
    path='data/my_dataset/',
)

If we have a root data directory which holds all your local datasets we can further need to define the paths of the dataset.

The dataset, raw, preprocessed, and events parameters define the names of the directories for the dataset, raw data, preprocessed data, and events data, respectively.

[10]:
dataset_paths = pm.DatasetPaths(
    root='data/',
    raw='raw',
    preprocessed='preprocessed',
    events='events',
)

dataset = pm.Dataset(
    definition=dataset_definition,
    path=dataset_paths,
)

Now let’s load the dataset into memory. Here we select a subset including the first page of texts with ID 1 and 2.

[11]:
subset = {
    'text_id': [1, 2],
    'page_id': 1,
}

dataset.load(subset=subset)
100%|██████████| 2/2 [00:00<00:00, 188.64it/s]
[11]:
<pymovements.dataset.dataset.Dataset at 0x7f503ad31b50>

Use the Dataset#

Once we have created the Dataset instance, we can use its methods to preprocess and analyze data in our local dataset.

[12]:
dataset.gaze[0].frame
[12]:
shape: (23_054, 5)
text_idpage_idtimex_pixy_pix
i64i64f64f64f64
112.415266e6176.8140.2
112.415267e6176.7139.8
112.415268e6176.7139.3
112.415269e6176.6139.3
112.41527e6176.7139.3
112.415271e6176.8139.5
112.415272e6177.3139.8
112.415273e6177.8140.0
112.415274e6178.3140.0
112.415275e6178.3139.9
112.415276e6178.0140.2
112.415277e6177.7140.4
112.438308e6649.1633.7
112.438309e6648.8633.9
112.43831e6649.1634.1
112.438311e6649.6634.2
112.438312e6650.1634.1
112.438313e6650.0634.0
112.438314e6649.9633.9
112.438315e6649.9633.9
112.438316e6650.1633.7
112.438317e6650.2633.5
112.438318e6650.0633.2
112.438319e6649.7633.1

Here we use the pix2deg method to convert the pixel coordinates to degrees of visual angle.

[13]:
dataset.pix2deg()

dataset.gaze[0].frame
100%|██████████| 2/2 [00:00<00:00, 536.56it/s]
[13]:
shape: (23_054, 7)
text_idpage_idtimex_pixy_pixy_posx_pos
i64i64f64f64f64f64f64
112.415266e6176.8140.2-12.297242-8.259494
112.415267e6176.7139.8-12.306793-8.261927
112.415268e6176.7139.3-12.318732-8.261927
112.415269e6176.6139.3-12.318732-8.264361
112.41527e6176.7139.3-12.318732-8.261927
112.415271e6176.8139.5-12.313957-8.259494
112.415272e6177.3139.8-12.306793-8.247325
112.415273e6177.8140.0-12.302018-8.235155
112.415274e6178.3140.0-12.302018-8.222985
112.415275e6178.3139.9-12.304406-8.222985
112.415276e6178.0140.2-12.297242-8.230287
112.415277e6177.7140.4-12.292466-8.237589
112.438308e6649.1633.7-0.1450823.415265
112.438309e6648.8633.9-0.1400793.407836
112.43831e6649.1634.1-0.1350773.415265
112.438311e6649.6634.2-0.1325753.427645
112.438312e6650.1634.1-0.1350773.440025
112.438313e6650.0634.0-0.1375783.437549
112.438314e6649.9633.9-0.1400793.435073
112.438315e6649.9633.9-0.1400793.435073
112.438316e6650.1633.7-0.1450823.440025
112.438317e6650.2633.5-0.1500853.442501
112.438318e6650.0633.2-0.1575893.437549
112.438319e6649.7633.1-0.1600913.430121

We can use the pos2vel method to calculate the velocity of the gaze position.

[14]:
dataset.pos2vel(method='savitzky_golay', window_length=7, polyorder=2)

dataset.gaze[0].frame
100%|██████████| 2/2 [00:00<00:00, 312.84it/s]
[14]:
shape: (23_054, 9)
text_idpage_idtimex_pixy_pixy_posx_posy_velx_vel
i64i64f64f64f64f64f64f64f64
112.415266e6176.8140.2-12.297242-8.259494-13.473668-5.302027
112.415267e6176.7139.8-12.306793-8.261927-9.49412-3.04214
112.415268e6176.7139.3-12.318732-8.261927-5.514573-0.782253
112.415269e6176.6139.3-12.318732-8.264361-1.5350251.477633
112.41527e6176.7139.3-12.318732-8.2619271.534964.085296
112.415271e6176.8139.5-12.313957-8.2594943.4110156.780025
112.415272e6177.3139.8-12.306793-8.2473253.155198.083958
112.415273e6177.8140.0-12.302018-8.2351553.1552526.867045
112.415274e6178.3140.0-12.302018-8.2229852.8995343.998521
112.415275e6178.3139.9-12.304406-8.2229853.4114070.347668
112.415276e6178.0140.2-12.297242-8.2302874.093787-1.738596
112.415277e6177.7140.4-12.292466-8.2375895.287927-1.999405
112.438308e6649.1633.7-0.1450823.4152650.714688-0.265312
112.438309e6648.8633.9-0.1400793.4078362.5907452.210777
112.43831e6649.1634.1-0.1350773.4152652.5907454.06786
112.438311e6649.6634.2-0.1325753.4276450.7146885.129065
112.438312e6650.1634.1-0.1350773.440025-0.5360164.686916
112.438313e6650.0634.0-0.1375783.437549-1.7867213.006674
112.438314e6649.9633.9-0.1400793.435073-2.6800811.503314
112.438315e6649.9633.9-0.1400793.435073-3.4841040.265288
112.438316e6650.1633.7-0.1450823.440025-4.020119-0.353725
112.438317e6650.2633.5-0.1500853.442501-4.913478-1.650699
112.438318e6650.0633.2-0.1575893.437549-5.806836-2.947673
112.438319e6649.7633.1-0.1600913.430121-6.700195-4.244647