Skip to content

ENH: add line_bandwidth to ZapLine to subtract only the line band - #148

Merged
BabaSanfour merged 2 commits into
mne-tools:mainfrom
BabaSanfour:enh/zapline-line-only
Nov 15, 2026
Merged

BabaSanfour merged 2 commits into
mne-tools:mainfrom
BabaSanfour:enh/zapline-line-only

Conversation

@BabaSanfour

@BabaSanfour BabaSanfour commented Nov 15, 2026 •

Copy link
Copy Markdown
Member

Addresses item 6 of #137.

ZapLine subtracts the whole time course of each removed component. Those components also carry broadband activity, so subtracting them can raise power away from the line on single channels (up to +4.8 dB in the benchmark below). With line_bandwidth, only the removed components' part within line_bandwidth Hz of the targeted harmonics is subtracted, and the spectrum outside these bands is unchanged.

  • line_bandwidth=None (default) keeps the current behaviour; the output is bit-identical to main.
  • ZapLine selects the same components as without it, in standard and adaptive mode. In adaptive mode the segments select and subtract as by default, and the band is applied once to the removed signal of the whole recording.
  • The FFT band mask moves to _filtering.line_band_mask and is shared with interpolate_spectrum (output unchanged).
  • Documented limitation: on short inputs, or epochs that are not contiguous, part of the line falls outside the band and is not removed.

Benchmark

66 real recordings at their native rate, cropped to 180 s (CTF, MEGIN, 4D/BTi, KIT, EEG, mobile EEG, ECoG; 160 Hz to 6 kHz), and 96 synthetic cases with a known clean signal (8–272 channels, 50/60 Hz, one to many line sources, stable and broadened lines), on the Alliance fir cluster, with line_bandwidth=1.0:

standard, default standard, line-only adaptive, default adaptive, line-only
Worst-channel change away from the line, max (real) 4.78 dB 0.00 dB 4.46 dB 0.00 dB
Recordings with a band > 0.5 dB higher on a channel (real) 15 0 19 0
Residual line peak, median (real) +0.33 dB +0.03 dB +0.17 dB −0.05 dB
Line removal, median (real) 9.19 dB 9.19 dB 9.39 dB 9.39 dB
SNR against the clean signal, median (synthetic) 21.7 dB 26.8 dB 21.0 dB 26.7 dB

Line-only keeps more than 1 dB more line than the default in 1 (standard) and 2 (adaptive) real recordings, all clinical EEG, at most +1.7 dB. Its SNR is higher in 75/96 (standard) and 77/96 (adaptive) synthetic cases; it is lower for some broadened lines (better in 27/48), whose power extends beyond the band.

On 1,237 clinical EEG recordings (19 channels, 200 Hz, standard mode with at most 3 components), recordings with a channel more than 0.5 dB higher in 1–45 Hz drop from 134 to 0, and line removal at 59–61 Hz is a median 0.02 dB weaker.

Related, not changed here: #149 (ZapLine's period smoother is causal). With line_bandwidth, it does not change power away from the line.

Claude Code was used to implement and benchmark these changes.

🤖 Generated with Claude Code

The removed components are subtracted only within line_bandwidth Hz of the
targeted harmonics, leaving the rest of the spectrum unchanged; ZapLine
selects the same components as without it. In adaptive mode the band is
applied to the removed signal of the whole recording. The FFT band mask
moves to _filtering and is shared with interpolate_spectrum.
@codecov

codecov Bot commented Nov 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.11765% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.53%. Comparing base (94f47e1) to head (4216833).

Files with missing lines Patch % Lines
mne_denoise/_filtering.py 71.42% 1 Missing and 1 partial ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #148      +/-   ##
==========================================
+ Coverage   85.47%   85.53%   +0.05%     
==========================================
  Files          77       77              
  Lines       10719    10747      +28     
  Branches     1888     1891       +3     
==========================================
+ Hits         9162     9192      +30     
+ Misses        931      929       -2     
  Partials      626      626              
Flag Coverage Δ
unittests 85.53% <94.11%> (+0.05%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
mne_denoise/spectrum_interpolation.py 83.19% <100.00%> (+2.86%) ⬆️
mne_denoise/zapline/core.py 85.08% <100.00%> (+1.12%) ⬆️
mne_denoise/_filtering.py 83.33% <71.42%> (-16.67%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@BabaSanfour
BabaSanfour merged commit 2330a01 into mne-tools:main Nov 15, 2026
21 checks passed
@BabaSanfour
BabaSanfour deleted the enh/zapline-line-only branch November 15, 2026 02:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant