Python中的EEG分析:Python MNE教程


Python MNE是一种开源磁脑电图(MEG)和脑电图(EEG)分析和可视化工具。它具有很高的通用性,因为它可以应用于许多设备的数据格式。在本文中,我们将解释由MEG和EEG的MNE进行的分析过程以及最基本的教程。

执行环境

  • Mac OS 10.15.3
  • Python 3.6.5
  • mne 0.20.4

安装

您可以使用Anaconda或pip安装它。建议在官方页面上使用Anaconda。

  • 对于水蟒
1
conda install -c conda-forge mne
  • 对于点
1
pip install -U mne

另外,似乎可以设置GPU,因此如果需要花费一些时间进行过滤和采样,则可能需要设置它。

1
2
3
4
5
6
$ conda install cupy
$ MNE_USE_CUDA=true python -c "import mne; mne.cuda.init_cuda(verbose=True)"
Enabling CUDA with 1.55 GB available memory

>>> mne.utils.set_config('MNE_USE_CUDA', 'true')  
# mne.io.Raw.filter()などにn_jobs='cuda'を渡す

MEG / EEG分析教程

在本教程之后,我们将解释EEG分析的典型处理和MNE的基本数据结构。我将以简洁的方式对其进行解释,因此,如果您具有英语和EEG分析的知识,则应阅读原始页面。

分析程序

  • 资料载入
  • 预处理
  • 事件检测
  • 切入一个时代
  • 时频分析
  • 诱发反应的估计

模块导入

1
2
3
import os
import numpy as np
import mne

数据加载

将作为样本准备的数据加载到

mne(所有数据集的列表)中。顺便说一下,MEG和EEG每种设备都有几种文件格式,但是有一个加载这些数据的功能(支持的数据格式)

1
2
3
4
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)

这一次,我们使用sample_audvis_filt-0-40_raw.fif,它是40Hz的低通滤波数据。下载数据大约需要1分钟(1.54GB)。 downloading.png

函数

mne.io.read_raw_fif()以fif格式从文件中读取称为"原始对象"的MNE数据结构的数据。

顺便说一下,在该数据的实验中,每750毫秒随机向对象显示视觉刺激和听觉刺激。另外,有时在屏幕上会显示出笑脸,而被摄对象看到它时会按下按钮。这些刺激的表现称为事件。

显示已加载的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
print(raw)
print(raw.info)

# output
<Raw | sample_audvis_filt-0-40_raw.fif, 376 x 41700 (277.7 s), ~3.6 MB, data not loaded>
<Info | 15 non-empty values
 bads: 2 items (MEG 2443, EEG 053)
 ch_names: MEG 0113, MEG 0112, MEG 0111, MEG 0122, MEG 0123, MEG 0121, MEG ...
 chs: 204 GRAD, 102 MAG, 9 STIM, 60 EEG, 1 EOG
 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: 376
 projs: PCA-v1: off, PCA-v2: off, PCA-v3: off, Average EEG reference: off
 sfreq: 150.2 Hz
>

MNE中,数据由称为Raw,Epoch和Evoked对象的对象管理。这些通过数据的时分来区分,并且Raw包含从EEG记录开始到停止的所有时间的数据,因此最好将其视为被事件分隔之前的状态。 。 raw.info保留诸如raw的通道数,名称和采样率之类的信息。

显示此Raw对象的功率谱密度(PSD)和每个传感器的波形。 plot()可以在交互式外壳中滚动和缩放。

1
2
raw.plot_psd(fmax=50)
raw.plot(duration=5, n_channels=30)

psd_and_plot.png

预处理

在EEG / MEG分析的噪声处理中,执行标准独立成分分析(ICA)。简而言之,通过将多通道信号分解为独立的分量,可以执行去除与大脑活动无关的事物(例如电源噪声和闪烁)的处理。

此处,对于364通道的信号,在分解为20个独立的分量之后,指定[1, 2]和要删除的分量的索引(实际的删除由以下ica.apply(raw)执行。我会)。 ica_properties()显示独立组件的信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# set up and fit the ICA
ica = mne.preprocessing.ICA(n_components=20, random_state=97, max_iter=800)
ica.fit(raw)
ica.exclude = [1, 2]  # details on how we picked these are omitted here
ica.plot_properties(raw, picks=ica.exclude)

# output
Fitting ICA to data using 364 channels (please be patient, this may take a while)
Inferring max_pca_components from picks
Selecting by number: 20 components
Fitting ICA took 2.4s.
    Using multitaper spectrum estimation with 7 DPSS windows
