Independent Component Analysis (ICA) is the workhorse of EEG artifact removal. It solves the cocktail party problem: given a room full of people talking simultaneously (mixed signals at each microphone/electrode), can we recover each individual speaker’s voice? ICA finds an unmixing matrix W that decomposes the mixed electrode signals into statistically independent sources — some brain, some artifact.
The Cocktail Party Problem
Mixing & Unmixing Matrices
Each EEG electrode records a weighted sum of all active sources in the brain (and body). Mathematically: X = A · S, where X is the electrode data, A is the (unknown) mixing matrix encoding how sources project to sensors, and S is the (unknown) source matrix containing the independent components.
ICA estimates the unmixing matrix W ≈ A−1 by maximizing the statistical independence of the recovered sources. The key assumption: brain sources and artifacts are generated by different physiological processes and are therefore statistically independent.
Cohen’s perspective (ANTS Ch. 27): ICA doesn’t “know” what an eye blink looks like. It simply finds directions in the data where sources are maximally non-Gaussian (since mixtures become more Gaussian by the Central Limit Theorem). The interpretation — “this is an eye blink” — is entirely your job.
Fitting ICA in MNE
Preprocessing Requirements
ICA is sensitive to the statistical properties of the input data. Two critical requirements:
1. High-pass filter at ≥1 Hz — low-frequency drifts dominate the variance and cause ICA to waste components on slow trends instead of artifacts.
2. Sufficient data length — rule of thumb: at least 20× the number of channels squared data points (e.g., for 64 channels: 64² × 20 = ~82,000 samples).
from mne.preprocessing import ICA # Create a copy filtered at 1 Hz for ICA fitting raw_filt = raw.copy().filter(l_freq=1.0, h_freq=None) # Fit ICA with n_components determined automatically ica = ICA(n_components=0.99, # explain 99% variance method='infomax', random_state=42, max_iter=800) ica.fit(raw_filt) print(ica) # shows number of components found
Critical: Fit ICA on filtered data but apply the removal to the original (unfiltered or lightly filtered) data. This preserves your low-frequency neural signals.
Identifying Artifact Components
Spatial Maps, Time Courses & Automated Detection
After fitting, you need to decide which components are artifacts. Three complementary approaches:
Visual inspection: Plot the spatial topography (ica.plot_components())
and time course (ica.plot_sources()). Eye blinks have a characteristic frontal dipolar
pattern; heartbeats show a strong left-lateralized pattern.
Correlation with EOG/ECG: Use ica.find_bads_eog() and
ica.find_bads_ecg() to automatically find components correlated with reference channels.
ICLabel (Day 7): Use deep learning to automatically classify all components.
# Visualize component topographies ica.plot_components(picks=range(20)) # Auto-detect EOG-related components eog_indices, eog_scores = ica.find_bads_eog(raw) ica.exclude = eog_indices print(f"EOG components: {eog_indices}") # Apply ICA removal to the ORIGINAL raw data raw_clean = ica.apply(raw.copy())
How many components to remove? Typically 1–3 for eye artifacts and 0–1 for heartbeat. If you find yourself removing >5 components, something may be wrong with your ICA decomposition (insufficient data, bad channels not removed, filter too low).