Skip to content

Commit 94c80c0

Browse files
BuchBuch
authored andcommitted
postprocessing: split summary output into behavioral & ERP tables
- generate separate tables for behavioral and ERP (post-processing) analyses - track data attribution between collection and final ERP-ready trials - fix percentage calculations to use correct denominators - add sum columns for error trials (soc-vis-err, nonsoc-vis-err) - match MATLAB column order for compatibility
1 parent 13885f4 commit 94c80c0

21 files changed

+470
-474
lines changed
Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,29 @@ cat("session started:", as.character(Sys.time()), "\n\n")
88
# clear environment
99
rm(list = ls())
1010

11+
# load required libraries
12+
library(tidyverse)
13+
library(jsonlite)
14+
1115
# load configuration & functions
1216
source("config/paths.R")
1317

1418
# get repo root from paths
15-
repo_root <- file.path(here::here(), "..", "..", "..")
19+
repo_root <- file.path(here::here(), "..", "..")
1620

1721
source("config/settings.R")
1822
source("functions/load_behavioral_data.R")
1923
source("functions/load_eeg_trial_info.R")
2024
source("functions/apply_eeg_exclusions.R")
2125
source("functions/apply_rt_trimming.R")
2226
source("functions/check_inclusion_criteria.R")
23-
source("functions/validate_against_eeg.R")
2427
source("functions/generate_reports.R")
2528

2629
# === user input: define subject list ===
2730
# slash-separated string of subject IDs to be processed in this run
2831
# leave empty ("") to process all available subjects
29-
subjects_to_process = "390002/390003/390004/390005/390006/390007/390008/390009/390010/390011/390012/390013/390014/390015/390020/390021/390022/390023/390024/390025/390026/390027/390028/390030/390031/390032/390033/390034/390036/390037/390038/390039/390041/390042";
32+
# subjects_to_process = "390001/390002/390003/390004/390005/390006/390007/390008/390009/390010/390011/390012/390013/390014/390015/390020/390021/390022/390023/390024/390025/390026/390027/390028/390030/390031/390032/390033/390034/390036/390037/390038/390039/390041/390042/390043/390044";
33+
subjects_to_process = "390035"
3034

