Scheduled service maintenance on November 22


On Friday, November 22, 2024, between 06:00 CET and 18:00 CET, GIN services will undergo planned maintenance. Extended service interruptions should be expected. We will try to keep downtimes to a minimum, but recommend that users avoid critical tasks, large data uploads, or DOI requests during this time.

We apologize for any inconvenience.

unfold_lab.m 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. function [ufresult]=unfold_lab(sub_num)
  2. %%
  3. cfg = struct();
  4. cfg.subject = num2str(sub_num);
  5. cfg.filtering='causal';
  6. cfg.mainfolder = ['/net/store/nbp/projects/IntoTheWild/Daten/EEG/Lab/VP' cfg.subject '/preprocessed/' cfg.filtering];
  7. tmp = load(fullfile(cfg.mainfolder,['4_ITW_Lab_subj' cfg.subject '_cleaningTimes.mat']));
  8. cfg.cleantimes = tmp.rej(:,1:2);
  9. cfg.file = ['3_ITW_Lab_subj' cfg.subject '_channelrejTriggersXensor.set'];
  10. tmp = pop_loadset('filename',['2_ITW_Lab_subj' cfg.subject '_bandpass_resample_deblank.set'],'filepath',cfg.mainfolder,'loadmode','info');
  11. tmp.chanlocs(find(strcmp({tmp.chanlocs.labels},'CzAmp2'))).labels = 'Iz';
  12. tmp=pop_chanedit(tmp, 'lookup','/net/store/nbp/projects/IntoTheWild/EEG_analysis/eeglab13_5_4b/plugins/dipfit2.3/standard_BESA/standard-10-5-cap385.elp');
  13. cfg.urchanlocs= tmp.chanlocs;
  14. cfg.avgref=1;
  15. cfg.saccade=1;
  16. cfg.regularize=0;
  17. tmp = load(fullfile(cfg.mainfolder,['6_ITW_Lab_subj' cfg.subject '_ICAcleancont.mat']));
  18. cfg.badcomponents = tmp.comps_to_rej;
  19. cfg.timewin=[-0.5 1];
  20. %% load EEG data
  21. EEG = pop_loadset('filename',cfg.file,'filepath',cfg.mainfolder);
  22. EEG = pop_select(EEG,'nochannel',{'AUX1','AUX2'});
  23. %% Load ICA and clean
  24. addpath('/net/store/nbp/projects/EEG/blind_spot/amica')
  25. mod = loadmodout12(fullfile(cfg.mainfolder,'amica'));
  26. EEG.icaweights = mod.W;
  27. EEG.icasphere = mod.S;
  28. EEG.icawinv = [];EEG.icaact = [];EEG.icachansind = [];
  29. EEG = eeg_checkset(EEG);
  30. EEG = pop_subcomp(EEG,cfg.badcomponents);
  31. %% interpolate missing channels
  32. % ET channels (Gaze etc.) not interpolated
  33. alldel = {'CzAmp2' 'BIP1' 'BIP2' 'BIP3' 'BIP4' 'BIP5' 'BIP6' 'BIP7' 'BIP8' 'AUX1' 'AUX2' 'AUX3' 'AUX4' 'AUX5' 'AUX6' 'AUX7' 'AUX8','TIME', 'L_GAZE_X', 'L_GAZE_Y', 'L_AREA','R_GAZE_X', 'R_GAZE_Y', 'R_AREA', 'INPUT', 'L-GAZE-X', 'L-GAZE-Y', 'L-AREA','R-GAZE-X', 'R-GAZE-Y', 'R-AREA', 'INPUT'};
  34. %remove VEOG from EEG, as there is no corresponding location
  35. EEG = pop_select(EEG, 'nochannel', {'VEOG'});
  36. if cfg.avgref
  37. %calculate avg ref
  38. EEG = pop_reref( EEG, []); %Participants’ averages were then re-referenced to a common average reference. (Rossion & Caharel, 2011)
  39. end
  40. idxs = ~ismember({cfg.urchanlocs.labels},alldel);
  41. %interpolate missing channels
  42. EEG= pop_interp(EEG,cfg.urchanlocs(idxs),'spherical');
  43. %% adjust for trigger delay
  44. % as calculated from photosensor
  45. if sub_num<36
  46. trigger_delay=0.0102; %10.2ms
  47. else
  48. trigger_delay=0.0348; %34.8ms
  49. end
  50. for evt=1:length(EEG.event)
  51. if str2num(EEG.event(evt).type)<=5 %only for stim onset [1 2 3 4 5]
  52. assert(str2num(EEG.event(evt).type)>0)
  53. EEG.event(evt).latency = EEG.event(evt).latency + (trigger_delay*EEG.srate);
  54. end
  55. end
  56. EEG = eeg_checkset(EEG);
  57. %% adjust cleaning times to be between 1 and length of data
  58. [a,b]=find(cfg.cleantimes(:,1)<1);
  59. if ~isempty(a)
  60. cfg.cleantimes(a,1)=1;
  61. end
  62. [a,b]=find(cfg.cleantimes(:,1)>length(EEG.data));
  63. if ~isempty(a)
  64. cfg.cleantimes(a,:)=[];
  65. end
  66. [a,b]=find(cfg.cleantimes(:,2)>length(EEG.data));
  67. if ~isempty(a)
  68. cfg.cleantimes(a,2)=length(EEG.data);
  69. end
  70. numstim=0;
  71. %% for each fixation we need to save ALL the necessary information in the EEG structure
  72. % for e = 1:length(EEG.event)
  73. for e = length(EEG.event):-1:1
  74. % 1-5 stimulus
  75. % 180 stim offset
  76. % 200 exp start
  77. % 255 exp stop
  78. % if the trigger is a fixation
  79. if ismember(EEG.event(e).type, ...
  80. {'L_fixation' 'R_fixation'})
  81. EEG.event(e).urtype = EEG.event(e).type;
  82. % if it is a meaningful fixation
  83. EEG.event(e).type = 'fixation';
  84. continue
  85. end
  86. % if the trigger was the beginning of a trial
  87. if str2num(EEG.event(e).type) <= 5
  88. EEG.event(e).urtype = EEG.event(e).type;
  89. EEG.event(e).type = 'stimulus';
  90. end
  91. % recode all fix that are not Face or None to "OtherHeads"
  92. if strcmp(EEG.event(e).type, 'stimulus')
  93. isStim=1;
  94. else
  95. isStim=0;
  96. end
  97. if isStim==1
  98. numstim=numstim+1;
  99. if EEG.event(e).humanface==1
  100. EEG.event(e).curr='HF';
  101. elseif EEG.event(e).car==1
  102. EEG.event(e).curr='Car';
  103. elseif EEG.event(e).obj==1
  104. EEG.event(e).curr='Obj';
  105. elseif EEG.event(e).none==1
  106. EEG.event(e).curr='None';
  107. else
  108. %first in a trial has no prevprev, therefore we define it as
  109. %'outside' as outside will not be analysed (easiest solution)
  110. EEG.event(e).curr='outside';
  111. end
  112. if EEG.event(e).prevhumanface==1
  113. EEG.event(e).prev='HF';
  114. elseif EEG.event(e).prevcar==1
  115. EEG.event(e).prev='Car';
  116. elseif EEG.event(e).prevobj==1
  117. EEG.event(e).prev='Obj';
  118. elseif EEG.event(e).prevnone==1
  119. EEG.event(e).prev='BlockBeginning';
  120. end
  121. if EEG.event(e).prevprevhumanface==1
  122. EEG.event(e).prevprev='HF';
  123. elseif EEG.event(e).prevprevnone==1
  124. EEG.event(e).prevprev='None';
  125. else
  126. %first in a trial has no prevprev, therefore we define it as
  127. %'outside' as outside will not be analysed (easiest solution)
  128. EEG.event(e).prevprev='BlockBeginning';
  129. end
  130. end
  131. end
  132. %% save information about saccaded and fixations
  133. if sum(strcmp({EEG.event.type},'L_saccade'))>0
  134. ixSac = find(strcmp({EEG.event.type},'L_saccade'));
  135. else
  136. ixSac = find(strcmp({EEG.event.type},'R_saccade'));
  137. end
  138. ixFix = find(strcmp({EEG.event.type},'fixation'));
  139. %find blinks
  140. ixBlk=find(cellfun(@(x)strcmp(x,'R_blink'),{EEG.event.type}));
  141. if isempty(ixBlk)
  142. ixBlk=find(cellfun(@(x)strcmp(x,'L_blink'),{EEG.event.type}));
  143. end
  144. latSac = [EEG.event(ixSac).latency];
  145. latFix = [EEG.event(ixFix).latency];
  146. latBlk = [EEG.event(ixBlk).latency];
  147. endSac = latSac+[EEG.event(ixSac).duration];
  148. endBlk = latBlk+[EEG.event(ixBlk).duration];
  149. % put the velocity for blink sac/data loss sections to NaN
  150. for ixS=1:length(ixSac)
  151. blkStarAftertSacStart=find(latBlk>=latSac(ixS),1,'first');
  152. blkStopBeforeSacStop=find(endBlk<=endSac(ixS),1,'last');
  153. if blkStarAftertSacStart== blkStopBeforeSacStop
  154. EEG.event(ixSac(ixS)).sac_vmax=NaN;
  155. end
  156. end
  157. for e = 1:length(latFix)
  158. % for each fixation, find the closest saccade before the event and copy
  159. % the saccade information
  160. % when Sac --> Fix, then lat(Fix) - lat(Sac) should be positive
  161. ix = find((latFix(e) - latSac) > 0,1,'last');
  162. if ~isempty(ix)
  163. for fn = {'sac_endpos_x','sac_endpos_y','sac_amplitude','sac_startpos_x','sac_startpos_y','sac_vmax'}
  164. EEG.event(ixFix(e)).(fn{1})= EEG.event(ixSac(ix)).(fn{1});
  165. end
  166. end
  167. end
  168. % remove all fixation with saccade velocity of 0 (= blink saccades)
  169. for ixF=1:length(ixFix)
  170. if EEG.event(ixFix(ixF)).sac_vmax==0
  171. EEG.event(ixFix(ixF)).type='blinkFix';
  172. end
  173. end
  174. ixFix = find(strcmp({EEG.event.type},'fixation'));
  175. latFix = [EEG.event(ixFix).latency];
  176. % remove all fixations close to a blink/data loss section
  177. if ~isempty(ixBlk)
  178. ixBlk=fliplr(ixBlk);
  179. for iB=ixBlk
  180. blkStop=EEG.event(iB).latency+EEG.event(iB).duration;
  181. delFix=find(abs(latFix-blkStop)<50);
  182. if ~isempty(delFix)
  183. for dF=1:length(delFix)
  184. EEG.event(ixFix(delFix(dF))).type='blinkFix';
  185. end
  186. end
  187. end
  188. end
  189. for e = 1:length(latSac)
  190. % for each fixation, find the closest saccade before the event and copy
  191. % the saccade information
  192. % when Sac --> Fix, then lat(Fix) - lat(Sac) should be positive
  193. ix = find((latSac(e) - latFix) > 0,1,'last');
  194. if ~isempty(ix) && ~isempty( EEG.event(ixFix(ix)).prev)
  195. for fn = {'prev','curr'}
  196. EEG.event(ixSac(e)).(fn{1})= EEG.event(ixFix(ix)).(fn{1});
  197. end
  198. EEG.event(ixSac(e)).type='saccade';
  199. end
  200. end
  201. %% Remove large saccades
  202. fprintf('\n\n %u large saccades (>40) will be removed. \n\n\n', length(find([EEG.event.sac_amplitude]>40)))
  203. EEG.event(cellfun(@(x)(x>40),{EEG.event.sac_amplitude})) = [];
  204. %% Config
  205. EEG.unfold = [];
  206. cfgDesign = [];
  207. cfgDesign.splinespacing = 'quantile';
  208. cfgDesign.eventtypes = {
  209. {'fixation'} % fixation onset
  210. {'stimulus'}}; % stimulus onset
  211. if sub_num==51 %51 has only 2 relevant sac before fix - maybe due to stopping recording between trials
  212. cfgDesign.formula = {
  213. 'y~1 + spl(fix_avgpos_x,5) + spl(fix_avgpos_y,5)'
  214. 'y~1 + cat(curr) + cat(prev)+ humanface:prevhumanface'};
  215. else
  216. cfgDesign.formula = {
  217. 'y~1 + spl(fix_avgpos_x,5) + spl(fix_avgpos_y,5) + spl(sac_amplitude,5)'
  218. 'y~1 + cat(curr) + cat(prev)+ humanface:prevhumanface'};
  219. end
  220. cfgDesign.codingschema = 'effects';
  221. cfgDesign.mashup=1;
  222. % if codingschema=effects -> reference cat is the last one
  223. % if codingschema=reference -> reference cat is the first one
  224. if strcmp(cfgDesign.codingschema, 'effects')
  225. cfgDesign.categorical = {
  226. 'curr',{'Car','HF','Obj'};
  227. 'prev',{'BlockBeginning','Car','HF','Obj'};
  228. };
  229. elseif strcmp(cfgDesign.codingschema, 'reference')
  230. cfgDesign.categorical = {
  231. 'curr',{'Obj','HF','Car','BlockBeginning'};
  232. 'prev',{'Obj','HF','Car','BlockBeginning'};
  233. };
  234. end
  235. cfgFit = [];
  236. cfgFit.precondition = 1;
  237. cfgFit.lsmriterations = 1500;
  238. % if subset of channels is wanted:
  239. % elecfind = @(x)find(strcmp(x,{EEG.chanlocs.labels}));
  240. % cfgFit.channel = [elecfind('PO8')];
  241. cfgFit.channel = 1:length(EEG.chanlocs);
  242. if cfg.regularize
  243. cfgFit.method='glmnet';
  244. cfgFit.glmnetalpha = 0;
  245. end
  246. EEG = uf_designmat(EEG,cfgDesign);
  247. % if we want a mashup design, exchange everything <1 with 0, in all
  248. % uninteresting parameters
  249. if cfgDesign.mashup
  250. parampos=find(ismember(EEG.unfold.colnames,{'prev_Car','curr_Car','prev_BlockBeginning'}));
  251. for p=1:length(parampos)
  252. pos=find(EEG.unfold.X(:,parampos(p))<0);
  253. EEG.unfold.X(pos,parampos(p))=0;
  254. end
  255. %the interaction is basically only the multiplicatiom of curr and prev
  256. interactpos=find(ismember(EEG.unfold.colnames,{'humanface:prevhumanface'}));
  257. mainpos =find(ismember(EEG.unfold.colnames,{'curr_HF','prev_HF'}));
  258. EEG.unfold.X(:,interactpos)=EEG.unfold.X(:,mainpos(1)).*EEG.unfold.X(:,mainpos(2));
  259. end
  260. %% Add cyclical regressor
  261. % if cfgDesign.cyclicalAngle
  262. % EEG = scenes_add_cyclical(EEG);
  263. % end
  264. %% Timeshift the design matrix
  265. for t=1:size(cfg.timewin,1)
  266. cfgTimeshift = [];
  267. cfgTimeshift.timelimits = cfg.timewin(t,:);
  268. EEG = uf_timeexpandDesignmat(EEG,cfgTimeshift);
  269. EEG = uf_continuousArtifactExclude(EEG,'winrej',cfg.cleantimes);
  270. %% Fit it iteratively
  271. EEG= uf_glmfit(EEG,cfgFit);
  272. %% Make a massive uni-variate fit without de-convolution
  273. EEGepoch = uf_epoch(EEG,'winrej',cfg.cleantimes,'timelimits',cfgTimeshift.timelimits);
  274. EEGepoch= uf_glmfit_nodc(EEGepoch);
  275. %% Save the data
  276. ufresult = uf_condense(EEGepoch);
  277. save(['/net/store/nbp/projects/IntoTheWild/Daten/EEG/Lab/unfold_Lab/' cfg.filtering '/ufresult_subj' num2str(sub_num) '_allElec_mashup_' num2str(cfg.timewin(t,1)*1000) '-' num2str(cfg.timewin(t,2)*1000) '_regularized' num2str(cfg.regularize) '.mat'],'ufresult')
  278. disp('successfully saved')
  279. end