STUDY Workflows#

EEGPrep includes standalone STUDY/session surfaces for common group-level workflows. These APIs mirror EEGLAB’s STUDY-facing names while storing cached measure data in EEGPrep-owned JSON-safe structures.

GUI Plus Console Flow#

Load two or more datasets, then use File > Create study > Using all loaded datasets. eegprep-console will show STUDY and CURRENTSTUDY update immediately. Continue with Study > Precompute channel measures and Study > Plot channel measures when the selected datasets have compatible metadata and shapes.

The same workflow in Python can reuse the sample epoched ICA dataset:

from pathlib import Path
from eegprep import pop_loadset, pop_precomp, pop_study

ALLEEG = [
    pop_loadset(Path("sample_data") / "eeglab_data_epochs_ica.set"),
    pop_loadset(Path("sample_data") / "eeglab_data_epochs_ica.set"),
]
for index, EEG in enumerate(ALLEEG, start=1):
    EEG["subject"] = f"S{index:02d}"
    EEG["condition"] = "tutorial"

STUDY, ALLEEG, com = pop_study(None, ALLEEG, name="Tutorial study", return_com=True)
STUDY, ALLEEG, com = pop_precomp(STUDY, ALLEEG, "channels", erp="on", return_com=True)

Implemented Workflows#

Create a STUDY from loaded datasets:

from eegprep import pop_study

STUDY, ALLEEG, com = pop_study(None, ALLEEG, name="My study", return_com=True)

Create a simple ERP STUDY:

from eegprep import pop_studyerp

STUDY, ALLEEG, com = pop_studyerp(ALLEEG, return_com=True)

Build and inspect design variables:

from eegprep import pop_importgroupvar, pop_listfactors, std_builddesignmat

STUDY, com = pop_importgroupvar(
    STUDY,
    1,
    variable="age_group",
    values={"S01": "young", "S02": "older"},
    return_com=True,
)
factors = pop_listfactors(STUDY, constant="off")
design_matrix, labels, categorical = std_builddesignmat(
    STUDY["design"][0],
    [{"condition": "target", "rt": 300.0}, {"condition": "standard", "rt": 400.0}],
    expanding=True,
)

Load and save EEGPrep .study files:

from eegprep import pop_loadstudy, pop_savestudy

STUDY, com = pop_savestudy(STUDY, EEG, "analysis.study", return_com=True)
STUDY, ALLEEG, com = pop_loadstudy("analysis.study", return_com=True)

Precompute and plot STUDY measures:

from eegprep import pop_chanplot, pop_precomp, std_erpplot, std_readitc

STUDY, ALLEEG, com = pop_precomp(
    STUDY,
    ALLEEG,
    "channels",
    erp="on",
    spec="on",
    return_com=True,
)
STUDY, com, fig = pop_chanplot(STUDY, ALLEEG, measure="erp", return_com=True)
STUDY, erpdata, erptimes, fig = std_erpplot(STUDY, ALLEEG, channels=[1])
STUDY, itcdata, itctimes, itcfreqs = std_readitc(STUDY, ALLEEG, channels=[1])

Plot functions arrange these caches into the selected design before returning them. A one-factor design returns a list with one array per factor level. A two-factor design returns data[condition][group]. Samples are the leading axes and subjects or cluster members are on the last axis; for example, ERP cells are times x cases and ERSP cells are frequencies x times x cases. This matches the cell organization used by EEGLAB while keeping the arrays directly usable from NumPy.

Channel measures are stored in STUDY.changrp. Component measures are stored on the parent STUDY.cluster[0] entry so preclustering can read the same cached arrays. Cached measure fields follow EEGLAB names such as erpdata, specdata, erspdata, and itcdata. The selected design is recorded in each measure group’s metadata. EEGPrep stores dataset-level averages, and optionally single-trial values, in the current standalone cache rather than EEGLAB sidecar measure files. pop_chanplot reads cached channel and component measures through the same std_readdata/std_erpplot/std_erspplot cache contract used by scripts, so GUI and console plots slice axes and cached channel groups consistently.

