Heatmap Plotting#
In this tutorial, we will demonstrate how to use the heatmap
function from the pymovements.plotting
module to create a heatmap of gaze data. The heatmap will show the distribution of gaze positions across the experiment screen, with color values indicating the time spent at each position in seconds.
Preparations#
We import pymovements
as the alias pm
for convenience.
[1]:
import pymovements as pm
Loading the Dataset#
Let’s start by downloading our ToyDataset
and loading in its data:
[2]:
dataset = pm.Dataset('ToyDataset', path='data/ToyDataset')
dataset.download()
dataset.load()
INFO:pymovements.dataset.dataset:
You are downloading the pymovements Toy Dataset. Please be aware that pymovements does not
host or distribute any dataset resources and only provides a convenient interface to
download the public dataset resources that were published by their respective authors.
Please cite the referenced publication if you intend to use the dataset in your research.
Using already downloaded and verified file: data/ToyDataset/downloads/pymovements-toy-dataset.zip
Extracting pymovements-toy-dataset.zip to data/ToyDataset/raw
100%|██████████| 23/23 [00:00<00:00, 303.43it/s]
[2]:
-
DatasetDefinitionDatasetDefinition
-
NoneNone
-
dict (0 items)
-
dict (1 items)
-
dict (4 items)
-
list (5 items)
- 'timestamp'
- 'x'
- (3 more)
-
dict (5 items)
-
Float64Float64
-
Float64Float64
- (3 more)
-
- (2 more)
-
-
-
NoneNone
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
NoneNone
-
dict (1 items)
-
'trial_{text_id:d}_{page_id:d}.csv''trial_{text_id:d}_{page_id:d}.csv'
-
-
dict (1 items)
-
dict (2 items)
-
<class 'int'><class 'int'>
-
<class 'int'><class 'int'>
-
-
-
TrueTrue
-
'pymovements Toy Dataset''pymovements Toy Dataset'
-
dict (0 items)
-
'ToyDataset''ToyDataset'
-
list (2 items)
- 'x'
- 'y'
-
NoneNone
-
list (1 items)
-
ResourceDefinition
-
'gaze''gaze'
-
'pymovements-toy-dataset.zip''pymovements-toy-dataset.zip'
-
'trial_{text_id:d}_{page_id:d}.csv''trial_{text_id:d}_{page_id:d}.csv'
-
dict (2 items)
-
<class 'int'><class 'int'>
-
<class 'int'><class 'int'>
-
-
'4da622457637a8181d86601fe17f3aa8''4da622457637a8181d86601fe17f3aa8'
-
str'http://github.com/aeye-lab/pymovements-toy-dataset/zipball/6cb5d663317bf418cec0c9abe1dde5085a8a8ebd/'
-
-
ResourceDefinition
-
'timestamp''timestamp'
-
'ms''ms'
-
NoneNone
-
NoneNone
-
-
list (0 items)
-
dict (1 items)
-
DataFrame (3 columns, 20 rows)shape: (20, 3)
text_id page_id filepath i64 i64 str 0 1 "aeye-lab-pymovements-toy-datas… 0 2 "aeye-lab-pymovements-toy-datas… 0 3 "aeye-lab-pymovements-toy-datas… 0 4 "aeye-lab-pymovements-toy-datas… 0 5 "aeye-lab-pymovements-toy-datas… … … … 3 1 "aeye-lab-pymovements-toy-datas… 3 2 "aeye-lab-pymovements-toy-datas… 3 3 "aeye-lab-pymovements-toy-datas… 3 4 "aeye-lab-pymovements-toy-datas… 3 5 "aeye-lab-pymovements-toy-datas…
-
-
list (20 items)
-
Gaze
-
DataFrame (6 columns, 17223 rows)shape: (17_223, 6)
time stimuli_x stimuli_y text_id page_id pixel i64 f64 f64 i64 i64 list[f64] 1988145 -1.0 -1.0 0 1 [206.8, 152.4] 1988146 -1.0 -1.0 0 1 [206.9, 152.1] 1988147 -1.0 -1.0 0 1 [207.0, 151.8] 1988148 -1.0 -1.0 0 1 [207.1, 151.7] 1988149 -1.0 -1.0 0 1 [207.0, 151.5] … … … … … … 2005363 -1.0 -1.0 0 1 [361.0, 415.4] 2005364 -1.0 -1.0 0 1 [358.0, 414.5] 2005365 -1.0 -1.0 0 1 [355.8, 413.8] 2005366 -1.0 -1.0 0 1 [353.1, 413.2] 2005367 -1.0 -1.0 0 1 [351.2, 412.9] -
EventsEvents
-
DataFrame (6 columns, 0 rows)shape: (0, 6)
text_id page_id name onset offset duration i64 i64 str i64 i64 i64 -
list (2 items)
- 'text_id'
- 'page_id'
-
-
list (2 items)
- 'text_id'
- 'page_id'
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
-
Gaze
-
DataFrame (6 columns, 29799 rows)shape: (29_799, 6)
time stimuli_x stimuli_y text_id page_id pixel i64 f64 f64 i64 i64 list[f64] 2008305 -1.0 -1.0 0 2 [141.4, 153.6] 2008306 -1.0 -1.0 0 2 [141.1, 153.2] 2008307 -1.0 -1.0 0 2 [140.7, 152.8] 2008308 -1.0 -1.0 0 2 [140.6, 152.7] 2008309 -1.0 -1.0 0 2 [140.5, 152.6] … … … … … … 2038099 -1.0 -1.0 0 2 [273.8, 773.8] 2038100 -1.0 -1.0 0 2 [273.8, 774.1] 2038101 -1.0 -1.0 0 2 [273.9, 774.5] 2038102 -1.0 -1.0 0 2 [274.0, 774.4] 2038103 -1.0 -1.0 0 2 [274.0, 773.9] -
EventsEvents
-
DataFrame (6 columns, 0 rows)shape: (0, 6)
text_id page_id name onset offset duration i64 i64 str i64 i64 i64 -
list (2 items)
- 'text_id'
- 'page_id'
-
-
list (2 items)
- 'text_id'
- 'page_id'
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
- (18 more)
-
Gaze
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
DatasetPathsDatasetPaths
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
PosixPath('data/ToyDataset/downloads')PosixPath('data/ToyDataset/downloads')
-
PosixPath('data/ToyDataset/events')PosixPath('data/ToyDataset/events')
-
PosixPath('data/ToyDataset/precomputed_events')PosixPath('data/ToyDataset/precomputed_events')
-
PosixPathPosixPath('data/ToyDataset/precomputed_reading_measures')
-
PosixPath('data/ToyDataset/preprocessed')PosixPath('data/ToyDataset/preprocessed')
-
PosixPath('data/ToyDataset/raw')PosixPath('data/ToyDataset/raw')
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
-
list (0 items)
-
list (0 items)
After loading the dataset, we will transform the pixel coordinates to degrees.
[3]:
dataset.pix2deg()
[3]:
-
DatasetDefinitionDatasetDefinition
-
NoneNone
-
dict (0 items)
-
dict (1 items)
-
dict (4 items)
-
list (5 items)
- 'timestamp'
- 'x'
- (3 more)
-
dict (5 items)
-
Float64Float64
-
Float64Float64
- (3 more)
-
- (2 more)
-
-
-
NoneNone
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
NoneNone
-
dict (1 items)
-
'trial_{text_id:d}_{page_id:d}.csv''trial_{text_id:d}_{page_id:d}.csv'
-
-
dict (1 items)
-
dict (2 items)
-
<class 'int'><class 'int'>
-
<class 'int'><class 'int'>
-
-
-
TrueTrue
-
'pymovements Toy Dataset''pymovements Toy Dataset'
-
dict (0 items)
-
'ToyDataset''ToyDataset'
-
list (2 items)
- 'x'
- 'y'
-
NoneNone
-
list (1 items)
-
ResourceDefinition
-
'gaze''gaze'
-
'pymovements-toy-dataset.zip''pymovements-toy-dataset.zip'
-
'trial_{text_id:d}_{page_id:d}.csv''trial_{text_id:d}_{page_id:d}.csv'
-
dict (2 items)
-
<class 'int'><class 'int'>
-
<class 'int'><class 'int'>
-
-
'4da622457637a8181d86601fe17f3aa8''4da622457637a8181d86601fe17f3aa8'
-
str'http://github.com/aeye-lab/pymovements-toy-dataset/zipball/6cb5d663317bf418cec0c9abe1dde5085a8a8ebd/'
-
-
ResourceDefinition
-
'timestamp''timestamp'
-
'ms''ms'
-
NoneNone
-
NoneNone
-
-
list (0 items)
-
dict (1 items)
-
DataFrame (3 columns, 20 rows)shape: (20, 3)
text_id page_id filepath i64 i64 str 0 1 "aeye-lab-pymovements-toy-datas… 0 2 "aeye-lab-pymovements-toy-datas… 0 3 "aeye-lab-pymovements-toy-datas… 0 4 "aeye-lab-pymovements-toy-datas… 0 5 "aeye-lab-pymovements-toy-datas… … … … 3 1 "aeye-lab-pymovements-toy-datas… 3 2 "aeye-lab-pymovements-toy-datas… 3 3 "aeye-lab-pymovements-toy-datas… 3 4 "aeye-lab-pymovements-toy-datas… 3 5 "aeye-lab-pymovements-toy-datas…
-
-
list (20 items)
-
Gaze
-
DataFrame (7 columns, 17223 rows)shape: (17_223, 7)
time stimuli_x stimuli_y text_id page_id pixel position i64 f64 f64 i64 i64 list[f64] list[f64] 1988145 -1.0 -1.0 0 1 [206.8, 152.4] [-10.697598, -8.852399] 1988146 -1.0 -1.0 0 1 [206.9, 152.1] [-10.695183, -8.859678] 1988147 -1.0 -1.0 0 1 [207.0, 151.8] [-10.692768, -8.866956] 1988148 -1.0 -1.0 0 1 [207.1, 151.7] [-10.690352, -8.869381] 1988149 -1.0 -1.0 0 1 [207.0, 151.5] [-10.692768, -8.874233] … … … … … … … 2005363 -1.0 -1.0 0 1 [361.0, 415.4] [-6.932438, -2.386672] 2005364 -1.0 -1.0 0 1 [358.0, 414.5] [-7.006376, -2.408998] 2005365 -1.0 -1.0 0 1 [355.8, 413.8] [-7.060582, -2.426362] 2005366 -1.0 -1.0 0 1 [353.1, 413.2] [-7.12709, -2.441245] 2005367 -1.0 -1.0 0 1 [351.2, 412.9] [-7.173881, -2.448686] -
EventsEvents
-
DataFrame (6 columns, 0 rows)shape: (0, 6)
text_id page_id name onset offset duration i64 i64 str i64 i64 i64 -
list (2 items)
- 'text_id'
- 'page_id'
-
-
list (2 items)
- 'text_id'
- 'page_id'
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
-
Gaze
-
DataFrame (7 columns, 29799 rows)shape: (29_799, 7)
time stimuli_x stimuli_y text_id page_id pixel position i64 f64 f64 i64 i64 list[f64] list[f64] 2008305 -1.0 -1.0 0 2 [141.4, 153.6] [-12.268583, -8.823284] 2008306 -1.0 -1.0 0 2 [141.1, 153.2] [-12.275749, -8.832989] 2008307 -1.0 -1.0 0 2 [140.7, 152.8] [-12.285302, -8.842695] 2008308 -1.0 -1.0 0 2 [140.6, 152.7] [-12.28769, -8.845121] 2008309 -1.0 -1.0 0 2 [140.5, 152.6] [-12.290078, -8.847547] … … … … … … … 2038099 -1.0 -1.0 0 2 [273.8, 773.8] [-9.071149, 6.490168] 2038100 -1.0 -1.0 0 2 [273.8, 774.1] [-9.071149, 6.497527] 2038101 -1.0 -1.0 0 2 [273.9, 774.5] [-9.06871, 6.50734] 2038102 -1.0 -1.0 0 2 [274.0, 774.4] [-9.066271, 6.504886] 2038103 -1.0 -1.0 0 2 [274.0, 773.9] [-9.066271, 6.492621] -
EventsEvents
-
DataFrame (6 columns, 0 rows)shape: (0, 6)
text_id page_id name onset offset duration i64 i64 str i64 i64 i64 -
list (2 items)
- 'text_id'
- 'page_id'
-
-
list (2 items)
- 'text_id'
- 'page_id'
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
-
- (18 more)
-
Gaze
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
DatasetPathsDatasetPaths
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
PosixPath('data/ToyDataset/downloads')PosixPath('data/ToyDataset/downloads')
-
PosixPath('data/ToyDataset/events')PosixPath('data/ToyDataset/events')
-
PosixPath('data/ToyDataset/precomputed_events')PosixPath('data/ToyDataset/precomputed_events')
-
PosixPathPosixPath('data/ToyDataset/precomputed_reading_measures')
-
PosixPath('data/ToyDataset/preprocessed')PosixPath('data/ToyDataset/preprocessed')
-
PosixPath('data/ToyDataset/raw')PosixPath('data/ToyDataset/raw')
-
PosixPath('data/ToyDataset')PosixPath('data/ToyDataset')
-
-
list (0 items)
-
list (0 items)
We now have a position
column available:
[4]:
dataset.gaze[5]
[4]:
-
DataFrame (7 columns, 23054 rows)shape: (23_054, 7)
time stimuli_x stimuli_y text_id page_id pixel position i64 f64 f64 i64 i64 list[f64] list[f64] 2415266 -1.0 -1.0 1 1 [176.8, 140.2] [-11.420403, -9.148145] 2415267 -1.0 -1.0 1 1 [176.7, 139.8] [-11.422806, -9.157834] 2415268 -1.0 -1.0 1 1 [176.7, 139.3] [-11.422806, -9.169943] 2415269 -1.0 -1.0 1 1 [176.6, 139.3] [-11.42521, -9.169943] 2415270 -1.0 -1.0 1 1 [176.7, 139.3] [-11.422806, -9.169943] … … … … … … … 2438315 -1.0 -1.0 1 1 [649.9, 633.9] [0.260146, 3.038748] 2438316 -1.0 -1.0 1 1 [650.1, 633.7] [0.265149, 3.033792] 2438317 -1.0 -1.0 1 1 [650.2, 633.5] [0.26765, 3.028836] 2438318 -1.0 -1.0 1 1 [650.0, 633.2] [0.262648, 3.021402] 2438319 -1.0 -1.0 1 1 [649.7, 633.1] [0.255144, 3.018924] -
EventsEvents
-
DataFrame (6 columns, 0 rows)shape: (0, 6)
text_id page_id name onset offset duration i64 i64 str i64 i64 i64 -
list (2 items)
- 'text_id'
- 'page_id'
-
-
list (2 items)
- 'text_id'
- 'page_id'
-
ExperimentExperiment
-
EyeTrackerEyeTracker
-
NoneNone
-
NoneNone
-
NoneNone
-
NoneNone
-
10001000
-
NoneNone
-
NoneNone
-
-
10001000
-
ScreenScreen
-
6868
-
30.230.2
-
10241024
-
'upper left''upper left'
-
3838
-
12801280
-
15.59938648778295315.599386487782953
-
-15.599386487782953-15.599386487782953
-
12.50804441088254612.508044410882546
-
-12.508044410882546-12.508044410882546
-
-
Creating a Heatmap#
Let’s create a heatmap using the heatmap
function from the pymovements
library. We will use the default gridsize
of 10x10 with interpolation and display the colorbar.
[5]:
figure = pm.plotting.heatmap(
gaze=dataset.gaze[5],
position_column='pixel',
origin='upper',
show_cbar=True,
cbar_label='Time [s]',
title='Gaze Heatmap with Interpolation On',
xlabel='X [pix]',
ylabel='Y [pix]',
gridsize=[10, 10]
)

To better understand the effect of the gridsize
parameter on the heatmap, we can turn off the interpolation. By doing this, we can clearly visualize the individual bins used to calculate the heatmap. With interpolation turned off, the heatmap will display the raw bin values rather than a smoothed representation.
[6]:
figure = pm.plotting.heatmap(
dataset.gaze[5],
position_column='pixel',
origin='upper',
show_cbar=True,
cbar_label='Time [s]',
title='Gaze Heatmap with Interpolation Off',
xlabel='X [pix]',
ylabel='Y [pix]',
gridsize=[10, 10],
interpolation='none'
)

Increasing the gridsize
parameter results in a finer grid and more detailed heatmap representation. With a higher grid size, we divide the plot into smaller bins, which can capture more nuances in the data distribution
[7]:
figure = pm.plotting.heatmap(
dataset.gaze[5],
position_column='pixel',
origin='upper',
show_cbar=True,
cbar_label='Time [s]',
title='Gaze Heatmap with Higher Grid Size',
xlabel='X [pix]',
ylabel='Y [pix]',
gridsize=[25, 25]
)
