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)
../_images/70367a12c012faa636f27ab4f295a232b60adb3e07adf7fa1408fdbf7f68f172.png

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.