Package: eeg-eyetracking-parser
Python parser for combined EEG and eye-tracking dataCopyright (2022-2024) Hermine Berberyan, Wouter Kruijne, Sebastiaan Mathôt, Ana VilotijevićTable of contentsAboutExampleInstallationDependenciesAssumptionsFunction referenceLicenseAboutA Python module for reading concurrently recorded EEG and eye-tracking data, and parsing this data into convenient objects for further analysis. For this to work, several assumptions need to be met, as described underAssumptions. At present, this module is largely for internal use, and focused on our own recording environment.Key features:Experimental variables (such as conditions) from the eye-tracking data are used as metadata for the EEG analysis.Gaze and pupil data is added as channels to the EEG data.Automated preprocessing of eye-tracking and EEG data.ExampleParse the data.importeeg_eyetracking_parseraseet# eet.read_subject.clear()  # uncomment to clear the cache and reparseraw,events,metadata=eet.read_subject(2)raw.plot()To avoid having to parse the data over and over again,read_subject()uses persistentmemoization, which is a way to store the return values of a function on disk and return them right away on subsequent calls. To clear the memoization cache, either call theread_subject.clear()function or remove the.memoizefolder.Plot the voltage across four occipital electrodes locked to cue onset for three seconds. This is done separately for three different conditions, defined bycue_eccentricity. The functioneet.autoreject_epochs()behaves similarly tomne.Epochs(), except that autorejection is applied and that, likeread_subject(), it uses persistent memoization.importnumpyasnpimportmnefrommatplotlibimportpyplotaspltfromdatamatriximportconvertascnvCUE_TRIGGER=1CHANNELS='O1','O2','Oz','P3','P4'cue_epoch=eet.autoreject_epochs(raw,eet.epoch_trigger(events,CUE_TRIGGER),tmin=-.1,tmax=3,metadata=metadata,picks=CHANNELS)We can convert the metadata, which is aDataFrame, to aDataMatrix, and addcue_epochas a multidimensional columnfromdatamatriximportconvertascnvimporttime_series_testaststdm=cnv.from_pandas(metadata)dm.erp=cnv.from_mne_epochs(cue_epoch)# rows x channel x timedm.mean_erp=dm.erp[:,...]# Average over channels: rows x timetst.plot(dm,dv='mean_erp',hue_factor='cue_eccentricity')Because the regularmne.Epoch()object doesn't play nice with non-data channels, such as pupil size, you need to use theeet.PupilEpochs()class instead. This is class otherwise identical, except that it by default removes trials where baseline pupil size is more than 2 SD from the mean baseline pupil size.pupil_cue_epoch=eet.PupilEpochs(raw,eet.epoch_trigger(events,CUE_TRIGGER),tmin=0,tmax=3,metadata=metadata,baseline=(0,.05))dm.pupil=cnv.from_mne_epochs(pupil_cue_epoch,ch_avg=True)# only 1 channeltst.plot(dm,dv='pupil',hue_factor='cue_eccentricity')Installationpip install eeg_eyetracking_parserDependenciesdatamatrix >= 1.0eyelinkparsermneautorejecth5iobraindecodepython-picardjson_tricksAssumptionsData formatEEG data should be in BrainVision format (.vhdr), recorded at 1000 HzEye-tracking data should be EyeLink format (.edf), recorded monocularly at 1000 HzFile and folder structureFiles should be organized followingBIDS.# Container folder for all data
data/
    # Subject 2
    sub-02/
        # EEG data
        eeg/
            sub-02_task-attentionalbreadth_eeg.eeg
            sub-02_task-attentionalbreadth_eeg.vhdr
            sub-02_task-attentionalbreadth_eeg.vmrk
        # Behavioral data (usually not necessary)
        beh/
            sub-02_task-attentionalbreadth_beh.csv
        # Eye-tracking data
        eyetracking/
            sub-02_task-attentionalbreadth_physio.edfYou can re-organize data files into the above structure automatically with thedata2bidscommand, which is part of this package.Assumptions:all EEG files (.eeg, .vhdr, .vmrk)
