Skip to content

feegeer/uni-eeg

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

113 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

The Sharknado EEG Project 🦈πŸŒͺ️

A re-analysis of Moerel et al. (2025), "Neural decoding of competitive decision-making in Rock–Paper–Scissors", using MNE-Python instead of the original MATLAB/FieldTrip/CoSMoMVPA toolchain.

This is a semester project for the EEG course at the University of Stuttgart. Rather than reproducing the original pipeline step-by-step, we re-implement it independently in Python and then extend it, checking whether the paper's findings hold up under a different preprocessing and decoding stack.

Team members: Emma Feege, Felipe Potenza, Radu-Mihai Savancea

What the Paper Does

Moerel et al. recorded 64-channel EEG from 31 pairs of participants playing 480 rounds of Rock–Paper–Scissors. They used multivariate decoding (LDA via CoSMoMVPA) to ask whether the EEG signal contains information about:

  1. The player's own response on the current trial
  2. The opponent's response on the current trial
  3. The player's own response on the previous trial
  4. The opponent's response on the previous trial

Key finding: players' own decisions were decodable from EEG during all task phases, and only overall match losers showed neural encoding of previous-trial information, suggesting that relying on past outcomes may hurt performance in a game where the optimal strategy is to be random.

What We Do

1. Reproduce the behavioural statistics

Before touching the EEG data, we first verify that the behavioural patterns reported in the paper (response biases, win-stay/lose-shift tendencies, Markov chain predictability) are present in the dataset. This serves as a sanity check that we're reading the data correctly and that there's something learnable.

2. Preprocess the EEG in MNE-Python

The original paper used FieldTrip for preprocessing (epoching, bad channel interpolation, downsampling to 256 Hz, no filtering). We re-implement this in MNE-Python, which required writing custom routines in some places (e.g., channel interpolation parameters) since an exact 1:1 match between FieldTrip and MNE is not always possible. We document where and why our pipeline deviates.

3. Reproduce the LDA decoding pipeline

The core decoding analysis (pseudo-trial construction, balanced cross-validation folds, channel searchlight) is re-implemented in Python, staying as close as possible to the CoSMoMVPA logic. This includes a faithful reimplementation of cosmo_classify_lda with its default regularisation ($\lambda = 0.01 \times \text{trace}(\Sigma)/p$), cosmo_sample_unique for balanced sampling, and cosmo_chunkize for fold assignment. The goal is to verify that the decoding results replicate before changing anything.

4. Extend with additional classifiers and preprocessing

Once the reproduction is in place, we go further:

  • Additional preprocessing: we test the effect of bandpass filtering (1–70 Hz) and a 50 Hz notch filter, and re-examine their bad channel selections.
  • Linear classifiers: we benchmark the original LDA against three alternatives (Shrinkage LDA, Linear SVM, and L2-regularised Logistic Regression) on the exact same data and folds to test whether the decoding results are robust to classifier choice.
  • Non-linear decoding with EEGNet: we apply EEGNet, a convolutional neural network designed for EEG, as a fundamentally different decoding approach.

Pipeline Overview

Raw BDF files (BioSemi 64ch, 2048 Hz)
        β”‚
        β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Preprocessing (MNE)    β”‚
β”‚  β€’ Average re-reference β”‚
β”‚  β€’ Epoch: -0.2 to 5.0 s β”‚
β”‚  β€’ Bad channel interp.  β”‚
β”‚  β€’ Downsample to 256 Hz β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Phase splitting & binning      β”‚
β”‚  β€’ Decision (0–2 s)             β”‚
β”‚  β€’ Response (2–4 s)             β”‚
β”‚  β€’ Feedback (4–5 s)             β”‚
β”‚  β€’ Baseline correction per part β”‚
β”‚  β€’ Average into 250 ms bins     β”‚
β”‚    β†’ 8 + 8 + 4 = 20 time bins   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
         β”‚
         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Decoding (per participant)          β”‚
β”‚  β€’ Remove block-first trials         β”‚
β”‚  β€’ 10 balanced CV folds              β”‚
β”‚  β€’ Pseudo-trials: avg 4, repeat 20Γ—  β”‚
β”‚  β€’ Temporal decoding (all channels)  β”‚
β”‚  β€’ Channel searchlight (4 neighbours)β”‚
β”‚                                      β”‚
β”‚  Classifiers:                        β”‚
β”‚    β”œβ”€ LDA (CoSMoMVPA-style)          β”‚
β”‚    β”œβ”€ Shrinkage LDA (Ledoit-Wolf)    β”‚
β”‚    β”œβ”€ Linear SVM                     β”‚
β”‚    β”œβ”€ Logistic Regression (L2)       β”‚
β”‚    └─ EEGNet (separate pipeline)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Project Structure

