EEGPhasePy documentation

EEGPhasePy is an open-source toolkit for developing real-time EEG phase estimation and analyzing the results of EEG phase estimation results. It includes implementations of common statistics and figures used to quantify EEG phase estimation results. EEGPhasePy also includes implementations of EEG phase estimation algorithms alongside optimizations of parameters of these algorithms to improve their performance on a participant-by-participant basis.

Getting started

We will walk through getting started with your first real-time EEG phase estimation experiment using EEGPhasePy, including real-time phase estimation and analysis of the outcome of the experiment. To get started, install the package from PyPi by running the command:

Install EEGPhasePy
pip install eegphasepy

Once you’ve installed EEGPhasePy, you will need to select the model you’re planning to run phase estimation with. Below, we are using the Educated Temporal Prediction (ETP) model Shirinpour et al. [1], see API Reference for the models currently implemented in EEGPhasePy.

Import necessary packages
from EEGPhasePy.estimators import ETP

We will perform mu-band phase estimation from the C3 electrode

Real-time phase estimation

In a live experiment your amplifier sends packets of samples at a fixed rate. On each packet arrival you append the new samples to a rolling 2 s buffer, run ETP on that buffer, and schedule a trigger to fire at the sample offset ETP returns. predict returns the number of samples from the end of the current window at which the target phase is expected — converting that to wall-clock time (offset / fs) gives you how far in the future to schedule the stimulus.

Real-time loop: rolling buffer, ETP, and trigger scheduling
import numpy as np
import scipy.signal as signal
import collections, time
from EEGPhasePy.estimators import ETP

fs = 2000
window_len = 2 * fs        # 2 s rolling buffer
packet_size = int(0.06 * fs)  # samples per amplifier packet (~60 ms)

rt_filter = signal.firwin(120, [8, 12], fs=fs, pass_zero=False)
gt_filter = signal.firwin(300, [8, 12], fs=fs, pass_zero=False)

etp = ETP(rt_filter, gt_filter, fs)
etp.fit(training_signal, min_ipi=int(fs * 1/12))

buffer = collections.deque(maxlen=window_len)  # auto-drops oldest samples
triggers = []

# This would typically be a while loop that gets the latest packet from your amplifier, here we've just simulated a sliding window with a simulated signal
for packet_start in range(0, len(testing_signal) - packet_size, packet_size):
    packet = testing_signal[packet_start:packet_start + packet_size]
    buffer.extend(packet)

    if len(buffer) < window_len:
        continue  # wait until the buffer is full

    window = np.array(buffer)
    try:
        offset = etp.predict(window, target_phase=0) # here we use ETP to predict the next sample of a peak on EEG
        trigger_time = time.time() + offset / fs   # absolute wall-clock time
        trigger_sample = packet_start + packet_size + offset
        triggers.append(trigger_sample)

        # schedule_trigger(trigger_time) # call your hardware trigger here
    except RuntimeError:
        pass  # no peaks found in this window

Analysing the output

In raw EEG, you would typically have trigger markers stored as annotations in your EEG file. EEGPhasePy just needs the sample number each trigger occurs at to analyze the outcome of your phase estimation experiment.

To quantify how well the desired phase was targetted you can use mean_phase_from_triggers() and std_phase_from_triggers(), which return the circular mean and standard deviation of the phase at each trigger (a std of 50°–70° is typical in published studies). phase_accuracy_from_triggers() returns a 0–1 score where 0.5 is chance, 0 is anti-phase, and 1 is perfect locking.

Compute phase statistics
mean_phase = etp.mean_phase_from_triggers(testing_signal, triggers, degree=True)
std_phase = etp.std_phase_from_triggers(testing_signal, triggers, degree=True)
accuracy = etp.phase_accuracy_from_triggers(testing_signal, triggers, 0)

print(f"Mean phase: {mean_phase:.1f}°  |  Std: {std_phase:.1f}°  |  Accuracy: {accuracy * 100:.1f}%")

Examples

Bibliography

[1]

Sina Shirinpour, Ivan Alekseichuk, Kathleen Mantell, and Alexander Opitz. Experimental evaluation of methods for real-time EEG phase-specific transcranial magnetic stimulation. J. Neural Eng., 17(4):046002, July 2020.

[2]

Christoph Zrenner, Dragana Galevska, Jaakko O Nieminen, David Baur, Maria-Ioanna Stefanou, and Ulf Ziemann. The shaky ground truth of real-time phase estimation. Neuroimage, 214(116761):116761, July 2020.

[3]

Christoph Zrenner, Debora Desideri, Paolo Belardinelli, and Ulf Ziemann. Real-time EEG-defined excitability states determine efficacy of TMS-induced plasticity in human motor cortex. Brain Stimul., 11(2):374–389, March 2018.

[4]

Hao Liu, Zihui Qi, Yihang Wang, Zhengyi Yang, Lingzhong Fan, Nianming Zuo, and Tianzi Jiang. A novel real-time phase prediction network in EEG rhythm. Neurosci. Bull., 41(3):391–405, March 2025. doi:10.1007/s12264-024-01321-z.

[5]

Sina Shirinpour, Ivan Alekseichuk, Malte R. Güth, Zachary Haigh, Miles Wischnewski, and Alexander Opitz. Bayesian temporal prediction: a robust algorithm for real-time eeg phase-dependent brain stimulation. IEEE Transactions on Biomedical Engineering, 73(2):611–620, 2026. doi:10.1109/TBME.2025.3589970.

[6]

Ahmed Fawzy Gad. Pygad: an intuitive genetic algorithm python library. Multimedia Tools and Applications, pages 1–14, 2023.