This page was generated from a Jupyter notebook. View on GitHub Binder badge

Plotting pupillometric data for exploration#

It is crucial to validate preprocessing steps by visually inspecting the results. Therefore, pypillometry implements several plotting functions that encourage active exploration of the dataset. Most of them are matplotlib-based and are available through the .plot attribute of each data object. For interactive exploration, there is also a separate viewer (see Graphical tools).

[1]:
import pypillometry as pp
from pypillometry import u
import matplotlib.pyplot as plt

d = pp.EyeData.from_file("../../data/test.pd")

Plotting pupil data#

Each EyeData object has a .plot attribute that gives access to the plotting functions. plot_pupil() plots the complete pupil signal into the current figure, together with the events (grey lines):

[2]:
plt.figure(figsize=(15, 5))
d.plot.plot_pupil();
../_images/notebooks_plotting_3_0.png

Often, the signal is too long to fit comfortably into one plot. Therefore, you can specify the range to plot with plot_range=. The range can be given with units, e.g., (4.5, 4.7) * u.min or ("260 sec", "280 sec"). The units of the x-axis are set with units= ("ms", "sec", "min" or "h"):

[3]:
plt.figure(figsize=(15, 5))
plt.subplot(1, 2, 1)
d.plot.plot_pupil(plot_range=(260, 280) * u.sec, units="sec")
plt.subplot(1, 2, 2)
d.plot.plot_pupil(plot_range=(4.5, 4.7) * u.min, units="min");
../_images/notebooks_plotting_5_0.png

Note that the y-axis goes all the way to zero because missing data is coded as 0 in this dataset.

When we detect blinks (see the blinks notebook) , the blinks are masked and highlighted in red:

[4]:
d.pupil.blinks_detect(vel_onset=-15)

plt.figure(figsize=(15, 5))
d.plot.plot_pupil(plot_range=(260, 280) * u.sec);
../_images/notebooks_plotting_7_0.png

Comparing datasets#

To compare two versions of a dataset, for example before and after preprocessing, we can plot both into the same figure. The argument label_prefix distinguishes them in the legend and style sets the line style:

[5]:
d2 = d | pp.pupil.lowpass_filter(cutoff=2)

plt.figure(figsize=(15, 5))
d.plot.plot_pupil(plot_range=(260, 280) * u.sec, label_prefix="raw", show_blinks=False)
d2.plot.plot_pupil(plot_range=(260, 280) * u.sec, label_prefix="lowpass",
                   style={"color": "red"}, show_events=False, show_blinks=False);
../_images/notebooks_plotting_9_0.png

The low-pass filter smears the blinks into the neighbouring signal. This is why blinks should be interpolated before filtering (see the blinks notebook).

Plotting all variables#

plot_pupil() is specific to pupil data. plot_timeseries() plots all variables (pupil, gaze coordinates, and any variable created during processing), one subplot per variable. Here we use an EyeData object which contains both pupil and gaze data:

[6]:
de = pp.get_example_data("rlmw_002_short")
de.pupil.blinks_detect()

de.plot.plot_timeseries(plot_range=(10, 20) * u.sec, units="sec", figsize=(15, 8));
../_images/notebooks_plotting_11_0.png

Objects with gaze data also have gaze-specific plots, e.g., a heatmap or a scanpath:

[7]:
de.plot.plot_heatmap(figsize=(14, 5));
../_images/notebooks_plotting_13_0.png
[8]:
fig, ax = plt.subplots(figsize=(6, 6))
de.plot.plot_scanpath(plot_range=(10, 12) * u.sec, eyes="left", ax=ax);
../_images/notebooks_plotting_14_0.png

plot_pupil_segments()#

It is usually a good idea to browse through the whole recording before and after preprocessing. This can be useful for tweaking preprocessing parameters or for detecting gross artifacts. plot_pupil_segments() produces as many plots as necessary, each displaying a part of the data (argument interv, e.g., 30 seconds).

By default, it returns a list of matplotlib figures that can be inspected:

[9]:
figs = d.plot.plot_pupil_segments(interv=30 * u.sec, units="sec", figsize=(15, 3))
../_images/notebooks_plotting_16_0.png
../_images/notebooks_plotting_16_1.png
../_images/notebooks_plotting_16_2.png
../_images/notebooks_plotting_16_3.png

Instead, the plots can be saved to a (multi-page) PDF file for later inspection:

d.plot.plot_pupil_segments(pdf_file="segments.pdf")

plot_timeseries_segments() does the same for all variables.

Interactive plotting#

For interactive exploration, pp.view(d) opens the data in a separate window in which you can zoom and scroll through the recording, and pp.tweak(d) lets you adjust the parameters of preprocessing steps while seeing the results. See Graphical tools for details. Alternatively, you can switch matplotlib to a non-inline backend (e.g., with %matplotlib qt) to zoom and pan in any of the plots above.