uni-eeg/
β”œβ”€β”€ src/                           # All source code (functions + analysis scripts)
β”‚   β”œβ”€β”€ main.py                    # Entry point for running the whole pipeline
β”‚   β”œβ”€β”€ data_setup.py              # Checks data folder structure correctness and which pipeline steps are complete
β”‚   β”œβ”€β”€ dataset.py                 # Organizes the original dataset and performs preprocessing
β”‚   β”œβ”€β”€ linear_decoding.py         # Custom implementation of all four linear decoding methods
β”‚   β”œβ”€β”€ decoding_visualization.py  # Plots linear decoding results similarly to original paper
β”‚   β”œβ”€β”€ non_linear_decoding.py     # Implements new EEGNet as the non-linear decoding method
β”‚   β”œβ”€β”€ eeg_visualization.py       # Plots EEGNet decoding results
β”‚   └── markov_analysis.py         # Markov analysis implementation
β”œβ”€β”€ images/                        # Figures used in the report notebooks 
β”œβ”€β”€ final_report.ipynb             # Notebook report                        
β”œβ”€β”€ pyproject.toml                 # Python package config & dependencies
β”œβ”€β”€ .style.yapf                    # Code formatting rules
└── README.md                      # Intro & instructions to the project

The data/ directory is not tracked by git (too large). See the installation instructions below for how to obtain it.

Installation

Requirements: Python β‰₯ 3.12

# Clone the repo
git clone https://github.com/feegeer/uni-eeg.git
cd uni-eeg
git checkout development

# Set up environment (we recommend uv (100x faster than pip plus you can choose the Python version), but pip works too (use uv and then just `uv sync`, it is much better!))
python -m venv .venv && source .venv/bin/activate
pip install -e .

# Download the dataset from OpenNeuro (~78 GiB β€” use tmux!)
aws s3 sync --no-sign-request s3://openneuro.org/ds006761 data/ds006761

Code formatting

We use yapf for consistent formatting:

yapf -ir src

Usage

After installing and downloading the data, run the full pipeline with:

python src/main.py

This will check the data folder structure, preprocess EEG data, run all decoding methods, perform Markov analysis, and save all results to data/results/. The script detects which steps have already been completed and skips them, so it is safe to re-run after interruptions.

Note: Given the size of the dataset, each step can take from a few seconds up to multiple hours to run. The decoding methods are specially time-consuming.

Final Folder Structure with Data in Detail

uni-eeg/
β”œβ”€β”€ src/                           # All source code (functions + analysis scripts)
β”‚   β”œβ”€β”€ main.py                    # Entry point for running the whole pipeline
β”‚   β”œβ”€β”€ data_setup.py              # Checks data folder structure correctness and which pipeline steps are complete
β”‚   β”œβ”€β”€ dataset.py                 # Organizes the original dataset and performs preprocessing
β”‚   β”œβ”€β”€ linear_decoding.py         # Custom implementation of all four linear decoding methods
β”‚   β”œβ”€β”€ decoding_visualization.py  # Plots linear decoding results similarly to original paper
β”‚   β”œβ”€β”€ non_linear_decoding.py     # Implements new EEGNet as the non-linear decoding method
β”‚   β”œβ”€β”€ eeg_visualization.py       # Plots EEGNet decoding results
β”‚   └── markov_analysis.py         # Markov analysis implementation
β”œβ”€β”€ images/                        # Figures used in the report notebooks 
β”œβ”€β”€ final_report.ipynb             # Notebook report   
β”œβ”€β”€ data/                          # All data files (original dataset, reproduction, and contributions)
β”‚   β”œβ”€β”€ ds006761/                  # Original downloaded dataset
β”‚   β”œβ”€β”€ results/                   # Reproduction and contribution results
β”‚   β”‚   β”œβ”€β”€ preprocessed_eeg/      # Preprocessed EEG *.fif files
β”‚   β”‚   β”œβ”€β”€ linear_decoding/       # Decoding results for all four linear decoding methods
β”‚   β”‚   β”œβ”€β”€ non_linear_decoding/   # Decoding results for EEGNet
β”‚   β”‚   └── markov_and_statistics/ # Results from Markov analysis and Statistical plots
β”‚   β”œβ”€β”€ biosemi64.mat              # Channel positions for the BioSemi 64-channel cap (MATLAB format)
β”‚   └── biosemi64.lay              # Channel layout for the BioSemi 64-channel cap (FieldTrip text format)
β”œβ”€β”€ pyproject.toml                 # Python package config & dependencies
β”œβ”€β”€ .style.yapf                    # Code formatting rules
└── README.md                      # Intro & instructions to the project

Results Kaggle Dataset

In case there is no time to run all the steps, we have uploaded the results from data/results/ as a public Kaggle dataset.

References

Paper:

Moerel, D., Grootswagers, T., Chin, J. L. L., Ciardo, F., Nijhuis, P., Quek, G. L., Smit, S., & Varlet, M. (2025). Neural decoding of competitive decision-making in Rock–Paper–Scissors. Social Cognitive and Affective Neuroscience, 20(1), nsaf101. https://doi.org/10.1093/scan/nsaf101

Dataset (OpenNeuro):

https://doi.org/10.18112/openneuro.ds006761.v1.0.0

Original analysis code (OSF):

https://doi.org/10.17605/OSF.IO/YJXKN

Key toolboxes used:

  • MNE-Python β€” EEG preprocessing and data handling
  • scikit-learn β€” LDA, SVM, and Logistic Regression classifiers
  • EEGNet β€” convolutional neural network for EEG decoding
  • CoSMoMVPA β€” the MATLAB toolbox used in the original paper (our Python code reimplements its core routines)

About

EEG project repository of group Sharknado 🦈πŸŒͺ️

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors