MATLAB | 用行为数据处理EEG数据事件码
更多技术干货第一时间送达
脑机接口社区推荐搜索
脑电图(Electroencephalogram,EEG)是通过精密的电子仪器,从头皮上将脑部的自发性生物电位加以放大记录而获得的图形,同时具有极高的时间分辨率。EEGLAB和ERPLAB常被用来处理脑电数据,因其操作方便且免费。
用EEGLAB和ERPLAB提取ERP成分的同学都知道,Extract epochs或Extract bin-based epochs的方法可以锁定感兴趣的事件诱发的时刻,对后续进行ICA分析、叠加平均有利。但它对事件码的依赖很高。比如关键的事件码丢失了,将无法识别潜在的有效的试次,会影响叠加平均的有效试次数,降低信噪比。
在每个试次开始时,用Writeport的方法打{1,2,3}中的一个。
接着用OnsetSignalData的方法在控件Stim处打{11,12,21,22,31,32,33,34,35}等9个码中的一个。
在700~1000ms之后,用Writeport的方法打{1}。
随后用OnsetSignalData的方法在控件StandardStim处打{11,22,31,32,33,34,35}等7个码中的一个。
在700~1000ms后,结束一个试次。
反应码{4,6}由被试按键触发,用Writeport的方法打入,它可能出现在某个inline语句结束的时刻。
图7
writeport &378,0
2.提取行为数据
图9
图10.实验结束后产生的数据文件
我们需要将E-DataAid文件转化为EXCEL数据并提取其中的:FixMark、EmoMark、EmoMark2、Wait1.OnsetTime、反应时间等数据。
这里,我们用Wait1.OnsetTime表示EmoMark的行为潜伏期,而EmoMark2由于没有记录到其行为潜伏期,所以无法予以匹配EEG潜伏期(详见下文结果)。
由于我的EEG数据是从正式实验开始记录的,所以行为数据需要剔除练习的行数据(顺便把休息阶段的行数据剔除)。得到几列数据为:
3.匹配EEG事件码潜伏期和行为数据潜伏期
在后续分析中,我们对{11,12,21,22,31,32,33,34,35}等9个码发生后的ERP成分感兴趣。它们是以OnsetSignalData的方式打入的,在行为数据中有潜伏期数据。所以我们以这些码为对象,尝试EEG潜伏期和行为潜伏期的匹配。
首先,在EEGLAB界面导入此被试的EEG数据。
%提取EEG事件码数据
ureventData = EEG.urevent;
eventData = EEG.event;
设置感兴趣的事件码。
%设置感兴趣的事件码。
Mark=[11 12 21 22 31 32 33 34 35];
Loc1=logical(zeros(1,length(ureventData)));%建立逻辑型空向量
Loc =logical(zeros(1,length(ureventData)));%建立逻辑型空向量
for i=1:length(Mark)%判断事件码在数据中的位置
Loc1=[ureventData(:).type]==Mark(i);%判断存在事件码的位置
Loc=Loc|Loc1; %或然关系,表示该位置存在一个事件码
end
%将感兴趣的事件码从EEG事件码数据中提取出来。
EEGMark = find(Loc);
for i=1:length(EEGMark)
MarkData(i).type=[ureventData(EEGMark(i)).type];
MarkData(i).latency=[ureventData(EEGMark(i)).latency];
end
[BehaviorData,~,Raw]=xlsread('D:\2018.04-2021.07Postgraduate\11-biye\XQEMMN\data\Sub1\Sub1Behavior.xlsx');
得到BehaviorData和Raw两组数据,后者是含标题的数据。
图14
3.3.将事件码的EEG潜伏期和行为潜伏期按顺序排列
TypeDif=0;%建立字段用于储存事件码的差异
for i=1:length(MarkData)
k=floor((i+1)/2); %行为数据的第k行数据放到Mark数据的第i行
j=2-mod(i,2);
MarkData(i).BehType=BehaviorData(k,j); %行为数据的第1列数据放到Type列单数行,第2列数据放到Type列双数行。
MarkData(i).BehLatency=(1+(-1)^(j+1))/2*BehaviorData(k,4); %行为数据的第4列数据放到Latency列单数行,第5列数据放到Latency列双数行。
MarkData(i).LatencyDif=(1+(-1)^(j+1))/2*(MarkData(i).BehLatency-MarkData(i).latency);%计算时间戳的差异
MarkData(i).TypeDif= MarkData(i).type-MarkData(i).BehType; %判断事件码是否有差异
TypeDif = TypeDif + abs(MarkData(i).TypeDif); %计算事件码的差异
end
得到新的MarkData:
图15
另一方面,我们在上述代码中用TypeDif记录了事件码的差值,结果TypeDif得到全为0,表明EEG事件码和行为数据是匹配的。
3.5.用行为数据复原事件码的EEG潜伏期的条件
事件码由OnsetSignalData的方式打,且行为数据记录了该控件的OnsetTime;
知道至少一个事件码的EEG潜伏期和对应的行为潜伏期,用以计算潜伏期差值;
保留感兴趣的事件{11,12,21,22,31,32};
保留事件码{1}后面的{11,12,21,22,31,32};
去除按键所在的事件;
SaveNum=0;%建立字段,用于计算保留的事件码数
for i=1:length(MarkData)
k=floor((i+1)/2); %行为数据的第k行数据放到Mark数据的第i行
j=2-mod(i,2);
%↓判断FixMark是否为1,去除2,3事件码
MarkData(i).FixChnMark = floor(1/MarkData(i).FixMark);%floor(1/2)=0,floor(1/3)=0;
%↓判断是否作了按键反应,去除按键所在的事件;
if BehaviorData(k,6)~=0 & j==2
MarkData(i).ReactMark = 0;
else
MarkData(i).ReactMark = 1;
end
%↓保留同时满足上述两个条件的事件
MarkData(i).Save = MarkData(i).ReactMark & MarkData(i).FixChnMark;
%↓计算保留的事件数
SaveNum = SaveNum+MarkData(i).Save;
end
得到SaveNum=1517。
图18
4.3.抽样检查运行结果是否符合预期
图19.行为数据
图20
通过简单的抽样检查发现运行结果是符合预期的。
4.4.取出符合条件的事件码
j=1;
for i=1:length(EEGMark)
if MarkData(i).Save==1
Chnevent(j).type=MarkData(i).type;
Chnevent(j).latency=MarkData(i).latency;
Chnevent(j).urevent=j;
Chnurevent(j).type=MarkData(i).type;
Chnurevent(j).latency=MarkData(i).latency;
j=j+1;
end
end
命名为Chnevent和Chnurevnt,对应EEG里的EEG.event和EEG.urevent。
%替换事件码数据
EEG.event=Chnevent;
EEG.urevent=Chnurevent;
图22
5.后续
%复原事件码数据
EEG.urevent = ureventData;
EEG.event = eventData;