Note
Go to the end to download the full example code.
Artifact Subspace Reconstruction: Raw QC and Annotations.#
This example shows ASR on an mne.io.RawArray with optional clean_windows-
style final window rejection. The repaired spans and rejected spans are kept
as annotations for downstream QC instead of deleting samples immediately.
Imports#
import matplotlib.pyplot as plt
import mne
import numpy as np
from mne_denoise.asr import ASR
from mne_denoise.viz import plot_signal_overlay
Synthetic Raw#
sfreq = 250.0
duration = 15.0
n_eeg = 8
n_times = int(sfreq * duration)
times = np.arange(n_times) / sfreq
rng = np.random.default_rng(7)
brain = np.zeros((n_eeg, n_times))
for ch_idx in range(n_eeg):
phase = rng.uniform(0, 2 * np.pi)
brain[ch_idx] = (
0.4 * np.sin(2 * np.pi * 10 * times + phase)
+ 0.15 * np.sin(2 * np.pi * 6 * times + 0.5 * phase)
+ 0.04 * rng.standard_normal(n_times)
)
eog = 0.25 * np.sin(2 * np.pi * 1.2 * times)[np.newaxis, :]
data = brain.copy()
spatial = rng.standard_normal((n_eeg, 2))
spatial /= np.linalg.norm(spatial, axis=0, keepdims=True)
for onset, stop in ((4.5, 5.3), (9.0, 9.7), (12.0, 12.6)):
start = int(onset * sfreq)
end = int(stop * sfreq)
data[:, start:end] += spatial @ (7.0 * rng.standard_normal((2, end - start)))
raw_data = np.vstack([data, eog])
ch_names = [f"EEG {idx:02d}" for idx in range(n_eeg)] + ["EOG 01"]
ch_types = ["eeg"] * n_eeg + ["eog"]
info = mne.create_info(ch_names, sfreq, ch_types)
raw = mne.io.RawArray(raw_data, info, verbose=False)
Fit and Apply ASR#
asr = ASR(
cutoff=5.0,
calibration="auto",
picks="eeg",
filter_kind="none",
window_criterion=0.25,
window_criterion_tolerances=(-np.inf, 2.5),
verbose=False,
)
raw_clean = asr.fit_transform(raw)
repair_annotations = asr.to_annotations()
reject_annotations = asr.to_annotations("rejection")
print(f"Repaired sample fraction: {asr.fraction_reconstructed_samples_:.2%}")
print(
"Retained after final window rejection: "
f"{asr.fraction_retained_after_window_rejection_:.2%}"
)
Repaired sample fraction: 52.08%
Retained after final window rejection: 86.67%
Plot One EEG Channel#
The repair and rejection annotations are passed straight to
plot_signal_overlay as highlight_spans instead of shading the axis by
hand; the reference trace uses the reference argument.
channel = "EEG 00"
noisy = raw.get_data(picks=[channel])[0]
clean = raw_clean.get_data(picks=[channel])[0]
spans = [
{
"onset": onset,
"duration": dur,
"color": "C3",
"alpha": 0.12,
"label": "ASR repair",
}
for onset, dur in zip(repair_annotations.onset, repair_annotations.duration)
] + [
{
"onset": onset,
"duration": dur,
"color": "0.2",
"alpha": 0.08,
"label": "Window reject mask",
}
for onset, dur in zip(reject_annotations.onset, reject_annotations.duration)
]
plot_signal_overlay(
noisy,
clean,
times,
scale_after=False,
before_label="Noisy EEG",
after_label="ASR cleaned EEG",
x_label="Time (s)",
y_label="Amplitude (a.u.)",
title="ASR repairs plus optional final window rejection mask",
reference=brain[0],
reference_label="Reference EEG",
highlight_spans=spans,
show=False,
)
plt.show()

Total running time of the script: (0 minutes 0.918 seconds)