本教程描述了如何从原始记录中读取event,以及如何在两种不同event的表示之间进行转换(Events arrays 和 Annotations objects,数组表述和注释表述)。在入门教程中,我们有了一个从“STIM”通道阅读实验事件的例子,而在这里,我们将更广泛地讨论event。
老样子,加载数据先:
1 | import numpy as np |
Events array和Annotations objects的区别
Events和Annotations都有相同的目的,它们提供EEG/MEG记录期间的时间映射,并描述在这些时间发生的事情。换句话说,他们把“何时”和“什么”联系在一起。主要的区别是:
- 单位:Events表示以采样率为单位的时间,而Annotations表示以秒为单位的时间。
- 描述:Events的事件是一个ID,是一个整数,而Annotations的事件是一个字符串。
- 持续时间如何编码:Event数组中的事件没有持续时间(尽管可以用Events数组中的开始/结束事件对表示持续时间),而annotation对象的每个元素都必须包含持续时间(尽管如果需要瞬时事件,持续时间可以为0)。
- 类型:events存储为NumPy数组,而annotation是在MNE-Python中定义的类似liest的一个新类型。
什么是STIM通道
STIM就是刺激通道,是一个不接收来自EEG, MEG或其他传感器信号的通道。STIM通道记录电压信号,通常是由实验室控制计算机发送的固定大小的方波直流脉冲,这些电压被时间锁定在实验事件上,例如刺激的开始,或受试者按下按钮等,这些脉冲有时被称为TTL脉冲、event脉冲、trigger或marker,我比较喜欢叫trigger)。
直流脉冲可以全部在一个STIM通道上,在这种情况下,不同的实验事件或试验类型被编码为不同的电压方波的振幅。或者它们可以分布在几个通道上,在这种情况下,脉冲发生的通道可以用来编码不同的事件或条件。即使在具有多个STIM通道的系统上,通常也会有一个通道记录其他STIM通道的加权和,因此该通道上的电压水平可以明确地解码为特定的事件类型。在旧的神经成像系统上,一般这个通道叫做STI 014,新的系统上,一般叫做STIM101。我们可以直接在raw上pick出来这些通道:
1 | raw.copy().pick(picks="stim").plot(start=3, duration=6) |
可以看到,STI 014是包含STI 001等通道的方波信号的。
把一个STIM通道的信号转换成event array
使用find_events可以将STIM上的信号转换为event array。每个信号的起始/结束的样本数被记录为事件时间,振幅被转换为整数,存储在NumPy数组中。在其最简单的形式中,该函数只需要Raw对象,以及从中读取事件的通道名称:
1 | events = mne.find_events(raw, stim_channel="STI 014") |
86 events found on stim channel STI 014
Event IDs: [ 1 2 3 4 5 32]
[[27977 0 2]
[28345 0 3]
[28771 0 1]
[29219 0 4]
[29652 0 2]]
MNE-Python事件实际上有三个值,第一个表示样本数,第三个表示事件代码,第二个表示前一个样本上的事件(一般都是0),它可以用于检测持续时间长于一个样本的事件的端点。
如果你没有提供STIM通道的名称,find_events
将首先查找MNE_STIM_CHANNEL、MNE_STIM_CHANNEL_1等变量的MNE-Python配置变量。如果没有找到,则尝试通道STI 014和STI101,还没有找到,就选择raw.ch_names
中类型为“STIM”的第一个通道。
如果我们经常使用来自具有不同STIM通道名称的几个不同MEG系统的数据,则设置MNE_STIM_CHANNEL 这个config变量可能不是很有用,但对于数据全部来自单个系统的研究人员来说,config该变量肯定可以节省时间。
find_events
有几个选项,包括将事件与STIM通道信号的起始/偏移量进行对齐、设置最小信号持续时间和处理连续信号(其间不返回零)的选项。例如,可以通过将output=’step’传递给find_events
来有效地编码事件持续时间。
将嵌入events转换为annotation
一些EEG/MEG系统生成的文件里,event存储在单独的数据阵列中而不是STIM通道上。例如,EEGLAB格式将事件存储为.set文件中。当读取这些文件时,MNE-Python会自动将存储的事件转换为一个Annotations对象,并将其存储为Raw对象的Annotations属性:
1 | testing_data_folder = mne.datasets.testing.data_path() |
Reading /home/circleci/mne_data/MNE-testing-data/EEGLAB/test_raw.fdt
<Annotations | 154 segments: rt (74), square (80)>
annotation对象中的数据可以通过它的三个属性访问:开始时间、持续时间和描述。在这里我们可以看到EEGLAB文件中存储了154个事件,它们的持续时间都是0秒,有两种不同的事件类型,第一个事件发生在记录开始后1秒左右:
1 | print(len(eeglab_raw.annotations)) |
154
{0.0}
{‘rt’, ‘square’}
1.000068
在Events arrays 和Annotations objects之间进行转换
这样做可能是因为,例如,需要Events array来遍历连续数据,或者需要利用某些函数的“annotation-aware”功能,该功能可以在数据与某些annotation重叠时自动忽略数据的范围。
要将annotation转换为Events,请使用函数mne.events_from_annotations这个函数将为raw.annotations.description的每个唯一元素分配一个整数ID,并将返回整数ID的映射的event array。
默认情况下,还将在每个annotation开始时创建一个event,可以通过events_from_annotations的chunk_duration参数来修改,以在每个注释范围内创建等间距的event。
1 | events_from_annot, event_dict = mne.events_from_annotations(eeglab_raw) |
Used Annotations descriptions: [‘rt’, ‘square’]
{‘rt’: 1, ‘square’: 2}
[[128 0 2]
[217 0 2]
[267 0 1]
[602 0 2]
[659 0 1]]
可以传递一个dict,指定映射作为events_from_annotations的event_id参数。
1 | custom_mapping = {"rt": 77, "square": 42} |
Used Annotations descriptions: [‘rt’, ‘square’]
{‘rt’: 77, ‘square’: 42}
[[128 0 42]
[217 0 42]
[267 0 77]
[602 0 42]
[659 0 77]]
进行想法 的转换,可以使用annotation_from_events
来进行,如:
1 | mapping = { |
现在,annotation将在绘制raw数据时自动出现,并将根据其标签值进行颜色编码:
1 | raw.plot(start=5, duration=5) |
用一个annotation生成多个events
如上所述,可以使用events_from_annotations
的chunk_duration参数从annotation对象生成等间隔的event。例如,假设我们在Raw对象中有一个annotation,表明受试者何时处于REM睡眠状态,并且我们希望对这些数据范围执行静息状态分析。我们可以在每个“REM”范围内创建一系列等间隔事件的Events,然后使用这些事件生成epoch,然后进一步分析这些epoch。
这其实就是静息态分析的一种方法。
1 | # create the REM annotations |
Used Annotations descriptions: [‘REM’]
现在我们可以检查我们的event确实落在5-21秒和41-52秒的范围内,并且间隔约1.5秒(由于采样率会引起的一些抖动)。以下是四舍五入到毫秒的事件时间:
1 | print(np.round((rem_events[:, 0] - raw.first_samp) / raw.info["sfreq"], 3)) |
[ 5. 6.5 8. 9.5 11. 12.501 14.001 15.501 16.999 18.499 41. 42.5 44. 45.5 47. 48.5 50. ]
其他静息态分析可以参考mne.make_fixed_length_events
的用法,官网可查,此处省略。