Tutorial-scale workflows#

The current EEGLAB tutorial-wrapper workflows are covered end to end with deterministic generated signals. This includes grouped ERP statistics and topographies, component preclustering for an N400-style study, all-channel time-frequency analysis, scalp movies, and a generated P300 BIDS pipeline from export/import through filtering, average reference, rank-reduced Picard ICA, component rejection, epoching, and STUDY plotting. Generated fixtures keep ordinary CI independent of large tutorial downloads while retaining numerical assertions at each processing boundary.

Two optional boundaries remain explicit. pop_iclabel needs the ICLabel model runtime; callers that already have compatible seven-class probabilities can use pop_icflag and pop_subcomp independently. Dataset-level spherical DIPFIT fitting and leadfield construction run standalone, while MRI/BEM/LORETA workflows that require FieldTrip fail clearly until an external source-analysis backend is configured.

Store reusable plot and statistics choices on the STUDY before plotting:

from eegprep import pop_erpparams, pop_statparams

STUDY = pop_erpparams(STUDY, timerange=[-200, 800], plotconditions="together")
STUDY = pop_statparams(STUDY, condstats="on", method="perm", naccu=2000)

Pass return_stats=True to std_erpplot, std_specplot, std_erspplot, or std_itcplot to receive pgroup, pcond, and pinter before the returned figure. These are p-values when threshold is NaN and numeric significance masks when a finite threshold is set, matching EEGLAB’s output contract. A sequence of thresholds produces EEGLAB’s graded masks. mcorrect="fdr" applies Benjamini-Hochberg correction. The same masks are attached to fig.eegprep_plot_metadata["statistics"] so downstream reporting code can inspect exactly what the plot highlighted.

Use savetrials="on" when a design factor varies between trials within one dataset. The measure plots select trials from datasetinfo[*]["trialinfo"] and aggregate them within each subject before forming condition and group cells. erpdatatrials stores baseline-corrected amplitudes; specdatatrials stores linear spectral density; erspdatatrials stores linear, baseline-corrected power; and itcdatatrials stores phase in radians. Spectrum and ERSP power are averaged before conversion to decibels, while ITC uses circular phase averaging. These EEGPrep-owned cache fields remain independent of an EEGLAB installation. A trial-level design without matching single-trial caches raises an error instructing you to rerun std_precomp with savetrials="on" instead of silently grouping dataset averages.

For component clusters, std_topoplot draws polarity-aligned centroid or member scalp maps and caches topo, topoall, and topopol on each plotted cluster.

The corresponding pop_erpimparams, pop_erspparams, pop_specparams, and pop_dipparams functions store settings under STUDY["etc"] using EEGLAB field names. Changing an ERP time range, spectrum frequency range, or ERSP/ITC time-frequency range invalidates the affected cached measure fields; run pop_precomp(..., recompute="on") before plotting them again.

Use std_checkfiles, std_checkdatasession, std_uniformfiles, and std_uniformsetinds to audit loaded dataset consistency and cached measure shapes before saving or plotting group-level results. std_savedat writes explicit EEGPrep-owned JSON or MATLAB-compatible measure sidecars when a workflow needs a durable array file outside the .study JSON.

Select datasets or trials from STUDY metadata:

from eegprep import std_getindvar, std_maketrialinfo, std_selectdataset

STUDY, trialinfo = std_maketrialinfo(STUDY, ALLEEG)
factors, factor_values, subjects, paired = std_getindvar(STUDY)
dataset_indices, trial_indices = std_selectdataset(
    STUDY,
    ALLEEG,
    "condition",
    ["target"],
)

These helpers return EEGLAB-facing 1-based dataset and trial indices. Trial metadata may be stored as row dictionaries or as EEGLAB-loaded columnar {"factor": [values...]} dictionaries; STUDY selectors normalize both forms before matching factor levels and numerical ranges. std_maketrialinfo uses each epoch’s time-locking event, including custom fields such as reaction time, and later STUDY synchronization preserves those derived rows.

