Skip to content

Commit fe4db65

Browse files
BuchBuch
authored andcommitted
feat: add require_matched_subjects flag and improve difference wave reporting
- Add explicit control for difference wave subject matching - Update write_processing_report to display per-code thresholds - Track subjects contributing to each condition - Pass min_trials_per_code to report generation
1 parent cff1928 commit fe4db65

File tree

4 files changed

+58
-30
lines changed

4 files changed

+58
-30
lines changed

code/postprocessing/postprocessing-eeg/batch_eeg_postprocessing.m

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,8 @@
7474

7575

7676
% inclusion thresholds
77-
min_epochs_threshold = 10;
7877
min_accuracy_threshold = 0.6;
78+
min_epochs_threshold = 10;
7979

8080
% RT trimming parameters
8181
rt_lower_bound = 150; % ms, set to 0 if no lower bound
@@ -119,6 +119,9 @@
119119

120120
fprintf('defined %d difference wave computations\n', size(diff_waves_table, 1));
121121

122+
% difference wave computation settings
123+
require_matched_subjects = true; % only compute difference waves from subjects present in both conditions
124+
122125
ern_time_window = [0, 100];
123126
pe_time_window = [200, 500];
124127

@@ -139,7 +142,6 @@
139142
processing_report.input_data.code_names = code_names;
140143

141144
% processing parameters
142-
processing_report.parameters.min_epochs_threshold = min_epochs_threshold;
143145
processing_report.parameters.min_accuracy_threshold = min_accuracy_threshold;
144146
processing_report.parameters.rt_lower_bound = rt_lower_bound;
145147
processing_report.parameters.rt_outlier_threshold = rt_outlier_threshold;
@@ -190,7 +192,7 @@
190192
% generate subject summary table
191193
fprintf('\ngenerating subject summary table...\n');
192194
grand_avg_data = load(grand_avg_file);
193-
generate_subject_summary_table(grand_avg_data.processing_stats, included_subjects, codes, output_dir, grand_avg_data.condition_inclusion);
195+
generate_subject_summary_table(grand_avg_data.processing_stats, included_subjects, codes, output_dir, grand_avg_data.condition_inclusion, grand_avg_data.min_trials_per_code);
194196

195197
else
196198
% load existing grand averages if not running step 1
@@ -217,7 +219,7 @@
217219

218220
% function call
219221
grand_avg_data = load(grand_avg_file);
220-
[difference_waves] = compute_difference_waves(grand_averages, included_subjects, diff_waves_table, output_dir, grand_avg_data.condition_inclusion, codes);
222+
[difference_waves] = compute_difference_waves(grand_averages, included_subjects, diff_waves_table, output_dir, grand_avg_data.condition_inclusion, codes, require_matched_subjects);
221223

222224
step2_end_time = datetime('now');
223225

@@ -278,8 +280,18 @@
278280

279281
% generate comprehensive text report
280282
report_file = fullfile(output_dir, sprintf('postprocessing_summary_%s.txt', datestr(now, 'yyyy-mm-dd_HH-MM-SS')));
281-
write_processing_report(processing_report, diff_waves_table, output_dir, report_file);
282-
283+
% load min_trials_per_code if available
284+
if exist(grand_avg_file, 'file')
285+
grand_avg_data = load(grand_avg_file);
286+
if isfield(grand_avg_data, 'min_trials_per_code')
287+
min_trials_per_code = grand_avg_data.min_trials_per_code;
288+
else
289+
min_trials_per_code = [];
290+
end
291+
else
292+
min_trials_per_code = [];
293+
end
294+
write_processing_report(processing_report, diff_waves_table, output_dir, report_file, min_trials_per_code);
283295
% print console summary
284296
print_console_summary(processing_report, output_dir, report_file, console_log_file);
285297

code/postprocessing/postprocessing-eeg/compute_difference_waves.m

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function difference_waves = compute_difference_waves(grand_averages, included_subjects, diff_waves_table, output_dir, condition_inclusion, codes)
1+
function difference_waves = compute_difference_waves(grand_averages, included_subjects, diff_waves_table, output_dir, condition_inclusion, codes, require_matched_subjects)
22
% compute_difference_waves - create configurable difference waves for ERP analysis
33
%
44
% this function computes difference waves based on user-defined table
@@ -8,6 +8,7 @@
88
% included_subjects - cell array of included subject IDs
99
% diff_waves_table - matrix [minuend_code, subtrahend_code, wave_name]
1010
% output_dir - path to save difference waves
11+
% require_matched_subjects - logical, if true only compute from subjects in both conditions (default: true)
1112
%
1213
% outputs:
1314
% difference_waves - struct containing computed difference waves
@@ -59,6 +60,7 @@
5960
fprintf(log_fid, 'script: compute_difference_waves.m\n');
6061
fprintf(log_fid, 'included subjects: %d\n', length(included_subjects));
6162
fprintf(log_fid, 'subjects: %s\n', strjoin(included_subjects, ', '));
63+
fprintf(log_fid, 'require_matched_subjects: %s\n', mat2str(require_matched_subjects));
6264
fprintf(log_fid, '\n');
6365

