如何使用MNE-Python库用Python处理EEG信号

1,367 阅读8分钟

简介

在这篇文章中,我们将学习如何使用MNE-Python库用Python处理EEG信号。

目录

  • 脑电图简介
  • 安装/设置
  • 加载数据
  • 绘制EEG信号图
  • 预处理
  • 划线
  • 结论

脑电图简介

脑电图(EEG)是一种以脑电波形式连续记录大脑活动的技术。脑电图被普遍使用,因为它提供了一种无创的、简单的、廉价的方法来测量高分辨率的神经活动。

脑电图分析被大量用于评估大脑疾病,特别是癫痫或其他发作性疾病。它也被用于脑-机接口(BCI)。

脑电信号可以被看作是一个时间序列,因为脑电记录测量的是特定时间段的大脑活动。

脑电图设备是由放置在头皮上的不同电极组成。这些电极用蒙太奇表示为通道。有不同类型的蒙太奇,在这段视频中有详细描述。一个典型的EEG系统可以有1到256个通道。这些通道是根据它们在头皮上的位置来命名的。

安装/设置

在这篇文章中,我们将使用 MNE-Python库。它包含很多工具和算法,我们可以用来轻松分析EEG/MEG的记录。

我们可以通过使用以下pip 命令来安装 MNE:

pip install mne

NumPy也需要被安装:

pip install numpy

关于更详细的安装说明,请看这个链接的MNE和这个链接的NumPy。

导入必要的模块/库:

import os
import numpy as np
import mne

处理脑电图数据

加载数据

MNE软件包支持各种EEG文件格式,包括以下几种:

  • 欧洲数据格式(.edf)
  • EGI简单的二进制文件(.egi)
  • EEGLAB集合文件(.set)

MNE有一个样本数据集,我们可以用它来熟悉处理EEG文件。下面的代码显示了我们如何读取一个MEG/EEG样本文件。对于不同的文件格式,有不同的方法。由于样本文件的扩展名是.fif,我们调用read_raw_fif 方法。另外,由于我们特别关注EEG通道,我们可以通过使用pick_types 方法排除所有非EEG通道:

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample', 'sample_audvis_filt-0-40_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file)
raw = raw.pick_types(meg=False, eeg=True, eog=False, exclude='bads')

这将创建一个Raw 对象(更多细节见这里)。

我们可以通过打印info 属性(一个类似字典的对象)来检查这个Raw 对象:

print(raw.info)
Output:
<Info | 14 non-empty values
 bads: []
 ch_names: EEG 001, EEG 002, EEG 003, EEG 004, EEG 005, EEG 006, EEG 007, ...
 chs: 59 EEG
 custom_ref_applied: False
 dev_head_t: MEG device -> head transform
 dig: 146 items (3 Cardinal, 4 HPI, 61 EEG, 78 Extra)
 file_id: 4 items (dict)
 highpass: 0.1 Hz
 hpi_meas: 1 item (list)
 hpi_results: 1 item (list)
 lowpass: 40.0 Hz
 meas_date: 2002-12-03 19:01:10 UTC
 meas_id: 4 items (dict)
 nchan: 59
 projs: Average EEG reference: off
 sfreq: 150.2 Hz
>

info 属性记录了频道位置、录制日期、频道数量等信息。关于Info 结构的进一步详细信息,可以在这里找到MNE文档。

裁剪数据

MNERaw 对象有一个crop 方法,可以用来限制原始文件中的数据在特定时间(以秒为单位)之间。这可以帮助节省内存:

raw.crop(0, 60) # keeps the first minute of data and discards the rest

绘制脑电图信号

MNE有几种方法来绘制Raw 对象。这里有一个方法可以生成原始数据的图:

raw.plot()

sample_plot-1

我们还可以绘制每个通道的功率谱密度(PSD)。PSD显示功率与频率的关系,以每单位频率的功率来衡量。它显示了哪些频率的变化是强的,以及哪些频率的变化是弱的:

raw.plot_psd()

sample_psd

在交互式Python会话中,plot 函数创建交互式图表。这些功能允许滚动、缩放、注释等。

预处理

重新取样

脑电图记录具有很高的时间分辨率,所以它们通常以高采样率记录(例如,1000赫兹或更高)。虽然这使得记录非常精确,但也消耗了更多的内存。在不需要高度精确计时的情况下,对EEG信号进行降频可以帮助节省大量的计算时间。

Raw 物体有一个 方法,可以用来从一个采样率转换到另一个。resample