138 matching events found
No baseline correction applied
Not setting metadata
0 projection items activated
0 bad epochs dropped
138 matching events found
No baseline correction applied
Not setting metadata
0 projection items activated
0 bad epochs dropped

ica_1.png
ica_2.png

为了获得去除指定独立分量的信号,请将ICA对象应用??于Raw对象。

1
2
3
orig_raw = raw.copy()
raw.load_data()
ica.apply(raw)

当显示前通道时,如以下两个图所示,消除了噪音。

1
2
3
4
5
6
7
8
9
10
11
12
13
# show some frontal channels to clearly illustrate the artifact removal
chs = ['MEG 0111', 'MEG 0121', 'MEG 0131', 'MEG 0211', 'MEG 0221', 'MEG 0231',
       'MEG 0311', 'MEG 0321', 'MEG 0331', 'MEG 1511', 'MEG 1521', 'MEG 1531',
       'EEG 001', 'EEG 002', 'EEG 003', 'EEG 004', 'EEG 005', 'EEG 006',
       'EEG 007', 'EEG 008']
chan_idxs = [raw.ch_names.index(ch) for ch in chs]
orig_raw.plot(order=chan_idxs, start=12, duration=4)
raw.plot(order=chan_idxs, start=12, duration=4)

# output
Reading 0 ... 41699  =      0.000 ...   277.709 secs...
Transforming to ICA space (20 components)
Zeroing out 2 ICA components

orig_raw.png
prune_raw.png

事件检测

此样本数据包含所谓的STIM通道,其中包含有关事件类型及其时间的数据。这次,名为STI 014的通道适用,因此请指定它并检索实验的事件。

1
2
3
4
5
6
7
8
9
10
11
12
events = mne.find_events(raw, stim_channel='STI 014')
print(events[:5])  # show the first 5

# output
319 events found
Event IDs: [ 1  2  3  4  5 32]

[[6994    0    2]
 [7086    0    3]
 [7192    0    1]
 [7304    0    4]
 [7413    0    2]]

events是一个numpy数组,第一列代表采样时间,最后一列代表事件类型(ID)(中间的0可以忽略)。在这里,似乎有一个ID为[ 1 2 3 4 5 32]的事件。

<表格>

事件ID

条件


<身体>

1

左耳的听觉刺激(音调)

2

右耳的听觉刺激(音调)

3

左侧视野的视觉刺激(棋盘)

4

视觉刺激(棋盘)到正确的视野

5

笑脸(赶上审判)

32

按下主题按钮


以字典类型存储事件ID和事件描述。顺便说一下,这里的键可以是任何字符串。

1
2
event_dict = {'auditory/left': 1, 'auditory/right': 2, 'visual/left': 3,
              'visual/right': 4, 'smiley': 5, 'buttonpress': 32}

在时间轴上绘制每个事件的演示时间。先前由event_id=evend_dict定义的ID和事件名称在图中相互对应。

1
2
fig = mne.viz.plot_events(events, event_id=event_dict, sfreq=raw.info['sfreq'],
                          first_samp=raw.first_samp)

events.png

切开纪元

在MEG / EEG分析中,当调查对某种刺激的响应时,信号被分为称为历元的单位,并被视为一个数据。通过在刺激前后切出一定的部分,将其制成一个数据单元。此切割过程称为外切。原始对象在录制过程中会一直保持连接状态,但是只有一部分对刺激的响应会被剪切掉并存储在称为Epochs对象的数据类型中。

在传世中,经常执行称为阈值拒绝的过程。这意味着,如果在相关时间内包含了太大声的信号,则该部分将产生很大的噪声,并将被从数据中排除。确定排除标准。

1
2
3
4
reject_criteria = dict(mag=4000e-15,     # 4000 fT
                       grad=4000e-13,    # 4000 fT/cm
                       eeg=150e-6,       # 150 μV
                       eog=250e-6)       # 250 μV

接下来,从Raw对象创建一个Epochs对象。 mne.Epochs的参数如下。

  • raw:包含信号数据的原始对象

  • events:具有时间和事件ID信息的numpy数组

  • event_id:将事件ID与它们的名称相关联的字典类型

  • tmin:削减刺激演示的秒数(在刺激演示之前加减号)

  • tmax:呈现刺激以剪切数据后几秒钟

  • reject:数据排除标准

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
epochs = mne.Epochs(raw, events, event_id=event_dict, tmin=-0.2, tmax=0.5,
                    reject=reject_criteria, preload=True)