6466
% initialize difference waves structure
@@ -107,30 +109,36 @@
107109
subject = included_subjects{subj_idx};
108110
subject_inclusion = condition_inclusion(subject);
109111

110-
% check if subject has both conditions
111-
if subject_inclusion(minuend_idx) && subject_inclusion(subtrahend_idx)
112-
subjects_for_this_diffwave{end+1} = subject;
113-
114-
% find this subject's position in each grand average
115-
% count how many subjects before this one have the minuend code
116-
minuend_pos = 0;
117-
for i = 1:subj_idx
118-
temp = condition_inclusion(included_subjects{i});
119-
if temp(minuend_idx)
120-
minuend_pos = minuend_pos + 1;
112+
% check if subject has both conditions (only if require_matched_subjects is true)
113+
if require_matched_subjects
114+
% strict mode: subject must have BOTH conditions
115+
if subject_inclusion(minuend_idx) && subject_inclusion(subtrahend_idx)
116+
subjects_for_this_diffwave{end+1} = subject;
117+
118+
% find this subject's position in each grand average
119+
% count how many subjects before this one have the minuend code
120+
minuend_pos = 0;
121+
for i = 1:subj_idx
122+
temp = condition_inclusion(included_subjects{i});
123+
if temp(minuend_idx)
124+
minuend_pos = minuend_pos + 1;
125+
end
121126
end
122-
end
123127

124-
subtrahend_pos = 0;
125-
for i = 1:subj_idx
126-
temp = condition_inclusion(included_subjects{i});
127-
if temp(subtrahend_idx)
128-
subtrahend_pos = subtrahend_pos + 1;
128+
subtrahend_pos = 0;
129+
for i = 1:subj_idx
130+
temp = condition_inclusion(included_subjects{i});
131+
if temp(subtrahend_idx)
132+
subtrahend_pos = subtrahend_pos + 1;
133+
end
129134
end
130-
end
131135

132-
minuend_indices = [minuend_indices, minuend_pos];
133-
subtrahend_indices = [subtrahend_indices, subtrahend_pos];
136+
minuend_indices = [minuend_indices, minuend_pos];
137+
subtrahend_indices = [subtrahend_indices, subtrahend_pos];
138+
end
139+
else
140+
% loose mode would go here if ever needed
141+
error('Loose mode (require_matched_subjects=false) not implemented for within-subjects design');
134142
end
135143
end
136144

code/postprocessing/postprocessing-eeg/generate_subject_summary_table.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function generate_subject_summary_table(processing_stats, included_subjects, codes, output_dir, condition_inclusion)
1+
function generate_subject_summary_table(processing_stats, included_subjects, codes, output_dir, condition_inclusion, min_trials_per_code)
22
% generate tab-separated summary table for all subjects
33
% creates table matching format from google doc for easy copy-paste
44
%

code/postprocessing/postprocessing-eeg/write_processing_report.m

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
function write_processing_report(processing_report, diff_waves_table, output_dir, report_file)
1+
function write_processing_report(processing_report, diff_waves_table, output_dir, report_file, min_trials_per_code)
22
% write comprehensive processing summary report to text file
33
%
44
% inputs:
@@ -43,7 +43,15 @@ function write_processing_report(processing_report, diff_waves_table, output_dir
4343

4444
% processing parameters
4545
fprintf(report_fid, '=== PROCESSING PARAMETERS ===\n');
46-
fprintf(report_fid, 'minimum epochs threshold: %d\n', processing_report.parameters.min_epochs_threshold);
46+
fprintf(report_fid, 'minimum epochs thresholds (per code):\n');
47+
if ~isempty(min_trials_per_code)
48+
codes_list = fieldnames(min_trials_per_code);
49+
for i = 1:length(codes_list)
50+
fprintf(report_fid, ' - %s: %d trials\n', codes_list{i}, min_trials_per_code.(codes_list{i}));
51+
end
52+
else
53+
fprintf(report_fid, ' - not available\n');
54+
end
4755
fprintf(report_fid, 'minimum accuracy threshold: %.1f%%\n', processing_report.parameters.min_accuracy_threshold * 100);
4856
fprintf(report_fid, 'RT lower bound: %d ms\n', processing_report.parameters.rt_lower_bound);
4957
fprintf(report_fid, 'RT outlier threshold: %.1f SD\n', processing_report.parameters.rt_outlier_threshold);

0 commit comments

Comments
 (0)