analyse_individual_sessions.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. function analyse_individual_sessions(fld, monkey, sessions)
  2. datadir = fullfile(fld.basedir,'Datasets', monkey ,'MAT_eye');
  3. logdir = fullfile(fld.basedir,'Datasets', monkey ,'log');
  4. fprintf('\n======================================================\n');
  5. fprintf(['-- Running analyse_individual_sessions for ' monkey ' --\n']);
  6. fprintf('======================================================\n');
  7. % loop sessions
  8. for sid = 1:length(sessions)
  9. disp(['analysing session ' num2str(sid) ' out of ' num2str(length(sessions))]);
  10. % session info
  11. session = sessions{sid};
  12. q = strsplit(session,'_');
  13. cdate = q{2};
  14. % find files
  15. FN = dir(fullfile(datadir, [monkey cdate '-block*']));
  16. % we will have 2 envs for each block, and at least 1 block for each
  17. % date. so, if length(FN)>2, we have more than 1 block.
  18. available_blocks = [];
  19. for f = 1:length(FN)
  20. available_blocks(end+1) = str2num(FN(f).name(end-9));
  21. end
  22. blocks = unique(available_blocks);
  23. % let's loop blocks, and concatenate Env within each block
  24. for b = blocks
  25. savedir = fullfile(fld.procdatadir, ...
  26. monkey, [monkey '_' cdate '_' num2str(b)]);
  27. mkdir(savedir); % create a directory to save processed data
  28. mkdir(fullfile(savedir, 'individual_channels'));
  29. disp(['block ' num2str(b) '...']);
  30. % make sure we have 2 files, Env1 and Env2
  31. fn = dir(fullfile(datadir, [monkey cdate '-block-' num2str(b) '*']));
  32. available_envs = [];
  33. for ff = 1:length(fn)
  34. available_envs(end+1) = str2num(fn(ff).name(end-4));
  35. end
  36. envs = unique(available_envs);
  37. % group the Env files so we have all channels together
  38. env = cell(0);
  39. for f = envs
  40. % each file contains two structures:
  41. % - Data (contains ENV)
  42. % - Info (unused)
  43. load(fullfile(datadir,[monkey cdate '-block-' num2str(b) '-ENV' num2str(f)]),'Data');
  44. % convert ENV to better format
  45. env_t = Data.Time;
  46. for ch = 1:size(Data.Signal,2)
  47. env{ch + (f-1)*96} = squeeze(Data.Signal(:,ch,:));
  48. end
  49. end
  50. % TrackerFile is the name of the directory, as well as the filename
  51. % the mat file will give us LOG, Par, and STIM.
  52. TrackerFile = [monkey '_' cdate '_B00' num2str(b)];
  53. load(fullfile(logdir,TrackerFile,[TrackerFile '.mat']),'LOG','Par','STIM');
  54. %% convert LOG to LUT
  55. lut = struct2table(LOG.Trial);
  56. monkey = repmat({monkey},size(lut,1),1);
  57. session = repmat({cdate},size(lut,1),1);
  58. block = repmat(b,size(lut,1),1);
  59. lut = addvars(lut,monkey,session,block,'Before','TrialNr');
  60. monkey = monkey{1};
  61. % we need to compute some extra variables
  62. statusMapping = {'Aborted','Target','Nontarget','Distractor','NoHit'};
  63. trialPerf = cellfun(@(x) find(strcmp(statusMapping,x))-1,lut.Status);
  64. prevTrialPerf = [nan; trialPerf(1:end-1)];
  65. prevStatus = [{''}; lut.Status(1:end-1)];
  66. % colswitch says whether the ColScheme of current and previous
  67. % trials were the same or not
  68. colswitch = zeros(size(lut,1),1);
  69. colswitch(1) = nan;
  70. colswitch(abs(lut.ColScheme - [nan; lut.ColScheme(1:end-1)]) > 0) = 1;
  71. % make variables containing previous reward values and current
  72. % reward values (1 = low, 2 = high), based on the reward value
  73. % settings for this session
  74. rewardvals = sort(STIM.RewardValues);
  75. rewardval = ones(size(lut,1),1);
  76. rewardval(lut.RewValue==rewardvals(2)) = 2;
  77. prevrewardval = [nan; rewardval(1:end-1)];
  78. % check whether the current trial immediately followed the previous
  79. % one (so diff between trial numbers should be 1, not more)
  80. immediateFollow = zeros(size(lut,1),1);
  81. immediateFollow(find(diff(lut.TrialNr)==1)+1) = 1; % we have to add 1 because of the offset of the diff function
  82. % now add the previously computed vectors to the LUT
  83. lut.trialPerf = trialPerf; % range: 0 - 4, denotes the chosen target (see statusmapping)
  84. lut.prevTrialPerf = prevTrialPerf; % same as trialPerf, but for previous trial
  85. lut.colSwitch = colswitch; % boolean: 0 = no switch
  86. lut.rewardVal = rewardval; % nan = no reward, 1 = low reward, 2 = high reward
  87. lut.prevRewardVal = prevrewardval;
  88. lut.immediateFollow = immediateFollow; % boolean, 0 = there were excluded trials in between
  89. lut = addvars(lut,prevStatus,'After','Status');
  90. %% select trials based on human analogs
  91. % 1. RT_colswitch_HR and Perf_colswitch_HR
  92. % 2. RT_colswitch_LR and Perf_colswitch_LR
  93. % 3. RT_colsame_HR and Perf_colsame_HR
  94. % 4. RT_colsame_LR and Perf_colsame_LR
  95. lut.RT_ColSwitch_HR = strcmp(lut.Status,'Target') & strcmp(lut.prevStatus,'Target') & lut.colSwitch==1 & lut.prevRewardVal==2 & lut.immediateFollow==1;
  96. lut.RT_ColSwitch_LR = strcmp(lut.Status,'Target') & strcmp(lut.prevStatus,'Target') & lut.colSwitch==1 & lut.prevRewardVal==1 & lut.immediateFollow==1;
  97. lut.RT_ColSame_HR = strcmp(lut.Status,'Target') & strcmp(lut.prevStatus,'Target') & lut.colSwitch==0 & lut.prevRewardVal==2 & lut.immediateFollow==1;
  98. lut.RT_ColSame_LR = strcmp(lut.Status,'Target') & strcmp(lut.prevStatus,'Target') & lut.colSwitch==0 & lut.prevRewardVal==1 & lut.immediateFollow==1;
  99. lut.Perf_ColSwitch_HR = strcmp(lut.prevStatus,'Target') & lut.colSwitch==1 & lut.prevRewardVal==2 & lut.immediateFollow==1;
  100. lut.Perf_ColSwitch_LR = strcmp(lut.prevStatus,'Target') & lut.colSwitch==1 & lut.prevRewardVal==1 & lut.immediateFollow==1;
  101. lut.Perf_ColSame_HR = strcmp(lut.prevStatus,'Target') & lut.colSwitch==0 & lut.prevRewardVal==2 & lut.immediateFollow==1;
  102. lut.Perf_ColSame_LR = strcmp(lut.prevStatus,'Target') & lut.colSwitch==0 & lut.prevRewardVal==1 & lut.immediateFollow==1;
  103. %% correct the reaction time (make reactiontime2)
  104. % there are three vectors in the LUT that have timing information
  105. % that we need to calculate reactiontime2. however, these are cell
  106. % arrays, that have some empty cells, so we can't use cell2mat
  107. % immediately, because that will skip the empty ones. let's fix,
  108. % and calculate reactTime2.
  109. rt = nan(length(lut.ReactTime),1);
  110. rt(cellfun('isempty',lut.ReactTime)==0,1) = cell2mat(lut.ReactTime);
  111. lut.ReactTime = rt;
  112. sacc = nan(size(lut,1),1);
  113. sacc(cellfun('isempty',lut.SaccTime)==0,1) = cell2mat(lut.SaccTime);
  114. lut.SaccTime = sacc;
  115. % apparently, StimTarDur is usually a cell array, but not always
  116. if iscell(lut.StimTarDur)
  117. stimtar = nan(size(lut,1),1);
  118. stimtar(cellfun('isempty',lut.StimTarDur)==0,1) = cell2mat(lut.StimTarDur);
  119. lut.StimTarDur = stimtar;
  120. end
  121. if STIM.KeepFixT < 175
  122. lut.reactTime2 = lut.ReactTime + lut.SaccTime + lut.StimTarDur*1000;
  123. end
  124. %% check which stimulus was in RF when
  125. % we need to define 6 variables
  126. % Tar - list of trials in which Target is in RF
  127. % iTar - same as Tar, but with proper trial number, corresponding to LUT
  128. % Dist - etc...
  129. % iDist
  130. % NonTar
  131. % iNonTar
  132. % we first load some RF information. this is a structure with 3
  133. % maps, one for M1, and 2 for M2. Apparently, there are two
  134. % configurations for M2, one up to 20141101, and another from
  135. % that date onwards. we need to make sure we use the right ones.
  136. % since only M2 has two configurations, and the date at which
  137. % they switch is hardcoded in Hickey_RFMAPS, I figured we can just
  138. % hardcode it here instead, to keep it simple.
  139. if strcmp(monkey,'M1')
  140. mp = RFMAPS_function(1);
  141. elseif strcmp(monkey,'M2')
  142. dt = datetime(cdate,'InputFormat','yyyyMMdd');
  143. if dt > datetime('20141101','InputFormat','yyyyMMdd') % the original script uses >, not >=. However, as far as I know, there is no recording from that actual date, so it doesn't really matter
  144. mp = RFMAPS_function(3);
  145. else
  146. mp = RFMAPS_function(2);
  147. end
  148. else
  149. error('Wrong monkey?');
  150. end
  151. % now we have a table with 1 row per channel. let's loop them, and
  152. % find, per channel, which trials we should include
  153. qn = size(mp,1);
  154. mp.iTar = cell(qn,1);
  155. mp.iDist = cell(qn,1);
  156. mp.iNonTar = cell(qn,1);
  157. mp.Tar = cell(qn,1);
  158. mp.Dist = cell(qn,1);
  159. mp.NonTar = cell(qn,1);
  160. for ch = 1:size(mp,1)
  161. if ~isempty(mp.rfpos(ch))
  162. mp.iTar{ch} = find(lut.TarPos==mp.rfpos(ch));
  163. mp.iDist{ch} = find(lut.DistPos==mp.rfpos(ch));
  164. mp.iNonTar{ch} = find(lut.TarPos~=mp.rfpos(ch) & lut.DistPos~=mp.rfpos(ch));
  165. mp.Tar{ch} = lut.TrialNr(mp.iTar{ch});
  166. mp.Dist{ch} = lut.TrialNr(mp.iDist{ch});
  167. mp.NonTar{ch} = lut.TrialNr(mp.iNonTar{ch});
  168. end
  169. end
  170. %% make some figures
  171. % plot_rt(lut);
  172. % plot_perf(lut);
  173. % plot_perf_fraction(lut);
  174. %% process MUA data
  175. % find nonaborted trials
  176. nonaborted = ~strcmp(lut.Status,'Aborted') & ~strcmp(lut.Status,'NoHit');
  177. RTMask=[];
  178. latencydelay = 50;
  179. for t=1:size(lut,1)
  180. RTMask = [RTMask (env_t >= (lut.reactTime2(t)+latencydelay)/1000)'];
  181. end
  182. % loop channels
  183. snr = nan(size(mp,1),1);
  184. snr_cons = nan(size(mp,1),1);
  185. allgoodtrials = nan(size(lut,1),size(mp,1));
  186. allsamplewisegoodtrials = nan(size(lut,1),size(mp,1));
  187. baseline_value = nan(size(mp,1),1);
  188. peakresp = nan(size(mp,1),1);
  189. for ch = 1:size(mp,1)
  190. if ch/10 == ceil(ch/10) || ch == 1
  191. disp(['analysing channel ' num2str(ch) '...']);
  192. end
  193. % check if we have data for this channel
  194. if length(env) < ch
  195. break
  196. end
  197. if isempty(env{ch})
  198. continue
  199. end
  200. % make sure that the lut trials line up with the env trials
  201. % (or at least that we have the same amount)
  202. if (size(lut,1) ~= size(env{ch},2))
  203. error('env and lut don''t align');
  204. end
  205. % preprocess
  206. data = env{ch};
  207. raw_data = data;
  208. [goodtrials, sample_wise_goodtrials] = remove_outliers(data,3);
  209. [data, blval] = correct_baseline(data,sample_wise_goodtrials'&nonaborted,env_t,[-0.1 0]);
  210. baseline_value(ch) = blval;
  211. % HickeyAnalysis_CK uses masked data to calculate the peak
  212. % response for normalization. i tried both, and it doesn't seem
  213. % to matter (i.e. the peak occurs before the RT)
  214. data_masked = data;
  215. data_masked(RTMask==1) = nan;
  216. % use the masked_data to get the peak response and normalize
  217. % the masked data
  218. [data_masked, cpeakresp] = normalize(data_masked,sample_wise_goodtrials'&nonaborted,env_t,[0 0.25]);
  219. peakresp(ch) = cpeakresp;
  220. % now also normalize the unmasked dataset
  221. data = data./cpeakresp;
  222. % get SNR based on the non-masked data
  223. [snr(ch), snr_cons(ch)] = get_snr(data,sample_wise_goodtrials'&nonaborted,env_t,[-0.1 0]);
  224. allgoodtrials(:,ch) = goodtrials;
  225. allsamplewisegoodtrials(:,ch) = sample_wise_goodtrials;
  226. % Env{ch} = data;
  227. % Env_masked{ch} = data_masked;
  228. % Env_raw{ch} = raw_data;
  229. % save individual channels
  230. save(fullfile(savedir,'individual_channels',...
  231. ['Env_preprocessed_ch' num2str(ch) '_' monkey '_' cdate '_' num2str(b)]),...
  232. 'data','data_masked','raw_data','cpeakresp','blval','goodtrials','sample_wise_goodtrials');
  233. end
  234. mp.snr = snr;
  235. mp.snr_conservative = snr_cons;
  236. mp.goodtrials = allgoodtrials';
  237. mp.sample_wise_goodtrials = allsamplewisegoodtrials';
  238. mp.baseline = baseline_value;
  239. mp.peak = peakresp;
  240. % save lut and mp
  241. save(fullfile(savedir,[monkey '_' cdate '_' num2str(b) '_LUT']),'lut');
  242. save(fullfile(savedir,[monkey '_' cdate '_' num2str(b) '_channels']),'mp');
  243. % clear some big variables and then save everything
  244. clear Env env data;
  245. save(fullfile(savedir, [monkey '_' cdate '_' num2str(b) '.mat']));
  246. end
  247. end
  248. end
  249. function fig = plot_rt(lut)
  250. fig = figure; set(gcf,'Position',[ 616 234 480 577]);
  251. % colswitch
  252. lr = lut.reactTime2(lut.RT_ColSwitch_LR);
  253. hr = lut.reactTime2(lut.RT_ColSwitch_HR);
  254. mn_lr = mean(lr);
  255. mn_hr = mean(hr);
  256. std_lr = std(lr)./sqrt(length(lr));
  257. std_hr = std(hr)./sqrt(length(hr));
  258. errorbar([.99 1.99],[mn_lr mn_hr],[std_lr,std_hr], 'bo-'); hold all;
  259. % colsame
  260. lr = lut.reactTime2(lut.RT_ColSame_LR);
  261. hr = lut.reactTime2(lut.RT_ColSame_HR);
  262. mn_lr = mean(lr);
  263. mn_hr = mean(hr);
  264. std_lr = std(lr)./sqrt(length(lr));
  265. std_hr = std(hr)./sqrt(length(hr));
  266. errorbar([1.01 2.01],[mn_lr mn_hr],[std_lr,std_hr], 'ro-');
  267. hl=legend({'Color swap','Color same'},'Location','SouthWest');
  268. set(gca,'xtick',1:2,'xticklabel',{'Low','High'},'FontSize',14);
  269. xlabel('Previous Trial Reward','FontSize',16);
  270. ylabel('Reaction time (ms)','FontSize',16);
  271. title(sprintf(['RT ' lut.monkey{1} ' ' lut.session{1} ' B' num2str(lut.block(1))]),'FontSize',18,'FontWeight','Demi');
  272. xlim([0.9 2.1]);
  273. end
  274. function fig = plot_perf(lut)
  275. figure; set(gcf,'Position',[574 212 560 420]);
  276. % colswitch
  277. lr = lut.Status(lut.Perf_ColSwitch_LR);
  278. lrp = sum(strcmp(lr,'Target'))./length(lr);
  279. hr = lut.Status(lut.Perf_ColSwitch_HR);
  280. hrp = sum(strcmp(hr,'Target'))./length(hr);
  281. bar([1:2],[lrp hrp],'FaceColor','b'); hold all;
  282. % colsame
  283. lr = lut.Status(lut.Perf_ColSame_LR);
  284. lrp = sum(strcmp(lr,'Target'))./length(lr);
  285. hr = lut.Status(lut.Perf_ColSame_HR);
  286. hrp = sum(strcmp(hr,'Target'))./length(hr);
  287. bar([3:4],[lrp hrp],'FaceColor','r');
  288. hl=legend({'Color swap','Color same'},'Location','NorthEastOutside', 'FontSize',14);
  289. set(gca,'xtick',1:4,'xticklabel',{sprintf('Low'),sprintf('High'),sprintf('Low'),sprintf('High')},'FontSize',14);
  290. xlabel('Previous Reward','FontSize',16);
  291. ylabel('Fraction correct','FontSize',16);
  292. title(['RT ' lut.monkey{1} ' ' lut.session{1} ' B' num2str(lut.block(1))],'FontSize',18,'FontWeight','Demi');
  293. end
  294. function fig = plot_perf_fraction(lut)
  295. fig = figure; set(gcf,'Position',[435 267 996 477]);
  296. usedata = {lut.Perf_ColSwitch_LR, lut.Perf_ColSwitch_HR, lut.Perf_ColSame_LR, lut.Perf_ColSame_HR};
  297. for i = 1:4
  298. u = usedata{i};
  299. subplot(2,2,i)
  300. b = []; status_list = {'Target','Nontarget','Distractor','NoHit'};
  301. for bi = 1:4
  302. b(bi) = sum(strcmp(lut.Status(u),status_list{bi}))/sum(u);
  303. end
  304. bar(1:4, b, 'FaceColor','k');
  305. set(gca,'xtick',1:4,'xticklabel',{'Targ','nonTarg','Distr','NoHit'},'FontSize',11,'ylim',[0 1]);
  306. ylabel('Fraction of trials','FontSize',12);
  307. title('Color Swap / High Reward','FontSize',14)
  308. end
  309. st=mtit(['Choices ' lut.monkey{1} ' ' lut.session{1} ' B' num2str(lut.block(1))],'xoff',0,'yoff',0.04);
  310. end