Bad Channel Detection
This tutorial walks through cogpy’s canonical bad-channel detection pipeline: extract per-channel features, normalize spatially, and label outliers.
Overview
The pipeline has four stages:
Raw signal → Feature extraction → Spatial normalization → Outlier labeling
(temporal measures) (neighborhood stats) (DBSCAN)
Each stage is a separate module under cogpy.preprocess.badchannel.
Step 1: Extract channel features
from cogpy.preprocess.badchannel.channel_features import extract_channel_features_xr
# sig: xarray.DataArray with dims (time, AP, ML) and fs coordinate
features = extract_channel_features_xr(
sig,
window_size=2048,
window_step=1024,
feature_names=["variance", "amplitude", "kurtosis", "hurst_exponent"],
)
# Returns xarray.Dataset with dims (time_win, AP, ML) and one variable per feature
Each feature is computed per channel per time window. The underlying
functions live in cogpy.measures.temporal:
from cogpy.measures.temporal import (
relative_variance,
amplitude,
kurtosis,
hurst_exponent,
)
Step 2: Spatial normalization
Raw feature values vary across the grid due to electrode impedance and anatomy. Spatial normalization expresses each channel’s feature value relative to its neighborhood:
from cogpy.preprocess.badchannel.pipeline import (
compute_features_sliding,
DEFAULT_FEATURE_SPECS,
)
# DEFAULT_FEATURE_SPECS defines 7 features with normalization strategies:
# - "ratio": channel / neighborhood_median (e.g., variance, amplitude)
# - "difference": channel - neighborhood_median (e.g., hurst_exponent)
# - "robust_z": (channel - median) / MAD (e.g., kurtosis)
# - "identity": no normalization (e.g., anticorrelation)
normalized = compute_features_sliding(
sig,
window_size=2048,
window_step=1024,
feature_specs=DEFAULT_FEATURE_SPECS,
)
Step 3: Label outliers
DBSCAN clusters channels in feature space. Outlier channels (noise label
-1) are flagged as bad:
from cogpy.preprocess.badchannel.badlabel import (
DbscanParams,
dbscan_outliers,
)
params = DbscanParams(eps=1.5, min_samples=5)
labels = dbscan_outliers(normalized, params=params)
# labels: array of 0 (good) / 1 (bad) per channel
Full pipeline
For a complete example composing all stages:
from cogpy.preprocess.badchannel.pipeline import compute_features_sliding
from cogpy.preprocess.badchannel.badlabel import dbscan_outliers, DbscanParams
from cogpy.preprocess.badchannel.feature_normalization import (
normalize_windowed_features,
summarize_windowed_features,
)
# Extract and normalize
features = compute_features_sliding(sig, window_size=2048, window_step=1024)
# Summarize across time windows (median per channel)
summary = summarize_windowed_features(features)
# Label outliers
bad_mask = dbscan_outliers(summary, params=DbscanParams())
Interpolating bad channels
Once bad channels are identified, interpolate them:
from cogpy.preprocess.interpolate import interpolate_bads
sig_clean = interpolate_bads(sig, bad_mask=bad_mask)
Next steps
Spatial Grid Measures — spatial grid characterization beyond bad-channel detection
How to build a Snakemake preprocessing pipeline — automate this pipeline with Snakemake