The Study design editor edits subjects, design names, and independent variables. New under the variable list opens Add variable. A continuous trial factor such as rt is stored on STUDY.design with pairing off. The dialog keeps that factor’s value list empty; std_makedesign fills the observed values and keeps the first four variables. If the factor is missing from STUDY trial metadata, OK also runs std_maketrialinfo. List factors shows the first-level and second-level text for the selected design, and the resave box writes the STUDY when it already has a file name. Cancel leaves the STUDY unchanged.

Use std_substudy or std_rmdat when a workflow needs to remove datasets; EEGPrep remaps STUDY references and invalidates cached measure arrays after membership changes.

std_findsameica groups matching ICA decompositions within each subject. This preserves the subject boundary used by STUDY designs instead of merging identical test fixtures across subjects.

Precluster and cluster ICA components:

from eegprep import pop_clust, pop_clustedit, pop_preclust

STUDY, ALLEEG, com = pop_preclust(
    STUDY,
    ALLEEG,
    preproc=[{"measure": "scalp", "npca": 3, "norm": 1, "weight": 1}],
    return_com=True,
)
STUDY, com = pop_clust(STUDY, ALLEEG, clus_num=4, random_state=0, return_com=True)
STUDY, com, fig = pop_clustedit(STUDY, ALLEEG, action="plot", return_com=True)

Finite outliers values in pop_clust use the EEGLAB-compatible robust_kmeans path and record ["robust_kmeans", clus_num] in each created cluster’s algorithm provenance. Infinite outliers keeps the plain k-means path. In both cases command history remains pasteable in eegprep-console.

algorithm="kmeanscluster" numbers Cls k from the k-th preclustering row. algorithm="kmeans" numbers Cls k from centroid k of the lowest-SSE seeded start. The same random_state reproduces the kmeans numbering.

DIPFIT Source Localization#

EEGPrep includes standalone DIPFIT-compatible spherical workflows for ICA component source localization. Configure DIPFIT metadata first, then run a coarse grid search, nonlinear refinement, and dipole plotting:

from eegprep.plugins.dipfit.pop_dipfit_settings import pop_dipfit_settings
from eegprep.plugins.dipfit.pop_dipfit_gridsearch import pop_dipfit_gridsearch
from eegprep.plugins.dipfit.pop_dipfit_nonlinear import pop_dipfit_nonlinear
from eegprep.plugins.dipfit.pop_dipplot import pop_dipplot

EEG, com = pop_dipfit_settings(EEG, model="standardBESA", return_com=True)
EEG, com = pop_dipfit_gridsearch(
    EEG,
    [1],
    [-40, -20, 0, 20, 40],
    [-40, -20, 0, 20, 40],
    [20, 40, 60],
    40,
    return_com=True,
)
EEG, com = pop_dipfit_nonlinear(EEG, component=1, return_com=True)
figures, com = pop_dipplot(EEG, [1], summary="on", projlines="on", return_com=True)

The spherical backend fits an average-referenced leadfield, stores posxyz, momxyz, rv, diffmap, sourcepot, and datapot in EEG.dipfit.model, and keeps command history replayable from eegprep-console. pop_multifit chains grid search, nonlinear fitting, RV rejection, optional outside-head removal, and optional dipole plotting. The deprecated EEGLAB aliases pop_dipfit_batch and pop_dipfit_manual remain available for script compatibility.

pop_leadfield can compute spherical leadfields for explicit source points provided as an Nx3 array, {"pos": ...} dictionary, or simple file with source positions. MRI-derived BEM headmodel creation, AFNI atlas clipping, and LORETA source analysis remain explicit backend limits and fail clearly rather than producing placeholder source-localization outputs.

Session Synchronization#

The GUI and eegprep-console share STUDY and CURRENTSTUDY through EEGPrepSession. Creating or loading a STUDY from the GUI sets CURRENTSTUDY to 1. Retrieving a dataset from the Datasets menu returns CURRENTSTUDY to 0 and records that transition in history.

Integration Notes#