# output
319 matching events found
Applying baseline correction (mode: mean)
Not setting metadata
Created an SSP operator (subspace dimension = 4)
4 projection items activated
Loading data for 319 events and 106 original time points ...
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on MAG : ['MEG 1711']
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on MAG : ['MEG 1711']
    Rejecting  epoch based on EEG : ['EEG 008']
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on EOG : ['EOG 061']
    Rejecting  epoch based on EOG : ['EOG 061']
10 bad epochs dropped

这次似乎已排除了10个时期。

由于这次没有处理按下被摄体的按钮的定时,因此仅以视听刺激事件为对象。另外,为了防止数据的数量在条件之间偏移,通过用epochs.equalize_event_counts()删除大量条件的事件来使每个条件的数量统一。

1
2
3
conds_we_care_about = ['auditory/left', 'auditory/right',
                       'visual/left', 'visual/right']
epochs.equalize_event_counts(conds_we_care_about)  # this operates in-place

event_dict中,将其指定为'auditory/left'auditory/right,但是通过将其传递给Epochs对象,如果指定了auditory,它将指向两个对象。在这里,我们将其分为听觉刺激数据和视觉刺激数据。

1
2
3
aud_epochs = epochs['auditory']
vis_epochs = epochs['visual']
del raw, epochs  # free up memory

在听觉数据中指定通道,并以相同的刺激提示时间显示平均波形。

1
2
3
4
5
6
7
8
9
10
11
12
13
aud_epochs.plot_image(picks=['MEG 1332', 'EEG 021'])

# output
136 matching events found
No baseline correction applied
Not setting metadata
0 projection items activated
0 bad epochs dropped
136 matching events found
No baseline correction applied
Not setting metadata
0 projection items activated
0 bad epochs dropped

meg_aud.png
meg_viz.png

作为补充,由于可以通过Epochs对象的get_data()方法以numpy数组的形式获取时间序列波形数据,因此可以通过为每个条件取一个数组将其输入到机器学习模型中是。

1
2
3
aud_epochs.get_data().shape

(136, 376, 106) # (エポック,チャンネル,時刻)

这同样适用于以下返回numpy数组的频率处理。

时频分析

进行时频分析以研究在MEG / EEG波形中出现刺激之前和之后的时间与频率之间的关系。在此,通过小波分析计算出对听觉刺激的反应并绘制。如彩条所示,红色越深,则该频率的幅度越大。 (更详细的示例)

1
2
3
4
5
6
7
frequencies = np.arange(7, 30, 3)
power = mne.time_frequency.tfr_morlet(aud_epochs, n_cycles=2, return_itc=False,
                                      freqs=frequencies, decim=3)
power.plot(['MEG 1332'])

# output
No baseline correction applied

tf.png

估计诱发反应

作为MNE的数据类型,Evoked对象是Epochs对象的总和。 MEG / EEG数据具有除大脑活动以外的大量噪声,通常通过求平均值来研究条件之间的差异。

在这里,我们绘制了听觉数据和视觉数据的添加波形之间的差异。

1
2
3
4
5
aud_evoked = aud_epochs.average()
vis_evoked = vis_epochs.average()

mne.viz.plot_compare_evokeds(dict(auditory=aud_evoked, visual=vis_evoked),
                             legend='upper left', show_sensors='upper right')

meg_evoked.png

eeg_evoked.png

grad_evoked.png

引发的对象具有进一步分析的方法。如果显示每个通道的波形,则可以看到跳动(P200)在前通道的100ms(N100)内变为负值,并在200ms左右具有正峰值。

1
aud_evoked.plot_joint(picks='eeg')

aud_eeg_evoked.png

您还可以在头皮上显示称为地形的分布。在这里,可以看到,在头皮上观察到的具有红色正电位和蓝色负电位的脑电波如下。

1
aud_evoked.plot_topomap(times=[0., 0.08, 0.1, 0.12, 0.2], ch_type='eeg')

eeg_aud_topo.png

另外,要查看条件之间的差异,mne.combine_evoked()使用两个条件差异的数据创建一个"已唤起"对象(此时,在其中一个对象上添加减号)。如果查看该对象的波形,则可以看到两个条件之间差异的波形。

1
2
evoked_diff = mne.combine_evoked([aud_evoked, -vis_evoked], weights='equal')
evoked_diff.pick_types('mag').plot_topo(color='r', legend=False)

evoked_diff.png

结论

我在这里仅介绍了基本功能,但是由于MNE官方页面上准备了许多教程和示例,所以我希望您可以将其作为进一步详细分析的第一步。

另外,由于Python具有许多用于统计和机器学习的库,我认为通过MEG / EEG机器学习进行分析将更加容易。