raw.resample(600) #resamples to a rate of 600 Hz
Output:
Measurement date	December 03, 2002 19:01:10 GMT
Experimenter	Unknown
Participant	Unknown
Digitized points	0 points
Good channels	59 EEG
Bad channels	None
EOG channels	Not available
ECG channels	Not available
Sampling frequency	600.00 Hz
Highpass	1.00 Hz
Lowpass	40.00 Hz
Projections	Average EEG reference : off
Filenames	sample_audvis_filt-0-40_raw.fif
Duration	00:01:00 (HH:MM:SS)

滤波

脑电图数据可能有各种假象和噪声,因此必须进行预处理,以最大限度地提高信噪比(SNR),它衡量的是信号功率与噪声功率的比率。

滤波是用于降低噪音/去除伪影的几种技术之一。

Raw 对象有一个 ,该方法需要两个参数-- ,代表低通带边缘, ,代表高通带边缘。filter lfreq hfreq

高通滤波

高通滤波衰减了低于某一截止频率的频率。信号的其余部分保持不变。

下面的代码过滤信号衰减了1Hz以下的信号,其余部分保持不变。由于hfreqNone ,没有上通带的边缘,所以信号是高通的。

raw.filter(1., None)

低通滤波

低通滤波本质上与高通滤波相反。它不是减弱低于某一频率的部分信号,而是减弱高于某一频率的部分信号。它之所以被称为低通,是因为它让低于某一截止点的频率通过。

下面的代码衰减了信号中高于50Hz的部分,其余部分没有变化。由于lfreqNone ,所以没有低通带的边缘,所以信号是低通的。

raw.filter(None, 50.)

陷波滤波器(带阻滤波器)

陷波滤波器是低通和高通滤波器的结合。它可以在一个特定的频率范围内衰减信号。带阻滤波器所衰减的频率范围被称为止损带。

Raw 对象有一个 方法,它接收一个特定的频率或一个频率列表来衰减信号。notch_filter

raw.notch_filter(60)

上面这个例子,将信号衰减在60赫兹。

陷波滤波器通常用于去除电力线噪音,电力线噪音的频率为50或60赫兹,这取决于录音地点。在谐波频率(电力线频率的整数倍,如60、120、180等)可能会有峰值。

我们可以通过使用numpy.arange 方法将陷波滤波器应用于这些频率中的每一个频率。它在start (含)和stop (不含)参数之间转出一个均匀间隔的数值阵列。这些均匀的值之间的距离为step ,这是另一个参数numpy.arange

下面的代码将对频率为60、120、180和240的部分信号进行衰减。

# the first 60 is start (inclusive), 241 is stop (exlusive), and 60 is step
raw.notch_filter(np.arange(60, 241, 60)) 

分期付款

历时是指从连续EEG数据中提取的等长的数据段。通常情况下,历时是围绕刺激事件或反应提取的,但有时也会使用连续或重叠的历时。

MNE有一个Epochs 对象,用来表示 epoched数据。Epochs 对象被用于EEG分析的其他步骤,包括用于机器学习的特征提取。

为了创建 epoched 数据,MNE-Python需要一个Raw 对象以及一个事件数组。

事件

MNE中的事件提供了EEG/MEG记录期间特定时间和这些时间发生的事情之间的映射。事件以二维NumPy数组的形式存储。

有两种方法来创建事件:从文件/Raw 对象中读取或创建同等大小的事件。

读取事件

我们将使用一个不同的样本记录,因为我们最初使用的那个样本不包含任何事件。

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)

下面的代码从一个Raw 对象中读取事件。

events = mne.find_events(raw)
Output:
320 events found
Event IDs: [ 1  2  3  4  5 32]

创建等长的事件

有时,原始EEG记录中可能不包含任何事件。在这种情况下,可以生成一个等距的事件阵列。下面的代码为EEG数据的前10秒创建了秒长的事件。

events = mne.make_fixed_length_events(raw, start=0, stop=10, duration=1.)

要创建重叠一定时间的事件,我们可以在调用make_fixed_length_events 方法时使用overlap 参数指定这个时间。

events = mne.make_fixed_length_events(raw, start=0, stop=10, duration=1., overlap=0.5)

从事件中创建Epoched数据

在加载/创建事件之后,创建一个Epochs 对象是相当简单的。

epochs = mne.Epochs(raw, events, preload=True).pick_types(eeg=True)

preload=True 在创建 对象时,从磁盘加载所有的历时。Epochs

选择历时数据

现在我们有了带有事件标签的Epochs 对象,我们可以用方括号选择历时。

例如,我们可以绘制事件标签为 "1 "的历时(这些事件标签有实际意义,但为了简单起见,这里没有显示)。

epochs['1'].plot()

输出。
epochs_plot

结论

在这篇文章中,我们了解了EEG信号,如何加载、分析、预处理等。了解如何处理脑电信号对建立在它之上的任务非常有帮助--其中一个重要的例子是训练机器学习模型来对脑电片段进行分类。

这篇文章就到这里,感谢您的阅读。