Sensor Noise Suppression (SNS)#
The mne_denoise.sns module implements Sensor Noise Suppression (SNS) [1], a
spatial method that regenerates each sensor from correlated neighboring
sensors. It targets noise that is specific to individual sensors while
preserving signals that are spatially redundant across the array.
SNS does not target ocular, cardiac, muscle, or other artifacts that are themselves correlated across sensors. Its assumptions should be checked for the recording system and scientific signal of interest.
Estimator API#
from mne_denoise.sns import SNS
model = SNS(n_neighbors=20)
model.fit(training_data)
cleaned = model.transform(evaluation_data)
fit() learns a channel mean and spatial operator from the training data.
transform() always uses that fitted mean, so a sample has the same result
whether transformed alone, in a temporal chunk, or among other epochs. By
default the fitted mean is subtracted and is not added back to the output.
Set preserve_mean=True to restore the fitted training mean:
cleaned = SNS(n_neighbors=20, preserve_mean=True).fit_transform(data)
For MNE Raw, Epochs, and Evoked inputs, SNS automatically selects one homogeneous data-channel type and returns a copy of the same container. Timing, annotations, events, epoch metadata, averaging information, and excluded channels are preserved. Fit and transform must use the same selected channel names in the same order.
Robust, iterative, and chunked fitting#
Global sample weights prevent selected samples from influencing the fitted mean
or projections. Continuous weights have shape (n_times,); epoched weights have
shape (n_epochs, n_times).
weights = good_samples.astype(float)
model = SNS(n_neighbors=20, n_iter=2, outlier_threshold=8.0, chunk_size=10_000)
cleaned = model.fit_transform(data, sample_weight=weights)
outlier_threshold applies an additional fixed binary mask while fitting. For
each channel, SNS computes a median and 1.4826 * MAD scale from manually
included samples, falls back to standard deviation and then unit scale when
needed, and rejects a sample if any channel exceeds the threshold. The combined
manual and automatic weights remain fixed across iterations. The learned
operator is applied to all samples, including rejected fitting samples.
With n_iter > 1, SNS learns a new projection from the output of each pass and
composes every pass into one fixed denoising_matrix_. chunk_size bounds the
temporary arrays used for weighted statistics and operator application. The
shared MNE extractor still materializes MNE input, so this option is not an
out-of-core MNE reader.
One-shot and covariance APIs#
compute_sns() self-centers a continuous array, learns the requested passes,
and applies them immediately:
from mne_denoise.sns import compute_sns, compute_sns_weights
cleaned, diagnostics = compute_sns(
data, n_neighbors=20, n_iter=2, outlier_threshold=8.0
)
centered = data - data.mean(axis=1, keepdims=True)
covariance = centered @ centered.T / centered.shape[1]
operator, n_used, ranks = compute_sns_weights(covariance, n_neighbors=20)
compute_sns_weights() is the covariance-level, single-pass primitive.
compute_sns() is the main array algorithm used by SNS; it adds centering,
weighting, masking, iteration, and chunking.
Diagnostics#
After fitting, the estimator exposes:
training_mean_: weighted training mean used by every transform;denoising_matrix_: composed spatial operator;denoising_matrices_: operator learned at each iteration;neighbor_ranks_andneighbor_ranks_per_iteration_: numerical local ranks;input_rank_,effective_weight_sum_, andrejected_sample_count_;n_neighbors_andn_iter_: effective fitted operating point.
The one-shot function returns the corresponding values in its diagnostics dictionary. It reports only the effective weight sum and rejection count, not a full sample-weight vector.
Choosing parameters#
n_neighbors=0uses all eligible sensors. A smaller set is faster and may be preferable for dense arrays.skipomits the most correlated neighbors, which can help when adjacent sensors share local noise.Start with
n_iter=1; repeated passes are more aggressive and typically show diminishing changes.Enable robust masking only after inspecting the data distribution. A threshold near 8 is a useful demonstration value, not a universal recommendation.
Compare signal preservation and sensor-noise attenuation on data representative of the intended analysis before adopting SNS in a pipeline.
See the Sensor Noise Suppression gallery for basic usage and a
deterministic exploration of the algorithm’s assumptions and diagnostics. The
standalone scripts/replicate_sns_paper.py
runs the larger synthetic experiments used for paper-oriented reproducibility.