mne_rt.tools.BadChannelDetector#

class mne_rt.tools.BadChannelDetector(info, method: str | list[str] = 'all', flat_threshold: float = 1e-07, variance_threshold: float = 5.0, corr_threshold: float = 0.4, hf_threshold: float = 5.0, hf_cutoff: float = 40.0, n_neighbors: int = 4, history_windows: int = 30, min_bad_frac: float = 0.5)[source]#

Bases: object

Multi-criterion real-time bad channel detector for streaming M/EEG.

Evaluates each incoming data window against up to four independent criteria. A channel is flagged as bad only when it exceeds its criterion in at least min_bad_frac of the rolling window history — this voting mechanism avoids false positives from transient artifacts.

Criteria

"flat"

Channels whose RMS amplitude falls below flat_threshold (dead electrode / disconnected lead).

"variance"

Channels whose RMS amplitude is a statistical outlier across all channels, measured by a robust z-score (\(z = (\text{rms} - \text{median}) / (\text{MAD} \times 1.4826)\)). Catches both excessively noisy channels and channels that have gone unusually quiet.

"correlation"

Channels whose mean Pearson correlation with their K nearest spatial neighbours drops below corr_threshold. A channel that has lost contact or broken its reference will de-correlate from its neighbours while the neighbours remain correlated with each other. Requires channel positions to be set in info (i.e. montage applied).

"hf_noise"

Channels with an abnormally high ratio of high-frequency power (> hf_cutoff Hz) to broadband power. Catches channels contaminated by EMG, electrode cable noise, or loose connections. Uses the same MAD-based robust z-score as "variance".

Parameters:
infomne.Info

MNE channel information. Must contain ch_names and sfreq. For "correlation", channel 3-D positions must be set (set_montage applied).

method{“all”, “flat”, “variance”, “correlation”, “hf_noise”} or list

Criterion or list of criteria to evaluate. "all" enables all four criteria. Default is "all".

flat_thresholdfloat, default 1e-7

Minimum RMS amplitude (in raw data units — V for EEG, T for MEG) for a channel not to be considered flat. Channels below this value are dead. Default 100 nV = 1e-7 V.

variance_thresholdfloat, default 5.0

Robust z-score cutoff for the variance criterion. A channel whose RMS deviates by more than this many MAD-units from the channel median is flagged. Default 5.0 (conservative; reduce to 3–4 to be more aggressive).

corr_thresholdfloat, default 0.4

Minimum mean Pearson correlation with spatial neighbours. Channels below this value are poorly coupled to their surroundings. Default 0.4.

hf_thresholdfloat, default 5.0

Robust z-score cutoff for the HF noise criterion. Default 5.0.

hf_cutofffloat, default 40.0

Frequency in Hz above which power is classified as high-frequency for the noise criterion. Default 40.0 Hz.

n_neighborsint, default 4

Number of nearest spatial neighbours used in the correlation criterion. Default 4.

history_windowsint, default 30

Number of per-window bad-flags to retain in the rolling history. Combined with min_bad_frac this sets the effective time-scale for declaring a channel persistently bad.

min_bad_fracfloat, default 0.5

Fraction of rolling-history windows in which a channel must be flagged before it is declared bad. 0.5 = majority vote. 1.0 = must be bad in every recent window (very lenient). 0.1 = bad in any 10 % of windows (very strict).

Attributes:
bad_channels_list of str

Channel names currently declared bad. Updated on every update() call.

scores_dict of strfloat

Per-channel composite badness score in [0, 1]: fraction of recent windows in which the channel was flagged by any active criterion.

n_windows_int

Total number of windows processed since initialisation or last reset().

Examples

Basic usage — update once per NF window and pass bad channels to MNE:

detector = BadChannelDetector(raw.info, method="all")
while streaming:
    window = stream.get_data(1.0)   # 1 s chunk
    bad = detector.update(window)
    print("Bad channels:", bad)

Use only the variance + flat criteria (no montage required):

detector = BadChannelDetector(
    info, method=["flat", "variance"], variance_threshold=4.0
)

Added in version 1.0.0.

__init__(info, method: str | list[str] = 'all', flat_threshold: float = 1e-07, variance_threshold: float = 5.0, corr_threshold: float = 0.4, hf_threshold: float = 5.0, hf_cutoff: float = 40.0, n_neighbors: int = 4, history_windows: int = 30, min_bad_frac: float = 0.5) None[source]#

Methods

__init__(info[, method, flat_threshold, ...])

get_bad_channels()

Return the current list of declared bad channels.

get_scores()

Return per-channel badness scores in the range [0, 1].

reset()

Clear rolling history and reset all counters.

update(data)

Process one data window and return current bad-channel list.

update(data: ndarray) list[str][source]#

Process one data window and return current bad-channel list.

Parameters:
datandarray, shape (n_channels, n_samples)

One analysis window of raw M/EEG data. The channel order must match info["ch_names"].

Returns:
bad_channelslist of str

Channel names currently declared bad (majority vote over recent history).

Raises:
ValueError

If data.shape[0] != n_channels.

get_bad_channels() list[str][source]#

Return the current list of declared bad channels.

Returns:
bad_channelslist of str

Channel names that have been bad in ≥ min_bad_frac of the most recent history_windows windows.

get_scores() dict[str, float][source]#

Return per-channel badness scores in the range [0, 1].

A score of 1.0 means the channel was flagged in every recent window; 0.0 means it was never flagged.

Returns:
scoresdict of strfloat
reset() None[source]#

Clear rolling history and reset all counters.

Constructor parameters and neighbour indices are preserved.