5. Pipeline#

Thus far, we’ve applied the preprocessing, epoching, and averaging steps to a single participant only. However, we typically study a larger group of participants. This is because (a) data from a single participant is typically quite noisy, and (b) we want to generalize our findings to the population from which our participants were drawn.

Therefore, we need to repeat the analysis for all participants by creating an analysis pipeline that takes each participant’s raw data as an input and performs the same set of processing steps on them.

We will first do this manually (using a loop) and then with a pre-packaged automatic pipeline function.

Goals

  • Repeating the processing steps for all participants

  • Using a fully automated pipeline

5.1. Load Python packages#

The functions for our custom pipeline are again provided by the MNE-Python package (Gramfort et al, 2013) and pandas. We will also use the hu-neuro-pipeline package for the fully automated pipeline, plus two new packages (matplotlib and seaborn) for visualization.

# %pip install mne hu-neuro-pipeline pandas matplotlib seaborn
import matplotlib.pyplot as plt
import pandas as pd
import seaborn as sns
from mne import Epochs, combine_evoked, merge_events, set_bipolar_reference
from mne.io import read_raw
from mne.preprocessing import ICA
from mne.viz import plot_compare_evokeds
from pipeline import group_pipeline
from pipeline.datasets import get_erpcore

5.2. Custom pipeline#

We’ll start by creating a custom pipeline that repeats the preprocessing, epoching, and averaging steps for all participants. This is as easy as taking all the code from the previous chapters and, instead of reading only a single raw EEG file, putting it inside a for loop that iterates over all raw EEG files.

Let’s first download some more datasets (for the sake of time, we’re only using 10 participants instead of all 40):

files_dict = get_erpcore('N170', participants=10, path='data')
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c4eba01090877890aa3' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c50ba0109087e8916aa' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c5286541a08f914ab2d' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//610215220c4cba026dbce4d0' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//610215260c4cba0277bc7a17' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c4be80d3708caa58293' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-001/eeg/sub-001_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c42e80d3708c5a57469' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c5486541a090014bb81' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c5c86541a08f814a3b7' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c5fe80d3708caa582c7' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c61ba01090876890471' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c63e80d3708c1a56934' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102152d0c4cba026dbce4e7' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-002/eeg/sub-002_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//610215310c4cba026abcd4ee' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102153c317620028637d408' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021537317620027d389014' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c7486541a090014bbec' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c72e80d3708c7a58090' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c66e80d3708caa582de' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c6dba0109087e8916f0' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c6fba01090873890087' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102154dc7a976029b9e5bfc' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c88ba0109087a8915d5' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c9086541a08fc14b889' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c92e80d3708c7a580c6' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c9586541a08fc14b894' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c97e80d3708c3a56bdf' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-005/eeg/sub-005_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021551317620027838216b' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102155a317620028737d513' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ca7ba0109087a891613' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021556c7a976029a9e2033' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ca5ba0109087a89160b' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075c9986541a090014bc3c' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ca0ba0109087e891779' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ca2ba0109087a891603' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075caae80d3708c5a574bd' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cb286541a08fc14b8bf' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cb586541a08fc14b8c7' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cb7ba0109087e8917c2' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cb9ba0109087a891642' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102155f317620028737d52a' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-007/eeg/sub-007_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021564c7a976029b9e5c51' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cc986541a090014bcab' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102156ec7a976029b9e5c86' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021568317620028737d553' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ccee80d3708c5a574e1' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cc6ba0109087e8917f2' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cbce80d3708caa583ce' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ccbba0109087a89166f' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ce0ba0109087a89168a' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021577317620027c3848d6' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cd0e80d3708c5a574e9' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cd8ba0109087a891682' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cdbe80d3708caa5842b' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cdee80d3708caa58436' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-009/eeg/sub-009_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//61021572c7a976029b9e5ca8' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_electrodes.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//6102157d317620028737d5b5' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_events.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cefba0109087e89184b' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.set' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075cede80d3708c7a5817f' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_coordsystem.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//610215800c4cba0269bc9aa4' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.fdt' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ce986541a090014bd1b' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_channels.tsv' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ce1e80d3708caa5844e' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
Downloading file 'erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.json' from 'https://files.de-1.osf.io/v1/resources/pfde9/providers/osfstorage//60075ceb86541a08f914ab9e' to '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data'.
files_dict
{'raw_files': ['/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.set'],
 'log_files': ['/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_events.tsv']}