Component ERP, spectrum, ERSP, and ITC arrays are cached on the parent STUDY.cluster[0] entry. Preclustering reads those cached component arrays and can also build scalp-map features directly from loaded ICA maps. MATLAB parity checks focus on deterministic metadata and cluster structure; exact numeric clustering labels can differ because EEGPrep uses deterministic in-package k-means helpers rather than MATLAB’s Statistics Toolbox.

PAC, LIMO Design, And Neighbors#

EEGPrep includes a standalone PAC backend for practical channel workflows. Use pac for epoched phase-amplitude coupling grids and pac_cont for continuous sliding-window PAC. At the STUDY level, std_pac computes EEGPrep-owned pacdata, pactimes, and pacfreqs caches on STUDY.changrp. std_readpac slices those caches and std_pacplot plots their magnitude using the same cache-reading contract as the other STUDY measure plots.

Empirical p-value conventions are explicit. pac applies the common (exceedances + 1) / (permutations + 1) finite-sample convention, bootstat exposes exact permutation proportions for bootstrap helper tests, and statistics helpers such as stat_surrogate_pvals follow EEGLAB’s surrogate-tail convention with FDR correction available through the statistics module. These definitions are intentionally not collapsed when they answer different inferential questions.

EEGPrep provides a standalone LIMO-compatible path from design preparation to core hierarchical statistics. std_limodesign builds categorical and continuous matrices from pop_listfactors output and trial metadata, including interaction and split-regressor descriptions. pop_limo uses the active STUDY design to fit mass-univariate OLS, LIMO PCOut-weighted WLS, or Tukey-bisquare IRLS models to epoched channel or component data. The returned model dictionaries contain betas, fitted values, residuals, R², residual variance, standard errors, t statistics, p values, robust weights, and their exact design matrix.

Pass outputdir to pop_limo to write versioned .npz model files. These files never require pickle and can be reopened with std_readfilelimo. std_limoresults and pop_limoresults compute and store first-level contrasts, one-sample, paired and Welch two-sample tests, mass-univariate regression, one-way ANOVA, ANCOVA, repeated-measures ANOVA, and mean or inverse-variance-weighted summaries. Group statistics always use the first axis as subjects; first-level parameter selection remains 1-based at the EEGLAB-facing boundary.

std_prepare_neighbors creates a distance-based FieldTrip-like neighbor list and a LIMO-compatible channel adjacency matrix from loaded channel locations. std_interp interpolates requested missing channels across STUDY datasets using EEGPrep’s existing channel interpolation backend.

Plotting localized cluster dipoles#

std_dipplot visualizes DIPFIT models that are already stored in each dataset’s EEG["dipfit"]["model"] field. Cluster membership is read from STUDY["cluster"] using EEGLAB-facing 1-based dataset and component indices. The function returns the exact selected models and computed cluster centroids alongside the figures, so positions, moments, and residual variance remain inspectable outside the plot.

Use mode="joined" to draw selected cluster members and centroids together, or mode="centroid" to draw only cluster centroids. The standard apart, together, multicolor, and comps layouts are also available. comps selects 1-based positions within the cluster membership list, consistent with the EEGLAB std_plotcompdip convention. Set plot=False to prepare and validate the same numerical plot inputs without creating figures.

Limitations#

The standalone layer does not silently read version-dependent MATLAB LIMO .mat structures. Convert them explicitly or use LIMO in MATLAB; std_readfilelimo accepts only EEGPrep-owned .npz output. First-level and group bootstrap, TFCE correction, factorial repeated-measures designs, and LIMO’s result plotting/report interface remain explicit unsupported boundaries. WLS follows LIMO’s PCOut residual projection and uses one robust observation weight across the fitted channel/time grid. IRLS uses feature-wise Tukey-bisquare weights.

std_dipplot does not compute dipole models: localize components first with the EEGPrep DIPFIT workflow. The broader FieldTrip-dependent std_dipoleclusters source workflow remains an explicit backend boundary.

See the Interactive Console guide for mixed GUI plus console usage and the GUI and Help Menus guide for menu inventory behavior.