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
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:
- The player's own response on the current trial
- The opponent's response on the current trial
- The player's own response on the previous trial
- 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.
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.
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.
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 (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.
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.
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) β
ββββββββββββββββββββββββββββββββββββββββ
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.
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/ds006761We use yapf for consistent formatting:
yapf -ir srcAfter installing and downloading the data, run the full pipeline with:
python src/main.pyThis 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.
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
In case there is no time to run all the steps, we have uploaded the results from data/results/ as a public Kaggle dataset.
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):
Original analysis code (OSF):
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)