are named in a 'Subject-00X-timestamp' format (e.g. Subject-002-[2022.06.12-14.35.46].eeg)eye-tracking files (.edf)
are named in a 'sub_X format' (e.g. sub_2.edf)For example, to re-organize from participants 1, 2, 3, and 4 for a task called 'attentional-breadth', you can run the following command. This assumes that the unorganized files are in a subfolder calleddataand that the re-organized (BIDS-compatible) files are also in this subfolder, i.e. as shown above.data2bids --source-path=data --target-path=data -s=1,2,3,4 -t=attentional-breadthTrigger codesThe start of each trial is indicated by a counter that starts at 128 for the first trial, and wraps around after 255, such that trial 129 is indicated again by 128. This trigger does not need to be sent to the eye tracker, which uses its ownstart_trialmessage. A temporal offset between thestart_trialmessage of the eye tracker and the start-trial trigger of the EEG is ok, and will be compensated for during parsing.EE.PulseLines(128+trialid%128,10)# EE is the EventExchange objectThe onset of each epoch is indicated by a counter that starts at 1 for the first epoch, and then increases for subsequent epochs. In other words, if the target presentation is the second epoch of the trial, then this would correspond to trigger 2 as in the example below. This trigger needs to be sent to both the EEG and the eye tracker at the exact same moment (a temporal offset isnotok).target_trigger=2eyetracker.log(f'start_phase{target_trigger}')# eyetracker is created by PyGazeEE.PulseLines(target_trigger,10)Triggers should only be used for temporal information. Conditions are only logged in the eye-tracking data.Function referenceautoreject_epochs(*args, ar_kwargs=None, **kwargs)A factory function that creates an Epochs() object, applies
autorejection, and then returns it.Important:This function uses persistent memoization, which means that
the results for a given set of arguments are stored on disk and returned
right away for subsequent calls. For more information, seehttps://pydatamatrix.eu/memoization/Parameters*args: iterableArguments passed to mne.Epochs()ar_kwargs: dict or None, optionalKeywords to be passed to AutoReject(). Ifn_interpolateis not
specified, a default value of [1, 4, 8, 16] is used.**kwargs: dictKeywords passed to mne.Epochs()ReturnsEpochs:An mne.Epochs() object with autorejection applied.epoch_trigger(events, trigger)Selects a single epoch trigger from a tuple with event information.
Epoch triggers have values between 1 and 127 (inclusive).Parametersevents: tupleEvent information as returned byread_subject().trigger: intA trigger code, which is a positive value.Returnsarray:A numpy array with events as expected by mne.Epochs().PupilEpochs(*args, baseline_trim=(-2, 2), **kwargs)An Epochs class for the PupilSize channel. This allows baseline
correction to be applied to pupil size, even though this channel is not a
regular data channel. In addition, this class allows pupil sizes to be
excluded based on deviant baseline values, which is recommended for
pupil analysis (but not typically done for eeg).Parameters*args: iterableArguments passed to mne.Epochs()baseline_trim: tuple of int, optionalThe range of acceptable baseline values. This refers to z-scores.**kwargs: dictKeywords passed to mne.Epochs()ReturnsEpochs:An mne.Epochs() object with autorejection applied.read_subject(subject_nr, folder='data/', trigger_parser=None, eeg_margin=30, min_sacc_dur=10, min_sacc_size=100, min_blink_dur=10, blink_annotation='BLINK', saccade_annotation='SACCADE', eeg_preprocessing=True, save_preprocessing_output=True, plot_preprocessing=False, eye_kwargs={}, downsample_data_kwargs={}, drop_unused_channels_kwargs={}, rereference_channels_kwargs={}, create_eog_channels_kwargs={}, set_montage_kwargs={}, annotate_emg_kwargs={}, band_pass_filter_kwargs={}, autodetect_bad_channels_kwargs={}, run_ica_kwargs={}, auto_select_ica_kwargs={}, interpolate_bads_kwargs={})Reads EEG, eye-tracking, and behavioral data for a single participant.
This data should be organized according to the BIDS specification.EEG data is assumed to be in BrainVision data format (.vhdr,.vmrk,.eeg). Eye-tracking data is assumed to be in EyeLink data format (.edfor.asc). Behavioral data is assumed to be in.csvformat.Metadata is taken from the behavioral.csvfile if present, and from
the eye-tracking data if not.Important:This function uses persistent memoization, which means that
the results for a given set of arguments are stored on disk and returned
right away for subsequent calls. For more information, seehttps://pydatamatrix.eu/memoization/Parameterssubject_nr: int or srThe subject number to parse. If an int is passed, the subject number
is assumed to be zero-padded to length two (e.g. '01'). If a string
is passed, the string is used directly.folder: str, optionalThe folder in which the data is stored.trigger_parser: callable, optionalA function that converts annotations to events. If no function is
specified, triggers are assumed to be encoded by the OpenVibe
acquisition software and to follow the convention for indicating
trial numbers and event onsets as described in the readme.eeg_margin: int, optionalThe number of seconds after the last trigger to keep. The rest of the
data will be cropped to save memory (in case long periods of extraneous
data were recorded).min_sacc_dur: int, optionalThe minimum duration of a saccade before it is annotated as a
BAD_SACCADE.min_sacc_size: int, optionalThe minimum size of a saccade (in pixels) before it is annotated as a
saccade.min_blink_dur: int, optionalThe minimum duration of a blink before it is annotated as a blink.blink_annotation: str, optionalThe annotation label to be used for blinks. Use a BAD_ suffix to
use blinks a bads annotations.saccade_annotation: str, optionalThe annotation label to be used for saccades. Use a BAD_ suffix to
use saccades a bads annotations.eeg_preprocessing: bool or list, optionalIndicates whether EEG preprocessing should be performed. IfTrue,
then all preprocessing steps are performed. If a list is passed, then
only those steps are performed for which the corresponding function
name is in the list (e.g.['downsample_data', 'set_montage'])save_preprocessing_output: bool, optionalIndicates whether output generated during EEG preprocessing should be
saved.plot_preprocessing: bool, optionalIndicates whether plots should be shown during EEG preprocessing.eye_kwargs: dict, optionalOptional keyword arguments to be passed onto the EyeLink parser. If
traceprocessor is provided, a default traceprocessor is used with
advanced blink reconstruction enabled and 10x downsampling.downsample_data_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.drop_unused_channels_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.rereference_channels_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.create_eog_channels_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.set_montage_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.annotate_emg_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.band_pass_filter_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.autodetect_bad_channels_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.run_ica_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.auto_select_ica_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.interpolate_bads_kwargs: dict, optionalPassed as keyword arguments to corresponding preprocessing function.Returnstuple:A raw (EEG data), events (EEG triggers), metadata (a table with
experimental variables) tuple.trial_trigger(events)Selects all trial triggers from event information. Trial triggers have
values between 128 and 255 (inclusive).Parametersevents: tupleEvent information as returned byread_subject().Returnsarray:A numpy array with events as expected by mne.Epochs().braindecode_utils.decode_subject(read_subject_kwargs, factors, epochs_kwargs, trigger, epochs_query='practice == "no"', epochs=4, window_size=200, window_stride=1, n_fold=4, crossdecode_read_subject_kwargs=None, crossdecode_factors=None, patch_data_func=None, read_subject_func=None, cuda=True, balance=True)The main entry point for decoding a subject's data.Parametersread_subject_kwargs: dictA dict with keyword arguments that are passed to eet.read_subject() to
load the data. Additional preprocessing as specified inpreprocess_raw()is applied afterwards.factors: str or list of strA factor or list of factors that should be decoded. Factors should be
str and match column names in the metadata.epochs_kwargs: dict, optionalA dict with keyword arguments that are passed to mne.Epochs() to
extract the to-be-decoded epoch.trigger: intThe trigger code that defines the to-be-decoded epoch.epochs_query: str, optionalA pandas-style query to select trials from the to-be-decoded epoch. The
default assumes that there is apracticecolumn from which we only
want to have the 'no' values, i.e. that we want exclude practice
trials.epochs: int, optionalThe number of training epochs, i.e. the number of times that the data
is fed into the model. This should be at least 2.window_size_samples: int, optionalThe length of the window to sample from the Epochs object. This should
be slightly shorter than the actual Epochs to allow for jittered
samples to be taken from the purpose of 'cropped decoding'.window_stride_samples: int, optionalThe number of samples to jitter around the window for the purpose of
cropped decoding.n_fold: int, optionalThe total number of splits (or folds). This should be at least 2.crossdecode_read_subject_kwargs: dict or None, optionalWhen provided these read_subject_kwargs are passed to read_subject_func
for reading the to-be-decoded test dataset.crossdecode_factors: str or list of str or None, optionalA factor or list of factors that should be decoded during tester. If
provided, the classifier is trained using the factors specified infactorsand tested using the factors specified incrossdecode_factors. In other words, specifying this keyword allow
for crossdecoding.patch_data_func: callable or None, optionalIf provided, this should be a function that accepts a tuple of(raw, events, metadata)as returned byread_subject()and also
returns a tuple of(raw, events, metadata). This function can modify
aspects of the data before decoding is applied.read_subject_func: callable or None, optionalIf provided, this should be a function that accepts keywords as
provided through theread_subject_kwargsargument, and returns a
tuple of(raw, events, metadata). If not provided, the defaultread_subject()function is used.cuda: bool, optionalIf True, cuda will be used for GPU processing if it is available. If
False, cuda won't be used, not even when it is available.balance: bool, optionalMakes sure that a dataset contains an equal number of observations for
each label by randomly duplicating observations from labels that have
too few observations.ReturnsDataMatrixContains the original metadata plus four additional columns:braindecode_labelis a numeric label that corresponds to the
to-be-decoded factor, i.e. the ground truthbraindecode_predictionis the predicted labelbraindecode_correctis 1 for correct predictions and 0 otherwisebraindecode_probabilitiesis a SeriesColumn with the predicted
probabilities for each label. The prediction itself corresponds to
the index with the highest probability.Licenseeeg_eyetracking_parseris licensed under theGNU General Public License
v3.