Then we’ll run a for loop with all the processing steps inside it. Before the loop, we create empty lists where we will store the processed outputs (in this case, the evokeds) for all participants.

evokeds_face = []
evokeds_car = []
evokeds_diff = []

for raw_file, log_file in zip(files_dict['raw_files'],
                              files_dict['log_files']):

    # Preprocessing
    raw = read_raw(raw_file, preload=True)
    raw = set_bipolar_reference(raw, anode='FP1', cathode='VEOG_lower',
                                ch_name='VEOG', drop_refs=False)
    raw = set_bipolar_reference(raw, anode='HEOG_right', cathode='HEOG_left',
                                ch_name='HEOG', drop_refs=False)
    raw = raw.set_channel_types({'VEOG': 'eog', 'HEOG': 'eog'})
    raw = raw.drop_channels(['VEOG_lower', 'HEOG_right', 'HEOG_left'])
    raw = raw.set_montage('biosemi64', match_case=False)
    raw = raw.filter(l_freq=0.1, h_freq=30.0)
    raw_copy = raw.copy().filter(l_freq=1.0, h_freq=None, verbose=False)
    ica = ICA(n_components=15)
    ica = ica.fit(raw_copy)
    eog_indices, eog_scores = ica.find_bads_eog(raw, ch_name=['VEOG', 'HEOG'],
                                                verbose=False)
    ica.exclude = eog_indices
    raw = ica.apply(raw)
    raw = raw.set_eeg_reference('average')

    # Epoching
    log = pd.read_csv(log_file, sep='\t')
    events = log[['sample', 'duration', 'value']].values.astype(int)
    events = merge_events(events, ids=range(1, 41), new_id=1)
    events = merge_events(events, ids=range(41, 81), new_id=2)
    event_id = {'face': 1, 'car': 2}
    epochs = Epochs(raw, events, event_id, tmin=-0.2, tmax=0.8,
                    baseline=(-0.2, 0.0), preload=True)
    epochs = epochs.drop_bad({'eeg': 200e-6})

    # Averaging
    evoked_face = epochs['face'].average()
    evokeds_face.append(evoked_face)
    evoked_car = epochs['car'].average()
    evokeds_car.append(evoked_car)
    evoked_list = [evoked_face, evoked_car]
    evoked_diff = combine_evoked(evoked_list, weights=[1, -1])
    evokeds_diff.append(evoked_diff)
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.fdt
Reading 0 ... 699391  =      0.000 ...   682.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=699392
    Range : 0 ... 699391 =      0.000 ...   682.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=699392
    Range : 0 ... 699391 =      0.000 ...   682.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 7.1s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
    Rejecting  epoch based on EEG : ['P8', 'PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'PO7', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'P7', 'PO7', 'Oz', 'CPz', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
9 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.fdt
Reading 0 ... 683007  =      0.000 ...   666.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=683008
    Range : 0 ... 683007 =      0.000 ...   666.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=683008
    Range : 0 ... 683007 =      0.000 ...   666.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 10.1s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.fdt
Reading 0 ... 579583  =      0.000 ...   565.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=579584
    Range : 0 ... 579583 =      0.000 ...   565.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=579584
    Range : 0 ... 579583 =      0.000 ...   565.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 6.0s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
6 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_eeg.fdt
Reading 0 ... 649215  =      0.000 ...   633.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=649216
    Range : 0 ... 649215 =      0.000 ...   633.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=649216
    Range : 0 ... 649215 =      0.000 ...   633.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 5.1s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
    Rejecting  epoch based on EEG : ['O2']
1 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.fdt
Reading 0 ... 603135  =      0.000 ...   588.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=603136
    Range : 0 ... 603135 =      0.000 ...   588.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=603136
    Range : 0 ... 603135 =      0.000 ...   588.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 9.8s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.fdt
Reading 0 ... 529407  =      0.000 ...   516.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=529408
    Range : 0 ... 529407 =      0.000 ...   516.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=529408
    Range : 0 ... 529407 =      0.000 ...   516.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 6.3s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 1 ICA component
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
    Rejecting  epoch based on EEG : ['FP2']
    Rejecting  epoch based on EEG : ['P10']
    Rejecting  epoch based on EEG : ['FP2']
3 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.fdt
Reading 0 ... 589823  =      0.000 ...   575.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=589824
    Range : 0 ... 589823 =      0.000 ...   575.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=589824
    Range : 0 ... 589823 =      0.000 ...   575.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 6.1s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.fdt
Reading 0 ... 804863  =      0.000 ...   785.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=804864
    Range : 0 ... 804863 =      0.000 ...   785.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=804864
    Range : 0 ... 804863 =      0.000 ...   785.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 11.5s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.fdt
Reading 0 ... 559103  =      0.000 ...   545.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=559104
    Range : 0 ... 559103 =      0.000 ...   545.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=559104
    Range : 0 ... 559103 =      0.000 ...   545.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 3.7s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.fdt
Reading 0 ... 556031  =      0.000 ...   542.999 secs...
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=556032
    Range : 0 ... 556031 =      0.000 ...   542.999 secs
Ready.
Added the following bipolar channels:
VEOG
EEG channel type selected for re-referencing
Creating RawArray with float64 data, n_channels=1, n_times=556032
    Range : 0 ... 556031 =      0.000 ...   542.999 secs
Ready.
Added the following bipolar channels:
HEOG
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 30 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 30.00 Hz
- Upper transition bandwidth: 7.50 Hz (-6 dB cutoff frequency: 33.75 Hz)
- Filter length: 33793 samples (33.001 s)

Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 7.7s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 1025 original time points ...
0 bad epochs dropped
0 bad epochs dropped
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.4s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s
[Parallel(n_jobs=1)]: Done  17 tasks      | elapsed:    0.3s

We have collected the averaged ERP (evoked) for both conditions (face and car) from all participants. Averaging one more time, this time not across trials but across participants, gives us the grand average. Let’s do this separately for both condition and display the grand averages as a time course plot:

grand_evoked_face = combine_evoked(evokeds_face, weights='equal')
grand_evoked_face.comment = 'face'

grand_evoked_car = combine_evoked(evokeds_car, weights='equal')
grand_evoked_car.comment = 'car'

grand_evoked_list = [grand_evoked_face, grand_evoked_car]
_ = plot_compare_evokeds(grand_evoked_list, picks='PO8')
../_images/821f2448e526cbf70817fd7dff912912232cdecd625db646b7ab86d1673429da.png

We see that the first negative peak in the ERP (N1/N170 component) is earlier and (and maybe also larger) for faces than for cars.

This becomes even more apparent when grand-averaging and plotting the difference waves:

grand_evoked_diff = combine_evoked(evokeds_diff, weights='equal')
grand_evoked_diff.comment = 'face - car'
_ = grand_evoked_diff.plot(picks='PO8')
Need more than one channel to make topography for eeg. Disabling interactivity.
../_images/a3fbb396d5bd0a3f70c94094b5a48f7e183a17b3556fd70500b1c045f6699f37.png

The corresponding butterfly and scalp topography plot looks like this:

_ = grand_evoked_diff.plot_joint(times=[0.0, 0.15, 0.17])
No projector specified for this dataset. Please consider the method self.add_proj.
../_images/96bf81ee71df6189607351098d97b122c509f3bfded756706d07afbda9726864.png

5.3. Neuro Lab pipeline#

At the Abdel Rahman Lab for Neurocognitive Psychology at HU Berlin (the “Neuro Lab,” for short), we’ve developed a Python package that provides a fully automated EEG processing pipeline. The pipeline was originally developed and published in the MATLAB language by Frömer et al (2018). The more recent Python version is available at https://hu-neuro-pipeline.readthedocs.io.

The pipeline is fully automated in the sense that it takes raw EEG data from multiple participants as an input and performs a number of standardized processing step at the participant and group level with minimal user input. The typical steps are visualized in this flowchart:

Flowchart of the processing steps in the `hu-neuro-pipeline package`

Fig. 5.1 Processing steps in the hu-neuro-pipeline package. Source: Docs.#

The pipeline package only has one main function, called group_pipeline(). Let’s use it to process the same example data as before:

trials, evokeds, config = group_pipeline(raw_files=files_dict['raw_files'],
                                         log_files=files_dict['log_files'],
                                         output_dir='output',
                                         montage='biosemi64',
                                         ica_method='fastica',
                                         ica_n_components=15,
                                         triggers=range(1, 81),
                                         skip_log_conditions={'value': range(81, 203)},
                                         components={'name': 'N170',
                                                     'tmin': 0.11,
                                                     'tmax': 0.15,
                                                     'roi': ['PO8']},
                                         average_by={'face': 'value <= 40',
                                                     'car': 'value > 40'})
=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.fdt
Reading 0 ... 699391  =      0.000 ...   682.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 7.3s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 1 ICA component
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['Oz', 'F8', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['PO7', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['FP2', 'F4', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F7', 'PO8']
    Rejecting  epoch based on EEG : ['PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'P7', 'PO7', 'F8', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['PO8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['C5', 'P7', 'PO7', 'Oz', 'CPz', 'F8', 'P8', 'PO8']
    Rejecting  epoch based on EEG : ['C5', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F4', 'F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['F8']
87 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.fdt
Reading 0 ... 683007  =      0.000 ...   666.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 14.1s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['P3']
1 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.fdt
Reading 0 ... 579583  =      0.000 ...   565.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 14.4s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
    Rejecting  epoch based on EEG : ['FP1']
3 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_eeg.fdt
Reading 0 ... 649215  =      0.000 ...   633.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 9.5s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 1 ICA component
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['F8']
    Rejecting  epoch based on EEG : ['PO7', 'PO8', 'O2']
    Rejecting  epoch based on EEG : ['O2']
    Rejecting  epoch based on EEG : ['F3']
4 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.fdt
Reading 0 ... 603135  =      0.000 ...   588.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 10.2s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
0 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.fdt
Reading 0 ... 529407  =      0.000 ...   516.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 5.6s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 1 ICA component
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['F7', 'FP2']
    Rejecting  epoch based on EEG : ['P10']
    Rejecting  epoch based on EEG : ['F4']
    Rejecting  epoch based on EEG : ['FP2']
4 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.fdt
Reading 0 ... 589823  =      0.000 ...   575.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 10.2s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
0 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.fdt
Reading 0 ... 804863  =      0.000 ...   785.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 17.6s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
0 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.fdt
Reading 0 ... 559103  =      0.000 ...   545.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 5.8s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
0 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).

=== Reading raw data from /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.set ===
Reading /home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.fdt
Reading 0 ... 556031  =      0.000 ...   542.999 secs...
Adding bipolar channel VEOG (FP1 - VEOG_lower)
Adding bipolar channel HEOG (HEOG_left - HEOG_right)
Loading standard montage biosemi64
EEG channel type selected for re-referencing
Applying average reference.
Applying a custom ('EEG',) reference.
Fitting ICA to data using 30 channels (please be patient, this may take a while)
Selecting by number: 15 components
Fitting ICA took 8.5s.
Applying ICA to Raw instance
    Transforming to ICA space (15 components)
    Zeroing out 2 ICA components
    Projecting back using 30 PCA components
Filtering raw data in 1 contiguous segment
Setting up band-pass filter from 0.1 - 40 Hz

FIR filter parameters
---------------------
Designing a one-pass, zero-phase, non-causal bandpass filter:
- Windowed time-domain design (firwin) method
- Hamming window with 0.0194 passband ripple and 53 dB stopband attenuation
- Lower passband edge: 0.10
- Lower transition bandwidth: 0.10 Hz (-6 dB cutoff frequency: 0.05 Hz)
- Upper passband edge: 40.00 Hz
- Upper transition bandwidth: 10.00 Hz (-6 dB cutoff frequency: 45.00 Hz)
- Filter length: 33793 samples (33.001 s)

Not setting metadata
160 matching events found
Applying baseline correction (mode: mean)
0 projection items activated
Using data from preloaded Raw for 160 events and 2049 original time points ...
0 bad epochs dropped
<Epochs |  160 events (all good), -0.5 - 1.49902 s, baseline -0.2 - 0 s, ~87.5 MB, data loaded,
 '1': 2
 '10': 2
 '11': 2
 '12': 2
 '13': 2
 '14': 2
 '15': 2
 '16': 2
 '17': 2
 '18': 2
 and 70 more events ...>
Adding metadata with 6 columns
    Rejecting  epoch based on EEG : ['C5']
    Rejecting  epoch based on EEG : ['F7', 'F8']
    Rejecting  epoch based on EEG : ['F7']
    Rejecting  epoch based on EEG : ['F8']
4 bad epochs dropped
Computing single trial ERP amplitudes for 'N170'
NOTE: pick_types() is a legacy function. New code should use inst.pick(...).


=== Processing group level ===
Identifying common channels ...
Identifying common channels ...
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:103: UserWarning: Converting `ica_n_components` to integer: 15 -> 15
  warn(f'Converting `ica_n_components` to integer: {n_components} -> ' +
/home/runner/micromamba/envs/intro-to-eeg/lib/python3.11/site-packages/pipeline/preprocessing.py:115: FutureWarning: The default for pick_channels will change from ordered=False to ordered=True in 1.5 and this will result in a change of behavior because the resulting channel order will not match. Either use a channel order that matches your instance or pass ordered=False.
  eog_indices, _ = ica.find_bads_eog(

In this example we’ve specified:

  • The input and output file paths (raw_files, log_files, output_dir)

  • Some preprocessing options (montage, ica_method, ica_n_components; note that the pipeline also applies a 0.1–40 Hz band-pass filter by default)

  • The event codes of interest for epoching (triggers; note that the pipeline also applies a default peak-to-peak rejection at 200 µV)

  • Some event codes to skip (skip_log_conditions; anything other than faces and houses)

  • The definition of our ERP component(s) of interest (components; here only the N170)

  • Rules to create by-participant condition averages/evokeds (average_by; here for faces and cars)

A (long) list of these and all other input options is available on the pipeline documentation website.

The pipeline returns three objects (which also get written as text files into the output_dir):

  • trials: A data frame with the single trial data for all participants, also containing the single trial ERP amplitudes (averaged across the time window and channels of interest)

  • evokeds: A data frame with the averaged (evoked) ERP amplitudes for all time points, channels, and participants

  • config: A dictionary with the pipeline configuration

Let’s look of each of these in turn:

5.3.1. Single trial data#

trials
participant_id onset duration sample trial_type stim_file value N170
0 sub-001_task-N170_eeg 15.0498 0.3 15412 stimulus NaN 79 0.298910
1 sub-001_task-N170_eeg 16.5986 0.3 16998 stimulus NaN 73 12.099460
2 sub-001_task-N170_eeg 18.0322 0.3 18466 stimulus NaN 75 1.478766
3 sub-001_task-N170_eeg 21.1641 0.3 21673 stimulus NaN 17 6.740304
4 sub-001_task-N170_eeg 22.7305 0.3 23277 stimulus NaN 32 -1.743442
... ... ... ... ... ... ... ... ...
1595 sub-010_task-N170_eeg 523.1045 0.3 535660 stimulus NaN 17 12.973517
1596 sub-010_task-N170_eeg 526.1035 0.3 538731 stimulus NaN 71 14.055316
1597 sub-010_task-N170_eeg 529.0850 0.3 541784 stimulus NaN 4 -14.064324
1598 sub-010_task-N170_eeg 530.6348 0.3 543371 stimulus NaN 22 -1.718830
1599 sub-010_task-N170_eeg 533.6338 0.3 546442 stimulus NaN 43 4.418730

1600 rows × 8 columns

We could (and will!) use this for statistically analyzing the ERP component(s) of interest. Specifically, we can fit a mixed-effects model that tests if the single trial N170 amplitudes differs as a function of the condition of the trial (face vs. car).

5.3.2. Evokeds#

evokeds
participant_id label query time FP1 F3 F7 FC3 C3 C5 ... Cz C4 C6 P4 P8 P10 PO8 PO4 O2 N170
0 sub-001_task-N170_eeg face value <= 40 -0.500000 5.789564 -0.034750 1.660734 -1.520637 -1.074939 -0.738493 ... -1.048279 -1.561112 -1.006365 -1.609887 1.768214 2.574583 0.807337 -1.772100 1.039566 0.807337
1 sub-001_task-N170_eeg face value <= 40 -0.499023 5.680271 -0.100856 1.595741 -1.594228 -1.011824 -0.663110 ... -1.086347 -1.568407 -1.134829 -1.396554 1.590214 2.462847 0.594162 -1.514759 0.947991 0.594162
2 sub-001_task-N170_eeg face value <= 40 -0.498047 5.492423 -0.201391 1.515443 -1.676766 -0.951158 -0.600500 ... -1.124984 -1.579886 -1.221997 -1.183525 1.420789 2.362887 0.417310 -1.246389 0.868075 0.417310
3 sub-001_task-N170_eeg face value <= 40 -0.497070 5.234354 -0.334659 1.427033 -1.766560 -0.895557 -0.553428 ... -1.163056 -1.595503 -1.272581 -0.980823 1.272416 2.279901 0.288978 -0.977250 0.807886 0.288978
4 sub-001_task-N170_eeg face value <= 40 -0.496094 4.916389 -0.496533 1.336095 -1.860806 -0.847166 -0.524221 ... -1.199010 -1.614431 -1.293715 -0.797415 1.155798 2.217527 0.218337 -0.717113 0.774866 0.218337
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
40955 sub-010_task-N170_eeg car value > 40 1.495117 1.696184 -1.211245 0.285654 -0.857636 -1.820990 -3.490791 ... -0.625288 -0.666407 0.613045 -0.259044 2.407768 3.256570 1.626962 -0.318652 1.387889 1.626962
40956 sub-010_task-N170_eeg car value > 40 1.496094 1.662606 -1.163206 0.330227 -0.782776 -1.747530 -3.317312 ... -0.579745 -0.660078 0.606120 -0.278228 2.331428 3.123536 1.553047 -0.378411 1.313106 1.553047
40957 sub-010_task-N170_eeg car value > 40 1.497070 1.627197 -1.113622 0.386814 -0.702416 -1.660422 -3.107137 ... -0.534325 -0.659212 0.590221 -0.302836 2.249132 2.984847 1.472701 -0.436253 1.230004 1.472701
40958 sub-010_task-N170_eeg car value > 40 1.498047 1.591662 -1.063696 0.452267 -0.617246 -1.561477 -2.868092 ... -0.489355 -0.663735 0.564956 -0.332295 2.161821 2.844188 1.387976 -0.491059 1.140647 1.387976
40959 sub-010_task-N170_eeg car value > 40 1.499023 1.557830 -1.014824 0.523435 -0.528657 -1.453341 -2.609703 ... -0.445078 -0.673118 0.530453 -0.365673 2.070765 2.705437 1.301141 -0.541826 1.047361 1.301141

40960 rows × 35 columns

The evokeds can be used for plotting the ERP time courses for both conditions. For this, we will use the seaborn package, which is great for visualizing tabular data (similar to the ggplot2 package in R).

_ = sns.lineplot(evokeds, x='time', y='PO8', hue='label',
                 estimator='mean', errorbar='se')
../_images/81dd04856651cf3eb287d1f1b2b0b3017c8b9a268162a3409ecb7f06df6ba8c6.png

We can make the plot yet a bit prettier by adding custom x-axis limits, vertical and horizontal lines at zero, and more informative axis labels:

_ = sns.lineplot(evokeds, x='time', y='PO8', hue='label',
                 estimator='mean', errorbar='se')
_ = plt.margins(x=0.0, y=0.1)
_ = plt.xlim(-0.2, 0.8)
_ = plt.axvline(0.0, color='black', linestyle='--')
_ = plt.axhline(0.0, color='black', linestyle='--')
_ = plt.xlabel('Time (s)')
_ = plt.ylabel('PO8 amplitude (µV)')
../_images/91d9d6acc4bd25025a9b2d0ab79872d7ed7f88c6b328ab50d13da4d0652e31b1.png

5.3.3. Pipeline configuration#

Finally, the config output is a dictionary with information about the pipeline run. It contains all the user-specified and default input arguments plus some new information that the pipeline has computed along the way (e.g., 'auto_rejected_epochs', the rejected epochs for each participant):

config
{'raw_files': ['/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_eeg.set',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_eeg.set'],
 'log_files': ['/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-001/eeg/sub-001_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-002/eeg/sub-002_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-003/eeg/sub-003_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-004/eeg/sub-004_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-005/eeg/sub-005_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-006/eeg/sub-006_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-007/eeg/sub-007_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-008/eeg/sub-008_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-009/eeg/sub-009_task-N170_events.tsv',
  '/home/runner/work/intro-to-eeg/intro-to-eeg/ipynb/data/erpcore/N170/sub-010/eeg/sub-010_task-N170_events.tsv'],
 'output_dir': 'output',
 'clean_dir': None,
 'epochs_dir': None,
 'report_dir': None,
 'to_df': True,
 'downsample_sfreq': None,
 'veog_channels': 'auto',
 'heog_channels': 'auto',
 'montage': 'biosemi64',
 'bad_channels': [None, None, None, None, None, None, None, None, None, None],
 'ref_channels': 'average',
 'besa_files': [None, None, None, None, None, None, None, None, None, None],
 'ica_method': 'fastica',
 'ica_n_components': 15,
 'highpass_freq': 0.1,
 'lowpass_freq': 40.0,
 'triggers': 'range(1, 81)',
 'triggers_column': None,
 'epochs_tmin': -0.5,
 'epochs_tmax': 1.5,
 'baseline': (-0.2, 0.0),
 'skip_log_rows': [None, None, None, None, None, None, None, None, None, None],
 'skip_log_conditions': {'value': 'range(81, 203)'},
 'reject_peak_to_peak': 200.0,
 'components': {'name': ['N170'],
  'tmin': [0.11],
  'tmax': [0.15],
  'roi': ['PO8']},
 'average_by': {'face': 'value <= 40', 'car': 'value > 40'},
 'perform_tfr': False,
 'tfr_subtract_evoked': False,
 'tfr_freqs': [4.0,
  5.0,
  6.0,
  7.0,
  8.0,
  9.0,
  10.0,
  11.0,
  12.0,
  13.0,
  14.0,
  15.0,
  16.0,
  17.0,
  18.0,
  19.0,
  20.0,
  21.0,
  22.0,
  23.0,
  24.0,
  25.0,
  26.0,
  27.0,
  28.0,
  29.0,
  30.0,
  31.0,
  32.0,
  33.0,
  34.0,
  35.0,
  36.0,
  37.0,
  38.0,
  39.0,
  40.0],
 'tfr_cycles': [2.0,
  2.5,
  3.0,
  3.5,
  4.0,
  4.5,
  5.0,
  5.5,
  6.0,
  6.5,
  7.0,
  7.5,
  8.0,
  8.5,
  9.0,
  9.5,
  10.0,
  10.5,
  11.0,
  11.5,
  12.0,
  12.5,
  13.0,
  13.5,
  14.0,
  14.5,
  15.0,
  15.5,
  16.0,
  16.5,
  17.0,
  17.5,
  18.0,
  18.5,
  19.0,
  19.5,
  20.0],
 'tfr_mode': 'percent',
 'tfr_baseline': (-0.45, -0.05),
 'tfr_components': {'name': [],
  'tmin': [],
  'tmax': [],
  'fmin': [],
  'fmax': [],
  'roi': []},
 'perm_contrasts': [],
 'perm_tmin': 0.0,
 'perm_tmax': 1.0,
 'perm_channels': None,
 'perm_fmin': None,
 'perm_fmax': None,
 'n_jobs': 1,
 'vhdr_files': None,
 'auto_rejected_epochs': {'sub-001_task-N170_eeg': [32,
   33,
   34,
   35,
   36,
   41,
   42,
   43,
   46,
   47,
   48,
   49,
   50,
   52,
   53,
   54,
   55,
   56,
   61,
   62,
   63,
   66,
   67,
   68,
   69,
   70,
   71,
   72,
   73,
   74,
   82,
   83,
   84,
   85,
   92,
   93,
   94,
   95,
   96,
   97,
   98,
   99,
   100,
   101,
   105,
   107,
   108,
   109,
   110,
   112,
   113,
   114,
   115,
   116,
   117,
   118,
   119,
   120,
   121,
   122,
   124,
   125,
   126,
   127,
   128,
   129,
   131,
   132,
   133,
   134,
   135,
   136,
   142,
   143,
   144,
   145,
   146,
   147,
   148,
   149,
   150,
   153,
   154,
   155,
   156,
   157,
   158],
  'sub-002_task-N170_eeg': [56],
  'sub-003_task-N170_eeg': [42, 66, 87],
  'sub-004_task-N170_eeg': [35, 96, 97, 98],
  'sub-005_task-N170_eeg': [],
  'sub-006_task-N170_eeg': [0, 45, 107, 131],
  'sub-007_task-N170_eeg': [],
  'sub-008_task-N170_eeg': [],
  'sub-009_task-N170_eeg': [],
  'sub-010_task-N170_eeg': [15, 49, 50, 85]},
 'auto_ica_bad_components': {'sub-001_task-N170_eeg': [0],
  'sub-002_task-N170_eeg': [0, 6],
  'sub-003_task-N170_eeg': [0, 7],
  'sub-004_task-N170_eeg': [0],
  'sub-005_task-N170_eeg': [0, 2],
  'sub-006_task-N170_eeg': [0],
  'sub-007_task-N170_eeg': [0, 1],
  'sub-008_task-N170_eeg': [0, 12],
  'sub-009_task-N170_eeg': [0, 2],
  'sub-010_task-N170_eeg': [0, 1]},
 'package_versions': {'python': '3.11.7',
  'pipeline': '0.8.3',
  'mne': '1.6.0',
  'numpy': '1.26.2',
  'pandas': '2.1.4',
  'scikit-learn': '1.3.2'}}

5.4. Exercises#

  1. Run the EEG analysis pipeline (using a custom for loop or the group_pipeline() function) for 10 participants from a different ERP CORE experiment (valid experiment names are 'N170', 'MMN', 'N2pc', 'N400', 'P3', or 'ERN'). Create evokeds for the two conditions of interest and visualize their time course.

# Your code goes here
...

5.5. Further reading#

  • Paper Group-level EEG-processing pipeline for flexible single trial-based analyses including linear mixed models (Frömer et al, 2018)

  • hu-neuro-pipeline package documentation

  • Slides on the hu-neuro-pipeline package

5.6. References#

Fromer et al., 2018(1,2)

Frömer, R., Maier, M., & Abdel Rahman, R. (2018). Group-level EEG-processing pipeline for flexible single trial-based analyses including linear mixed models. Frontiers in Neuroscience, 12, 48. doi:10.3389/fnins.2018.00048

Gramfort et al., 2013

Gramfort, A., Luessi, M., Larson, E., Engemann, D. A., Strohmeier, D., Brodbeck, C., … Hämäläinen, M. (2013). MEG and EEG data analysis with MNE-Python. Frontiers in Neuroscience, 7, 267. doi:10.3389/fnins.2013.00267