b_scenecat_n1_bl.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. currentFile = mfilename('fullpath');
  2. [pathstr,~,~] = fileparts(currentFile);
  3. cd(fullfile(pathstr,'..'))
  4. rootpath = pwd;
  5. pn.data_eeg = fullfile(rootpath, '..', 'eegmp_preproc', 'data', 'outputs', 'eeg');
  6. pn.data_erp = fullfile(rootpath, 'data', 'erp');
  7. pn.data_erf = fullfile(rootpath, 'data', 'erf');
  8. pn.tools = fullfile(rootpath, 'tools');
  9. addpath(fullfile(rootpath, '..', 'eegmp_preproc', 'tools', 'fieldtrip')); ft_defaults
  10. addpath(fullfile(pn.tools, 'BrewerMap'));
  11. addpath(fullfile(pn.tools, 'shadedErrorBar'));
  12. addpath(genpath(fullfile(pn.tools, 'RainCloudPlots')));
  13. pn.figures = fullfile(rootpath, 'figures');
  14. %% load erp_bl
  15. for ind_id = 1:33
  16. id = sprintf('sub-%03d', ind_id);
  17. load(fullfile(pn.data_erp, [id,'_erp_bl.mat']));
  18. for ind_option = 1:numel(conds.scene_category)
  19. if ind_id == 1
  20. erpgroup.scene_category.(conds.scene_category{ind_option}) = erp_bl.scene_category{ind_option};
  21. erpgroup.scene_category.(conds.scene_category{ind_option}) = ...
  22. rmfield(erpgroup.scene_category.(conds.scene_category{ind_option}), {'avg', 'var', 'dof'});
  23. erpgroup.scene_category.(conds.scene_category{ind_option}).dimord = 'sub_chan_time';
  24. end
  25. erpgroup.scene_category.(conds.scene_category{ind_option}).avg(ind_id,:,:) = erp_bl.scene_category{ind_option}.avg;
  26. end
  27. end
  28. time = erpgroup.scene_category.manmade.time;
  29. elec = erpgroup.scene_category.manmade.elec;
  30. channels = erpgroup.scene_category.manmade.label;
  31. mergeddata = cat(4, erpgroup.scene_category.manmade.avg, ...
  32. erpgroup.scene_category.natural.avg);
  33. %% plot the ERPs (for initial inspection)
  34. % manmade = erpgroup.scene_category.manmade;
  35. % manmade.avg = squeeze(nanmean(manmade.avg,1));
  36. % manmade.dimord = 'chan_time';
  37. %
  38. % natural = erpgroup.scene_category.natural;
  39. % natural.avg = squeeze(nanmean(natural.avg,1));
  40. % natural.dimord = 'chan_time';
  41. %
  42. % cfg = [];
  43. % cfg.layout = 'EEG1010.lay';
  44. % cfg.interactive = 'yes';
  45. % cfg.showoutline = 'yes';
  46. % cfg.xlim = [-.2 .3];
  47. % ft_multiplotER(cfg, natural, manmade)
  48. %% plot topography of visual N1
  49. % set custom colormap
  50. cBrew = brewermap(500,'RdBu');
  51. cBrew = flipud(cBrew);
  52. colormap(cBrew)
  53. h = figure('units','centimeters','position',[0 0 10 10]);
  54. set(gcf,'renderer','Painters')
  55. cfg = [];
  56. cfg.layout = 'EEG1005.lay';
  57. cfg.parameter = 'powspctrm';
  58. cfg.comment = 'no';
  59. cfg.colormap = cBrew;
  60. cfg.colorbar = 'EastOutside';
  61. plotData = [];
  62. plotData.label = elec.label(1:64); % {1 x N}
  63. plotData.dimord = 'chan';
  64. plotData.powspctrm = squeeze(nanmean(nanmean(nanmin(mergeddata(:,:,time>0.04 & time <0.12,1:2),[],3),1),4))';
  65. [~, sortidx] = sort(plotData.powspctrm, 'ascend');
  66. idx_chans = sortidx(1);
  67. cfg.marker = 'off';
  68. cfg.highlight = 'yes';
  69. cfg.highlightchannel = plotData.label(idx_chans);
  70. cfg.highlightcolor = [1 0 0];
  71. cfg.highlightsymbol = '.';
  72. cfg.highlightsize = 18;
  73. cfg.zlim = [-10 10]*10^-4;
  74. cfg.figure = h;
  75. ft_topoplotER(cfg,plotData);
  76. cb = colorbar('location', 'EastOutside'); set(get(cb,'ylabel'),'string','Amplitude');
  77. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  78. figureName = ['b_topo_minimum'];
  79. saveas(h, fullfile(pn.figures, figureName), 'epsc');
  80. saveas(h, fullfile(pn.figures, figureName), 'png');
  81. %% visualize N1 over negative maximum
  82. % avg across channels and conditions
  83. condAvg = squeeze(nanmean(nanmean(mergeddata(:,idx_chans,:,1:2),2),4));
  84. condAvg1 = squeeze(nanmean(nanmean(mergeddata(:,idx_chans,:,1),2),4));
  85. condAvg2 = squeeze(nanmean(nanmean(mergeddata(:,idx_chans,:,2),2),4));
  86. h = figure('units','centimeters','position',[0 0 10 8]);
  87. cla; hold on;
  88. % new value = old value ? subject average + grand average
  89. curData = squeeze(nanmean(mergeddata(:,idx_chans,:,1),2));
  90. curData = curData-condAvg+repmat(nanmean(condAvg,1),size(condAvg,1),1);
  91. standError = nanstd(curData,1)./sqrt(size(curData,1));
  92. l1 = shadedErrorBar(time,nanmean(curData,1),standError, 'lineprops', {'color', 'k','linewidth', 2}, 'patchSaturation', .1);
  93. curData = squeeze(nanmean(mergeddata(:,idx_chans,:,2),2));
  94. curData = curData-condAvg+repmat(nanmean(condAvg,1),size(condAvg,1),1);
  95. standError = nanstd(curData,1)./sqrt(size(curData,1));
  96. l2 = shadedErrorBar(time,nanmean(curData,1),standError, 'lineprops', {'color', 'r','linewidth', 2}, 'patchSaturation', .1);
  97. % ax = gca; ax.YDir = 'reverse';
  98. xlabel('Time (s) from stim onset')
  99. xlim([-.025 .16]); %ylim([-.03 .18])
  100. ylabel({'ERP';'(microVolts)'});
  101. xlabel({'Time (s)'});
  102. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  103. %% ERP components for subjects 1-17 and 18-33 are shifted in time!
  104. h = figure('units','centimeters','position',[0 0 10 8]);
  105. cla; hold on;
  106. curData = squeeze(nanmean(mergeddata(1:17,idx_chans,:,1),2));
  107. standError = nanstd(curData,1)./sqrt(size(curData,1));
  108. l1 = shadedErrorBar(time,nanmean(curData,1),standError, 'lineprops', {'color', 'k','linewidth', 2}, 'patchSaturation', .1);
  109. curData = squeeze(nanmean(mergeddata(18:end,idx_chans,:,1),2));
  110. standError = nanstd(curData,1)./sqrt(size(curData,1));
  111. l2 = shadedErrorBar(time,nanmean(curData,1),standError, 'lineprops', {'color', 'r','linewidth', 2}, 'patchSaturation', .1);
  112. % ax = gca; ax.YDir = 'reverse';
  113. legend({'Initial 17', 'Final 16'}, 'location', 'NorthWest'); legend('boxoff')
  114. xlabel('Time (s) from stim onset')
  115. xlim([-.025 .16]); %ylim([-.03 .18])
  116. ylabel({'ERP';'(microVolts)'});
  117. xlabel({'Time (s)'});
  118. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  119. figureName = ['b_twogroups'];
  120. saveas(h, fullfile(pn.figures, figureName), 'epsc');
  121. saveas(h, fullfile(pn.figures, figureName), 'png');
  122. %% align individual subjects N1 to first negative peak
  123. % find individual minimum (avg. across conditions) between 40 and 120 ms
  124. time2search = find(time>0.04 & time <0.12);
  125. newtime = 0-100*(time(2)-time(1)):(time(2)-time(1)):0+100*(time(2)-time(1));
  126. for indID = 1:size(condAvg,1)
  127. tmp = find(islocalmin(condAvg(indID,time2search), ...
  128. 'FlatSelection', 'center', ...
  129. 'MinSeparation', 25));
  130. minVal1(indID) = condAvg1(indID,time2search(tmp(1)));
  131. curmin = time2search(tmp(1));
  132. alignedN1_1(indID,:) = condAvg1(indID,curmin-100:curmin+100);
  133. minVal2(indID) = condAvg2(indID,time2search(tmp(1)));
  134. curmin = time2search(tmp(1));
  135. alignedN1_2(indID,:) = condAvg2(indID,curmin-100:curmin+100);
  136. alignedTopo(indID,:,:) = squeeze(nanmean(mergeddata(indID,:,curmin-100:curmin+100,1:2),4));
  137. end
  138. % alternatively: consider global minimum
  139. % for indID = 1:size(condAvg,1)
  140. % [~, minLoc1(indID)] = min(condAvg(indID,time2search));
  141. % curmin = time2search(minLoc1(indID));
  142. % minVal1(indID) = condAvg1(indID,curmin);
  143. % alignedN1_1(indID,:) = condAvg1(indID,curmin-100:curmin+100);
  144. % [~, minLoc2(indID)] = min(condAvg(indID,time2search));
  145. % curmin = time2search(minLoc2(indID));
  146. % minVal1(indID) = condAvg2(indID,curmin);
  147. % alignedN1_2(indID,:) = condAvg2(indID,curmin-100:curmin+100);
  148. % end
  149. mergeddata_aligned = cat(3, alignedN1_1, alignedN1_2);
  150. [h, p, ci, stats] = ttest(minVal1, minVal2)
  151. % avg across channels and conditions
  152. condAvg_al = squeeze(nanmean(mergeddata_aligned(:,:,1:2),3));
  153. % check that troughs are aligned
  154. % figure; imagesc(zscore(condAvg_al,[],2))
  155. % figure; imagesc(condAvg_al)
  156. h = figure('units','centimeters','position',[0 0 10 8]);
  157. cla; hold on;
  158. % new value = old value ? subject average + grand average
  159. curData = squeeze(mergeddata_aligned(:,:,1));
  160. curData = curData-condAvg_al+repmat(nanmean(condAvg_al,1),size(condAvg_al,1),1);
  161. standError = nanstd(curData,1)./sqrt(size(curData,1));
  162. l1 = shadedErrorBar(newtime*1000,nanmean(curData,1),standError, 'lineprops', {'color', 'k','linewidth', 2}, 'patchSaturation', .1);
  163. curData = squeeze(mergeddata_aligned(:,:,2));
  164. curData = curData-condAvg_al+repmat(nanmean(condAvg_al,1),size(condAvg_al,1),1);
  165. standError = nanstd(curData,1)./sqrt(size(curData,1));
  166. l2 = shadedErrorBar(newtime*1000,nanmean(curData,1),standError, 'lineprops', {'color', 'r','linewidth', 2}, 'patchSaturation', .1);
  167. % ax = gca; ax.YDir = 'reverse';
  168. legend({'manmade', 'natural'}, 'location', 'NorthWest'); legend('boxoff')
  169. xlabel('Time (s) from local minimum')
  170. xlim([-100 100]); %ylim([-.03 .18])
  171. ylabel({'ERP';'(microVolts)'});
  172. xlabel({'Time (ms) from local minimum'});
  173. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  174. figureName = ['b_scenecat_N1'];
  175. saveas(h, fullfile(pn.figures, figureName), 'epsc');
  176. saveas(h, fullfile(pn.figures, figureName), 'png');
  177. %% plot topography around detected trough
  178. h = figure('units','centimeters','position',[0 0 10 10]);
  179. set(gcf,'renderer','Painters')
  180. cfg = [];
  181. cfg.layout = 'EEG1010.lay';
  182. cfg.parameter = 'powspctrm';
  183. cfg.comment = 'no';
  184. cfg.colormap = cBrew;
  185. cfg.colorbar = 'EastOutside';
  186. plotData = [];
  187. plotData.label = elec.label(1:64); % {1 x N}
  188. plotData.dimord = 'chan';
  189. plotData.powspctrm = squeeze(nanmean(nanmean(alignedTopo(:, :, newtime>-.01 & newtime < .01),3),1))';
  190. [~, sortidx] = sort(plotData.powspctrm, 'ascend');
  191. idx_chans = sortidx(1);
  192. idx_chans_visual = idx_chans;
  193. cfg.marker = 'off';
  194. cfg.highlight = 'yes';
  195. cfg.highlightchannel = plotData.label(idx_chans);
  196. cfg.highlightcolor = [1 0 0];
  197. cfg.highlightsymbol = '.';
  198. cfg.highlightsize = 18;
  199. cfg.zlim = [-5 5]*10^-4;
  200. cfg.figure = h;
  201. ft_topoplotER(cfg,plotData);
  202. cb = colorbar('location', 'EastOutside'); set(get(cb,'ylabel'),'string','Amplitude');
  203. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  204. figureName = ['b_trough_topo'];
  205. saveas(h, fullfile(pn.figures, figureName), 'epsc');
  206. saveas(h, fullfile(pn.figures, figureName), 'png');
  207. %% plot raincloudplot of extracted N1 peak amplitudes
  208. % Note that the stats reported here may vary from above in the case of
  209. % outliers.
  210. conds = {'manmade'; 'natural'};
  211. uData{1} = [minVal1; minVal2];
  212. cBrew(1,:) = [.6 .6 .6];
  213. idx_outlier = cell(1); idx_standard = cell(1);
  214. for indGroup = 1
  215. dataToPlot = uData{indGroup}';
  216. % define outlier as lin. modulation that is more than three scaled median absolute deviations (MAD) away from the median
  217. X = [1 1; 1 2]; b=X\dataToPlot'; IndividualSlopes = b(2,:);
  218. IndividualSlopes = uData{indGroup}(2,:)-uData{indGroup}(1,:);
  219. outliers = isoutlier(IndividualSlopes, 'median');
  220. idx_outlier{indGroup} = find(outliers);
  221. idx_standard{indGroup} = find(outliers==0);
  222. end
  223. h = figure('units','centimeter','position',[0 0 10 8]);
  224. for indGroup = 1
  225. dataToPlot = uData{indGroup}';
  226. % read into cell array of the appropriate dimensions
  227. data = []; data_ws = [];
  228. for i = 1:2
  229. for j = 1:1
  230. data{i, j} = dataToPlot(:,i);
  231. % individually demean for within-subject visualization
  232. data_ws{i, j} = dataToPlot(:,i)-...
  233. nanmean(dataToPlot(:,:),2)+...
  234. repmat(nanmean(nanmean(dataToPlot(:,:),2),1),size(dataToPlot(:,:),1),1);
  235. data_nooutlier{i, j} = data{i, j};
  236. data_nooutlier{i, j}(idx_outlier{indGroup}) = [];
  237. data_ws_nooutlier{i, j} = data_ws{i, j};
  238. data_ws_nooutlier{i, j}(idx_outlier{indGroup}) = [];
  239. % sort outliers to back in original data for improved plot overlap
  240. data_ws{i, j} = [data_ws{i, j}(idx_standard{indGroup}); data_ws{i, j}(idx_outlier{indGroup})];
  241. end
  242. end
  243. % IMPORTANT: plot individually centered estimates, stats on uncentered estimates!
  244. set(gcf,'renderer','Painters')
  245. cla;
  246. cl = cBrew(indGroup,:);
  247. [~, dist] = rm_raincloud_fixedSpacing(data_ws, [.8 .8 .8],1);
  248. h_rc = rm_raincloud_fixedSpacing(data_ws_nooutlier, cl,1,[],[],[],dist);
  249. view([90 -90]);
  250. axis ij
  251. box(gca,'off')
  252. set(gca, 'YTickLabels', {'natural'; 'manmade'});
  253. yticks = get(gca, 'ytick'); ylim([yticks(1)-(yticks(2)-yticks(1))./2, yticks(2)+(yticks(2)-yticks(1))./1.5]);
  254. minmax = [min(min(cat(2,data_ws{:}))), max(max(cat(2,data_ws{:})))];
  255. xlim(minmax+[-0.2*diff(minmax), 0.2*diff(minmax)])
  256. ylabel('scene category'); xlabel({'ERP'; '[Individually centered]'})
  257. % test linear effect
  258. curData = [data_nooutlier{1, 1}, data_nooutlier{2, 1}];
  259. X = [1 1; 1 2]; b=X\curData'; IndividualSlopes = b(2,:);
  260. [~, p, ci, stats] = ttest(IndividualSlopes);
  261. title(['M:', num2str(round(mean(IndividualSlopes),6)), '; p=', num2str(round(p,6))])
  262. end
  263. set(findall(gcf,'-property','FontSize'),'FontSize',12)
  264. figureName = ['b_rcp'];
  265. saveas(h, fullfile(pn.figures, figureName), 'epsc');
  266. saveas(h, fullfile(pn.figures, figureName), 'png');
  267. %% plot topography for different timewindows
  268. %
  269. % timewins = [0.03 0.075; ...
  270. % 0.075, 0.1; ...
  271. % 0.1, 0.15; ...
  272. % 0.495, 0.535];
  273. %
  274. % % set custom colormap
  275. % cBrew = brewermap(500,'RdBu');
  276. % cBrew = flipud(cBrew);
  277. % colormap(cBrew)
  278. %
  279. % h = figure('units','centimeters','position',[0 0 10 10]);
  280. % set(gcf,'renderer','Painters')
  281. %
  282. % for indTime = 1:size(timewins,1)
  283. % subplot(3,2,indTime)
  284. %
  285. % cfg = [];
  286. % cfg.layout = 'EEG1010.lay';
  287. % cfg.parameter = 'powspctrm';
  288. % cfg.comment = 'no';
  289. % cfg.colormap = cBrew;
  290. % cfg.colorbar = 'EastOutside';
  291. %
  292. % plotData = [];
  293. % plotData.label = elec.label(1:64); % {1 x N}
  294. % plotData.dimord = 'chan';
  295. % plotData.powspctrm = squeeze(nanmean(nanmean(nanmean(mergeddata(:,:,time>=timewins(indTime,1) & time <=timewins(indTime,2),1),3),1),4))';
  296. % [~, sortidx] = sort(plotData.powspctrm, 'ascend');
  297. % idx_chans = sortidx(1:6);
  298. %
  299. % cfg.marker = 'off';
  300. % cfg.highlight = 'yes';
  301. % cfg.highlightchannel = plotData.label(idx_chans);
  302. % cfg.highlightcolor = [1 0 0];
  303. % cfg.highlightsymbol = '.';
  304. % cfg.highlightsize = 18;
  305. % cfg.zlim = [-10 10]*10^-4;
  306. % cfg.figure = h;
  307. % ft_topoplotER(cfg,plotData);
  308. % cb = colorbar('location', 'EastOutside'); set(get(cb,'ylabel'),'string','Amplitude');
  309. %
  310. % end