3135
# parse subject list
3236
if (subjects_to_process == "") {
@@ -100,11 +104,94 @@ inclusion_file <- file.path(output_dir, "inclusion_summary.rds")
100104
saveRDS(inclusion_summary, inclusion_file)
101105
cat("saved inclusion summary to:", inclusion_file, "\n")
102106

107+
processing_params <- list(
108+
rt_lower_bound = RT_LOWER_BOUND,
109+
rt_outlier_threshold = RT_OUTLIER_THRESHOLD,
110+
min_accuracy = MIN_ACCURACY,
111+
min_trials_per_code = MIN_EPOCHS_PER_CODE
112+
)
113+
114+
# === SAVE CSV OUTPUTS FOR MATLAB ===
115+
cat("\n=== SAVING CSV OUTPUTS FOR MATLAB ===\n")
116+
117+
# 1. save behavioral summary as CSV for MATLAB
118+
behavioral_summary_csv <- inclusion_summary %>%
119+
select(
120+
subject,
121+
included,
122+
exclusion_reason,
123+
accuracy,
124+
total_trials,
125+
n_code_102, n_code_104, n_code_111, n_code_112, n_code_113,
126+
n_code_202, n_code_204, n_code_211, n_code_212, n_code_213
127+
) %>%
128+
mutate(
129+
included = as.numeric(included), # convert TRUE/FALSE to 1/0
130+
exclusion_reason = replace_na(exclusion_reason, "none")
131+
)
132+
133+
behavioral_summary_file <- file.path(output_dir, "behavioral_summary.csv")
134+
write_csv(behavioral_summary_csv, behavioral_summary_file)
135+
cat("saved behavioral summary csv to:", behavioral_summary_file, "\n")
136+
137+
# 2. save trial-level data as CSV for MATLAB (if needed for future analyses)
138+
behavioral_trials_csv <- behavioral_with_rt %>%
139+
select(
140+
subject,
141+
trial_idx,
142+
code,
143+
rt = flankerResponse.rt,
144+
accuracy = responseType,
145+
in_eeg,
146+
eeg_analysis_ready,
147+
eeg_exclusion_reason,
148+
rt_exclusion_reason
149+
) %>%
150+
mutate(
151+
in_eeg = as.numeric(in_eeg),
152+
eeg_analysis_ready = as.numeric(eeg_analysis_ready),
153+
accuracy = as.numeric(accuracy),
154+
eeg_exclusion_reason = replace_na(eeg_exclusion_reason, "none"),
155+
rt_exclusion_reason = replace_na(rt_exclusion_reason, "none")
156+
)
157+
158+
behavioral_trials_file <- file.path(output_dir, "behavioral_trials.csv")
159+
write_csv(behavioral_trials_csv, behavioral_trials_file)
160+
cat("saved trial-level data csv to:", behavioral_trials_file, "\n")
161+
162+
# 3. save processing metadata as JSON for documentation
163+
metadata <- list(
164+
processing_date = Sys.time(),
165+
parameters = processing_params,
166+
n_subjects_processed = n_distinct(behavioral_data$subject),
167+
n_subjects_included = sum(inclusion_summary$included),
168+
n_subjects_excluded = sum(!inclusion_summary$included),
169+
output_files = list(
170+
behavioral_summary = "behavioral_summary.csv",
171+
behavioral_trials = "behavioral_trials.csv",
172+
subject_summary_table = paste0("subject_summary_table_", format(Sys.time(), "%Y-%m-%d_%H-%M-%S"), ".txt")
173+
)
174+
)
175+
176+
metadata_file <- file.path(output_dir, "processing_metadata.json")
177+
jsonlite::write_json(metadata, metadata_file, pretty = TRUE)
178+
cat("saved processing metadata to:", metadata_file, "\n")
179+
103180
# generate reports
104181
cat("\n=== GENERATING REPORTS ===\n")
105182

106-
# subject summary table
107-
subject_table <- generate_subject_summary_table(
183+
# generate reports
184+
cat("\n=== GENERATING REPORTS ===\n")
185+
186+
# generate both summary tables
187+
behavioral_table <- generate_behavioral_summary_table(
188+
behavioral_with_rt,
189+
inclusion_summary,
190+
output_dir,
191+
verbose = TRUE
192+
)
193+
194+
erp_table <- generate_erp_summary_table(
108195
behavioral_with_rt,
109196
inclusion_summary,
110197
output_dir,
@@ -119,36 +206,9 @@ processing_params <- list(
119206
min_trials_per_code = MIN_EPOCHS_PER_CODE
120207
)
121208

122-
cat("\n=== VALIDATION ===\n")
123-
124-
# validate against most recent eeg postprocessing
125-
# find most recent eeg postprocessing date
126-
eeg_dates <- list.dirs(derivatives_dir,
127-
full.names = FALSE, recursive = FALSE)
128-
eeg_dates <- eeg_dates[grepl("_erp-postprocessing$", eeg_dates)]
129-
130-
if (length(eeg_dates) > 0) {
131-
most_recent_eeg <- sort(eeg_dates, decreasing = TRUE)[1]
132-
eeg_date <- gsub("_erp-postprocessing", "", most_recent_eeg)
133-
134-
cat("\nvalidating against eeg postprocessing from:", eeg_date, "\n")
135-
136-
eeg_summary <- load_eeg_summary_table(eeg_date, derivatives_dir)
137-
validation_results <- validate_trial_counts(inclusion_summary, eeg_summary, verbose = TRUE)
138-
139-
# save validation results
140-
validation_file <- file.path(output_dir, "validation_results.rds")
141-
saveRDS(validation_results, validation_file)
142-
cat("\nsaved validation results to:", validation_file, "\n")
143-
} else {
144-
cat("\nwarning: no eeg postprocessing folders found, skipping validation\n")
145-
}
146-
147-
# generate final summary report (needs validation results)
148209
summary_report <- generate_postprocessing_summary(
149210
behavioral_with_rt,
150211
inclusion_summary,
151-
validation_results,
152212
output_dir,
153213
processing_params,
154214
verbose = TRUE

code/postprocessing/postprocessing-behavior/config/paths.R renamed to code/postprocessing-behavior/config/paths.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ library(stringr)
66

77
# read link to preprocessed data (same file used by matlab)
88
# here() gives us the r project root, need to go up to repo root
9-
repo_root <- file.path(here(), "..", "..", "..")
9+
repo_root <- file.path(here(), "..", "..")
1010
preprocessed_link <- file.path(repo_root, "input", "preprocessed")
1111
preprocessed_path <- readLines(preprocessed_link, warn = FALSE) %>% str_trim()
1212

0 commit comments

Comments
 (0)