Analyze Eye Tracker Data
Contents
Analyze Eye Tracker Data#
Preliminaries#
For parsing and converting the pupilometry data, we will use the recent fork of python_eyelinkparser
module.
The dataset comes from [Mat18] and represents the eye response to a working-memory task on digits, following the classical work [KB66]. The original data was captured using EyeLink 1000
by SR Research
.
# install the fixed repo (fork)
%pip install --upgrade setuptools
%pip install git+https://github.com/maciejskorski/python-eyelinkparser.git
# download and convert the data
import pkg_resources
edf2asc_path = pkg_resources.resource_filename('python_eyelinkparser', "utils/edf2ascii")
!mkdir data
!wget -O data/awm.edf https://github.com/smathot/pupillometry_review/blob/master/data/auditory%20working%20memory/data/awm.edf?raw=true
!{edf2asc_path} data/awm.edf -Y
!rm data/awm.edf
Requirement already satisfied: setuptools in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (63.4.2)
Collecting git+https://github.com/maciejskorski/python-eyelinkparser.git
Cloning https://github.com/maciejskorski/python-eyelinkparser.git to /tmp/pip-req-build-ilp9dn_6
Running command git clone --filter=blob:none --quiet https://github.com/maciejskorski/python-eyelinkparser.git /tmp/pip-req-build-ilp9dn_6
Resolved https://github.com/maciejskorski/python-eyelinkparser.git to commit b0829b7a4d9a0d294f982516ec1143a88a8670db
Installing build dependencies ... ?25ldone
?25h Getting requirements to build wheel ... ?25ldone
?25h Installing backend dependencies ... ?25ldone
?25h Preparing metadata (pyproject.toml) ... ?25ldone
?25hRequirement already satisfied: numpy>=1.18 in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from python-eyelinkparser==0.18.0) (1.23.1)
Requirement already satisfied: prettytable>=3.0 in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from python-eyelinkparser==0.18.0) (3.3.0)
Requirement already satisfied: scipy>=1.7 in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from python-eyelinkparser==0.18.0) (1.9.0)
Requirement already satisfied: python-datamatrix>=0.13 in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from python-eyelinkparser==0.18.0) (0.14.3)
Requirement already satisfied: fastnumbers>=3.0 in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from python-eyelinkparser==0.18.0) (3.2.1)
Requirement already satisfied: wcwidth in /home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages (from prettytable>=3.0->python-eyelinkparser==0.18.0) (0.2.5)
--2022-08-07 20:14:08-- https://github.com/smathot/pupillometry_review/blob/master/data/auditory%20working%20memory/data/awm.edf?raw=true
Resolving github.com (github.com)... 140.82.121.4
Connecting to github.com (github.com)|140.82.121.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://github.com/smathot/pupillometry_review/raw/master/data/auditory%20working%20memory/data/awm.edf [following]
--2022-08-07 20:14:08-- https://github.com/smathot/pupillometry_review/raw/master/data/auditory%20working%20memory/data/awm.edf
Reusing existing connection to github.com:443.
HTTP request sent, awaiting response... 302 Found
Location: https://raw.githubusercontent.com/smathot/pupillometry_review/master/data/auditory%20working%20memory/data/awm.edf [following]
--2022-08-07 20:14:08-- https://raw.githubusercontent.com/smathot/pupillometry_review/master/data/auditory%20working%20memory/data/awm.edf
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.110.133, 185.199.111.133, 185.199.108.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.110.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 17685109 (17M) [application/octet-stream]
Saving to: ‘data/awm.edf’
data/awm.edf 100%[===================>] 16.87M 21.5MB/s in 0.8s
2022-08-07 20:14:10 (21.5 MB/s) - ‘data/awm.edf’ saved [17685109/17685109]
EDF2ASC: EyeLink EDF file -> ASCII (text) file translator
EDF2ASC version 4.2.1.0 Linux standalone Jun 18 2021
(c)1995-2021 by SR Research, last modified Jun 18 2021
processing file data/awm.edf
loadEvents = 1
=========================Preamble of file data/awm.edf=========================
| DATE: Wed Jan 24 05:57:43 2018 |
| TYPE: EDF_FILE BINARY EVENT SAMPLE TAGGED |
| VERSION: EYELINK II 1 |
| SOURCE: EYELINK CL |
| EYELINK II CL v4.56 Aug 18 2010 |
| CAMERA: EyeLink CL Version 1.4 Sensor=BGE |
| SERIAL NUMBER: CL1-ABE24 |
| CAMERA_CONFIG: ABE24140.SCD |
===============================================================================
Converted successfully: 15156 events, 1114647 samples, 75 blocks.
rm: cannot remove 'awm.edf': No such file or directory
Parse into Tabular Format#
The converted data comes in the asc
format which is essentially semi-structured text.
To parse it, we use the package functions: interpolation for blinks is enabled, and the recording is downsampled by the factor of 10, from 1000 to 100Hz.
from datamatrix import (
operations as ops,
functional as fnc,
series as srs
)
from python_eyelinkparser.eyelinkparser import parse, defaulttraceprocessor
def get_data():
# The heavy lifting is done by eyelinkparser.parse()
dm = parse(
folder='data', # Folder with .asc files
traceprocessor=defaulttraceprocessor(
blinkreconstruct=True, # Interpolate pupil size during blinks
downsample=10, # Reduce sampling rate to 100 Hz,
mode='advanced' # Use the new 'advanced' algorithm
)
)
# To save memory, we keep only a subset of relevant columns.
dm = dm[dm.set_size, dm.correct, dm.ptrace_sounds, dm.ptrace_retention,
dm.fixxlist_retention, dm.fixylist_retention]
return dm
dm = get_data()
print(dm)
..
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/python_eyelinkparser/eyelinkparser/_events.py:166: UserWarning: Unexpected exception during parsing of list index out of range
warnings.warn(
.........
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/datamatrix/series.py:1476: RuntimeWarning: Mean of empty slice
return fnc(a.reshape(-1, by), axis=1)
................................................................
EDF2ASC: EyeLink EDF file -> ASCII (text) file translator
EDF2ASC version 4.2.1.0 Linux standalone Jun 18 2021
(c)1995-2021 by SR Research, last modified Jun 18 2021
processing file data/awm.edf
loadEvents = 1
=========================Preamble of file data/awm.edf=========================
| DATE: Wed Jan 24 05:57:43 2018 |
| TYPE: EDF_FILE BINARY EVENT SAMPLE TAGGED |
| VERSION: EYELINK II 1 |
| SOURCE: EYELINK CL |
| EYELINK II CL v4.56 Aug 18 2010 |
| CAMERA: EyeLink CL Version 1.4 Sensor=BGE |
| SERIAL NUMBER: CL1-ABE24 |
| CAMERA_CONFIG: ABE24140.SCD |
===============================================================================
Converted successfully: 15156 events, 1114647 samples, 75 blocks.
/tmp/tmpawm.edf.asc
..
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/python_eyelinkparser/eyelinkparser/_events.py:166: UserWarning: Unexpected exception during parsing of list index out of range
warnings.warn(
.........
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/datamatrix/series.py:1476: RuntimeWarning: Mean of empty slice
return fnc(a.reshape(-1, by), axis=1)
................................................................+----+---------+---------------------------+---------------------------+-----------------------------------+-----------------------------------+----------+
| # | correct | fixxlist_retention | fixylist_retention | ptrace_retention | ptrace_sounds | set_size |
+----+---------+---------------------------+---------------------------+-----------------------------------+-----------------------------------+----------+
| 0 | 1 | [525.9 527.1 ... nan nan] | [382.9 380.4 ... nan nan] | [1810. 1810.3 ... 1902.9 1908.2] | [1655.6 1657.1 ... nan nan] | 5 |
| 1 | 1 | [528.9 526.8 ... nan nan] | [386.4 387.5 ... nan nan] | [1644.6 1648.7 ... 1675.4 1673.4] | [1717.3 1718.5 ... nan nan] | 3 |
| 2 | 1 | [521.4 524.2 ... nan nan] | [377.3 371.4 ... nan nan] | [1787. 1791.4 ... 1719.9 1719.3] | [1471.8 1470.4 ... 1783.2 1785.3] | 7 |
| 3 | 1 | [523.2 520.7 ... nan nan] | [382.6 390.7 ... nan nan] | [1773.5 1774.8 ... 1755.7 1752.5] | [1857.7 1859.1 ... nan nan] | 3 |
| 4 | 1 | [522.1 530.1 ... nan nan] | [373.5 378.1 ... nan nan] | [1939.2 1939.6 ... 1801.3 1802.7] | [1847.2 1849.6 ... 1940. 1938.7] | 7 |
| 5 | 1 | [521. 515.6 ... nan nan] | [379.1 386. ... nan nan] | [1867.4 1867.9 ... 1765.6 1768.4] | [1615.9 1619.3 ... 1872.6 1870.6] | 7 |
| 6 | 1 | [520.8 524.2 ... nan nan] | [387.5 380. ... nan nan] | [1675.2 1674.1 ... 1585. 1581.4] | [1826.5 1821.8 ... nan nan] | 5 |
| 7 | 1 | [527.5 531.6 ... nan nan] | [396. 400.9 ... nan nan] | [1766.5 1766.7 ... 1685.7 1682.2] | [1728.6 1726.7 ... nan nan] | 3 |
| 8 | 1 | [518.4 523. ... nan nan] | [391.8 391.1 ... nan nan] | [1733.3 1736.7 ... 1733.2 1730.2] | [1636. 1637.6 ... 1730.3 1729.3] | 7 |
| 9 | 0 | [528.2 523.1 ... nan nan] | [382.7 386.4 ... nan nan] | [1692. 1692.8 ... 1655.5 nan] | [1533. 1534.5 ... 1688. 1693.6] | 7 |
| 10 | 1 | [528.4 534.3 ... nan nan] | [388.3 384. ... nan nan] | [1602.2 1601.3 ... 1574.1 1575.9] | [1789.2 1793.1 ... 1599.6 1602.1] | 7 |
| 11 | 0 | [525. 523.6 ... nan nan] | [386.7 390.2 ... nan nan] | [1742.5 1740.1 ... 1567.1 1559.5] | [1696.8 1700.6 ... 1747.5 1746.8] | 7 |
| 12 | 1 | [503.1 505.1 ... nan nan] | [376.2 377.4 ... nan nan] | [1668.9 1668.8 ... 1556.1 nan] | [1667.6 1667.6 ... nan nan] | 5 |
| 13 | 1 | [517.2 508.3 ... nan nan] | [387.1 384.7 ... nan nan] | [1609.5 1602.3 ... 1590.9 1584.3] | [1479.8 1477.9 ... 1614.1 1609.2] | 7 |
| 14 | 1 | [507.6 505.3 ... nan nan] | [388.4 386.5 ... nan nan] | [1475.4 1473.7 ... 1484.6 1490.7] | [1481.9 1477.6 ... nan nan] | 3 |
| 15 | 1 | [508.6 507.2 ... nan nan] | [387. 389. ... nan nan] | [1494.8 1494.2 ... 1522.9 1522.1] | [1522.4 1518.7 ... 1496.1 1495.4] | 7 |
| 16 | 1 | [498.6 510.1 ... nan nan] | [386.1 399.2 ... nan nan] | [1550.4 1550.3 ... 1390.8 1385.9] | [1651.6 1649.1 ... nan nan] | 3 |
| 17 | 1 | [510.2 508.1 ... nan nan] | [387. 382.6 ... nan nan] | [1557.6 1559. ... 1532.2 1532. ] | [1537.8 1534. ... nan nan] | 3 |
| 18 | 1 | [521.7 519.8 ... nan nan] | [382.8 385.4 ... nan nan] | [1523.1 1521.9 ... 1409.5 1409.6] | [1471.8 1472.6 ... nan nan] | 5 |
| 19 | 1 | [517.4 518.2 ... nan nan] | [386.5 384.5 ... nan nan] | [1500.1 1502. ... 1500.5 1498.4] | [1530.4 1531. ... nan nan] | 5 |
+----+---------+---------------------------+---------------------------+-----------------------------------+-----------------------------------+----------+
(+ 130 rows not shown)
Visualize#
The baseline correction is applied, with first 2 samples as the baseline. The time is clipped at 1200 which corresponds to 12s (1000Hz in the original recording was downsampled 10x).
dm.pupil = srs.concatenate(
srs.endlock(dm.ptrace_sounds),
dm.ptrace_retention
)
dm.pupil = srs.baseline(
series=dm.pupil,
baseline=dm.ptrace_sounds,
bl_start=0,
bl_end=2
)
dm.pupil.depth = 1200
import numpy as np
from matplotlib import pyplot as plt
def plot_series(x, s, color, label):
se = s.std / np.sqrt(len(s))
plt.fill_between(x, s.mean-se, s.mean+se, color=color, alpha=.25)
plt.plot(x, s.mean, color=color, label=label)
x = np.linspace(-7, 5, 1200)
dm3, dm5, dm7 = ops.split(dm.set_size, 3, 5, 7)
plt.figure()
plt.xlim(-7, 5)
plt.ylim(-150, 150)
plt.axvline(0, linestyle=':', color='black')
plt.axhline(1, linestyle=':', color='black')
plot_series(x, dm3.pupil, color='green', label='3 (N=%d)' % len(dm3))
plot_series(x, dm5.pupil, color='blue', label='5 (N=%d)' % len(dm5))
plot_series(x, dm7.pupil, color='red', label='7 (N=%d)' % len(dm7))
plt.ylabel('Pupil size (norm)')
plt.xlabel('Time relative to onset retention interval (s)')
plt.legend(frameon=False, title='Memory load')
plt.show()
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/numpy/lib/nanfunctions.py:1878: RuntimeWarning: Degrees of freedom <= 0 for slice.
var = nanvar(a, axis=axis, dtype=dtype, out=out, ddof=ddof,
/home/ubuntu/projects/eye-processing/.venv/lib/python3.10/site-packages/datamatrix/_datamatrix/_seriescolumn.py:130: RuntimeWarning: Mean of empty slice
return nanmean(self._seq, axis=0)

References#
- KB66
Daniel Kahneman and Jackson Beatty. Pupil Diameter and Load on Memory. Science, 154(3756):1583–1585, December 1966. URL: https://www.science.org/doi/10.1126/science.154.3756.1583 (visited on 2022-08-07), doi:10.1126/science.154.3756.1583.
- Mat18
Sebastiaan Mathôt. Pupillometry: Psychology, Physiology, and Function. Journal of Cognition, 1(1):16, February 2018. URL: http://www.journalofcognition.org/articles/10.5334/joc.18/ (visited on 2022-07-30), doi:10.5334/joc.18.
- MV22
Sebastiaan Mathôt and Ana Vilotijević. Methods in Cognitive Pupillometry: Design, Preprocessing, and Statistical Analysis. preprint, Animal Behavior and Cognition, February 2022. URL: http://biorxiv.org/lookup/doi/10.1101/2022.02.23.481628 (visited on 2022-07-12), doi:10.1101/2022.02.23.481628.