PIPR preprocessing pipelineΒΆ

What follows is an example pipeline for preprocessing PIPR data with tools from pyplr.utils and pyplr.preproc. The data were collected from three subjects using this protocol.

[15]:
import os.path as op

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set_context('notebook', font_scale=1.2)

from pyplr import graphing, utils, preproc
from pyplr.plr import PLR

# Some useful constants
SAMPLE_RATE = 120
DURATION = 7800
ONSET_IDX = 600

# Columns to load
use_cols = ['confidence',
            'method',
            'pupil_timestamp',
            'eye_id',
            'diameter_3d',
            'diameter']

# Pupil Labs recording directories / exports
subjects = {
    '001': [r'/Users/jtm/Projects/PyPlr/examples/PIPR/data/sub001', '000'],
    '002': [r'/Users/jtm/Projects/PyPlr/examples/PIPR/data/sub002', '001'],
    '003': [r'/Users/jtm/Projects/PyPlr/examples/PIPR/data/sub003', '000']
}

# Empty DataFrame to store processed PIPR data
df = pd.DataFrame()

# Loop over subjects
for k in subjects.keys():
    # Get a handle on a subject
    rec = subjects[k][0]
    export = subjects[k][1]
    s = utils.new_subject(
        rec, export=export, out_dir_nm='pyplr_analysis')

    # Load pupil data
    samples = utils.load_pupil(
        s['data_dir'], eye_id='best', method='3d', cols=use_cols)

    # Pupil columns to analyse
    pupil_cols = ['diameter_3d', 'diameter']

    # Make figure for processing
    f, axs = graphing.pupil_preprocessing(nrows=5, subject=k)

    # Plot the raw data
    samples[pupil_cols].plot(title='Raw', ax=axs[0], legend=True)
    axs[0].legend(loc='center right', labels=['mm', 'pixels'])

    # Mask first derivative threshold
    samples = preproc.mask_pupil_first_derivative(
        samples, threshold=3.0, mask_cols=pupil_cols)
    samples[pupil_cols].plot(
        title='Masked 1st deriv (3*SD)', ax=axs[1], legend=False)

    # Mask confidence threshold
    samples = preproc.mask_pupil_confidence(
        samples, threshold=0.99, mask_cols=pupil_cols)
    samples[pupil_cols].plot(
        title='Masked confidence (<0.99)', ax=axs[2], legend=False)

    # Interpolate
    samples = preproc.interpolate_pupil(
        samples, interp_cols=pupil_cols)
    samples[pupil_cols].plot(
        title='Linear interpolation', ax=axs[3], legend=False)

    # Smooth with Butterworth filter
    samples = preproc.butterworth_series(
        samples, fields=pupil_cols, filt_order=3,
        cutoff_freq=4/(SAMPLE_RATE/2))
    samples[pupil_cols].plot(
        title='3rd order Butterworth filter with 4 Hz cut-off',
        ax=axs[4], legend=False)

    # Load events
    events = utils.load_annotations(s['data_dir'])

    # Extract the event ranges
    ranges = utils.extract(
        samples,
        events,
        offset=-ONSET_IDX,
        duration=DURATION,
        borrow_attributes=['color'])

    # Calculate baselines
    baselines = ranges.loc[:, range(0, ONSET_IDX), :].mean(level=0)

    # New columns for percent signal change
    ranges = preproc.percent_signal_change(
        ranges, baselines, pupil_cols)

    # Add to DataFrame
    ranges['Subject'] = k
    df = df.append(ranges)

    # Convert samples index-level to time (s)
    new_onset = (ranges.index.get_level_values('onset')
                       .unique()
                 - ONSET_IDX) / SAMPLE_RATE
    ranges.index = ranges.index.set_levels(
        levels=new_onset, level='onset')
    ranges.to_csv(op.join(s['out_dir'], 'ranges.csv'))

    # Plot PIPRs
    fig, ax = plt.subplots(figsize=(6,4))
    for r in range(6):
        c = ranges.loc[r, 'color'][0]
        ranges.loc[r, 'diameter_pc'].plot(
            color=c, lw='.1', ax=ax, legend=False)

    # Now show the means
    avgs = (ranges.reset_index()
                  .groupby(['color','onset'], as_index=False)
                  .mean())
    sns.lineplot(data=avgs, x='onset', y='diameter_pc', hue='color',
                 palette={'blue':'b','red':'r'}, legend=False)

    # Tweak figures
    ax.axvspan(0, 1, color='k', alpha=.1)
    ax.axhline(0, 0, 1, color='k', ls='--')
    ax.set_xlabel('Time (s)')
    ax.set_ylabel('Pupil diameter \n(%-change from baseline)')
    ax.set_title('Subject = {}'.format(s['id']))

    # Save
    fig.savefig('../img/PIPR_{}.svg'.format(s['id']))

# Save Processed PIPR data
df.to_csv('processed_PIPR.csv')
************************************************************
************************** sub001 **************************
************************************************************
Loaded 94406 samples
Loaded 6 events
Extracted ranges for 6 events
************************************************************
************************** sub002 **************************
************************************************************
Loaded 95307 samples
Loaded 6 events
Extracted ranges for 6 events
************************************************************
************************** sub003 **************************
************************************************************
Loaded 117246 samples
Loaded 6 events
Extracted ranges for 6 events
_images/06a_pipr_analysis_pipeline_1_1.png
_images/06a_pipr_analysis_pipeline_1_2.png
_images/06a_pipr_analysis_pipeline_1_3.png
_images/06a_pipr_analysis_pipeline_1_4.png
_images/06a_pipr_analysis_pipeline_1_5.png
_images/06a_pipr_analysis_pipeline_1_6.png