说明
转到末尾下载完整示例代码。
处理坏导联#
本教程介绍如何手动标记坏导联,以及如何根据其他传感器上的良好信号重建坏导联。
与往常一样,我们先导入所需的模块并加载一些示例数据:
# Authors: The MNE-Python contributors.
# License: BSD-3-Clause
# Copyright the MNE-Python contributors.
import os
from copy import deepcopy
import numpy as np
import mne
sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(
sample_data_folder, "MEG", "sample", "sample_audvis_raw.fif"
)
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
标记坏导联#
有时个别导联会发生故障,提供的数据噪声过大而无法使用。MNE-Python 可以方便地在分析流程中忽略这些导联,而无需真正删除这些导联的数据。其做法是在一个列表中记录坏导联的索引,并在进行分析或绘图任务时查看该列表。坏导联列表存储在 Info 对象的 'bads' 字段中,而该对象附加在 Raw、Epochs 和 Evoked 对象上。
print(raw.info["bads"])
['MEG 2443', 'EEG 053']
这里可以看到,我们从磁盘加载的 .fif 文件一定记录过被标记为“坏”的导联——这是个好消息,因为这意味着,如果我们在中间阶段保存数据并在之后重新加载,我们对坏导联列表所做的任何修改都会被保留。由于上面已经看到 EEG 053 是坏导联之一,我们把它与其他一些 EEG 导联一起绘制,看看它到底哪里有问题。我们可以使用标准的 plot() 方法,并且不用逐个列出导联名称(['EEG 050', 'EEG 051', ...]),而是用正则表达式配合 pick_channels_regexp() 函数选出 050 到 059 之间的所有 EEG 导联(其中 . 是通配符):
picks = mne.pick_channels_regexp(raw.ch_names, regexp="EEG 05.")
raw.plot(order=picks, n_channels=len(picks))

对坏的 MEG 导联(MEG 2443)也可以这样做。由于我们知道 Neuromag 系统(录制示例数据所用的系统)用 MEG 导联编号的最后一位表示传感器类型,这里的正则表达式将选出所有以 2 开头、以 3 结尾的导联:
picks = mne.pick_channels_regexp(raw.ch_names, regexp="MEG 2..3")
raw.plot(order=picks, n_channels=len(picks))

首先要注意,被标记为“坏”的导联以浅灰色绘制在其他导联后面的一层中,以便与“好”导联区分开。图中可以清楚看出,EEG 053 根本没有记录到头皮电位,而 MEG 2443 的内部噪声看起来比邻近导联大得多——其信号比其他 MEG 导联高出几个数量级,显然应当排除。
如果你想更改哪些导联被标记为坏导联,可以直接编辑 raw.info['bads'];它是一个普通的 Python 列表,因此常用的列表方法都适用:
original_bads = deepcopy(raw.info["bads"])
raw.info["bads"].append("EEG 050") # add a single channel
raw.info["bads"].extend(["EEG 051", "EEG 052"]) # add a list of channels
bad_chan = raw.info["bads"].pop(-1) # remove the last entry in the list
raw.info["bads"] = original_bads # change the whole list at once
在 raw.plot() 或 epochs.plot() 的绘图窗口中,你也可以通过点击纵轴上的导联名称来交互式地切换某个导联是否被标记为“坏”(在 raw.plot() 窗口中,还可以点击绘图区域内该导联的波形来实现)。每次切换时 bads 字段都会立即更新,并且在绘图窗口关闭后仍保持修改后的状态。
mne.Info 对象 bads 字段中的坏导联列表会被 MNE-Python 代码库中数十个函数和方法自动考虑。这是通过函数或方法签名中的参数 exclude='bads' 一致实现的。通常该 exclude 参数也接受导联名称或索引列表,因此如果你想包含坏导联,可以传入 exclude=[](或其他要排除的导联列表)。例如:
# default is exclude='bads':
good_eeg = mne.pick_types(raw.info, meg=False, eeg=True)
all_eeg = mne.pick_types(raw.info, meg=False, eeg=True, exclude=[])
print(np.setdiff1d(all_eeg, good_eeg))
print(np.array(raw.ch_names)[np.setdiff1d(all_eeg, good_eeg)])
[367]
['EEG 053']
何时查找坏导联#
你可以在实验过程中采集数据时就开始寻找坏导联。如果发现任何平坦或噪声过大的导联,可以在实验日志或记录表中记下它们。如果你的系统会计算在线平均,这也是发现坏导联的好方法。数据采集完成后,可以通过使用 mne.io.Raw.plot() 浏览原始数据来做更彻底的坏导联检查,此时不要应用任何投影器或 ICA。最后,可以计算离线平均(同样禁用投影器、ICA 和 EEG 参考)来查找特性异常的导联。下面是坏导联未被正确标记时的 ERP/F 图示例:
raw2 = raw.copy()
raw2.info["bads"] = []
events = mne.find_events(raw2, stim_channel="STI 014")
epochs = mne.Epochs(raw2, events=events)["2"].average().plot()

