emosex_prep_s1_write_events_tsv.m 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. %% LaBGAScore_prep_s1_write_events_tsv
  2. %
  3. % This script reads logfiles, extracts the onsets, durations, and ratings for
  4. % different conditions, and writes events.tsv files to the BIDS dir for
  5. % each subject
  6. % It also contains an option to write a single phenotype file with
  7. % trial-by-trial ratings for all subjects
  8. %
  9. % USAGE
  10. %
  11. % Script should be run from the root directory of the superdataset, e.g.
  12. % /data/proj_discoverie
  13. % The script is highly study-specific, as logfiles will vary with design,
  14. % stimulus presentation software used, etc
  15. % Hence, it is provided in LaBGAScore as an example and needs to be
  16. % downloaded and adapted to the code subdataset for your study/project
  17. % This example is from LaBGAS proj_erythritol_4a
  18. % (https://gin.g-node.org/labgas/proj_erythritol_4a)
  19. %
  20. %
  21. % DEPENDENCIES
  22. %
  23. % LaBGAScore Github repo on Matlab path, with subfolders
  24. % https://github.com/labgas/LaBGAScore
  25. %
  26. %
  27. % INPUTS
  28. %
  29. % Presentation .log files in sourcedata dir for each subject
  30. %
  31. %
  32. % OUTPUTS
  33. %
  34. % events.tsv files for each run in BIDS dir for each subject
  35. % phenotype.tsv file in BIDS/phenotype dir (optional)
  36. %
  37. %__________________________________________________________________________
  38. %
  39. % author: Lukas Van Oudenhove
  40. % date: December, 2021
  41. %
  42. %__________________________________________________________________________
  43. % @(#)% LaBGAScore_prep_s1_write_events_tsv.m v1.3
  44. % last modified: 2023/01/05
  45. %% DEFINE DIRECTORIES, SUBJECTS, RUNS, CONDITIONS, AND IMPORT OPTIONS
  46. %--------------------------------------------------------------------------
  47. emosex_prep_s0_define_directories; % lukasvo edited from original LaBGAScore script to enable standalone functioning of proj_ery_4a dataset
  48. subjs2write = {'sub-03518','sub-03618','sub-03719','sub-03819','sub-03920','sub-04020','sub-04121','sub-04221','sub-04322','sub-04422','sub-04523','sub-04623','sub-04724','sub-04824','sub-04925','sub-05025','sub-05528','sub-05628','sub-05729','sub-05829','sub-06332','sub-06432','sub-06533','sub-06633'}; % enter subjects separated by comma if you only want to write files for selected subjects e.g. {'sub-01','sub-02'}
  49. pheno_tsv = false; % turn to false if you do not wish to generate a phenotype.tsv file with trial-by-trial ratings; will only work if subjs2write is empty (i.e. when you loop over all your subjects)
  50. pheno_name = 'ratings_online.tsv';
  51. runnames = {'run-1'};
  52. logfilenames = {'*.log',};
  53. taskname = 'emosex_movies_';
  54. stimuli_labels = ["Neutral_clip: movie = 'Neu1.mov'"; "Neutral_clip: movie = 'Neu2.mov'"; "Neutral_clip: movie = 'Neu3.mov'"; "Affect_clip: movie = 'Sex.mov'"; "Affect_clip: movie = 'Pos.mov'"; "Affect_clip: movie = 'Neg.mov'"];% labels for sweet substance delivery in Code var of logfile
  55. stimuli_labels = cellstr(stimuli_labels)'; %Need these steps to deal with the quotation marks in the stimuli_labels
  56. rating_label_pre = {'Q_affect: autoDraw = True'};
  57. rating_labels_post = {'Q_post_neu_rating: autoDraw = True', 'Q_post_affect_rating: autoDraw = True'}; %labels for start of rating period in Code var of logfile
  58. rest_label = {'Rest: autoDraw = True'}; %label for start of resting period
  59. endmovie_labels = { 'Sound stopped'}%labels for fixation cross in Code var of logfile
  60. %keypress_label = {'Keypress: 5'}; %additional label to distinguish between different sound stops (endmovie_labels)
  61. keypress_label = {'Imported Questions_Retrospectief.xlsx as conditions, 15 conditions, 1 params'};
  62. blocks_interest = {'neutral', 'neutral', 'neutral', 'sex', 'positive', 'negative'} %names of events of interest to be written to event.tsv
  63. blocks_noninterest = {'pre_rating', 'post_rating', 'rest'};
  64. endmovie_type = {'endmovie'};
  65. score_types = {'score_post_neu', 'score_post_aff', 'score_pre_neu'};
  66. varNames = {'Time', 'Warning', 'Action'}; %varnames of logfile
  67. varTypes = {'double', 'categorical', 'categorical'}; %matlab vartypes to be used when importing log file as table
  68. delimiter = '\t';
  69. opts = delimitedTextImportOptions('VariableNames', varNames, ...
  70. 'VariableTypes', varTypes,...
  71. 'Delimiter', delimiter);
  72. %% LOOP OVER SUBJECTS TO READ LOGFILES, CREATE TABLE WITH ONSETS AND DURATIONS, AND SAVE AS EVENTS.TSV TO BIDSSUBJDIRS
  73. %-------------------------------------------------------------------------------------------------------------------
  74. if ~isempty(subjs2write)
  75. [C,ia,~] = intersect(sourcesubjs,subjs2write);
  76. if ~isequal(C',subjs2write)
  77. error('\nsubject %s present in subjs2write not present in %s, please check before proceeding',subjs2write{~ismember(subjs2write,C)},derivdir);
  78. else
  79. for sub = ia'
  80. % DEFINE SUBJECT LEVEL DIRS
  81. subjsourcedir = sourcesubjdirs{sub};
  82. subjBIDSdir = fullfile(BIDSsubjdirs{sub},'func');
  83. % LOOP OVER RUNS
  84. for run = 1:size(logfilenames,2)
  85. logfilename = dir(fullfile(subjsourcedir,'logfiles',logfilenames{run}));
  86. logfilename = char(logfilename(:).name);
  87. logfilepath = fullfile(subjsourcedir,'logfiles',logfilename);
  88. if ~isfile(logfilepath)
  89. warning('\nlogfile missing for run %d in %s, please check before proceeding',run,logfilepath);
  90. continue
  91. elseif size(logfilepath,1) > 1
  92. error('\nmore than one logfile with run index %s for %s, please check before proceeding',run,sourcesubjs{sub})
  93. else
  94. log = readtable(logfilepath,opts);
  95. %log = log(~isnan(log.Action),:);
  96. time_zero = log.Time(log.Action == "Intro_text: autoDraw = False"); % time for onsets and durations is counted from the first scanner pulse onwards
  97. % if size(time_zero,1) > 1
  98. % error('\nambiguity about time zero in %s%s, please check logfile',subjs{sub},logfilenames{run});
  99. % end
  100. log.TimeZero = log.Time - time_zero;
  101. log.onset = log.TimeZero;
  102. %log(log.EventType == 'Pulse',:) = [];
  103. log.trial_type = cell(height(log),1);
  104. for k = 1:height(log)
  105. if ismember(log.Action(k),rating_label_pre)
  106. log.trial_type{k} = blocks_noninterest{1};
  107. elseif ismember(log.Action(k),rating_labels_post)
  108. log.trial_type{k} = blocks_noninterest{2};
  109. log.onset(k) = log.onset(k)+4;
  110. elseif ismember(log.Action(k),rest_label)
  111. log.trial_type{k} = blocks_noninterest{3};
  112. log.onset(k) = log.onset(k)+4;
  113. elseif ismember(log.Action(k),stimuli_labels)
  114. idx = (log.Action(k) == stimuli_labels);
  115. log.trial_type{k} = blocks_interest{idx'};
  116. elseif ismember(log.Action(k),endmovie_labels) && k>2 && ismember(log.Action(k+1),keypress_label) %log.Action(k-1) when using keypresslabel Keypress: 5
  117. log.trial_type{k} = endmovie_type{1};
  118. else
  119. log.trial_type{k} = '';
  120. end
  121. end
  122. log.rating = zeros(height(log),1);
  123. for l = 1:height(log)
  124. if contains(char(log.Action(l)),'Q_post_neu_ratings: (key response) rating=','IgnoreCase',true)
  125. scorestring = char(log.Action(l));
  126. char_end = string(scorestring(end));
  127. log.rating(l) = str2double(char_end);
  128. log.trial_type{l} = score_types{1};
  129. elseif contains(char(log.Action(l)),'Q_post_neu_ratings: (mouse response) rating=','IgnoreCase',true)
  130. scorestring = char(log.Action(l));
  131. char_end = string(scorestring(end));
  132. log.rating(l) = str2double(char_end);
  133. log.trial_type{l} = score_types{1};
  134. elseif contains(char(log.Action(l)),'Q_post_affect_ratings: (key response) rating=','IgnoreCase',true)
  135. scorestring = char(log.Action(l));
  136. char_end = string(scorestring(end));
  137. log.rating(l) = str2double(char_end);
  138. log.trial_type{l} = score_types{2};
  139. elseif contains(char(log.Action(l)),'Q_affect_rating: (key response) rating=','IgnoreCase',true)
  140. scorestring = char(log.Action(l));
  141. char_end = string(scorestring(end));
  142. log.rating(l) = str2double(char_end);
  143. log.trial_type{l} = score_types{3};
  144. else
  145. log.rating(l) = NaN;
  146. end
  147. end
  148. log.trial_type = categorical(log.trial_type);
  149. log = log((~isundefined(log.trial_type) | ~isnan(log.rating)),:);
  150. copy_log = log;
  151. log.bedroefd = zeros(height(log),1);
  152. log.opgewekt = zeros(height(log),1);
  153. log.teneergeslagen = zeros(height(log),1);
  154. log.ontspannen = zeros(height(log),1);
  155. log.angstig = zeros(height(log),1);
  156. log.enthousiast = zeros(height(log),1);
  157. log.alert = zeros(height(log),1);
  158. log.vol_inspiratie = zeros(height(log),1);
  159. log.gespannen = zeros(height(log),1);
  160. log.tevreden = zeros(height(log),1);
  161. log.vastberaden = zeros(height(log),1);
  162. log.bang = zeros(height(log),1);
  163. log.seksueel_opgewonden = zeros(height(log),1);
  164. log.geil = zeros(height(log),1);
  165. log.seksueel_verlangen = zeros(height(log),1);
  166. for n = 31:height(log);
  167. for quest = 1:15; %number of questions is 15
  168. if quest == 1 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  169. log.bedroefd(n-1-2*quest,1) = log.rating(n);
  170. elseif quest == 2 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  171. log.opgewekt(n-1-2*quest,1) = log.rating(n);
  172. elseif quest == 3 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  173. log.teneergeslagen(n-1-2*quest,1) = log.rating(n);
  174. elseif quest == 4 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  175. log.ontspannen(n-1-2*quest,1) = log.rating(n);
  176. elseif quest == 5 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  177. log.angstig(n-1-2*quest,1) = log.rating(n);
  178. elseif quest == 6 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  179. log.enthousiast(n-1-2*quest,1) = log.rating(n);
  180. elseif quest == 7 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  181. log.alert(n-1-2*quest,1) = log.rating(n);
  182. elseif quest == 8 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  183. log.vol_inspiratie(n-1-2*quest,1) = log.rating(n);
  184. elseif quest == 9 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  185. log.gespannen(n-1-2*quest,1) = log.rating(n);
  186. elseif quest == 10 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  187. log.tevreden(n-1-2*quest,1) = log.rating(n);
  188. elseif quest == 11 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  189. log.vastberaden(n-1-2*quest,1) = log.rating(n);
  190. elseif quest == 12 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  191. log.bang(n-1-2*quest,1) = log.rating(n);
  192. elseif quest == 13 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  193. log.seksueel_opgewonden(n-1-2*quest,1) = log.rating(n);
  194. elseif quest == 14 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  195. log.geil(n-1-2*quest,1) = log.rating(n);
  196. elseif quest == 15 && ismember(log.trial_type(n-2*quest),endmovie_type) && (ismember(log.trial_type(n),score_types{1}) | ismember(log.trial_type(n),score_types{2}));
  197. log.seksueel_verlangen(n-1-2*quest,1) = log.rating(n);
  198. end
  199. end
  200. end
  201. copy_log.bedroefd = zeros(height(log),1);
  202. copy_log.opgewekt = zeros(height(log),1);
  203. copy_log.teneergeslagen = zeros(height(log),1);
  204. copy_log.ontspannen = zeros(height(log),1);
  205. copy_log.angstig = zeros(height(log),1);
  206. copy_log.enthousiast = zeros(height(log),1);
  207. copy_log.alert = zeros(height(log),1);
  208. copy_log.vol_inspiratie = zeros(height(log),1);
  209. copy_log.gespannen = zeros(height(log),1);
  210. copy_log.tevreden = zeros(height(log),1);
  211. copy_log.vastberaden = zeros(height(log),1);
  212. copy_log.bang = zeros(height(log),1);
  213. copy_log.seksueel_opgewonden = zeros(height(log),1);
  214. copy_log.geil = zeros(height(log),1);
  215. copy_log.seksueel_verlangen = zeros(height(log),1);
  216. for n = 1:height(log)-30;
  217. for quest = 1:15;
  218. if quest == 15 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  219. copy_log.bedroefd(n-1+2*quest,1) = log.rating(n);
  220. elseif quest == 14 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  221. copy_log.opgewekt(n-1+2*quest,1) = log.rating(n);
  222. elseif quest == 13 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  223. copy_log.teneergeslagen(n-1+2*quest,1) = log.rating(n);
  224. elseif quest == 12 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  225. copy_log.ontspannen(n-1+2*quest,1) = log.rating(n);
  226. elseif quest == 11 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  227. copy_log.angstig(n-1+2*quest,1) = log.rating(n);
  228. elseif quest == 10 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  229. copy_log.enthousiast(n-1+2*quest,1) = log.rating(n);
  230. elseif quest == 9 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  231. copy_log.alert(n-1+2*quest,1) = log.rating(n);
  232. elseif quest == 8 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  233. copy_log.vol_inspiratie(n-1+2*quest,1) = log.rating(n);
  234. elseif quest == 7 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  235. copy_log.gespannen(n-1+2*quest,1) = log.rating(n);
  236. elseif quest == 6 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  237. copy_log.tevreden(n-1+2*quest,1) = log.rating(n);
  238. elseif quest == 5 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  239. copy_log.vastberaden(n-1+2*quest,1) = log.rating(n);
  240. elseif quest == 4 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  241. copy_log.bang(n-1+2*quest,1) = log.rating(n);
  242. elseif quest == 3 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  243. copy_log.seksueel_opgewonden(n-1+2*quest,1) = log.rating(n);
  244. elseif quest == 2 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  245. copy_log.geil(n-1+2*quest,1) = log.rating(n);
  246. elseif quest == 1 && ismember(log.trial_type(n+2*quest),endmovie_type) && ismember(log.trial_type(n), score_types{3});
  247. copy_log.seksueel_verlangen(n-1+2*quest,1) = log.rating(n);
  248. end
  249. end
  250. end
  251. % for n = 1:height(log)
  252. %
  253. % if ismember(log.trial_type(n),events_interest)
  254. % log.rating(n) = log.rating(n+3);
  255. %
  256. % end
  257. %
  258. % end
  259. log = removevars(log,{'Time','Warning','Action','rating','TimeZero'}); % get rid of junk variables from logfile we don't need
  260. copy_log = removevars(copy_log,{'Time','Warning','Action','rating','TimeZero'});
  261. %log = log(~isundefined(log.trial_type),:);
  262. log.duration = zeros(height(log),1);
  263. for m = 1:height(log)-1;
  264. if ismember(log.trial_type(m+1),endmovie_type)
  265. log.duration(m) = log.onset(m+1) - log.onset(m);
  266. else
  267. log.duration(m) = NaN;
  268. end
  269. end
  270. copy_log.duration = zeros(height(log),1)
  271. for m2 = 1:height(copy_log)-1;
  272. if ismember(copy_log.trial_type(m2+1),endmovie_type)
  273. copy_log.duration(m2) = copy_log.onset(m2+1) - copy_log.onset(m2);
  274. else
  275. copy_log.duration(m2) = NaN;
  276. end
  277. end
  278. log = log(~ismember(log.trial_type, blocks_noninterest{3}),:); %remove rows which concern rest
  279. copy_log = copy_log(~ismember(copy_log.trial_type, blocks_noninterest{3}),:);
  280. log = log(~isnan(log.duration),:);
  281. copy_log = copy_log(~isnan(copy_log.duration),:);
  282. copy_log = copy_log(ismember(copy_log.trial_type, blocks_interest{1}),:);
  283. log = [log(:,2) log(:,1) log(:,18) log(:,3:17)];
  284. copy_log = [copy_log(:,2) copy_log(:,1) copy_log(:,18), copy_log(:,3:17)];
  285. filename = fullfile(subjBIDSdir,[sourcesubjs{sub},'_task-',taskname,runnames{run},'_events.tsv']);
  286. filename2 = fullfile(subjBIDSdir, [sourcesubjs{sub},'_task-', taskname,runnames{run},'_eventsnoninterest.tsv']);
  287. writetable(log,filename,'Filetype','text','Delimiter','\t');
  288. writetable(copy_log,filename2,'Filetype','text','Delimiter','\t');
  289. clear logfile log time_zero filename filename2
  290. end % if loop checking whether logfile exists
  291. end % for loop runs
  292. end % for loop subjects
  293. end % if loop checking subjs2write present in sourcesubjs
  294. else
  295. if ~isequal(sourcesubjs, BIDSsubjs)
  296. [D,~,~] = intersect(sourcesubjs,BIDSsubjs);
  297. error('\nsubject %s present in %s not present in %s, please check before proceeding',BIDSsubjs{~ismember(BIDSsubjs,D)},BIDSdir,derivdir);
  298. else
  299. if pheno_tsv
  300. pheno_file = table();
  301. pheno_dir = fullfile(BIDSdir,'phenotype');
  302. if ~isfolder(pheno_dir)
  303. mkdir(pheno_dir);
  304. end
  305. end
  306. for sub = 1:size(sourcesubjs,1)
  307. if pheno_tsv
  308. pheno_file_subj = table();
  309. end
  310. % DEFINE SUBJECT LEVEL DIRS
  311. subjsourcedir = sourcesubjdirs{sub};
  312. subjBIDSdir = fullfile(BIDSsubjdirs{sub},'func');
  313. % LOOP OVER RUNS
  314. for run = 1:size(logfilenames,2)
  315. logfilename = dir(fullfile(subjsourcedir,'logfiles',logfilenames{run}));
  316. logfilename = char(logfilename(:).name);
  317. logfilepath = fullfile(subjsourcedir,'logfiles',logfilename);
  318. if ~isfile(logfilepath)
  319. warning('\nlogfile missing for run %d in %s, please check before proceeding',run,logfilepath);
  320. continue
  321. elseif size(logfilepath,1) > 1
  322. error('\nmore than one logfile with run index %s for %s, please check before proceeding',run,sourcesubjs{sub})
  323. else
  324. log = readtable(logfilepath,opts);
  325. log = log(~isnan(log.Trial),:);
  326. time_zero = log.Time(log.Trial == 0 & log.EventType == 'Pulse'); % time for onsets and durations is counted from the first scanner pulse onwards
  327. if size(time_zero,1) > 1
  328. error('\nambiguity about time zero in %s%s, please check logfile',subjs{sub},logfilenames{run});
  329. end
  330. log.TimeZero = log.Time - time_zero;
  331. log.onset = log.TimeZero ./ 10000; % convert to seconds
  332. log(log.EventType == 'Pulse',:) = [];
  333. log.trial_type = cell(height(log),1);
  334. for k = 1:height(log)
  335. if ismember(log.Code(k),swallow_rinse_labels)
  336. log.trial_type{k} = events_nuisance{1};
  337. elseif ismember(log.Code(k),rating_labels)
  338. log.trial_type{k} = events_nuisance{2};
  339. log.onset(k) = log.onset(k)+4;
  340. elseif ismember(log.Code(k),sweet_labels)
  341. idx = (log.Code(k) == sweet_labels);
  342. log.trial_type{k} = events_interest{idx'};
  343. elseif ismember(log.Code(k),fixation_labels)
  344. log.trial_type{k} = 'fixation';
  345. else
  346. log.trial_type{k} = '';
  347. end
  348. end
  349. log.rating = zeros(height(log),1);
  350. for l = 1:height(log)
  351. if contains(char(log.Code(l)),'score','IgnoreCase',true)
  352. scorestring = char(log.Code(l));
  353. if strcmp(scorestring(1,end-3:end),'-100')
  354. log.rating(l) = str2double(scorestring(1,end-3:end));
  355. elseif ~contains(scorestring(1,end-2:end),':')
  356. log.rating(l) = str2double(strtrim(scorestring(1,end-2:end)));
  357. else
  358. log.rating(l) = str2double(scorestring(1,end));
  359. end
  360. else
  361. log.rating(l) = NaN;
  362. end
  363. end
  364. log.trial_type = categorical(log.trial_type);
  365. log = log((~isundefined(log.trial_type) | ~isnan(log.rating)),:);
  366. for n = 1:height(log)
  367. if ismember(log.trial_type(n),events_interest)
  368. log.rating(n) = log.rating(n+3);
  369. end
  370. end
  371. log = removevars(log,{'Trial','EventType','Code','Time','TTime','Duration','ReqTime','TimeZero'}); % get rid of junk variables from logfile we don't need
  372. log = log(~isundefined(log.trial_type),:);
  373. log.duration = zeros(height(log),1);
  374. for m = 1:height(log)
  375. if ~isequal(log.trial_type(m),'fixation')
  376. log.duration(m) = log.onset(m+1) - log.onset(m);
  377. else
  378. log.duration(m) = NaN;
  379. end
  380. end
  381. log = log(~isnan(log.duration),:);
  382. filename = fullfile(subjBIDSdir,[sourcesubjs{sub},'_task-',taskname,runnames{run},'_events.tsv']);
  383. writetable(log,filename,'Filetype','text','Delimiter','\t');
  384. if pheno_tsv
  385. log2 = log(~isnan(log.rating),:);
  386. log2 = removevars(log2,{'onset','duration'});
  387. for n = 1:height(log2)
  388. log2.participant_id(n,:) = BIDSsubjs{sub};
  389. log2.run_id(n) = run;
  390. log2.trial_id_run(n) = n; % generates consecutive trial numbers within each run
  391. log2.trial_id_concat(n) = height(pheno_file_subj) + n; % generates consecutive trial numbers over all conditions & runs
  392. end
  393. for o = 1:size(events_interest,2)
  394. idx_run = log2.trial_type == events_interest{o};
  395. log2.trial_id_cond_run(idx_run) = 1:sum(idx_run);
  396. if height(pheno_file_subj) > 0
  397. idx_sub = pheno_file_subj.trial_type == events_interest{o};
  398. log2.trial_id_cond_concat(idx_run) = sum(idx_sub)+1:(sum(idx_sub)+sum(idx_run));
  399. else
  400. log2.trial_id_cond_concat = log2.trial_id_cond_run;
  401. end
  402. clear idx_run idx_sub
  403. end
  404. pheno_file_subj = [pheno_file_subj;log2];
  405. end
  406. clear logfile log time_zero filename log2
  407. end % if loop checking whether logfile exists
  408. end % for loop runs
  409. pheno_file = [pheno_file;pheno_file_subj];
  410. clear pheno_file_subj;
  411. end % for loop subjects
  412. pheno_filename = fullfile(pheno_dir,pheno_name);
  413. writetable(pheno_file,pheno_filename,'Filetype','text','Delimiter','\t');
  414. end % if loop checking sourcesubjs == BIDSsubjs
  415. end % if loop checking writing option