Aluode/PerceptionLabPortable
0
1"""Bad channel detection using Local Outlier Factor (LOF)."""2 3# Authors: The MNE-Python contributors.4# License: BSD-3-Clause5# Copyright the MNE-Python contributors.6 7import numpy as np8 9from .._fiff.pick import _picks_to_idx10from ..io.base import BaseRaw11from ..utils import _soft_import, _validate_type, logger, verbose12 13 14@verbose15def find_bad_channels_lof(16 raw,17 n_neighbors=20,18 *,19 picks=None,20 metric="euclidean",21 threshold=1.5,22 return_scores=False,23 verbose=None,24):25 """Find bad channels using Local Outlier Factor (LOF) algorithm.26 27 Parameters28 ----------29 raw : instance of Raw30 Raw data to process.31 n_neighbors : int32 Number of neighbors defining the local neighborhood (default is 20).33 Smaller values will lead to higher LOF scores.34 %(picks_good_data)s35 metric : str36 Metric to use for distance computation. Default is “euclidean”,37 see :func:`sklearn.metrics.pairwise.distance_metrics` for details.38 threshold : float39 Threshold to define outliers. Theoretical threshold ranges anywhere40 between 1.0 and any positive integer. Default: 1.541 It is recommended to consider this as an hyperparameter to optimize.42 return_scores : bool43 If ``True``, return a dictionary with LOF scores for each44 evaluated channel. Default is ``False``.45 %(verbose)s46 47 Returns48 -------49 noisy_chs : list50 List of bad M/EEG channels that were automatically detected.51 scores : ndarray, shape (n_picks,)52 Only returned when ``return_scores`` is ``True``. It contains the53 LOF outlier score for each channel in ``picks``.54 55 See Also56 --------57 maxwell_filter58 annotate_amplitude59 60 Notes61 -----62 See :footcite:`KumaravelEtAl2022` and :footcite:`BreunigEtAl2000` for background on63 choosing ``threshold``.64 65 .. versionadded:: 1.766 67 References68 ----------69 .. footbibliography::70 """ # noqa: E50171 _soft_import("sklearn", "using LOF detection", strict=True)72 from sklearn.neighbors import LocalOutlierFactor73 74 _validate_type(raw, BaseRaw, "raw")75 # Get the channel types76 channel_types = raw.get_channel_types()77 picks = _picks_to_idx(raw.info, picks=picks, none="data", exclude="bads")78 picked_ch_types = set(channel_types[p] for p in picks)79 80 # Check if there are different channel types81 if len(picked_ch_types) != 1:82 raise ValueError(83 f"Need exactly one channel type in picks, got {sorted(picked_ch_types)}"84 )85 ch_names = [raw.ch_names[pick] for pick in picks]86 data = raw.get_data(picks=picks)87 clf = LocalOutlierFactor(n_neighbors=n_neighbors, metric=metric)88 clf.fit_predict(data)89 scores_lof = clf.negative_outlier_factor_90 bad_channel_indices = [91 i for i, v in enumerate(np.abs(scores_lof)) if v >= threshold92 ]93 bads = [ch_names[idx] for idx in bad_channel_indices]94 logger.info(f"LOF: Detected bad channel(s): {bads}")95 if return_scores:96 return bads, scores_lof97 else:98 return bads99 