Finding events on: STI 014
320 events found on stim channel STI 014
Event IDs: [ 1 2 3 4 5 32]
Not setting metadata
320 matching events found
Setting baseline interval to [-0.19979521315838786, 0.0] s
Applying baseline correction (mode: mean)
Created an SSP operator (subspace dimension = 3)
3 projection items activated
坏的 EEG 导联不太明显,但坏的梯度计很容易看出来。
请记住,标记坏导联应尽可能早地在分析流程中完成。当在 Raw 对象中标记坏导联后,这些标记会自动传递到派生对象类型的链条中:包括 Epochs 和 Evoked 对象,还包括噪声协方差对象、前向解计算、逆算子等。如果你直到分析流程的后期才发现坏导联,可能就需要回头重新运行整个流程,因此尽早仔细检查数据中的坏导联是值得投入时间的。
为什么要标记坏导联?#
许多分析计算都可能受到坏导联的强烈影响。例如,一个完全平坦的故障导联其导联方差为零,这会导致噪声估计低得不切实际。这种偏低的噪声估计会使该导联在皮层电流估计中获得很大的权重,而由于该导联是平坦的,皮层电流估计的幅度会急剧缩小。
反过来,噪声很大的导联也会造成问题。例如,它们可能导致过多 epoch 因信号幅度拒绝阈值而被丢弃,进而使跨传感器噪声协方差的估计不够稳健。噪声导联还会干扰 SSP 计算,因为投影器会在空间上偏向噪声导联的方向,从而可能抑制相邻的好导联。ICA 也会因类似原因被噪声导联破坏。另一方面,在进行机器学习分析时,坏导联的影响可能很有限,甚至没有影响(即坏导联不提供信息,因此会被算法忽略或降低权重)。
坏导联插值#
在某些情况下,仅排除坏导联就足够了(例如,如果你只打算分析某个特定的传感器 ROI,而坏导联不在该 ROI 内)。然而在跨被试分析中,通常希望所有被试保持相同的数据维度,而并不能保证所有被试的坏导联都相同。这种情况下可以删除只要在任一被试中为坏的导联,但这可能导致数据秩急剧下降(并在此过程中丢弃相当多的干净数据)。此时更理想的做法是根据坏导联周围良好传感器的信号插值重建坏导联的信号。
插值的工作原理#
在 MNE-Python 中,EEG 导联的插值使用球面样条方法 [1],该方法将传感器位置投影到单位球面上,并根据良好位置的信号插值坏传感器位置处的信号。数学细节见 Bad channel repair via interpolation。MEG 导联的插值使用计算前向解时所用的场映射算法。
MNE-Python 中的插值#
在 Raw 对象中插值坏导联使用 interpolate_bads() 方法,它会自动对 EEG 和 MEG 导联分别应用正确的方法(分别为球面样条和场插值)(Epochs 对象有对应的方法 mne.Epochs.interpolate_bads())。为说明其工作方式,我们先把 raw 对象裁剪到仅 3 秒,以便绘图:
raw.crop(tmin=0, tmax=3).load_data()
Reading 0 ... 1802 = 0.000 ... 3.000 secs...
默认情况下,interpolate_bads() 会在插值后清空 raw.info['bads'],使插值后的导联不再被后续计算排除。这里为了说明起见,我们通过指定 reset_bads=False 来阻止这一行为,这样在插值前后绘制数据时,受影响的导联仍会以红色显示:
eeg_data = raw.copy().pick(picks="eeg")
eeg_data_interp = eeg_data.copy().interpolate_bads(reset_bads=False)
for title, data in zip(["orig.", "interp."], [eeg_data, eeg_data_interp]):
with mne.viz.use_browser_backend("matplotlib"):
fig = data.plot(butterfly=True, color="#00000022", bad_color="r")
fig.subplots_adjust(top=0.9)
fig.suptitle(title, size="xx-large", weight="bold")
Setting channel interpolation method to {'eeg': 'spline'}.
Interpolating bad channels.
Automatic origin fit: head of radius 91.2 mm
Computing interpolation matrix from 59 sensor positions
Interpolating 1 sensors
Using matplotlib as 2D backend.
Using qt as 2D backend.
Using matplotlib as 2D backend.
Using qt as 2D backend.
注意,方法 pick() 的默认参数包含 exclude=(),这确保坏导联不会被自动从选择中剔除。下面是插值后的梯度计导联的对应示例;由于导联更多,这次我们使用更透明的灰色:
grad_data = raw.copy().pick(picks="grad")
grad_data_interp = grad_data.copy().interpolate_bads(reset_bads=False)
for data in (grad_data, grad_data_interp):
data.plot(butterfly=True, color="#00000009", bad_color="r")
Setting channel interpolation method to {'meg': 'MNE'}.
Interpolating bad channels.
Automatic origin fit: head of radius 91.2 mm
Computing dot products for 203 MEG channels...
Computing cross products for 203 → 1 MEG channel...
Preparing the mapping matrix...
Truncating at 84/203 components to omit less than 0.0001 (9.7e-05)
小结#
坏导联的排除或插值是 EEG/MEG 预处理中的重要步骤。MNE-Python 提供了标记和插值坏导联的工具;“坏”导联的列表会自动传播到后续处理阶段。若想要更自动化的坏导联检测与插值方法,可以考虑使用 autoreject 包,它能很好地与基于 MNE-Python 的流程配合。
参考文献#
脚本总运行时间:(0 分钟 5.418 秒)



