OSCR

The human hippocampus can pattern separate memories by meaning.

Code ↔ Paper

9 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 9 matches
  1. [1] § Behavioral Methods › Materials. › Covariate assessments. ↔ scripts/smst2_bids_participants_maker.py, lines 191–215 · score 0.70 · WAIS IV, Digit Symbol, subtests, Vocabulary, covariate, words
  2. [2] § Behavioral Methods › MRI Methods. › Hippocampal subfield segmentation. ↔ scripts/smst_mr1_HCmask_snapshot.sh, lines 1–50 · score 0.64 · coronal slices, template T1, brain, masks, SI
  3. [3] § Behavioral Methods › Materials. › Main task (sMST). › Stimuli. ↔ scripts/smst2_bids_tsv_maker.py, lines 303–386 · score 0.63 · Hungarian National Corpus, imageability, metric, arousal, concreteness, fillers
  4. [4] § Behavioral Methods › MRI Methods. › Analysis. ↔ scripts/smst_mr1_analysis.Rmd, lines 156–247 · score 0.61 · close lure, distant lure, lure correct, incorrect, hits, foil
  5. [5] § Results › RS Sensitive Clusters in the Hippocampal Head Differentially Activate for Exact Repeats and Modified Repeats During Encoding. ↔ scripts/smst_mr1_analysis.Rmd, lines 2190–2332 · score 0.61 · DG CA3, right hemisphere, left hemisphere, CA12, ROI, Facets
  6. [6] § Results › Neural Response Is Increased for Semantically Close Modified Phrases Compared to Exact Repetitions, with No Further Difference between Semantic Similarity Bins. ↔ scripts/smst_mr1_analysis.Rmd, lines 2190–2332 · score 0.54 · DG CA3, distant repeats, CA1, anatomically, ANOVA, head
  7. [7] § Behavioral Methods › Analysis. ↔ scripts/smst_mr1_analysis.Rmd, lines 398–440 · score 0.54 · Wald, confint, intercept, arousal, concreteness, GLMEMs
  8. [8] § Results › RS Sensitive Clusters in the Hippocampal Head Differentially Activate for Exact Repeats and Modified Repeats During Encoding. ↔ scripts/smst_mr1_analysis.Rmd, lines 442–540 · score 0.53 · Post hoc, covariate, clusters, encoding
  9. [9] § Results › Clusters Sensitive to Repetition at Encoding Do Not Track Mnemonic Discrimination Success within Lures but Differentiate Correctly Rejected Lures from Correctly Rejected Foils During Recogni ↔ scripts/smst_mr1_analysis.Rmd, lines 3552–3596 · score 0.50 · lure correct rejections, target hits, modified repeat, baseline, clusters

Paper

Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC

The paper is loaded when this pane is shown.

The authors' code

R Markdown · 5,188 lines · 155 KB · no license · 6 matches

  1. ---
  2. title: "Analysis pipeline for the behavioral and functional MRI data from the semantic Mnemonic Similarity Task"
  3. output: html_document
  4. editor_options:
  5. chunk_output_type: console
  6. ---
  7. # Glossary & packages
  8. ENC = Encoding task phase
  9. REC = Recognition task phase
  10. RS = repetition suppression (first presentation - repeat)
  11. PS = pattern separation (hippocampal operation)
  12. MD = mnemonic discrimination (behavioural correlate)
  13. LDI = lure discrimination index (proxy of MD)
  14. ```{r}
  15. knitr::opts_chunk$set(echo = TRUE)
  16. library(ppcor)
  17. library(tidyverse)
  18. library(tidytuesdayR)
  19. library(readxl)
  20. library(tidyr)
  21. library(scales)
  22. library(ggplot2)
  23. library(psycho)
  24. library(ggpubr)
  25. library(rstatix)
  26. library(afex)
  27. library(MuMIn)
  28. library(lme4)
  29. library(lmerTest)
  30. library(ggstatsplot)
  31. library(sjPlot)
  32. library(smplot2)
  33. library(openxlsx)
  34. library(stats)
  35. library(performance)
  36. library(emmeans)
  37. library(purrr)
  38. library(broom)
  39. library(ggpattern)
  40. library(ggtext)
  41. library(dplyr)
  42. library(ppcor)
  43. library(readr)
  44. library(stringr)
  45. library(grid)
  46. library(gridExtra)
  47. library(gtable)
  48. library(patchwork)
  49. theme_set(theme_light())
  50. custom_theme = theme(
  51. plot.title = element_text(color="black", size=30, face="bold", hjust = 0, vjust = 3), #30 for covariate plots
  52. axis.text.x = element_text(vjust = 0.5, hjust=0.5, size = 20, face="bold"), #was 16 for other plots besides rec resp rate
  53. axis.text.y = element_text(vjust = 0.5, hjust=0.5, size = 20, face="bold"),
  54. axis.title.x = element_text(vjust = -2, hjust=0.5, size = 26, face="bold"),
  55. axis.title.y = element_text(vjust = 3, hjust=0.5, size = 26, face="bold"),
  56. legend.text = element_text(vjust = 0.5, hjust=0.5, size = 22, face="bold"),
  57. legend.title = element_text(vjust = 0.5, hjust=0.5, size = 24, face="bold"),
  58. strip.text = element_text(vjust = 0.5, hjust=0.5, size = 22, face="bold"),
  59. plot.margin = margin(30, 30, 30, 30),
  60. panel.grid.major = element_blank(),
  61. panel.grid.minor = element_blank(),
  62. panel.background = element_blank(),
  63. axis.line = element_line(colour = "black")
  64. )
  65. ```
  66. # Data handling
  67. ## Read-in MR
  68. ```{r}
  69. semmst_mr_data = read.xlsx("./data/fmri/ses-01/subfield_data_featquery_2025.xlsx")
  70. ```
  71. ## Preprocess MR
  72. ```{r}
  73. semmst_mr_data_filtered = semmst_mr_data %>%
  74. dplyr::select(-c(cope_num, query_type, groupanalysis_type)) %>%
  75. dplyr::rename(ID = subject) %>%
  76. mutate(
  77. ID = as.factor(ID),
  78. space_type = as.factor(space_type),
  79. task = as.factor(task),
  80. cope_name = as.factor(cope_name),
  81. hemisphere = as.factor(hemisphere),
  82. roi = as.factor(roi),
  83. mask_type = as.factor(mask_type),
  84. contrast = as.factor(contrast),
  85. cluster = as.factor(cluster),
  86. estimate = as.numeric(estimate),
  87. )
  88. ```
  89. ## Read-in behavior
  90. ```{r}
  91. semmst_behav_data <- read_xlsx("./data/behavioural/smst/smst_mr_behvaioural_grand.xlsx")
  92. ```
  93. ## Preprocess behavior
  94. ```{r}
  95. # Clean and factorize
  96. semmst_behav_data_filtered <- semmst_behav_data %>%
  97. dplyr::rename(ID = participant_id) %>%
  98. mutate(ID = factor(str_c("sub-", ID)),
  99. task = factor(task, levels = c("enc", "rec")),
  100. run = factor(run),
  101. trial_type = factor(trial_type),
  102. stim_file = factor(stim_file),
  103. itemno = factor(itemno),
  104. order = factor(order),
  105. response = tolower(response),
  106. sex = factor(sex, levels = c("male", "female")),
  107. handedness = factor(handedness, levels = "R"),
  108. response_time = as.numeric(response_time),
  109. onset = as.numeric(onset),
  110. correctness = factor(correctness)) %>%
  111. filter(ID %in% semmst_mr_data_filtered$ID)
  112. semmst_mr_behav_data_enc <- semmst_behav_data_filtered %>%
  113. filter(task == "enc",
  114. trial_type != "FILLER") %>%
  115. mutate(former_type = ifelse(trial_type=="REPEAT", "TARGET", "LURE")) %>%
  116. dplyr::select(ID, noun, former_type) %>%
  117. distinct()
  118. semmst_mr_behav_data_rec <- semmst_behav_data_filtered %>%
  119. filter(task == "rec",
  120. !is.na(response)) %>%
  121. left_join(semmst_mr_behav_data_enc, by = c("ID", "noun") ) %>%
  122. mutate(response = factor(response, levels=c("old", "new"))) %>%
  123. mutate(trial_type = factor(trial_type)) %>%
  124. mutate(former_type = factor(former_type)) %>%
  125. mutate(correctness = factor(correctness, levels=c(0,1)))
  126. ```
  127. ### LDI
  128. ```{r}
  129. semmst_behav_data_rec_filtered = semmst_mr_behav_data_rec %>%
  130. filter(task == "rec") %>%
  131. filter(response != "None") %>%
  132. mutate(response = as.factor(response))
  133. semmst_behav_data_rec_wide_dprime = semmst_behav_data_rec_filtered %>%
  134. dplyr::count(ID, trial_type, response) %>%
  135. unite("trial_resp", c("trial_type", "response"), sep = "_") %>%
  136. spread(trial_resp, n) %>%
  137. mutate_at(vars(matches("_")), ~ifelse(is.na(.), 0, .)) %>%
  138. mutate(targ_n = TARGET_new + TARGET_old) %>%
  139. mutate(close_n = CLOSE_new + CLOSE_old) %>%
  140. mutate(distant_n = DISTANT_new + DISTANT_old) %>%
  141. mutate(foil_n = FOIL_new + FOIL_old) %>%
  142. mutate(LURE_old = CLOSE_old + DISTANT_old) %>%
  143. mutate(LURE_new = CLOSE_new + DISTANT_new) %>%
  144. mutate(lure_n = LURE_new + LURE_old)
  145. # Simple recognition - TARG vs FOIL
  146. rec_dprime <- psycho::dprime(
  147. n_hit = semmst_behav_data_rec_wide_dprime$TARGET_old,
  148. n_fa = semmst_behav_data_rec_wide_dprime$FOIL_old,
  149. n_miss = semmst_behav_data_rec_wide_dprime$TARGET_new,
  150. n_cr = semmst_behav_data_rec_wide_dprime$FOIL_new,
  151. n_targets = semmst_behav_data_rec_wide_dprime$targ_n,
  152. n_distractors = semmst_behav_data_rec_wide_dprime$foil_n,
  153. adjusted = TRUE
  154. )
  155. # Close lure discrimination - TARG vs CLOSE
  156. close_dprime <- psycho::dprime(
  157. n_hit = semmst_behav_data_rec_wide_dprime$TARGET_old,
  158. n_fa = semmst_behav_data_rec_wide_dprime$CLOSE_old,
  159. n_miss = semmst_behav_data_rec_wide_dprime$TARGET_new,
  160. n_cr = semmst_behav_data_rec_wide_dprime$CLOSE_new,
  161. n_targets = semmst_behav_data_rec_wide_dprime$targ_n,
  162. n_distractors = semmst_behav_data_rec_wide_dprime$close_n,
  163. adjusted = TRUE
  164. )
  165. # Distant lure discrimination - TARG vs DISTANT
  166. dist_dprime <- psycho::dprime(
  167. n_hit = semmst_behav_data_rec_wide_dprime$TARGET_old,
  168. n_fa = semmst_behav_data_rec_wide_dprime$DISTANT_old,
  169. n_miss = semmst_behav_data_rec_wide_dprime$TARGET_new,
  170. n_cr = semmst_behav_data_rec_wide_dprime$DISTANT_new,
  171. n_targets = semmst_behav_data_rec_wide_dprime$targ_n,
  172. n_distractors = semmst_behav_data_rec_wide_dprime$distant_n,
  173. adjusted = TRUE
  174. )
  175. # All lure discrimination - TARG vs LURE
  176. lure_dprime <- psycho::dprime(
  177. n_hit = semmst_behav_data_rec_wide_dprime$TARGET_old,
  178. n_fa = semmst_behav_data_rec_wide_dprime$LURE_old,
  179. n_miss = semmst_behav_data_rec_wide_dprime$TARGET_new,
  180. n_cr = semmst_behav_data_rec_wide_dprime$LURE_new,
  181. n_targets = semmst_behav_data_rec_wide_dprime$targ_n,
  182. n_distractors = semmst_behav_data_rec_wide_dprime$lure_n,
  183. adjusted = TRUE
  184. )
  185. semmst_behav_data_rec_summary <- semmst_behav_data_rec_wide_dprime %>%
  186. transmute(
  187. ID,
  188. # Recognition
  189. rec_dprime = rec_dprime$dprime,
  190. rec_correct = (TARGET_old) / (targ_n),
  191. rec_incorrect = (TARGET_new) / (targ_n),
  192. # Close LDI
  193. close_dprime = close_dprime$dprime,
  194. close_correct = (CLOSE_new) / (close_n),
  195. close_incorrect= (CLOSE_old) / (close_n),
  196. # Distant LDI
  197. distant_dprime = dist_dprime$dprime,
  198. distant_correct = (DISTANT_new) / (distant_n),
  199. distant_incorrect= (DISTANT_old) / (distant_n),
  200. # Lure LDI
  201. lure_dprime = lure_dprime$dprime,
  202. lure_correct = (LURE_new) / (lure_n),
  203. lure_incorrect= (LURE_old) / (lure_n),
  204. )
  205. ```
  206. ## Merge
  207. ```{r}
  208. semmst_merged_data <- semmst_mr_data_filtered %>%
  209. left_join(
  210. semmst_behav_data_filtered %>%
  211. dplyr::select(ID, sex:GRO_score_SES02) %>%
  212. distinct(ID, .keep_all = TRUE),
  213. by = "ID"
  214. ) %>%
  215. left_join(semmst_behav_data_rec_summary, by = "ID") %>%
  216. droplevels()
  217. ```
  218. ## FILTER for HYPOTHESIS TESTING
  219. ```{r}
  220. # REPETITION SENSTIVITY
  221. semmst_merged_data_filtered = semmst_merged_data %>%
  222. filter(contrast %in% c("ENC_ERP_ALLRS")) %>%
  223. filter(space_type == "standard") %>%
  224. filter(voxels > 50) %>%
  225. filter(as.numeric(as.character(cluster)) < 140) %>%
  226. mutate(roi_cluster = paste(hemisphere, roi, cluster, sep = "_")) %>%
  227. mutate(roi_hemisphere = paste(hemisphere, roi, sep = "_")) %>%
  228. mutate(Vocabulary_scale = scale(Vocabulary)) %>%
  229. mutate(Digit_Symbol_scale = scale(Digit_Symbol)) %>%
  230. mutate(NonWord_sum_scale = scale(NonWord_sum))
  231. # ANATOMY CONTROL
  232. semmst_merged_data_filtered_anat = semmst_merged_data %>%
  233. filter(contrast %in% c("anatHC")) %>%
  234. mutate(roi_cluster = paste(hemisphere, roi, cluster, sep = "_")) %>%
  235. mutate(roi_hemisphere = paste(hemisphere, roi, sep = "_")) %>%
  236. mutate(Vocabulary_scale = scale(Vocabulary)) %>%
  237. mutate(Digit_Symbol_scale = scale(Digit_Symbol)) %>%
  238. mutate(NonWord_sum_scale = scale(NonWord_sum))
  239. ```
  240. # Revision data handling - read-in, filter and merge
  241. ```{r}
  242. # Read in
  243. semmst_mr_data_supplement = read.xlsx("./data/fmri/ses-01/subfield_data_featquery_supplement.xlsx")
  244. # Filter
  245. semmst_mr_data_supplement_filtered = semmst_mr_data_supplement %>%
  246. dplyr::select(-any_of(c("cope_num", "query_type", "groupanalysis_type"))) %>%
  247. dplyr::rename(ID = subject) %>%
  248. mutate(
  249. ID = as.factor(ID),
  250. space_type = as.factor(space_type),
  251. task = as.factor(task),
  252. cope_name = as.factor(cope_name),
  253. hemisphere = as.factor(hemisphere),
  254. roi = as.factor(roi),
  255. mask_type = as.factor(mask_type),
  256. contrast = as.factor(contrast),
  257. cluster = as.factor(cluster),
  258. estimate = as.numeric(estimate)
  259. )
  260. # Merge
  261. semmst_merged_data_supplement <- semmst_mr_data_supplement_filtered %>%
  262. left_join(
  263. semmst_behav_data_filtered %>%
  264. dplyr::select(ID, sex:GRO_score_SES02) %>%
  265. distinct(ID, .keep_all = TRUE),
  266. by = "ID"
  267. ) %>%
  268. left_join(semmst_behav_data_rec_summary, by = "ID") %>%
  269. droplevels()
  270. ```
  271. ## CLEAN
  272. ```{r}
  273. semmst_merged_data_supplement_filtered = semmst_merged_data_supplement %>%
  274. mutate(roi_cluster = paste(hemisphere, roi, cluster, sep = "_")) %>%
  275. mutate(roi_hemisphere = paste(hemisphere, roi, sep = "_")) %>%
  276. mutate(Vocabulary_scale = scale(Vocabulary)) %>%
  277. mutate(Digit_Symbol_scale = scale(Digit_Symbol)) %>%
  278. mutate(NonWord_sum_scale = scale(NonWord_sum))
  279. ```
  280. # Results
  281. ## Analysis 1 - Behavioural
  282. ### ANOVA on LDI
  283. ```{r}
  284. semmst_behav_data_rec_LDI_ANCOVA = semmst_behav_data_rec_LDI_ANOVA %>%
  285. left_join(semmst_behav_data_rec_filtered %>% dplyr::select(ID, age, sex, education, Vocabulary, Digit_Symbol, NonWord_sum) %>% distinct(), by="ID") %>%
  286. mutate(Vocabulary_scale = scale(Vocabulary)) %>%
  287. mutate(Digit_Symbol_scale = scale(Digit_Symbol)) %>%
  288. mutate(NonWord_sum_scale = scale(NonWord_sum)) %>% drop_na()
  289. # ASSUMPTION CHECKS
  290. ## OUTLIER
  291. semmst_behav_data_rec_LDI_ANCOVA %>%
  292. group_by(dprime_type) %>%
  293. identify_outliers(dprime_value)
  294. ##NORMALITY
  295. semmst_behav_data_rec_LDI_ANCOVA %>%
  296. group_by(dprime_type) %>%
  297. shapiro_test(dprime_value) %>% print(n=40)
  298. ggqqplot(semmst_behav_data_rec_LDI_ANCOVA, "dprime_value", ggtheme = theme_bw()) +
  299. facet_grid(. ~ dprime_type, labeller = "label_both")
  300. # ANCOVA
  301. ANCOVA_behav_data_rec_LDI = semmst_behav_data_rec_LDI_ANCOVA %>%
  302. anova_test(dv = dprime_value, wid = ID, within = c(dprime_type), covariate = c(sex, age, Vocabulary_scale, Digit_Symbol_scale, NonWord_sum_scale))
  303. get_anova_table(ANCOVA_behav_data_rec_LDI)
  304. # POST-HOC
  305. semmst_behav_data_rec_LDI_ANCOVA_PWC = semmst_behav_data_rec_LDI_ANCOVA %>%
  306. pairwise_t_test(
  307. dprime_value ~ dprime_type,
  308. paired = TRUE,
  309. detailed = TRUE
  310. ) %>%
  311. adjust_pvalue(method = "bonferroni") %>%
  312. add_significance() %>%
  313. ungroup() %>%
  314. mutate(label = paste0("p=", signif(p.adj, 3)))
  315. semmst_behav_data_rec_LDI_ANCOVA_PWC %>%
  316. dplyr::select(-c(alternative, method, .y.)) %>%
  317. print(n = 50)
  318. # REPORT
  319. ## dprime_type 174.597 <0.001 *** 0.582000
  320. ## NonWord_sum_scale 8.346 0.008 ** 0.229000
  321. ## Digit_Symbol_scale:dprime_type 3.593 0.035 * 0.028000
  322. ```
  323. ### GLMEM: semantic similarity ~ response
  324. ```{r}
  325. semmst_behav_data_rec_filtered_GLMEM <- semmst_behav_data_rec_filtered %>%
  326. mutate(across(all_of(c("Vocabulary", "Digit_Symbol", "NonWord_sum", "arousal", "concreteness", "imageability", "meaningfulness")),
  327. ~ as.numeric(scale(.x)), # drop scale() attributes
  328. .names = "{.col}_scale")) %>%
  329. drop_na(Vocabulary) %>%
  330. mutate(response = relevel(response, ref = "new"))
  331. semmst_behav_data_rec_filtered_GLMEM_lure = semmst_behav_data_rec_filtered_GLMEM %>%
  332. filter(trial_type %in% c("CLOSE", "DISTANT"))
  333. model_simple_cont = glmer(response ~ cosine * trial_type * former_type +
  334. (1|ID) +
  335. (1|itemno),
  336. family = binomial("probit"),
  337. data = semmst_behav_data_rec_filtered_GLMEM_lure,
  338. control = glmerControl(optimizer ='bobyqa', optCtrl=list(maxfun = 20000)), nAGQ=0)
  339. model_complex_cont = glmer(response ~ cosine * trial_type * former_type +
  340. age + sex + education + Vocabulary_scale + Digit_Symbol_scale +
  341. NonWord_sum_scale + arousal_scale + meaningfulness_scale + concreteness_scale +
  342. (1|ID) +
  343. (1|itemno),
  344. family = binomial("probit"),
  345. data = semmst_behav_data_rec_filtered_GLMEM_lure,
  346. control = glmerControl(optimizer ='bobyqa', optCtrl=list(maxfun = 20000)), nAGQ=0)
  347. anova(model_simple_cont, model_complex_cont)
  348. summary(model_complex_cont)
  349. confint(model_complex_cont, parm="beta_", method="Wald")
  350. # REPORT Estimate Std. Error z value Pr(>|z|) 2.5 % 97.5 % (Intercept)
  351. ## cosine 5.131681 1.806091 2.841 0.00449 ** -7.444086424 -0.35150302
  352. ## NonWord_sum_scale -0.213690 0.069192 -3.088 0.00201 ** -0.349304360 -0.07807487
  353. ## meaningfulness_scale 0.145144 0.073066 1.986 0.04698 * 0.001936827 0.28835127
  354. ## trial_typeDISTANT 2.011234 1.931225 1.041 0.29768
  355. ## former_typeTARGET -1.376078 1.970692 -0.698 0.48501
  356. ```
  357. ## Analysis 2 - ENC RS
  358. ### ANOVA - REPEAT vs. LURE
  359. ```{r}
  360. # FILTER DATA
  361. semmst_merged_data_H1 = semmst_merged_data_filtered %>%
  362. filter(task == "Encoding") %>%
  363. filter(cope_name %in% c("LureFirst", "ExactFirst", "LureRepeat", "ExactRepeat")) %>%
  364. extract(
  365. col = cope_name,
  366. into = c("stim_type", "order"),
  367. regex = "([A-Za-z]+?)(First|Repeat)$"
  368. ) %>%
  369. mutate(
  370. stim_type = as.factor(stim_type),
  371. order = as.factor(order)
  372. ) %>%
  373. mutate(stim_type = factor(stim_type, levels = c("Exact", "Lure")))
  374. # ASSUMPTION CHECKS
  375. ## OUTLIER
  376. semmst_merged_data_H1 %>%
  377. group_by(stim_type, order, roi_cluster) %>%
  378. identify_outliers(estimate) %>%
  379. dplyr::select(ID, is.outlier, is.extreme) %>%
  380. filter(is.extreme==TRUE)
  381. semmst_merged_data_H1_nooutlier = semmst_merged_data_H1 %>%
  382. group_by(stim_type, order, roi_cluster) %>%
  383. filter(! ID %in% c("sub-434971")) %>% #, " sub-012421", "sub-632012", "sub-800472")) %>% # c("sub-434971", "sub-982347")) %>%
  384. drop_na(NonWord_sum_scale) %>%
  385. ungroup()
  386. ##NORMALITY
  387. semmst_merged_data_H1_nooutlier %>%
  388. group_by(stim_type, order) %>%
  389. shapiro_test(estimate)
  390. # ANOVA
  391. semmst_merged_data_H1_nooutlier_AOV = semmst_merged_data_H1_nooutlier %>%
  392. anova_test(dv = estimate, wid = ID, within = c(stim_type, order, roi_cluster), covariate = c(NonWord_sum_scale, Digit_Symbol_scale))
  393. get_anova_table(semmst_merged_data_H1_nooutlier_AOV)
  394. # REPORT Effect DFn DFd F p p<.05 ges
  395. ## 3 stim_type 1 25 16.271 4.54e-04 * 4.50e-02
  396. ## 4 order 1 25 50.948 1.76e-07 * 1.41e-01
  397. ## 4 stim_type:order 1 25 12.810 1.00e-03 * 3.20e-02
  398. ## 6 order:roi_cluster 1 25 5.677 2.50e-02 * 6.00e-03
  399. # COVARIATES
  400. ## 16 Digit_Symbol_scale:stim_type:order 1 25 6.123 2.00e-02 * 1.60e-02
  401. ## 19 NonWord_sum_scale:order:roi_cluster 1 25 9.266 5.00e-03 * 1.00e-02
  402. # POST-HOC
  403. # pairwise comparisons - stim_type * order
  404. semmst_merged_data_H1_nooutlier_PWC = semmst_merged_data_H1_nooutlier %>%
  405. drop_na(Digit_Symbol) %>%
  406. group_by(stim_type) %>%
  407. pairwise_t_test(
  408. estimate ~ order,
  409. paired = TRUE,
  410. detailed = TRUE,
  411. ) %>%
  412. ungroup() %>%
  413. # group_by(stim_type) %>%
  414. adjust_pvalue(method = "bonferroni") %>%
  415. add_significance() %>%
  416. ungroup() %>%
  417. mutate(label = paste0("p=", signif(p.adj, 3)))
  418. semmst_merged_data_H1_nooutlier_PWC
  419. # REPORT estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj p.adj.signif label
  420. ## Exact 0.190 estimate First Repeat 56 56 8.86 3.52e-12 55 0.147 0.232 T-test two.sided 7.04e-12 **** p=7.04e-12
  421. ## Lure 0.0720 estimate First Repeat 56 56 3.44 1 e- 3 55 0.0301 0.114 T-test two.sided 2 e- 3 ** p=0.002
  422. # pairwise comparisons - order * roi_cluster
  423. semmst_merged_data_H1_nooutlier_PWC = semmst_merged_data_H1_nooutlier %>%
  424. drop_na(Digit_Symbol) %>%
  425. group_by(order) %>%
  426. pairwise_t_test(
  427. estimate ~ stim_type,
  428. paired = TRUE,
  429. detailed = TRUE,
  430. ) %>%
  431. ungroup() %>%
  432. # group_by(stim_type) %>%
  433. adjust_pvalue(method = "bonferroni") %>%
  434. add_significance() %>%
  435. ungroup() %>%
  436. mutate(label = paste0("p=", signif(p.adj, 3)))
  437. semmst_merged_data_H1_nooutlier_PWC
  438. # REPORT estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj p.adj.signif labe
  439. ## First -0.0114 estimate Exact Lure 56 56 -0.734 0.466 55 -0.0427 0.0198 T-test two.sided 0.932 ns p=0.932
  440. ## Repeat -0.129 estimate Exact Lure 56 56 -5.45 0.00000124 55 -0.176 -0.0815 T-test two.sided 0.00000248 **** p=2.48e-06
  441. ```
  442. ### ANOVA - REPEAT vs. LURE and CORRECT vs. INCORRECT
  443. For subsequent correctness we use those models, where only those runs were included, where non-zero amount of trials were in each bin.
  444. ```{r}
  445. semmst_merged_data_filtered_subseq = semmst_merged_data_filtered %>%
  446. dplyr::filter(task %in% c("EncodingSubseq", "EncodingSubseq_limited")) %>%
  447. dplyr::group_by(ID) %>%
  448. dplyr::filter(
  449. task == "EncodingSubseq" &
  450. !any(task == "EncodingSubseq_limited")
  451. ) %>%
  452. dplyr::ungroup()
  453. semmst_merged_data_HE = semmst_merged_data_filtered_subseq %>%
  454. filter(cope_name %in% c("CorrectExactRepeat", "CorrectExactFirst", "CorrectLureRepeat", "CorrectLureFirst", "IncorrectExactFirst", "IncorrectExactRepeat", "IncorrectLureFirst", "IncorrectLureRepeat"))
  455. ```
  456. ```{r}
  457. # STANDARD
  458. semmst_merged_data_HE_test = semmst_merged_data_HE %>%
  459. extract(col = cope_name,
  460. into = c("correctness", "stim_type", "order"),
  461. regex = "^(Correct|Incorrect)(Exact|Lure)(First|Repeat)$") %>%
  462. mutate(
  463. stim_type = as.factor(stim_type),
  464. order = as.factor(order)
  465. ) %>%
  466. mutate(stim_type = factor(stim_type, levels = c("Exact", "Lure"))) %>%
  467. mutate(correctness = factor(correctness, levels = c("Correct", "Incorrect"))) %>%
  468. droplevels()
  469. # ASSUMPTION CHECKS
  470. ## OUTLIER
  471. semmst_merged_data_HE_test %>%
  472. group_by(order, correctness, roi_cluster) %>%
  473. identify_outliers(estimate) %>%
  474. dplyr::select(ID, roi_cluster, stim_type, order, correctness, is.outlier, is.extreme) %>%
  475. filter(is.extreme == TRUE)
  476. semmst_merged_data_HE_test_nooutlier = semmst_merged_data_HE_test %>%
  477. #filter(!ID %in% c("sub-434971")) %>% #, "sub-124846")) %>% #c("sub-434971", "sub-632012", "sub-680997")) %>% #, "sub-193800")) %>%
  478. ungroup()
  479. ##NORMALITY
  480. semmst_merged_data_HE_test_nooutlier %>%
  481. group_by(stim_type, order, correctness, roi, hemisphere) %>%
  482. shapiro_test(estimate) %>% print(n=100)
  483. # ANOVA
  484. semmst_merged_data_HE_test_AOV = semmst_merged_data_HE_test_nooutlier %>%
  485. anova_test(dv = estimate, wid = ID, within = c(stim_type, correctness, order, roi_cluster))#, covariate = c(NonWord_sum_scale))
  486. get_anova_table(semmst_merged_data_HE_test_AOV)
  487. # REPORT
  488. ## 1 stim_type 1 28 13.459000 1.00e-03 * 1.80e-02
  489. ## 3 order 1 28 43.614000 3.66e-07 * 4.60e-02
  490. ## 6 stim_type:order 1 28 7.165000 1.20e-02 * 1.50e-02
  491. ## 7 correctness:order 1 28 0.048000 8.28e-01 1.18e-04
  492. ## 11 stim_type:correctness:order 1 28 0.277000 6.03e-01 4.92e-04
  493. # POST-HOC
  494. # pairwise comparisons
  495. semmst_merged_data_HE_test_nooutlier_PWC = semmst_merged_data_HE_test_nooutlier %>%
  496. group_by(stim_type) %>%
  497. pairwise_t_test(
  498. estimate ~ order,
  499. paired = TRUE,
  500. detailed = TRUE,
  501. ) %>%
  502. adjust_pvalue(method = "bonferroni") %>%
  503. add_significance() %>%
  504. ungroup() %>%
  505. mutate(label = paste0("p=", signif(p.adj, 3)))
  506. semmst_merged_data_HE_test_nooutlier_PWC %>% dplyr::select(-c(alternative, method, .y., )) %>% print(n=50)
  507. # REPORT stim_type estimate group1 group2 n1 n2 statistic p df conf.low conf.high p.adj p.adj.signif label
  508. ## 1 Exact 0.164 First Repeat 116 116 6.32 0.00000000513 115 0.112 0.215 0.0000000103 **** p=1.03e-08
  509. ## 2 Lure 0.0454 First Repeat 116 116 1.72 0.088 115 -0.00681 0.0975 0.176 ns p=0.176
  510. ```
  511. ## Revision Analysis 3 - Exact, close, distant
  512. ```{r}
  513. # FILTER DATA
  514. semmst_merged_data_H1_clodis = semmst_merged_data_filtered %>%
  515. filter(task == "EncodingFirst") %>%
  516. filter(cope_name %in% c("ExactRepeat", "CloseRepeat", "DistantRepeat")) %>%
  517. mutate(
  518. roi_cluster = paste(hemisphere, roi, cluster, sep = "_"),
  519. roi_hemisphere = paste(hemisphere, roi, sep = "_"),
  520. condition = recode(
  521. cope_name,
  522. "ExactRepeat" = "Exact",
  523. "CloseRepeat" = "Close",
  524. "DistantRepeat" = "Distant"
  525. )
  526. ) %>%
  527. mutate(
  528. condition = factor(condition, levels = c("Exact", "Close", "Distant")),
  529. roi_cluster = factor(roi_cluster)
  530. )
  531. # ASSUMPTION CHECKS
  532. ## OUTLIER
  533. semmst_merged_data_H1_clodis %>%
  534. group_by(condition, roi_cluster) %>%
  535. identify_outliers(estimate) %>%
  536. dplyr::select(ID, condition, roi_cluster, is.outlier, is.extreme) %>%
  537. filter(is.extreme == TRUE)
  538. # REMOVE OUTLIERS IF NEEDED
  539. semmst_merged_data_H1_clodis_nooutlier = semmst_merged_data_H1_clodis %>%
  540. drop_na(estimate, NonWord_sum_scale, Digit_Symbol_scale) %>%
  541. ungroup()
  542. ## NORMALITY
  543. semmst_merged_data_H1_clodis_nooutlier %>%
  544. group_by(condition, roi_cluster) %>%
  545. shapiro_test(estimate)
  546. # ANOVA
  547. semmst_merged_data_H1_clodis_AOV = semmst_merged_data_H1_clodis_nooutlier %>%
  548. anova_test(
  549. dv = estimate,
  550. wid = ID,
  551. within = c(condition, roi_cluster),
  552. )
  553. get_anova_table(semmst_merged_data_H1_clodis_AOV)
  554. # POST-HOC
  555. # pairwise comparisons - condition within each ROI
  556. semmst_merged_data_H1_clodis_PWC = semmst_merged_data_H1_clodis_nooutlier %>%
  557. group_by(roi_cluster) %>%
  558. pairwise_t_test(
  559. estimate ~ condition,
  560. paired = TRUE,
  561. detailed = TRUE,
  562. p.adjust.method = "none"
  563. ) %>%
  564. ungroup() %>%
  565. add_significance("p") %>%
  566. mutate(
  567. label = case_when(
  568. p < .001 ~ "p < .001",
  569. p < .01 ~ "p < .01",
  570. p < .05 ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p))),
  571. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.2f", p)))
  572. )
  573. )
  574. semmst_merged_data_H1_clodis_PWC
  575. ```
  576. ## Analysis 4 - ENC RS ~ LDI
  577. ### Prepare bias scores
  578. ```{r}
  579. group_vars_H3 <- c("ID", "mask_type", "space_type", "contrast", "roi_cluster")
  580. # baseline
  581. semmst_merged_data_H3_neural_ps = semmst_merged_data_filtered %>%
  582. filter(task %in% c("Encoding")) %>%
  583. filter(cope_name %in% c("ExactFirst", "ExactRepeat", "LureFirst","LureRepeat", "CloseFirst", "CloseRepeat", "DistantFirst", "DistantRepeat")) %>%
  584. dplyr::select(ID, space_type, cope_name, hemisphere, roi, mask_type, cluster, voxels, contrast, task, roi_cluster, roi_hemisphere, estimate, NonWord_sum_scale, Digit_Symbol_scale, rec_dprime:lure_incorrect) %>%
  585. group_by(across(all_of(group_vars_H3))) %>%
  586. pivot_wider(names_from = cope_name, values_from = estimate, values_fill = NA_real_) %>%
  587. mutate(
  588. # --- Non-RS bias scores (Exact denominator) ---
  589. LureBiasScore = LureRepeat - ExactRepeat,
  590. CloseBiasScore = CloseRepeat - ExactRepeat,
  591. DistantBiasScore = DistantRepeat - ExactRepeat
  592. ) %>%
  593. ungroup()
  594. ```
  595. ### Correlation - LURES
  596. ```{r}
  597. # 1) Filter / define roi_group
  598. df_corr_input_all <- semmst_merged_data_H3_neural_ps %>%
  599. mutate(roi_group = roi_cluster) %>%
  600. dplyr::select(
  601. ID, roi_group, roi, hemisphere, cluster, voxels,
  602. LureBiasScore, lure_dprime,
  603. NonWord_sum_scale, Digit_Symbol_scale
  604. ) %>%
  605. ungroup()
  606. # 2) Mahalanobis outlier detection (per ROI)
  607. alpha <- 0.99 # cutoff for chi-square (df=2)
  608. md_tbl_all <- df_corr_input_all %>%
  609. dplyr::group_by(roi_group) %>%
  610. dplyr::group_modify(~{
  611. d <- .x %>% dplyr::select(ID, LureBiasScore, lure_dprime) %>% tidyr::drop_na()
  612. if (nrow(d) < 3) {
  613. return(tibble::tibble(ID = character(), MD = numeric(), p = numeric(),
  614. cutoff = numeric(), is_outlier = logical()))
  615. }
  616. X <- as.matrix(d[, c("LureBiasScore", "lure_dprime")])
  617. center <- colMeans(X)
  618. covmat <- stats::cov(X)
  619. md <- stats::mahalanobis(X, center = center, cov = covmat)
  620. tibble::tibble(
  621. ID = d$ID,
  622. MD = md,
  623. p = 1 - stats::pchisq(md, df = 2),
  624. cutoff = stats::qchisq(alpha, df = 2),
  625. is_outlier = md > stats::qchisq(alpha, df = 2)
  626. )
  627. }) %>%
  628. ungroup()
  629. md_outliers_all <- md_tbl_all %>% dplyr::filter(is_outlier)
  630. md_multi_all <- md_outliers_all %>% dplyr::count(ID, sort = TRUE) %>% dplyr::filter(n > 1)
  631. md_tbl_all
  632. md_outliers_all
  633. md_multi_all
  634. # 3) Remove outlier ID
  635. df_corr_input_all_nooutlier <- df_corr_input_all %>%
  636. dplyr::filter(ID != "sub-434971") %>%
  637. droplevels() %>%
  638. ungroup()
  639. # 4) Fisher-z CI for Pearson r (95% hard-coded)
  640. pearson_ci <- function(r, n) {
  641. z <- atanh(r)
  642. se <- 1 / sqrt(n - 3)
  643. zcrit <- stats::qnorm(1 - (1 - 0.95) / 2)
  644. c(low = tanh(z - zcrit * se), high = tanh(z + zcrit * se))
  645. }
  646. # 5) SIMPLE correlations only (Pearson) per ROI + Bonferroni
  647. H3_corr_lure_simple <- df_corr_input_all_nooutlier %>%
  648. dplyr::group_by(roi_group) %>%
  649. dplyr::group_modify(~{
  650. d <- .x %>% dplyr::select(LureBiasScore, lure_dprime) %>% tidyr::drop_na()
  651. ct <- stats::cor.test(d$LureBiasScore, d$lure_dprime, method = "pearson")
  652. r <- unname(ct$estimate)
  653. p <- ct$p.value
  654. n <- nrow(d)
  655. ci <- pearson_ci(r, n)
  656. tibble::tibble(
  657. n = n,
  658. r = r,
  659. p = p,
  660. ci = sprintf("[%.3f, %.3f]", ci["low"], ci["high"])
  661. )
  662. }) %>%
  663. ungroup() %>%
  664. mutate(
  665. p_adj = p.adjust(p, method = "bonferroni")
  666. ) %>%
  667. arrange(p_adj)
  668. H3_corr_lure_simple
  669. ```
  670. ### Compare correlations
  671. ```{r}
  672. # --- A) Build subject-level wide table (bias per ROI, plus lure_dprime per ID)
  673. wide_bias <- df_corr_input_all_nooutlier %>%
  674. group_by(ID, roi_group) %>%
  675. summarise(
  676. bias = mean(LureBiasScore, na.rm = TRUE),
  677. lure_dprime = first(lure_dprime),
  678. .groups = "drop"
  679. ) %>%
  680. tidyr::pivot_wider(
  681. names_from = roi_group,
  682. values_from = bias,
  683. names_repair = "minimal"
  684. )
  685. roi_cols <- setdiff(names(wide_bias), c("ID", "lure_dprime"))
  686. # --- B) Pairwise dependent-correlation tests:
  687. # compares r(lure_dprime, ROI1_bias) vs r(lure_dprime, ROI2_bias)
  688. H3_pairwise_roi_corrdiff <- combn(roi_cols, 2, simplify = FALSE) %>%
  689. purrr::map_dfr(function(pair){
  690. roi1 <- pair[1]
  691. roi2 <- pair[2]
  692. d <- wide_bias %>%
  693. select(lure_dprime, all_of(roi1), all_of(roi2)) %>%
  694. drop_na()
  695. n <- nrow(d)
  696. r1 <- cor(d$lure_dprime, d[[roi1]], method = "pearson")
  697. r2 <- cor(d$lure_dprime, d[[roi2]], method = "pearson")
  698. r12 <- cor(d[[roi1]], d[[roi2]], method = "pearson")
  699. # Williams/Steiger-style test for two dependent correlations sharing one variable
  700. tst <- psych::r.test(n = n, r12 = r1, r13 = r2, r23 = r12)
  701. tibble(
  702. roi1 = roi1,
  703. roi2 = roi2,
  704. n = n,
  705. r_roi1 = r1,
  706. r_roi2 = r2,
  707. r_roi1_roi2 = r12,
  708. t = unname(tst$t),
  709. p = unname(tst$p)
  710. )
  711. }) %>%
  712. mutate(p_adj_bonf = p.adjust(p, method = "bonferroni")) %>%
  713. arrange(p_adj_bonf, p)
  714. H3_pairwise_roi_corrdiff
  715. ```
  716. ## Analysis 5 - REC MD
  717. ### ANOVA #1 - LureMD
  718. ```{r}
  719. ### FILTERING FR AT LEAST 5 items per condition per run
  720. run_status <- semmst_behav_data_rec_filtered %>%
  721. select(ID, run, trial_type, correctness) %>%
  722. filter(trial_type %in% c("CLOSE", "DISTANT")) %>%
  723. mutate(run = as.integer(str_extract(as.character(run), "\\d+"))) %>%
  724. count(ID, run, correctness, name = "n") %>%
  725. group_by(ID, run) %>%
  726. complete(correctness, fill = list(n = 0)) %>%
  727. summarise(
  728. min_n = min(n),
  729. run_ok = (min_n >= 3),
  730. .groups = "drop"
  731. ) %>%
  732. select(ID, run, run_ok) %>%
  733. tidyr::pivot_wider(
  734. names_from = run,
  735. values_from = run_ok,
  736. names_prefix = "run",
  737. values_fill = FALSE
  738. ) %>%
  739. mutate(
  740. group = case_when(
  741. run1 & run2 ~ "both_ok",
  742. !run1 & run2 ~ "run1_low_only",
  743. run1 & !run2 ~ "run2_low_only",
  744. TRUE ~ "both_low"
  745. )
  746. )
  747. # We use both runs only if both have n > 5 items in all bins, otherwise only 1 run or exclude
  748. ids_both_ok <- run_status %>% filter(group == "both_ok") %>% pull(ID)
  749. ids_run1_lowonly <- run_status %>% filter(group == "run1_low_only") %>% pull(ID)
  750. ids_run2_lowonly <- run_status %>% filter(group == "run2_low_only") %>% pull(ID)
  751. semmst_merged_data_filtered_lures = semmst_merged_data_filtered
  752. df_LureCR_all <- semmst_merged_data_filtered_lures %>%
  753. filter(task == "RecognitionRespAll", cope_name == "LureCR")
  754. df_LureFA_both_ok <- semmst_merged_data_filtered_lures %>%
  755. filter(task == "RecognitionRespAll", cope_name == "LureFA") %>%
  756. semi_join(tibble(ID = ids_both_ok), by = "ID")
  757. df_LureFA_use_run2 <- semmst_merged_data_filtered_lures %>%
  758. filter(task == "RecognitionRespAllRun2", cope_name == "LureFA") %>%
  759. semi_join(tibble(ID = ids_run1_lowonly), by = "ID")
  760. df_LureFA_use_run1 <- semmst_merged_data_filtered_lures %>%
  761. filter(task == "RecognitionRespAllRun1", cope_name == "LureFA") %>%
  762. semi_join(tibble(ID = ids_run2_lowonly), by = "ID")
  763. # Merge for testing
  764. df_LureFA_all <- bind_rows(
  765. df_LureFA_both_ok,
  766. df_LureFA_use_run2,
  767. df_LureFA_use_run1
  768. )
  769. semmst_merged_data_H4_lureFACR <- bind_rows(df_LureCR_all, df_LureFA_all)
  770. keep_ids <- semmst_merged_data_H4_lureFACR %>%
  771. group_by(ID, roi_cluster) %>%
  772. summarise(
  773. has_LureCR = any(cope_name == "LureCR"),
  774. has_LureFA = any(cope_name == "LureFA"),
  775. .groups = "drop"
  776. ) %>%
  777. filter(has_LureCR & has_LureFA) %>% count(ID) %>%
  778. filter(n > 1) %>%
  779. pull(ID)
  780. semmst_merged_data_H4_lureFACR <- semmst_merged_data_H4_lureFACR %>%
  781. filter(ID %in% keep_ids) %>%
  782. droplevels()
  783. # ASSUMPTION CHECKS
  784. ## OUTLIER
  785. semmst_merged_data_H4_lureFACR %>%
  786. group_by(cope_name, roi_cluster, task) %>%
  787. identify_outliers(estimate) %>%
  788. dplyr::select(ID, roi_cluster, cope_name, is.outlier, is.extreme) %>%
  789. filter(is.extreme==TRUE)
  790. semmst_merged_data_H4_lureFACR_nooutlier = semmst_merged_data_H4_lureFACR %>%
  791. #filter(ID != "sub-632012") %>%
  792. ungroup()
  793. semmst_merged_H4_AOV = semmst_merged_data_H4_lureFACR_nooutlier %>%
  794. droplevels() %>%
  795. anova_test(dv = estimate, wid = ID, within = c(cope_name, roi_cluster))#, covariate = c(NonWord_sum_scale))
  796. get_anova_table(semmst_merged_H4_AOV)
  797. # REPORT Effect DFn DFd F p p<.05 ges
  798. ## 1 cope_name 1 23 1.621 0.216 0.013
  799. ## 2 roi_cluster 1 23 0.584 0.453 0.005
  800. ## 3 cope_name:roi_cluster 1 23 1.079 0.310 0.006
  801. # POST-HOC
  802. # pairwise comparisons - stim_type * order
  803. semmst_merged_H4_REC_lureFACR_PWC = semmst_merged_data_H4_lureFACR_nooutlier %>%
  804. group_by(roi_cluster) %>%
  805. pairwise_t_test(
  806. estimate ~ cope_name,
  807. paired = TRUE,
  808. detailed = TRUE,
  809. ) %>%
  810. ungroup() %>%
  811. adjust_pvalue(method = "bonferroni") %>%
  812. add_significance() %>%
  813. ungroup() %>%
  814. mutate(label = paste0("p=", signif(p.adj, 3)))
  815. semmst_merged_H4_REC_lureFACR_PWC
  816. ```
  817. ### ANOVA #2 - REC CONDITIONS
  818. ```{r}
  819. # Control analysis for CR only on the total sample
  820. semmst_merged_data_filtered_H4_REC_conditions = semmst_merged_data_filtered %>%
  821. filter(task == "RecognitionRespAll") %>%
  822. filter(cope_name %in% c("LureCR", "FoilCR", "TargetHIT"))
  823. # ASSUMPTION CHECKS
  824. ## OUTLIER
  825. semmst_merged_data_filtered_H4_REC_conditions %>%
  826. group_by(cope_name, roi_cluster) %>%
  827. identify_outliers(estimate) %>%
  828. dplyr::select(ID, roi_cluster, cope_name, is.outlier, is.extreme) %>%
  829. filter(is.extreme==TRUE)
  830. semmst_merged_data_filtered_H4_REC_conditions_nooutlier = semmst_merged_data_filtered_H4_REC_conditions %>%
  831. ungroup()
  832. semmst_merged_H4_REC_conditions_AOV = semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  833. droplevels() %>%
  834. anova_test(dv = estimate, wid = ID, within = c(cope_name, roi_cluster))#, covariate = c(NonWord_sum_scale))
  835. get_anova_table(semmst_merged_H4_REC_conditions_AOV)
  836. # Effect DFn DFd F p p<.05 ges
  837. ## 1 cope_name 2 58 6.967 0.002 * 0.040
  838. ## 2 roi_cluster 1 29 2.018 0.166 0.015
  839. ## 3 cope_name:roi_cluster 2 58 0.508 0.604 0.002
  840. # POST-HOC
  841. # pairwise comparisons - stim_type * order
  842. semmst_merged_H4_REC_conditions_PWC = semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  843. group_by(task) %>%
  844. pairwise_t_test(
  845. estimate ~ cope_name,
  846. paired = TRUE,
  847. detailed = TRUE,
  848. ) %>%
  849. ungroup() %>%
  850. adjust_pvalue(method = "bonferroni") %>%
  851. add_significance() %>%
  852. ungroup() %>%
  853. mutate(label = paste0("p=", signif(p.adj, 3)))
  854. semmst_merged_H4_REC_conditions_PWC
  855. ## roi_cluster estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj
  856. ## 1 L_HEAD_DGCA23_HEAD_SUB_14 0.138 estimate FoilCR LureCR 30 30 3.43 0.002 29 0.0558 0.221 T-test two.sided 0.012 *
  857. ## 4 R_HEAD_DGCA23_HEAD_SUB_15 0.103 estimate FoilCR LureCR 30 30 3.05 0.005 29 0.0341 0.173 T-test two.sided 0.03 *
  858. # AGAINTS ZERO BASLEINE
  859. semmst_merged_H4_REC_FoilLure_vs0 <- semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  860. filter(cope_name %in% c("FoilCR", "LureCR")) %>%
  861. group_by(cope_name) %>%
  862. t_test(
  863. estimate ~ 1,
  864. mu = 0,
  865. detailed = TRUE
  866. ) %>%
  867. adjust_pvalue(method = "bonferroni") %>%
  868. add_significance("p.adj") %>%
  869. mutate(label = paste0("p=", signif(p.adj, 3))) %>%
  870. ungroup()
  871. semmst_merged_H4_REC_FoilLure_vs0
  872. ### cope_name estimate .y. group1 group2 n statistic p df conf.low conf.high method alternative p.adj p.adj.signif label
  873. ## 1 FoilCR 0.0298 estimate 1 null model 60 0.976 0.333 59 -0.0313 0.0909 T-test two.sided 0.666 ns p=0.666
  874. ## 2 LureCR -0.0911 estimate 1 null model 60 -2.77 0.00758 59 -0.157 -0.0252 T-test two.sided 0.0152 * p=0.0152
  875. ```
  876. # Figures
  877. ## Figure 1 - Behavioral figures (response, LDI)
  878. ### Figure 1A - response ratio
  879. ```{r}
  880. knitr::opts_chunk$set(
  881. fig.width = 11, # same as ggsave width
  882. fig.height = 16, # same as ggsave height
  883. dpi = 300,
  884. fig.retina = 2, # crisp in HTML
  885. out.width = "45%", # display smaller, but proportions/text scale together
  886. dev = "ragg_png"
  887. )
  888. dodge <- 0.7
  889. trial_order <- c("TARGET", "CLOSE", "DISTANT", "FOIL")
  890. # 1) Build per-ID percentages so the two responses sum to 100% within each ID × trial_type
  891. figA_base <- semmst_behav_data_rec_filtered %>%
  892. filter(!is.na(ID), !is.na(trial_type), !is.na(response)) %>%
  893. mutate(
  894. trial_type = factor(trial_type, levels = trial_order),
  895. response = factor(response)
  896. )
  897. resp_lvls <- levels(figA_base$response)
  898. figA_pct <- figA_base %>%
  899. dplyr::count(ID, trial_type, response, name = "n") %>%
  900. tidyr::complete(response = factor(resp_lvls, levels = resp_lvls), fill = list(n = 0)) %>%
  901. dplyr::mutate(
  902. percent_response = 100 * n / sum(n),
  903. .by = c(ID, trial_type)
  904. )
  905. # 2) Within-subject SE across ID for each (trial_type × response)
  906. figA_barsum <- Rmisc::summarySEwithin(
  907. data = figA_pct,
  908. measurevar = "percent_response",
  909. withinvars = c("trial_type", "response"),
  910. idvar = "ID",
  911. na.rm = TRUE
  912. )
  913. # Paul Tol "Light" (pastel, print-friendly): light blue / light orange
  914. fill_vals <- setNames(c("#EE8866", "#77AADD"), levels(figA_barsum$response))
  915. behav_fig_A <- ggplot(figA_barsum, aes(
  916. x = trial_type, y = percent_response,
  917. fill = response, group = response
  918. )) +
  919. geom_col(position = position_dodge(width = dodge), width = 0.70) +
  920. geom_errorbar(
  921. aes(ymin = percent_response - se, ymax = percent_response + se),
  922. position = position_dodge(width = dodge), width = 0.2, size = 1.4
  923. ) +
  924. scale_fill_manual(values = fill_vals) +
  925. scale_y_continuous(limits = c(0, 100), expand = expansion(mult = c(0.01, 0.06))) +
  926. scale_x_discrete(
  927. drop = FALSE,
  928. labels = c(
  929. "TARGET" = "TARGET",
  930. "CLOSE" = "CLOSE\nLURE",
  931. "DISTANT" = "DISTANT\nLURE",
  932. "FOIL" = "FOIL"
  933. )
  934. ) +
  935. labs(
  936. x = "Trial type",
  937. y = "Response percentages",
  938. fill = "Response"
  939. ) +
  940. theme_classic(base_size = 16) +
  941. theme(
  942. legend.position = c(0.15, 0.95),
  943. )
  944. behav_fig_A
  945. ```
  946. ```{r}
  947. text_base_export=40
  948. behav_fig_A_export <- behav_fig_A +
  949. theme_classic(base_size = text_base_export) +
  950. theme(
  951. legend.position = c(0.18, 0.95),
  952. legend.title = element_text(size = text_base_export * 0.95),
  953. legend.text = element_text(size = text_base_export * 0.90),
  954. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  955. axis.text.y = element_text(size = text_base_export * 0.90),
  956. axis.title = element_text(size = text_base_export * 1.05)
  957. )
  958. ggsave(
  959. "derivatives/figures/Figure1A.png",
  960. plot = behav_fig_A_export,
  961. width = 18, height = 13, units = "in",
  962. dpi = 300, bg = "white", device = "png"
  963. )
  964. ```
  965. ### Figure 1 D - D-primes
  966. ```{r}
  967. # 1) Prep & adjustment (remove linear effects of covariates)
  968. figD_plot_base <- semmst_behav_data_rec_LDI_ANCOVA %>%
  969. filter(!is.na(ID), !is.na(dprime_type), !is.na(dprime_value)) %>%
  970. mutate(
  971. dprime_type = factor(dprime_type),
  972. sex = factor(sex)
  973. )
  974. # order as: Close, Distant, Target
  975. figD_contrast_order <- c("close_dprime", "distant_dprime", "rec_dprime")
  976. figD_plot_base <- figD_plot_base %>%
  977. mutate(dprime_type = fct_relevel(dprime_type, figD_contrast_order))
  978. # covariate-only model for adjustment
  979. figD_mod_cov <- lm(dprime_value ~ sex + age + Vocabulary_scale + Digit_Symbol_scale + NonWord_sum_scale,
  980. data = figD_plot_base)
  981. figD_plot_base <- figD_plot_base %>%
  982. mutate(dprime_adj = dprime_value - predict(figD_mod_cov, newdata = figD_plot_base) + mean(dprime_value, na.rm = TRUE))
  983. # 2) Labels & colors
  984. figD_x_lab_map <- c(
  985. "close_dprime" = "CLOSE\nLURE",
  986. "distant_dprime" = "DISTANT\nLURE",
  987. "rec_dprime" = "FOIL"
  988. )
  989. # Paul Tol (muted): blue / yellow / red — CVD-safe, good in print & grayscale
  990. figD_fill_vals <- c("close_dprime"="#4477AA", "distant_dprime"="#DDCC77", "rec_dprime"="#117733")
  991. # 3) Plot
  992. behav_fig_D <- ggplot(figD_plot_base, aes(x = dprime_type, y = dprime_adj, fill = dprime_type)) +
  993. geom_boxplot(width = 0.6, outlier.alpha = 0.4, linewidth = 1.8, color = "black") +
  994. stat_summary(fun = mean, geom = "point", shape = 21, size = 2.2, fill = "white", color = "black") +
  995. scale_x_discrete(labels = figD_x_lab_map, drop = FALSE) +
  996. scale_fill_manual(values = figD_fill_vals, guide = "none") +
  997. labs(
  998. x = "Discriminability contrasts",
  999. y = "Covariate-adjusted d'\n [p(\"old\"|target) - p(\"old\"|condition)]"
  1000. ) +
  1001. theme_classic(base_size = 14)
  1002. behav_fig_D
  1003. ```
  1004. ```{r}
  1005. behav_fig_D_export <- behav_fig_D +
  1006. theme_classic(base_size = 40) +
  1007. theme(
  1008. axis.title = element_text(size = 42),
  1009. axis.text = element_text(size = 34)
  1010. ) + labs(x = NULL)
  1011. ggsave(
  1012. "derivatives/figures/Figure1D.png",
  1013. plot = behav_fig_D_export,
  1014. width = 9, height = 16, units = "in",
  1015. dpi = 300,
  1016. bg = "white",
  1017. device = "png" # base png
  1018. )
  1019. ```
  1020. ### Figure 1 E - continous GLMEM
  1021. ```{r}
  1022. # In this plot, we predict p|old by cosine similarity
  1023. # Base data: binary outcome + keep CLOSE & DISTANT only
  1024. semmst_behav_data_rec_filtered_GLMEM_plot <- semmst_behav_data_rec_filtered_GLMEM %>%
  1025. mutate(response = as.integer(response == "old")) %>%
  1026. filter(trial_type %in% c("CLOSE", "DISTANT"))
  1027. # If your column is 'trial_type', replace 'trial_type' with 'trial_type' above.
  1028. # Build a facetting variable: Overall, DISTANT-only, CLOSE-only
  1029. semmst_behav_data_rec_filtered_GLMEM_plot_facet <- bind_rows(
  1030. semmst_behav_data_rec_filtered_GLMEM_plot %>% mutate(panel = "ALL LURE"),
  1031. semmst_behav_data_rec_filtered_GLMEM_plot %>% filter(trial_type == "DISTANT") %>% mutate(panel = "DISTANT LURE"),
  1032. semmst_behav_data_rec_filtered_GLMEM_plot %>% filter(trial_type == "CLOSE") %>% mutate(panel = "CLOSE LURE")
  1033. ) %>%
  1034. mutate(panel = factor(panel, levels = c("ALL LURE", "DISTANT LURE", "CLOSE LURE")))
  1035. # Okabe–Ito (CVD-safe): purple, yellow, blue
  1036. figE_cols <- c("ALL LURE" = "#CC79A7", "DISTANT LURE" = "#DDCC77", "CLOSE LURE" = "#4477AA")
  1037. behav_fig_E <- ggplot(semmst_behav_data_rec_filtered_GLMEM_plot_facet,
  1038. aes(x = cosine, y = response, color = panel, fill = panel)) +
  1039. geom_smooth(
  1040. method = "glm", method.args = list(family = "binomial"),
  1041. linewidth = 2, se = TRUE, alpha = 0.25
  1042. ) +
  1043. facet_wrap(~ panel, nrow = 1, scales = "free_x") +
  1044. scale_x_continuous(
  1045. breaks = function(lims) round(lims[1] + c(0.1, 0.5, 0.9) * (lims[2] - lims[1]), 2),
  1046. labels = function(x) sprintf("%.2f", x)
  1047. ) +
  1048. scale_y_continuous(labels = scales::percent, limits = c(0, 1)) +
  1049. scale_color_manual(values = figE_cols, guide = "none") +
  1050. scale_fill_manual(values = figE_cols, guide = "none") +
  1051. labs(x = "Cosine similarity", y = "Percentage of 'old' responses") +
  1052. theme_classic(base_size = 14) +
  1053. theme(
  1054. panel.grid.minor = element_blank(),
  1055. strip.text = element_text(face = "bold")
  1056. )
  1057. behav_fig_E
  1058. ```
  1059. ```{r}
  1060. behav_fig_E_export <- behav_fig_E +
  1061. theme_classic(base_size = 40) +
  1062. theme(
  1063. axis.title = element_text(size = 42),
  1064. axis.text = element_text(size = 32)
  1065. )
  1066. ggsave(
  1067. "derivatives/figures/Figure1E.png",
  1068. plot = behav_fig_E_export,
  1069. width = 16, height = 16, units = "in",
  1070. dpi = 300,
  1071. bg = "white",
  1072. device = "png" # base png
  1073. )
  1074. ```
  1075. ### Figure 1 C - Lure cosine similarities
  1076. ```{r}
  1077. figC_plot_base <- semmst_behav_data_rec_filtered %>%
  1078. filter(trial_type %in% c("CLOSE", "DISTANT")) %>%
  1079. mutate(trial_type = factor(trial_type,
  1080. levels = c("CLOSE", "DISTANT"),
  1081. labels = c("CLOSE LURE", "DISTANT LURE"))) %>%
  1082. dplyr::select(trial_type, noun, cosine) %>%
  1083. unique()
  1084. # Paul Tol (muted): blue / yellow / red — CVD-safe, good in print & grayscale
  1085. figC_fill_vals <- c("CLOSE LURE"="#4477AA", "DISTANT LURE"="#DDCC77")
  1086. # 3) Plot
  1087. behav_fig_C_hists <- ggplot(figC_plot_base, aes(x = cosine, fill = trial_type)) +
  1088. geom_histogram(
  1089. bins = 20, # tweak bins/binwidth as you like
  1090. color = "black",
  1091. linewidth = 1.0
  1092. ) +
  1093. facet_wrap(~ trial_type, ncol = 1) + # <-- vertically stacked (2 panels if 2 trial types)
  1094. scale_fill_manual(values = figC_fill_vals, guide = "none") +
  1095. labs(
  1096. x = "Cosine similarity [1.0 = identical]",
  1097. y = "Number of items in 20 equally spaced bins"
  1098. ) +
  1099. theme_classic(base_size = 14) +
  1100. theme(
  1101. strip.background = element_blank(),
  1102. strip.text = element_text(face = "bold"),
  1103. panel.spacing = unit(0.7, "lines")
  1104. )
  1105. behav_fig_C_hists
  1106. ```
  1107. ```{r}
  1108. behav_fig_C_export <- behav_fig_C_hists +
  1109. theme_classic(base_size = 40) +
  1110. theme(
  1111. axis.title = element_text(size = 38),
  1112. axis.text = element_text(size = 34)
  1113. )
  1114. ggsave(
  1115. "derivatives/figures/Figure1C.png",
  1116. plot = behav_fig_C_export,
  1117. width = 12, height = 19, units = "in",
  1118. dpi = 300,
  1119. bg = "white",
  1120. device = "png" # base png
  1121. )
  1122. ```
  1123. ## Figure 2 - ENC RS specificity
  1124. ```{r}
  1125. # BARPLOT
  1126. fig2_dodge <- 0.8
  1127. fig2_barsum <- Rmisc::summarySEwithin(
  1128. data = semmst_merged_data_H1_nooutlier,
  1129. measurevar = "estimate",
  1130. withinvars = c("stim_type","order", "roi_cluster"),
  1131. idvar = "ID",
  1132. na.rm = TRUE
  1133. ) %>%
  1134. mutate(stim_type = factor(as.character(stim_type),
  1135. levels = c("Exact","Lure"),
  1136. labels = c("Exact","Modified")))
  1137. fig2_roi_lab <- semmst_merged_data_H1_nooutlier %>%
  1138. dplyr::distinct(roi_cluster, hemisphere, roi, cluster, voxels) %>%
  1139. dplyr::mutate(
  1140. roi_label = dplyr::case_when(
  1141. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "R" ~ "Right hippocampal head cluster",
  1142. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "L" ~ "Left hippocampal head cluster"
  1143. )
  1144. ) %>%
  1145. dplyr::select(roi_cluster, roi_label)
  1146. # ------------------------------------------------------------
  1147. # NEW: paired tests of stim_type (Exact vs Modified) WITHIN each order,
  1148. # collapsed across roi_cluster (same p-values in both facets)
  1149. # ------------------------------------------------------------
  1150. fig2_stat.test <- semmst_merged_data_H1_nooutlier %>%
  1151. mutate(stim_type = factor(as.character(stim_type),
  1152. levels = c("Exact","Lure"),
  1153. labels = c("Exact","Modified"))) %>%
  1154. group_by(ID, order, stim_type) %>%
  1155. summarise(estimate = mean(estimate, na.rm = TRUE), .groups = "drop") %>%
  1156. group_by(order) %>%
  1157. rstatix::t_test(estimate ~ stim_type, paired = TRUE) %>%
  1158. ungroup() %>%
  1159. rstatix::adjust_pvalue(method = "bonferroni") %>%
  1160. rstatix::add_significance("p.adj") %>%
  1161. mutate(
  1162. label = dplyr::case_when(
  1163. p.adj < 0.001 ~ "p < .001",
  1164. p.adj < .01 ~ "p < .01",
  1165. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.2f", p.adj)))
  1166. )
  1167. )
  1168. # 1) Attach ROI labels to barsum
  1169. fig2_barsum <- fig2_barsum %>% dplyr::left_join(fig2_roi_lab, by = "roi_cluster")
  1170. # 2) replicate the same stats into both facets and set y.position per facet+order
  1171. fig2_stat.test <- fig2_stat.test %>%
  1172. tidyr::crossing(fig2_barsum %>% dplyr::distinct(roi_label)) %>%
  1173. dplyr::left_join(
  1174. fig2_barsum %>%
  1175. dplyr::group_by(order, roi_label) %>%
  1176. dplyr::summarise(y.position = max(estimate + se, na.rm = TRUE) * 1.08, .groups = "drop"),
  1177. by = c("order", "roi_label")
  1178. )
  1179. # 3) compute xmin/xmax to connect Exact ↔ Modified within each ORDER (dodge offset)
  1180. fig2_stim_lvls <- levels(factor(fig2_barsum$stim_type)) # should be c("Exact","Modified")
  1181. exact_idx <- which(fig2_stim_lvls == "Exact")
  1182. mod_idx <- which(fig2_stim_lvls == "Modified")
  1183. fig2_stat.test <- fig2_stat.test %>%
  1184. mutate(
  1185. order = factor(order, levels = c("First", "Repeat")),
  1186. offset = if_else(order == "First", -fig2_dodge/2, +fig2_dodge/2),
  1187. xmin = exact_idx + offset,
  1188. xmax = mod_idx + offset
  1189. )
  1190. # --- bar plot (fill by stim_type color, opacity by order), same dodging ---
  1191. fig2_plot <- ggplot(fig2_barsum, aes(
  1192. x = stim_type, y = estimate,
  1193. fill = stim_type, # color by stim type
  1194. group = `order` # ensures side-by-side bars per stim_type
  1195. )) +
  1196. geom_col(aes(alpha = order), position = position_dodge(width = fig2_dodge), width = 0.65) +
  1197. geom_errorbar(
  1198. aes(ymin = estimate - se, ymax = estimate + se),
  1199. position = position_dodge(width = fig2_dodge), width = 0.15, size=1.2
  1200. ) +
  1201. facet_wrap(~ roi_label) +
  1202. theme_classic(base_size = 13) +
  1203. labs(x = "Condition during encoding", y = "Mean % signal change", fill = "Stimulus", alpha = "Order") +
  1204. # accessible, pleasant green & violet
  1205. scale_fill_manual(values = c(
  1206. "Exact" = "#66BB6A", # soft green
  1207. "Modified" = "#9575CD" # soft violet
  1208. )) +
  1209. # subtle opacity difference for First vs Repeat
  1210. scale_alpha_manual(values = c(
  1211. "First" = 0.95,
  1212. "Repeat" = 0.55
  1213. )) +
  1214. scale_y_continuous(expand = expansion(mult = c(0.02, 0.14))) +
  1215. custom_theme + guides(fill = "none") +
  1216. theme(
  1217. legend.position = c(0.88, 0.93) # a bit up
  1218. )
  1219. # --- add bracket + p (Exact vs Modified within each order; same in both facets) ---
  1220. fig2_plot +
  1221. ggpubr::stat_pvalue_manual(
  1222. fig2_stat.test,
  1223. label = "label",
  1224. xmin = "xmin",
  1225. xmax = "xmax",
  1226. y.position = "y.position",
  1227. tip.length = 0.01,
  1228. hide.ns = TRUE,
  1229. inherit.aes = FALSE,
  1230. bracket.shorten=1.2,
  1231. size = 5.4,
  1232. bracket.size = 1.0
  1233. )
  1234. ```
  1235. ```{r}
  1236. text_base_export=40
  1237. p_text_size_mm <- text_base_export * 0.20 # ~points → mm-ish (tweak 0.30–0.45)
  1238. p_bracket_mm <- p_text_size_mm * 0.15 # bracket thickness relative to text
  1239. fig2_plot_export <- fig2_plot +
  1240. theme_classic(base_size = text_base_export) +
  1241. theme(
  1242. legend.position = c(0.88, 0.88),
  1243. legend.title = element_text(size = text_base_export * 0.88),
  1244. legend.text = element_text(size = text_base_export * 0.90),
  1245. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1246. axis.text.y = element_text(size = text_base_export * 0.90),
  1247. axis.title = element_text(size = text_base_export * 1.05),
  1248. strip.text = element_blank(),
  1249. panel.spacing.x = unit(4, "in")
  1250. )
  1251. ggsave(
  1252. "derivatives/figures/Figure2B.png",
  1253. plot = fig2_plot_export,
  1254. width = 26, height = 13, units = "in",
  1255. dpi = 300, bg = "white", device = "png"
  1256. )
  1257. ```
  1258. ## Revised Figure 3 - exact, close, distant
  1259. ```{r}
  1260. # BARPLOT SUMMARY
  1261. fig3_barsum <- Rmisc::summarySEwithin(
  1262. data = semmst_merged_data_H1_clodis_nooutlier,
  1263. measurevar = "estimate",
  1264. withinvars = c("condition", "roi_cluster"),
  1265. idvar = "ID",
  1266. na.rm = TRUE
  1267. ) %>%
  1268. mutate(
  1269. condition = factor(as.character(condition), levels = c("Exact", "Close", "Distant"))
  1270. )
  1271. # ROI LABELS
  1272. fig3_roi_lab <- semmst_merged_data_H1_clodis_nooutlier %>%
  1273. dplyr::distinct(roi_cluster, hemisphere, roi, cluster, voxels) %>%
  1274. dplyr::mutate(
  1275. roi_label = dplyr::case_when(
  1276. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "R" ~ "Right hippocampal head cluster",
  1277. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "L" ~ "Left hippocampal head cluster",
  1278. TRUE ~ as.character(roi_cluster)
  1279. )
  1280. ) %>%
  1281. dplyr::select(roi_cluster, roi_label)
  1282. # ATTACH ROI LABELS
  1283. fig3_barsum <- fig3_barsum %>%
  1284. dplyr::left_join(fig3_roi_lab, by = "roi_cluster")
  1285. # SIGNIFICANT PAIRWISE BRACKETS
  1286. fig3_stat.test <- semmst_merged_data_H1_clodis_PWC %>%
  1287. dplyr::filter(p < .05) %>%
  1288. dplyr::left_join(fig3_roi_lab, by = "roi_cluster")
  1289. # BRACKET HEIGHTS
  1290. fig3_ypos <- fig3_barsum %>%
  1291. dplyr::group_by(roi_label) %>%
  1292. dplyr::summarise(
  1293. y_max = max(estimate + se, na.rm = TRUE),
  1294. y_min = min(estimate - se, na.rm = TRUE),
  1295. y_span = y_max - y_min,
  1296. .groups = "drop"
  1297. ) %>%
  1298. dplyr::mutate(
  1299. y_span = if_else(y_span == 0 | is.na(y_span), 0.1, y_span),
  1300. y_base = y_max + 0.10 * y_span,
  1301. y_step = 0.12 * y_span
  1302. )
  1303. # STACK BRACKETS WITHIN EACH ROI
  1304. fig3_stat.test <- fig3_stat.test %>%
  1305. dplyr::left_join(fig3_ypos, by = "roi_label") %>%
  1306. dplyr::group_by(roi_label) %>%
  1307. dplyr::arrange(group1, group2, .by_group = TRUE) %>%
  1308. dplyr::mutate(y.position = y_base + (dplyr::row_number() - 1) * y_step) %>%
  1309. dplyr::ungroup()
  1310. # BASE FIGURE
  1311. fig3_plot <- ggplot(fig3_barsum, aes(x = condition, y = estimate, fill = condition)) +
  1312. geom_col(width = 0.68) +
  1313. geom_errorbar(
  1314. aes(ymin = estimate - se, ymax = estimate + se),
  1315. width = 0.12,
  1316. size = 1.2
  1317. ) +
  1318. ggpubr::stat_pvalue_manual(
  1319. fig3_stat.test,
  1320. label = "label",
  1321. xmin = "group1",
  1322. xmax = "group2",
  1323. y.position = "y.position",
  1324. tip.length = 0.015,
  1325. hide.ns = FALSE,
  1326. inherit.aes = FALSE,
  1327. bracket.shorten = 0.03,
  1328. size = 6.6,
  1329. bracket.size = 1.35
  1330. ) +
  1331. facet_wrap(~ roi_label, ncol = 2) +
  1332. labs(
  1333. x = "Conditions of repeats during encoding",
  1334. y = "Mean % signal change"
  1335. ) +
  1336. scale_fill_manual(values = c(
  1337. "Exact" = "#66BB6A",
  1338. "Close" = "#4477AA",
  1339. "Distant" = "#DDCC77"
  1340. )) +
  1341. scale_y_continuous(expand = expansion(mult = c(0.03, 0.22))) +
  1342. theme_classic(base_size = 18) +
  1343. custom_theme +
  1344. guides(fill = "none")
  1345. fig3_plot
  1346. ```
  1347. ```{r}
  1348. # EXPORT
  1349. text_base_export = 40
  1350. fig3_plot_export <- ggplot(fig3_barsum, aes(x = condition, y = estimate, fill = condition)) +
  1351. geom_col(width = 0.68) +
  1352. geom_errorbar(
  1353. aes(ymin = estimate - se, ymax = estimate + se),
  1354. width = 0.12,
  1355. linewidth = 1.8
  1356. ) +
  1357. ggpubr::stat_pvalue_manual(
  1358. fig3_stat.test,
  1359. label = "label",
  1360. xmin = "group1",
  1361. xmax = "group2",
  1362. y.position = "y.position",
  1363. tip.length = 0.015,
  1364. hide.ns = FALSE,
  1365. inherit.aes = FALSE,
  1366. bracket.shorten = 0.03,
  1367. size = 10.5,
  1368. bracket.size = 2.1
  1369. ) +
  1370. facet_wrap(~ roi_label, ncol = 2) +
  1371. labs(
  1372. x = "Conditions of repeats during encoding",
  1373. y = "Mean % signal change"
  1374. ) +
  1375. scale_fill_manual(values = c(
  1376. "Exact" = "#66BB6A",
  1377. "Close" = "#4477AA",
  1378. "Distant" = "#DDCC77"
  1379. )) +
  1380. scale_y_continuous(expand = expansion(mult = c(0.03, 0.22))) +
  1381. theme_classic(base_size = text_base_export) +
  1382. custom_theme +
  1383. guides(fill = "none") +
  1384. theme(
  1385. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1386. axis.text.y = element_text(size = text_base_export * 0.95),
  1387. axis.title.x = element_text(size = text_base_export * 1.05, face = "plain"),
  1388. axis.title.y = element_text(size = text_base_export * 1.25, face = "plain"),
  1389. strip.text = element_text(size = text_base_export * 0.95, face = "plain"),
  1390. strip.background = element_rect(fill = "white", color = "black", linewidth = 2.2)
  1391. )
  1392. ggsave(
  1393. "derivatives/figures/Figure3.png",
  1394. plot = fig3_plot_export,
  1395. width = 22, height = 12.5, units = "in",
  1396. dpi = 300, bg = "white", device = "png"
  1397. )
  1398. ```
  1399. ## Figure 4 - RS ~ LDI
  1400. ```{r}
  1401. # 1) Long table (ALL trials only)
  1402. figure4_df <- df_corr_input_all_nooutlier %>%
  1403. dplyr::select(ID, roi_group, roi, hemisphere, bias = LureBiasScore, lure_dprime) %>%
  1404. tidyr::drop_na(bias, lure_dprime) %>%
  1405. dplyr::mutate(
  1406. facet_lab = dplyr::case_when(
  1407. roi_group == "L_HEAD_DGCA23_HEAD_SUB_14" ~ "Left hippocampal head cluster",
  1408. roi_group == "R_HEAD_DGCA23_HEAD_SUB_15" ~ "Right hippocampal head cluster",
  1409. TRUE ~ as.character(roi_group)
  1410. )
  1411. )
  1412. # 2) Per-ROI correlation + Bonferroni + label text
  1413. figure4_labs <- figure4_df %>%
  1414. dplyr::group_by(roi_group, facet_lab) %>%
  1415. dplyr::summarise(
  1416. n = dplyr::n(),
  1417. r = unname(stats::cor(bias, lure_dprime, method = "pearson")),
  1418. p = stats::cor.test(bias, lure_dprime, method = "pearson")$p.value,
  1419. .groups = "drop"
  1420. ) %>%
  1421. dplyr::mutate(
  1422. p_adj = p.adjust(p, method = "bonferroni"),
  1423. stars = dplyr::case_when(
  1424. p_adj < .001 ~ "***",
  1425. p_adj < .01 ~ "**",
  1426. p_adj < .05 ~ "*",
  1427. TRUE ~ ""
  1428. ),
  1429. sig = if_else(p_adj < .05, "sig", "ns"),
  1430. ann = dplyr::case_when(
  1431. is.na(p_adj) ~ sprintf("r = %.2f, p = NA", r),
  1432. p_adj < .001 ~ sprintf("r = %.2f, p < .001%s", r, stars),
  1433. TRUE ~ sprintf("r = %.2f, p = %.3f%s", r, p_adj, stars)
  1434. )
  1435. )
  1436. figure4_full <- figure4_df %>%
  1437. left_join(figure4_labs %>% select(roi_group, sig), by = "roi_group")
  1438. # 3) Plot
  1439. fig4 = ggplot(figure4_full, aes(bias, lure_dprime)) +
  1440. geom_point(alpha = 0.7, size = 4.5) +
  1441. scale_color_manual(values = c(ns = "grey30", sig = "red"), guide = "none") +
  1442. facet_wrap(~ roi_group, scales = "free",
  1443. labeller = labeller(roi_group = setNames(figure4_labs$facet_lab, figure4_labs$roi_group))) +
  1444. labs(
  1445. x = "Neural pattern separation\nModified repeat - exact repeat\n(% signal change)",
  1446. y = "Behavioral mnemonic discrimination\nd' (lures vs targets)"
  1447. ) +
  1448. theme_classic(base_size = 18) +
  1449. custom_theme
  1450. fig4 +
  1451. geom_smooth(aes(color = sig), method = "lm", se = FALSE, linewidth = 1.1) +
  1452. geom_text(
  1453. data = figure4_labs,
  1454. aes(x = -Inf, y = Inf, label = ann, color = sig),
  1455. inherit.aes = FALSE,
  1456. hjust = -0.05, vjust = 1.2, size = 5.5
  1457. )
  1458. ```
  1459. ```{r}
  1460. text_base_export=40
  1461. fig4_export <- fig4 +
  1462. geom_smooth(aes(color = sig), method = "lm", se = FALSE, linewidth = 2.6) +
  1463. geom_text(
  1464. data = figure4_labs,
  1465. aes(x = -Inf, y = Inf, label = ann, color = sig),
  1466. inherit.aes = FALSE,
  1467. hjust = -0.05, vjust = 1.2, size = 10.5
  1468. ) +
  1469. theme_classic(base_size = text_base_export) +
  1470. theme(
  1471. legend.title = element_text(size = text_base_export * 0.95),
  1472. legend.text = element_text(size = text_base_export * 0.90),
  1473. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1474. axis.text.y = element_text(size = text_base_export * 0.90),
  1475. axis.title = element_text(size = text_base_export * 1.05)
  1476. )
  1477. ggsave(
  1478. "derivatives/figures/Figure4.png",
  1479. plot = fig4_export,
  1480. width = 20, height = 12, units = "in",
  1481. dpi = 300, bg = "white", device = "png"
  1482. )
  1483. ```
  1484. ## Figure 5 - ENC RS mask -> REC conditions
  1485. ```{r}
  1486. fig5_dodge <- 0.8
  1487. level_map <- c("FoilCR", "LureCR", "TargetHIT")
  1488. level_lab <- c("Foil", "Lure", "Target")
  1489. # 1) Within-subject summary
  1490. fig5_barsum <- Rmisc::summarySEwithin(
  1491. data = semmst_merged_data_filtered_H4_REC_conditions_nooutlier,
  1492. measurevar = "estimate",
  1493. withinvars = c("cope_name", "roi_cluster"),
  1494. idvar = "ID",
  1495. na.rm = TRUE
  1496. )
  1497. fig5_roi_lab <- semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  1498. distinct(roi_cluster, hemisphere, roi, cluster, voxels) %>%
  1499. mutate(
  1500. roi_label = case_when(
  1501. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "R" ~ "Right hippocampal head cluster",
  1502. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "L" ~ "Left hippocampal head cluster",
  1503. TRUE ~ NA_character_
  1504. )
  1505. ) %>%
  1506. select(roi_cluster, roi_label)
  1507. fig5_barsum <- fig5_barsum %>%
  1508. mutate(cope_name = factor(cope_name, levels = level_map, labels = level_lab)) %>%
  1509. left_join(fig5_roi_lab, by = "roi_cluster")
  1510. # 2) Base plot (no stats)
  1511. fig5_plot <- ggplot(fig5_barsum, aes(x = cope_name, y = estimate, fill = cope_name)) +
  1512. geom_col(position = position_dodge(width = fig5_dodge), width = 0.65) +
  1513. geom_errorbar(
  1514. aes(ymin = estimate - se, ymax = estimate + se),
  1515. position = position_dodge(width = fig5_dodge),
  1516. width = 0.15,
  1517. size = 1.2
  1518. ) +
  1519. facet_wrap(~ roi_label) +
  1520. theme_classic(base_size = 13) +
  1521. labs(x = "Condition during recognition", y = "Mean % signal change") +
  1522. scale_fill_manual(values = c(
  1523. "Target" = "#66BB6A",
  1524. "Lure" = "#9575CD",
  1525. "Foil" = "#EE8866"
  1526. )) +
  1527. scale_y_continuous(expand = expansion(mult = c(0.02, 0.12))) +
  1528. custom_theme +
  1529. guides(fill = "none")
  1530. # 3) Global paired pairwise stats (collapsed across ROI)
  1531. fig5_stats_global <- semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  1532. mutate(cope_name = factor(cope_name, levels = level_map, labels = level_lab)) %>%
  1533. group_by(ID, cope_name) %>%
  1534. summarise(estimate = mean(estimate, na.rm = TRUE), .groups = "drop") %>%
  1535. pairwise_t_test(
  1536. estimate ~ cope_name,
  1537. paired = TRUE,
  1538. p.adjust.method = "bonferroni"
  1539. ) %>%
  1540. mutate(
  1541. label = case_when(
  1542. p.adj < 0.001 ~ "p < .001",
  1543. p.adj < 0.01 ~ "p < .01",
  1544. p.adj < 0.05 ~ "p < .05",
  1545. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p.adj)))
  1546. )
  1547. )
  1548. # Global ymax across BOTH facets (shared bracket height)
  1549. ymax_global <- fig5_barsum %>%
  1550. summarise(ymax = max(estimate + se, na.rm = TRUE)) %>%
  1551. pull(ymax)
  1552. # Replicate the same stats into each facet; same y.position levels everywhere
  1553. fig5_stats_for_facets <- fig5_barsum %>%
  1554. distinct(roi_label) %>%
  1555. tidyr::crossing(fig5_stats_global) %>%
  1556. group_by(roi_label) %>%
  1557. arrange(group1, group2, .by_group = TRUE) %>%
  1558. mutate(
  1559. y.position = ymax_global * 1.08 + (row_number() - 1) * (0.10 * ymax_global)
  1560. ) %>%
  1561. ungroup()
  1562. # 4) In-window plot WITH stats
  1563. fig5_plot_with_stats_global <- fig5_plot +
  1564. stat_pvalue_manual(
  1565. fig5_stats_for_facets,
  1566. label = "label",
  1567. xmin = "group1",
  1568. xmax = "group2",
  1569. y.position = "y.position",
  1570. tip.length = 0.01,
  1571. hide.ns = TRUE,
  1572. inherit.aes = FALSE,
  1573. bracket.size = 1.0,
  1574. size = 5.0
  1575. ) +
  1576. scale_y_continuous(expand = expansion(mult = c(0.02, 0.18)))
  1577. fig5_plot_with_stats_global
  1578. ```
  1579. ```{r}
  1580. text_base_export <- 40
  1581. p_text_export <- text_base_export * 0.28 # bigger p text on export only
  1582. fig5_plot_export <- fig5_plot +
  1583. stat_pvalue_manual(
  1584. fig5_stats_for_facets,
  1585. label = "label",
  1586. xmin = "group1",
  1587. xmax = "group2",
  1588. y.position = "y.position",
  1589. tip.length = 0.01,
  1590. hide.ns = TRUE,
  1591. inherit.aes = FALSE,
  1592. bracket.size = 1.3,
  1593. size = p_text_export
  1594. ) +
  1595. scale_y_continuous(expand = expansion(mult = c(0.02, 0.18))) +
  1596. theme_classic(base_size = text_base_export) +
  1597. theme(
  1598. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1599. axis.text.y = element_text(size = text_base_export * 0.90),
  1600. axis.title = element_text(size = text_base_export * 1.05),
  1601. strip.text = element_text(size = text_base_export * 0.95)
  1602. )
  1603. ggsave(
  1604. "derivatives/figures/Figure5.png",
  1605. plot = fig5_plot_export,
  1606. width = 22, height = 14, units = "in",
  1607. dpi = 300, bg = "white", device = "png"
  1608. )
  1609. ```
  1610. # Supplementary Results
  1611. ### Supplementary Results 1 - covariate effects
  1612. #### Behavioural GLMEM covariates
  1613. ```{r}
  1614. model_complex_cont = glmer(response ~ cosine * trial_type * former_type +
  1615. age + sex + education + Vocabulary_scale + Digit_Symbol_scale +
  1616. NonWord_sum_scale + arousal_scale + meaningfulness_scale + concreteness_scale +
  1617. (1|ID) +
  1618. (1|itemno),
  1619. family = binomial("probit"),
  1620. data = semmst_behav_data_rec_filtered_GLMEM_lure,
  1621. control = glmerControl(optimizer ='bobyqa', optCtrl=list(maxfun = 20000)), nAGQ=0)
  1622. ```
  1623. ##### Supplementary Figure 2AB.
  1624. ```{r}
  1625. # 1) Marginal (averaged-over-covariates) predictions
  1626. pred_nonword <- ggeffects::ggaverage(
  1627. model_complex_cont,
  1628. terms = "NonWord_sum_scale [n=100]" # smooth grid
  1629. ) %>%
  1630. as.data.frame() %>%
  1631. mutate(effect = "Phonological awareness of participants")
  1632. pred_meaning <- ggeffects::ggaverage(
  1633. model_complex_cont,
  1634. terms = "meaningfulness_scale [n=100]"
  1635. ) %>%
  1636. as.data.frame() %>%
  1637. mutate(effect = "Meaningfulness of items")
  1638. preds_2ABSI <- bind_rows(pred_nonword, pred_meaning)
  1639. # 2) Plot (2 facets next to each other)
  1640. fig_ABSI_behav <- ggplot(preds_2ABSI, aes(x = x, y = predicted)) +
  1641. geom_ribbon(aes(ymin = conf.low, ymax = conf.high), alpha = 0.18, fill="#4C72B0") +
  1642. geom_line(size = 1.2, colour="#4C72B0") +
  1643. facet_wrap(~ effect, nrow = 1, scales = "free_x") +
  1644. theme_classic(base_size = 13) +
  1645. labs(
  1646. x = NULL,
  1647. y = "p(response 'old'|lure)"
  1648. ) +
  1649. scale_y_continuous(limits = c(0, 0.5), expand = expansion(mult = c(0.02, 0.06))) +
  1650. custom_theme
  1651. fig_ABSI_behav
  1652. ```
  1653. ```{r}
  1654. # 3) Export (matches your Figure2B export approach)
  1655. text_base_export <- 40
  1656. fig_ABSI_export <- fig_ABSI_behav +
  1657. theme_classic(base_size = text_base_export) +
  1658. theme(
  1659. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1660. axis.text.y = element_text(size = text_base_export * 0.90),
  1661. axis.title = element_text(size = text_base_export * 1.05)
  1662. )
  1663. ggsave(
  1664. "derivatives/figures/SI_figures/Figure2ABSI.png",
  1665. plot = fig_ABSI_export,
  1666. width = 21, height = 11, units = "in",
  1667. dpi = 300, bg = "white", device = "png"
  1668. )
  1669. ```
  1670. #### Encoding ANCOVA MEDIAN SPLIT (Digit-Symbol)
  1671. ```{r}
  1672. df <- semmst_merged_data_H1_nooutlier %>%
  1673. drop_na(Digit_Symbol) %>%
  1674. mutate(Digit_Symbol_scale = scale(Digit_Symbol))
  1675. df_E1 <- df %>%
  1676. mutate(
  1677. digit_group = if_else(Digit_Symbol_scale >= median(Digit_Symbol_scale, na.rm = TRUE), "High", "Low"),
  1678. digit_group = factor(digit_group, levels = c("Low", "High"))
  1679. )
  1680. semmst_merged_data_E1_digit_median_split = df_E1 %>%
  1681. group_by(stim_type, digit_group) %>%
  1682. pairwise_t_test(
  1683. estimate ~ order,
  1684. paired = TRUE,
  1685. detailed = TRUE,
  1686. ) %>%
  1687. ungroup() %>%
  1688. adjust_pvalue(method = "bonferroni") %>%
  1689. add_significance() %>%
  1690. ungroup() %>%
  1691. mutate(label = paste0("p=", signif(p.adj, 3)))
  1692. semmst_merged_data_E1_digit_median_split
  1693. # stim_type digit_group estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj
  1694. ## 1 Exact Low 0.221 estimate First Repeat 28 28 8.04 1.22e-8 27 0.164 0.277 T-test two.sided 4.88e-8 ****
  1695. ## 2 Exact High 0.158 estimate First Repeat 28 28 4.92 3.82e-5 27 0.0923 0.225 T-test two.sided 1.53e-4 ***
  1696. ## 3 Lure Low 0.0490 estimate First Repeat 28 28 1.54 1.36e-1 27 -0.0165 0.115 T-test two.sided 5.44e-1 ns
  1697. ## 4 Lure High 0.0949 estimate First Repeat 28 28 3.53 2 e-3 27 0.0397 0.150 T-test two.sided 8 e-3 **
  1698. ```
  1699. ##### Supplementary Figure 2C.
  1700. ```{r}
  1701. fig_mediansplit_CSI_dodge <- 0.8
  1702. # OPTIONAL but recommended if you have multiple rows per ID/condition (e.g., ROI clusters):
  1703. # collapse to one value per ID × stim_type × order × digit_group
  1704. df_E1_collapsed <- df_E1 %>%
  1705. group_by(ID, stim_type, order, digit_group) %>%
  1706. mutate(stim_type = factor(as.character(stim_type),
  1707. levels = c("Exact", "Lure"),
  1708. labels = c("Exact", "Modified"))) %>%
  1709. mutate(digit_group = factor(as.character(digit_group),
  1710. levels = c("Low", "High"),
  1711. labels = c("Low processing speed", "High processing speed")))
  1712. # --- summary for bars (within-subject correction for stim_type+order, between = digit_group) ---
  1713. fig_mediansplit_CSI_barsum <- Rmisc::summarySEwithin(
  1714. data = df_E1_collapsed,
  1715. measurevar = "estimate",
  1716. withinvars = c("stim_type", "order"),
  1717. betweenvars = "digit_group",
  1718. idvar = "ID",
  1719. na.rm = TRUE
  1720. )
  1721. # --- paired tests: First vs Repeat within each stim_type × digit_group ---
  1722. fig_mediansplit_CSI_stat.test <- df_E1_collapsed %>%
  1723. group_by(stim_type, digit_group) %>%
  1724. pairwise_t_test(
  1725. estimate ~ order,
  1726. paired = TRUE,
  1727. detailed = TRUE
  1728. ) %>%
  1729. ungroup() %>%
  1730. adjust_pvalue(method = "bonferroni") %>%
  1731. add_significance("p.adj") %>%
  1732. mutate(
  1733. label = case_when(
  1734. p.adj < 0.001 ~ "p < .001",
  1735. p.adj < 0.01 ~ "p < .01",
  1736. p.adj < 0.05 ~ "p < .05",
  1737. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.2f", p.adj)))
  1738. )
  1739. )
  1740. # y positions per facet + stim_type
  1741. fig_mediansplit_CSI_stat.test <- fig_mediansplit_CSI_stat.test %>%
  1742. left_join(
  1743. fig_mediansplit_CSI_barsum %>%
  1744. group_by(stim_type, digit_group) %>%
  1745. summarise(y.position = max(estimate + se, na.rm = TRUE) * 1.08, .groups = "drop"),
  1746. by = c("stim_type", "digit_group")
  1747. ) %>%
  1748. mutate(
  1749. stim_idx = as.numeric(stim_type),
  1750. xmin = stim_idx - fig_mediansplit_CSI_dodge/2,
  1751. xmax = stim_idx + fig_mediansplit_CSI_dodge/2
  1752. )
  1753. # --- plot ---
  1754. fig_mediansplit_CSI_plot <- ggplot(
  1755. fig_mediansplit_CSI_barsum,
  1756. aes(x = stim_type, y = estimate, fill = stim_type, group = order)
  1757. ) +
  1758. geom_col(
  1759. aes(alpha = order),
  1760. position = position_dodge(width = fig_mediansplit_CSI_dodge),
  1761. width = 0.65
  1762. ) +
  1763. geom_errorbar(
  1764. aes(ymin = estimate - se, ymax = estimate + se),
  1765. position = position_dodge(width = fig_mediansplit_CSI_dodge),
  1766. width = 0.15, size = 1.2
  1767. ) +
  1768. facet_wrap(~ digit_group) +
  1769. theme_classic(base_size = 13) +
  1770. labs(
  1771. x = "Condition during encoding",
  1772. y = "Mean % signal change",
  1773. fill = "Stimulus",
  1774. alpha = "Order"
  1775. ) +
  1776. scale_fill_manual(values = c(
  1777. "Exact" = "#66BB6A",
  1778. "Modified" = "#9575CD"
  1779. )) +
  1780. scale_alpha_manual(values = c(
  1781. "First" = 0.95,
  1782. "Repeat" = 0.55
  1783. )) +
  1784. scale_y_continuous(expand = expansion(mult = c(0.02, 0.14))) +
  1785. custom_theme +
  1786. guides(fill = "none") +
  1787. theme(
  1788. legend.position = c(0.88, 0.13)
  1789. )
  1790. fig_mediansplit_CSI_plot +
  1791. ggpubr::stat_pvalue_manual(
  1792. fig_mediansplit_CSI_stat.test,
  1793. label = "label",
  1794. xmin = "xmin",
  1795. xmax = "xmax",
  1796. y.position = "y.position",
  1797. tip.length = 0.01,
  1798. hide.ns = TRUE,
  1799. inherit.aes = FALSE,
  1800. bracket.shorten = 1.2,
  1801. size = 5.4,
  1802. bracket.size = 1.0
  1803. )
  1804. ```
  1805. ```{r}
  1806. text_base_export <- 40
  1807. fig_mediansplit_CSI_plot_export <- fig_mediansplit_CSI_plot +
  1808. ggpubr::stat_pvalue_manual(
  1809. fig_mediansplit_CSI_stat.test,
  1810. label = "label",
  1811. xmin = "xmin",
  1812. xmax = "xmax",
  1813. y.position = "y.position",
  1814. tip.length = 0.01,
  1815. hide.ns = TRUE,
  1816. inherit.aes = FALSE,
  1817. bracket.shorten = 1.2,
  1818. size = 8.4,
  1819. bracket.size = 1.4
  1820. ) +
  1821. theme_classic(base_size = text_base_export) +
  1822. theme(
  1823. legend.position = c(0.88, 0.12),
  1824. legend.title = element_text(size = text_base_export * 0.88),
  1825. legend.text = element_text(size = text_base_export * 0.90),
  1826. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  1827. axis.text.y = element_text(size = text_base_export * 0.90),
  1828. axis.title = element_text(size = text_base_export * 1.05)
  1829. )
  1830. ggsave(
  1831. "derivatives/figures/SI_figures/Figure2CSI.png",
  1832. plot = fig_mediansplit_CSI_plot_export,
  1833. width = 21, height = 13, units = "in",
  1834. dpi = 300, bg = "white", device = "png"
  1835. )
  1836. ```
  1837. ### Revised Supplementary Results 2 - Exact, close, distant
  1838. ```{r}
  1839. # HELPERS
  1840. parse_roi_clean_anat <- function(roi_string) {
  1841. roi_string <- as.character(roi_string)
  1842. dplyr::case_when(
  1843. str_detect(roi_string, regex("CA12|CA1/2|CA1_2|CA1-2|CA1", ignore_case = TRUE)) ~ "CA12",
  1844. str_detect(roi_string, regex("DGCA3|DGCA23|DG-CA3|DG-CA23|DG", ignore_case = TRUE)) ~ "DGCA3",
  1845. str_detect(roi_string, regex("SUB", ignore_case = TRUE)) ~ "SUB",
  1846. TRUE ~ NA_character_
  1847. )
  1848. }
  1849. # BODY DATA FROM ORIGINAL ANATOMICAL TABLE
  1850. H1_clodis_anat_body <- semmst_merged_data_filtered_anat %>%
  1851. filter(task == "EncodingFirst") %>%
  1852. filter(cope_name %in% c("ExactRepeat", "CloseRepeat", "DistantRepeat")) %>%
  1853. mutate(
  1854. roi_cluster = paste(hemisphere, roi, sep = "_"),
  1855. roi_type = if_else(str_detect(roi_cluster, "HEAD"), "head", "body")
  1856. ) %>%
  1857. filter(roi_type == "body") %>%
  1858. mutate(
  1859. condition = recode(
  1860. cope_name,
  1861. "ExactRepeat" = "Exact",
  1862. "CloseRepeat" = "Close",
  1863. "DistantRepeat" = "Distant"
  1864. ),
  1865. hemi_lab = case_when(
  1866. hemisphere == "L" ~ "Left hemisphere",
  1867. hemisphere == "R" ~ "Right hemisphere",
  1868. TRUE ~ as.character(hemisphere)
  1869. ),
  1870. roi_clean = parse_roi_clean_anat(roi_cluster)
  1871. ) %>%
  1872. dplyr::select(
  1873. ID, task, cope_name, condition, estimate,
  1874. hemisphere, hemi_lab, roi, roi_clean, roi_cluster, roi_type,
  1875. everything()
  1876. )
  1877. # HEAD DATA FROM SUPPLEMENTARY ANATOMICAL TABLE
  1878. H1_clodis_anat_head <- semmst_merged_data_supplement_filtered %>%
  1879. filter(contrast == "anatHC_berron") %>%
  1880. filter(task == "EncodingFirst") %>%
  1881. filter(cope_name %in% c("ExactRepeat", "CloseRepeat", "DistantRepeat")) %>%
  1882. mutate(
  1883. roi = str_remove(roi, "^_"),
  1884. roi = str_remove(roi, "^HEAD"),
  1885. roi = str_remove(roi, "^_"),
  1886. roi_type = "head",
  1887. roi_cluster = paste(hemisphere, roi, sep = "_")
  1888. ) %>%
  1889. mutate(
  1890. condition = recode(
  1891. cope_name,
  1892. "ExactRepeat" = "Exact",
  1893. "CloseRepeat" = "Close",
  1894. "DistantRepeat" = "Distant"
  1895. ),
  1896. hemi_lab = case_when(
  1897. hemisphere == "L" ~ "Left hemisphere",
  1898. hemisphere == "R" ~ "Right hemisphere",
  1899. TRUE ~ as.character(hemisphere)
  1900. ),
  1901. roi_clean = parse_roi_clean_anat(roi_cluster)
  1902. ) %>%
  1903. dplyr::select(
  1904. ID, task, cope_name, condition, estimate,
  1905. hemisphere, hemi_lab, roi, roi_clean, roi_cluster, roi_type,
  1906. everything()
  1907. )
  1908. # MERGE HEAD AND BODY
  1909. H1_clodis_anat <- bind_rows(
  1910. H1_clodis_anat_head,
  1911. H1_clodis_anat_body
  1912. ) %>%
  1913. mutate(
  1914. condition = factor(condition, levels = c("Exact", "Close", "Distant")),
  1915. roi_type = factor(roi_type, levels = c("head", "body")),
  1916. hemi_lab = factor(hemi_lab, levels = c("Left hemisphere", "Right hemisphere")),
  1917. roi_clean = factor(roi_clean, levels = c("CA12", "DGCA3", "SUB"))
  1918. )
  1919. # ASSUMPTION CHECKS
  1920. ## OUTLIER
  1921. H1_clodis_anat %>%
  1922. group_by(roi_type, hemi_lab, roi_clean, condition) %>%
  1923. identify_outliers(estimate) %>%
  1924. dplyr::select(ID, roi_type, hemi_lab, roi_clean, condition, is.outlier, is.extreme) %>%
  1925. filter(is.extreme == TRUE)
  1926. # REMOVE OUTLIERS IF NEEDED
  1927. H1_clodis_anat_nooutlier <- H1_clodis_anat %>%
  1928. filter(! ID %in% c("sub-632012", "sub-499854")) %>%
  1929. drop_na(estimate) %>%
  1930. ungroup()
  1931. ## NORMALITY
  1932. H1_clodis_anat_nooutlier %>%
  1933. group_by(roi_type, hemi_lab, roi_clean, condition) %>%
  1934. shapiro_test(estimate)
  1935. # ANOVA
  1936. # condition effect is tested separately within each anatomical facet
  1937. H1_clodis_anat_AOV = H1_clodis_anat_nooutlier %>%
  1938. anova_test(
  1939. dv = estimate,
  1940. wid = ID,
  1941. within = c(condition, roi_type, hemi_lab, roi_clean),
  1942. )
  1943. get_anova_table(H1_clodis_anat_AOV)
  1944. # POST-HOC
  1945. # pairwise comparisons - FWE correction across conditions only within each facet
  1946. H1_clodis_anat_PWC <- H1_clodis_anat_nooutlier %>%
  1947. group_by(roi_type, hemi_lab, roi_clean) %>%
  1948. pairwise_t_test(
  1949. estimate ~ condition,
  1950. paired = TRUE,
  1951. detailed = TRUE,
  1952. p.adjust.method = "holm"
  1953. ) %>%
  1954. ungroup() %>%
  1955. add_significance("p") %>%
  1956. mutate(
  1957. label = case_when(
  1958. p < .001 ~ "p < .001",
  1959. p < .01 ~ "p < .01",
  1960. p < .05 ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p))),
  1961. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.2f", p)))
  1962. )
  1963. )
  1964. H1_clodis_anat_PWC %>% print(n=40)
  1965. ```
  1966. ##### Revised Supplementary Figure 3
  1967. ```{r}
  1968. # BARPLOT SUMMARY
  1969. fig_anat_barsum <- Rmisc::summarySEwithin(
  1970. data = H1_clodis_anat_nooutlier,
  1971. measurevar = "estimate",
  1972. withinvars = c("condition", "roi_type", "hemi_lab", "roi_clean"),
  1973. idvar = "ID",
  1974. na.rm = TRUE
  1975. ) %>%
  1976. mutate(
  1977. condition = factor(as.character(condition), levels = c("Exact", "Close", "Distant")),
  1978. roi_type = factor(roi_type, levels = c("head", "body")),
  1979. hemi_lab = factor(hemi_lab, levels = c("Left hemisphere", "Right hemisphere")),
  1980. roi_clean = factor(roi_clean, levels = c("CA12", "DGCA3", "SUB"))
  1981. )
  1982. # SIGNIFICANT FWE-CORRECTED PAIRWISE BRACKETS
  1983. fig_anat_stat.test <- H1_clodis_anat_PWC %>%
  1984. dplyr::filter(p < .05) %>%
  1985. mutate(
  1986. roi_type = factor(roi_type, levels = c("head", "body")),
  1987. hemi_lab = factor(hemi_lab, levels = c("Left hemisphere", "Right hemisphere")),
  1988. roi_clean = factor(roi_clean, levels = c("CA12", "DGCA3", "SUB")),
  1989. label = case_when(
  1990. p < .001 ~ "p < .001",
  1991. p < .01 ~ "p < .01",
  1992. p < .05 ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p))),
  1993. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.2f", p)))
  1994. )
  1995. )
  1996. # BRACKET HEIGHTS
  1997. fig_anat_ypos <- fig_anat_barsum %>%
  1998. dplyr::group_by(roi_type, hemi_lab, roi_clean) %>%
  1999. dplyr::summarise(
  2000. y_max = max(estimate + se, na.rm = TRUE),
  2001. y_min = min(estimate - se, na.rm = TRUE),
  2002. y_span = y_max - y_min,
  2003. .groups = "drop"
  2004. ) %>%
  2005. dplyr::mutate(
  2006. y_span = if_else(y_span == 0 | is.na(y_span), 0.1, y_span),
  2007. y_base = y_max + 0.10 * y_span,
  2008. y_step = 0.12 * y_span
  2009. )
  2010. # STACK BRACKETS WITHIN EACH FACET
  2011. fig_anat_stat.test <- fig_anat_stat.test %>%
  2012. dplyr::left_join(fig_anat_ypos, by = c("roi_type", "hemi_lab", "roi_clean")) %>%
  2013. dplyr::group_by(roi_type, hemi_lab, roi_clean) %>%
  2014. dplyr::arrange(group1, group2, .by_group = TRUE) %>%
  2015. dplyr::mutate(y.position = y_base + (dplyr::row_number() - 1) * y_step) %>%
  2016. dplyr::ungroup()
  2017. # PANEL FUNCTION
  2018. make_anat_panel <- function(which_type, title_txt,
  2019. base_size = 14,
  2020. bar_width = 0.68,
  2021. err_width = 0.12,
  2022. err_size = 1.1,
  2023. p_size = 4.2,
  2024. p_bracket = 0.9) {
  2025. p_dat <- fig_anat_barsum %>%
  2026. dplyr::filter(roi_type == which_type)
  2027. p_sig <- fig_anat_stat.test %>%
  2028. dplyr::filter(roi_type == which_type)
  2029. p <- ggplot(
  2030. p_dat,
  2031. aes(x = condition, y = estimate, fill = condition)
  2032. ) +
  2033. geom_col(width = bar_width) +
  2034. geom_errorbar(
  2035. aes(ymin = estimate - se, ymax = estimate + se),
  2036. width = err_width,
  2037. linewidth = err_size
  2038. ) +
  2039. facet_grid(rows = vars(roi_clean), cols = vars(hemi_lab), scales = "free_y") +
  2040. scale_fill_manual(values = c(
  2041. "Exact" = "#66BB6A",
  2042. "Close" = "#4477AA",
  2043. "Distant" = "#DDCC77"
  2044. )) +
  2045. scale_y_continuous(expand = expansion(mult = c(0.03, 0.22))) +
  2046. coord_cartesian(clip = "off") +
  2047. labs(
  2048. x = "Conditions of repeats during encoding",
  2049. y = "Mean % signal change",
  2050. title = title_txt
  2051. ) +
  2052. theme_classic(base_size = base_size) +
  2053. custom_theme +
  2054. guides(fill = "none") +
  2055. theme(
  2056. plot.title = element_text(hjust = 0.5, face = "bold"),
  2057. strip.background = element_blank(),
  2058. strip.text = element_text(face = "bold"),
  2059. panel.spacing.x = unit(1.25, "lines"),
  2060. panel.spacing.y = unit(0.6, "lines")
  2061. )
  2062. # ADD BRACKETS ONLY IF SIGNIFICANT TESTS EXIST
  2063. if (nrow(p_sig) > 0) {
  2064. p <- p +
  2065. ggpubr::stat_pvalue_manual(
  2066. p_sig,
  2067. label = "label",
  2068. xmin = "group1",
  2069. xmax = "group2",
  2070. y.position = "y.position",
  2071. tip.length = 0.01,
  2072. hide.ns = FALSE,
  2073. inherit.aes = FALSE,
  2074. bracket.shorten = 0.05,
  2075. size = p_size,
  2076. bracket.size = p_bracket
  2077. )
  2078. }
  2079. p
  2080. }
  2081. # VIEW
  2082. p_head_view <- make_anat_panel(
  2083. "head", "Hippocampal head",
  2084. base_size = 14,
  2085. bar_width = 0.68,
  2086. err_width = 0.12,
  2087. err_size = 1.1,
  2088. p_size = 4.2,
  2089. p_bracket = 0.9
  2090. )
  2091. p_body_view <- make_anat_panel(
  2092. "body", "Hippocampal body",
  2093. base_size = 14,
  2094. bar_width = 0.68,
  2095. err_width = 0.12,
  2096. err_size = 1.1,
  2097. p_size = 4.2,
  2098. p_bracket = 0.9
  2099. )
  2100. supp_fig_anat_bars <- p_head_view | p_body_view
  2101. supp_fig_anat_bars
  2102. ```
  2103. ```{r}
  2104. # EXPORT
  2105. text_base_export <- 24
  2106. FigureSI3_export_theme <- theme_classic(base_size = text_base_export) +
  2107. theme(
  2108. axis.text.x = element_text(size = text_base_export * 0.80, margin = margin(t = 6)),
  2109. axis.text.y = element_text(size = text_base_export * 0.80),
  2110. axis.title.x = element_text(size = text_base_export * 0.95, margin = margin(t = 10)),
  2111. axis.title.y = element_text(size = text_base_export * 0.95, margin = margin(r = 10)),
  2112. # more visible square facet boxes
  2113. strip.background = element_rect(
  2114. fill = "white",
  2115. colour = "black",
  2116. linewidth = 1.0
  2117. ),
  2118. strip.text = element_text(
  2119. size = text_base_export * 0.76,
  2120. face = "bold",
  2121. margin = margin(4, 8, 4, 8)
  2122. ),
  2123. plot.title = element_text(size = text_base_export * 1.00, face = "bold", hjust = 0.5),
  2124. legend.position = "none",
  2125. panel.spacing.x = unit(0.9, "lines"),
  2126. panel.spacing.y = unit(0.9, "lines"),
  2127. panel.grid.major = element_blank(),
  2128. panel.grid.minor = element_blank(),
  2129. panel.border = element_blank(),
  2130. axis.line = element_line(linewidth = 0.5)
  2131. )
  2132. p_head_export <- make_anat_panel(
  2133. "head", "Hippocampal head",
  2134. base_size = text_base_export,
  2135. bar_width = 0.65,
  2136. err_width = 0.15,
  2137. err_size = 1.1,
  2138. # slightly smaller p labels/brackets
  2139. p_size = 5.8,
  2140. p_bracket = 0.65
  2141. ) +
  2142. FigureSI3_export_theme +
  2143. scale_y_continuous(expand = expansion(mult = c(0.02, 0.16)))
  2144. p_body_export <- make_anat_panel(
  2145. "body", "Hippocampal body",
  2146. base_size = text_base_export,
  2147. bar_width = 0.65,
  2148. err_width = 0.15,
  2149. err_size = 1.1,
  2150. # slightly smaller p labels/brackets
  2151. p_size = 5.8,
  2152. p_bracket = 0.65
  2153. ) +
  2154. FigureSI3_export_theme +
  2155. scale_y_continuous(expand = expansion(mult = c(0.02, 0.16)))
  2156. supp_fig_anat_bars_export <- p_head_export | p_body_export
  2157. ggsave(
  2158. "derivatives/figures/SI_figures/Figure3SI.png",
  2159. plot = supp_fig_anat_bars_export,
  2160. width = 23, height = 12, units = "in",
  2161. dpi = 300,
  2162. bg = "white",
  2163. device = ragg::agg_png
  2164. )
  2165. ```
  2166. ### Revised Supplementary Results 3 - anatomical masks
  2167. ```{r}
  2168. # BODY ROIs from the original anatomical dataset
  2169. revision_results3_body_from_anat <- semmst_merged_data_filtered_anat %>%
  2170. filter(task == "Encoding") %>%
  2171. filter(cope_name %in% c("LureFirst", "ExactFirst", "LureRepeat", "ExactRepeat")) %>%
  2172. filter(!str_detect(roi, "HEAD")) %>%
  2173. extract(
  2174. col = cope_name,
  2175. into = c("stim_type", "order"),
  2176. regex = "([A-Za-z]+?)(First|Repeat)$"
  2177. ) %>%
  2178. mutate(
  2179. stim_type = as.factor(stim_type),
  2180. order = as.factor(order)
  2181. ) %>%
  2182. mutate(
  2183. stim_type = factor(stim_type, levels = c("Exact", "Lure")),
  2184. order = factor(order),
  2185. roi_type = "body"
  2186. )
  2187. # HEAD/other supplement anatomical data
  2188. revision_results3_supplement_anat <- semmst_merged_data_supplement_filtered %>%
  2189. filter(contrast == "anatHC_berron") %>%
  2190. filter(task == "Encoding") %>%
  2191. filter(cope_name %in% c("LureFirst", "ExactFirst", "LureRepeat", "ExactRepeat")) %>%
  2192. extract(
  2193. col = cope_name,
  2194. into = c("stim_type", "order"),
  2195. regex = "([A-Za-z]+?)(First|Repeat)$"
  2196. ) %>%
  2197. mutate(
  2198. stim_type = as.factor(stim_type),
  2199. order = as.factor(order),
  2200. roi = str_remove(roi, "^_"),
  2201. roi = str_remove(roi, "^HEAD"),
  2202. roi = str_remove(roi, "^_"),
  2203. roi_type = if_else(str_detect(roi_cluster, "HEAD") | str_detect(roi, "^SUB|^CA1|^DGCA23"), "head", "body")
  2204. ) %>%
  2205. mutate(
  2206. stim_type = factor(stim_type, levels = c("Exact", "Lure")),
  2207. order = factor(order)
  2208. )
  2209. # MERGE body anat + supplement anat
  2210. semmst_merged_data_supplement_filtered_revision_results3 = bind_rows(
  2211. revision_results3_body_from_anat,
  2212. revision_results3_supplement_anat
  2213. ) %>%
  2214. mutate(
  2215. roi_type = factor(roi_type, levels = c("head", "body"))
  2216. )
  2217. # ASSUMPTION CHECKS
  2218. ## OUTLIER
  2219. semmst_merged_data_supplement_filtered_revision_results3 %>%
  2220. group_by(stim_type, order, roi_type, roi, hemisphere) %>%
  2221. identify_outliers(estimate) %>%
  2222. dplyr::select(ID, roi_type, roi, hemisphere, is.outlier, is.extreme) %>%
  2223. filter(is.extreme == TRUE)
  2224. semmst_merged_data_supplement_filtered_revision_results3_nooutlier = semmst_merged_data_supplement_filtered_revision_results3 %>%
  2225. group_by(stim_type, order, roi_type, roi, hemisphere) %>%
  2226. filter(!ID %in% c("sub-499854", "sub-982347")) %>%
  2227. ungroup()
  2228. ## NORMALITY
  2229. semmst_merged_data_supplement_filtered_revision_results3_nooutlier %>%
  2230. group_by(stim_type, order, roi_type) %>%
  2231. shapiro_test(estimate)
  2232. # ANOVA
  2233. semmst_merged_data_supplement_filtered_revision_results3_nooutlier_AOV = semmst_merged_data_supplement_filtered_revision_results3_nooutlier %>%
  2234. anova_test(
  2235. dv = estimate,
  2236. wid = ID,
  2237. within = c(stim_type, order, roi_type, roi, hemisphere)
  2238. )
  2239. get_anova_table(semmst_merged_data_supplement_filtered_revision_results3_nooutlier_AOV)
  2240. # REPORT Effect DFn DFd F p p<.05 ges
  2241. ## 2 order 1.00 27.00 10.116 4.00e-03 * 1.70e-02
  2242. ## 7 stim_type:roi_type 1.00 27.00 5.878 2.20e-02 * 2.00e-03
  2243. ## 8 order:roi_type 1.00 27.00 28.916 1.11e-05 * 1.90e-02
  2244. ## 9 stim_type:roi 2.00 54.00 4.116 2.20e-02 * 2.00e-03
  2245. ## 10 order:roi 1.61 43.44 4.124 3.00e-02 * 2.00e-03
  2246. ## 11 roi_type:roi 2.00 54.00 13.740 1.50e-05 * 2.70e-02
  2247. ## 13 order:hemisphere 1.00 27.00 6.667 1.60e-02 * 2.00e-03
  2248. ## 18 stim_type:roi_type:roi 2.00 54.00 3.655 3.20e-02 * 9.16e-04
  2249. ## 25 roi_type:roi:hemisphere 2.00 54.00 3.282 4.50e-02 * 3.00e-03
  2250. ## 29 stim_type:roi_type:roi:hemisphere 2.00 54.00 5.885 5.00e-03 * 1.00e-03
  2251. ```
  2252. ##### Revised Supplementary Figure 4.
  2253. ```{r}
  2254. FigureR3_dodge <- 0.8
  2255. # within-subject summary per stim_type × order × roi_type × roi × hemisphere
  2256. FigureR3_barsum <- Rmisc::summarySEwithin(
  2257. data = semmst_merged_data_supplement_filtered_revision_results3_nooutlier,
  2258. measurevar = "estimate",
  2259. withinvars = c("stim_type", "order", "roi_type", "roi", "hemisphere"),
  2260. idvar = "ID",
  2261. na.rm = TRUE
  2262. ) %>%
  2263. mutate(
  2264. stim_type = factor(stim_type, levels = c("Exact", "Lure")),
  2265. order = factor(order, levels = c("First", "Repeat")),
  2266. roi_type = factor(roi_type, levels = c("head", "body")),
  2267. roi = factor(roi),
  2268. hemisphere = factor(
  2269. hemisphere,
  2270. levels = c("L", "R"),
  2271. labels = c("Left hemisphere", "Right hemisphere")
  2272. )
  2273. ) %>%
  2274. mutate(
  2275. stim_type = factor(
  2276. as.character(stim_type),
  2277. levels = c("Exact", "Lure"),
  2278. labels = c("Exact", "Modified")
  2279. )
  2280. )
  2281. FigureR3_cols <- c(
  2282. "Exact" = "#66BB6A",
  2283. "Modified" = "#9575CD"
  2284. )
  2285. FigureR3_make_panel <- function(df) {
  2286. ggplot(
  2287. df,
  2288. aes(
  2289. x = stim_type,
  2290. y = estimate,
  2291. fill = stim_type,
  2292. alpha = order,
  2293. group = order
  2294. )
  2295. ) +
  2296. geom_col(
  2297. position = position_dodge(width = FigureR3_dodge),
  2298. width = 0.65
  2299. ) +
  2300. geom_errorbar(
  2301. aes(ymin = estimate - se, ymax = estimate + se),
  2302. position = position_dodge(width = FigureR3_dodge),
  2303. width = 0.15,
  2304. linewidth = 1.1
  2305. ) +
  2306. facet_grid(rows = vars(roi), cols = vars(hemisphere)) +
  2307. theme_classic(base_size = 13) +
  2308. labs(
  2309. x = "Condition during encoding",
  2310. y = "Mean % signal change",
  2311. alpha = "Order"
  2312. ) +
  2313. scale_fill_manual(values = FigureR3_cols) +
  2314. scale_alpha_manual(values = c("First" = 0.95, "Repeat" = 0.55)) +
  2315. scale_y_continuous(expand = expansion(mult = c(0.02, 0.12))) +
  2316. guides(
  2317. fill = "none",
  2318. alpha = guide_legend(title = "Order")
  2319. ) +
  2320. theme(
  2321. strip.background = element_blank(),
  2322. strip.text = element_text(face = "bold"),
  2323. panel.spacing = unit(0.6, "lines")
  2324. )
  2325. }
  2326. # Left panel (head): no legend
  2327. FigureR3_head_plot <- FigureR3_barsum %>%
  2328. filter(roi_type == "head") %>%
  2329. FigureR3_make_panel() +
  2330. ggtitle("Hippocampal head") +
  2331. theme(
  2332. plot.title = element_text(hjust = 0.5, face = "bold"),
  2333. legend.position = "none"
  2334. )
  2335. # Right panel (body): legend inset top-right
  2336. FigureR3_body_plot <- FigureR3_barsum %>%
  2337. filter(roi_type == "body") %>%
  2338. FigureR3_make_panel() +
  2339. ggtitle("Hippocampal body") +
  2340. theme(
  2341. plot.title = element_text(hjust = 0.5, face = "bold"),
  2342. legend.position = c(0.98, 0.98),
  2343. legend.justification = c(1, 1),
  2344. legend.background = element_rect(fill = alpha("white", 0.75), colour = NA),
  2345. legend.key = element_blank()
  2346. )
  2347. # Combine as two columns
  2348. FigureR3_plot <- (FigureR3_head_plot | FigureR3_body_plot)
  2349. FigureR3_plot
  2350. ```
  2351. ```{r}
  2352. text_base_export <- 24 # 20–28 usually looks best for this many facets
  2353. # IMPORTANT: do NOT set legend.position in the export theme
  2354. FigureR3_export_theme <- theme_classic(base_size = text_base_export) +
  2355. theme(
  2356. axis.text.x = element_text(size = text_base_export * 0.80, margin = margin(t = 6)),
  2357. axis.text.y = element_text(size = text_base_export * 0.80),
  2358. axis.title.x = element_text(size = text_base_export * 0.95, margin = margin(t = 10)),
  2359. axis.title.y = element_text(size = text_base_export * 0.95, margin = margin(r = 10)),
  2360. strip.text = element_text(size = text_base_export * 0.85, face = "bold"),
  2361. plot.title = element_text(size = text_base_export * 1.05, face = "bold", hjust = 0.5),
  2362. legend.title = element_text(size = text_base_export * 0.85),
  2363. legend.text = element_text(size = text_base_export * 0.80),
  2364. panel.spacing = unit(0.8, "lines")
  2365. )
  2366. # Apply export theme per panel + enforce legend behavior
  2367. FigureR3_head_export <- FigureR3_head_plot &
  2368. FigureR3_export_theme &
  2369. theme(legend.position = "none")
  2370. FigureR3_body_export <- FigureR3_body_plot &
  2371. FigureR3_export_theme &
  2372. theme(
  2373. legend.position = c(0.98, 0.98), # inside top-right of RIGHT panel
  2374. legend.justification = c(1, 1),
  2375. legend.background = element_rect(fill = alpha("white", 0.75), colour = NA),
  2376. legend.key = element_blank()
  2377. )
  2378. FigureR3_plot_export <- FigureR3_head_export | FigureR3_body_export
  2379. ggsave(
  2380. "derivatives/figures/SI_figures/Figure4SI.png",
  2381. plot = FigureR3_plot_export,
  2382. width = 23, height = 12, units = "in",
  2383. dpi = 300, bg = "white", device = ragg::agg_png
  2384. )
  2385. ```
  2386. ### Supplementary Results 4 - close vs distant REC
  2387. ```{r}
  2388. semmst_merged_data_filtered_H4b_REC_clodis = semmst_merged_data_filtered %>%
  2389. filter(task == "RecognitionRespCloDis") %>%
  2390. filter(cope_name %in% c("DistantCR", "CloseCR"))
  2391. # ASSUMPTION CHECKS
  2392. ## OUTLIER
  2393. semmst_merged_data_filtered_H4b_REC_clodis %>%
  2394. group_by(cope_name, roi_cluster) %>%
  2395. identify_outliers(estimate) %>%
  2396. dplyr::select(ID, roi_cluster, cope_name, is.outlier, is.extreme) %>%
  2397. filter(is.extreme==TRUE)
  2398. semmst_merged_data_filtered_H4b_REC_clodis_nooutlier = semmst_merged_data_filtered_H4b_REC_clodis %>%
  2399. ungroup()
  2400. semmst_merged_H4b_REC_clodis_PWC = semmst_merged_data_filtered_H4b_REC_clodis_nooutlier %>%
  2401. group_by(roi_cluster) %>%
  2402. pairwise_t_test(
  2403. estimate ~ cope_name,
  2404. paired = TRUE,
  2405. detailed = TRUE,
  2406. ) %>%
  2407. ungroup() %>%
  2408. adjust_pvalue(method = "bonferroni") %>%
  2409. add_significance() %>%
  2410. ungroup() %>%
  2411. mutate(label = paste0("p=", signif(p.adj, 3)))
  2412. semmst_merged_H4b_REC_clodis_PWC
  2413. # REP roi_cluster estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj
  2414. ## 1 L_HEAD_DGCA23_HEAD_SUB_14 -0.110 estimate CloseCR Distan… 30 30 -1.55 0.133 29 -0.255 0.0355 T-test two.sided 0.266 ns
  2415. ## 2 R_HEAD_DGCA23_HEAD_SUB_15 -0.0498 estimate CloseCR Distan… 30 30 -1.07 0.294 29 -0.145 0.0455 T-test two.sided 0.588 ns
  2416. ```
  2417. #### Supplementary Figure 5.
  2418. ```{r}
  2419. # BARPLOT
  2420. figSI_clodisREC_dodge <- 0.8
  2421. figSI_clodisREC_barsum <- Rmisc::summarySEwithin(
  2422. data = semmst_merged_data_filtered_H4b_REC_clodis,
  2423. measurevar = "estimate",
  2424. withinvars = c("cope_name", "roi_cluster"),
  2425. idvar = "ID",
  2426. na.rm = TRUE
  2427. )
  2428. figSI_clodisREC_roi_lab <- semmst_merged_data_filtered_H4_REC_conditions_nooutlier %>%
  2429. distinct(roi_cluster, hemisphere, roi, cluster, voxels) %>%
  2430. mutate(
  2431. roi_label = case_when(
  2432. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "R" ~ "Right hippocampal head cluster",
  2433. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "L" ~ "Left hippocampal head cluser",
  2434. TRUE ~ NA_character_
  2435. )
  2436. ) %>%
  2437. select(roi_cluster, roi_label)
  2438. figSI_clodisREC_level_map <- c("CloseCR", "DistantCR")
  2439. figSI_clodisREC_level_lab <- c("Close lure", "Distant lure")
  2440. figSI_clodisREC_barsum <- figSI_clodisREC_barsum %>%
  2441. mutate(cope_name = factor(cope_name, levels = figSI_clodisREC_level_map, labels = figSI_clodisREC_level_lab)) %>%
  2442. left_join(figSI_clodisREC_roi_lab, by = "roi_cluster")
  2443. figSI_clodisREC_plot <- ggplot(figSI_clodisREC_barsum, aes(x = cope_name, y = estimate, fill = cope_name)) +
  2444. geom_col(position = position_dodge(width = figSI_clodisREC_dodge), width = 0.65) +
  2445. geom_errorbar(
  2446. aes(ymin = estimate - se, ymax = estimate + se),
  2447. position = position_dodge(width = figSI_clodisREC_dodge),
  2448. width = 0.15,
  2449. size = 1.2
  2450. ) +
  2451. facet_wrap(~ roi_label) +
  2452. theme_classic(base_size = 13) +
  2453. labs(x = "Condition during recognition", y = "Mean % signal change") +
  2454. scale_fill_manual(values = c(
  2455. "Close lure" = "#4477AA",
  2456. "Distant lure" = "#DDCC77"
  2457. )) +
  2458. scale_y_continuous(expand = expansion(mult = c(0.02, 0.12))) +
  2459. custom_theme +
  2460. guides(fill = "none")
  2461. figSI_clodisREC_plot
  2462. ```
  2463. ```{r}
  2464. text_base_export=40
  2465. figSI_clodisREC_plot_export <- figSI_clodisREC_plot +
  2466. theme_classic(base_size = text_base_export) +
  2467. theme(
  2468. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  2469. axis.text.y = element_text(size = text_base_export * 0.90),
  2470. axis.title = element_text(size = text_base_export * 1.05),
  2471. strip.text = element_text(size = text_base_export * 0.95)
  2472. )
  2473. ggsave(
  2474. "derivatives/figures/SI_figures/Figure5SI.png",
  2475. plot = figSI_clodisREC_plot_export,
  2476. width = 22, height = 14, units = "in",
  2477. dpi = 300, bg = "white", device = "png"
  2478. )
  2479. ```
  2480. ### Supplementary Results 5 - Behaviour wo. covariates and outlier
  2481. ```{r}
  2482. semmst_behav_data_rec_LDI_ANOVA = semmst_behav_data_rec_summary %>%
  2483. dplyr::select(ID, rec_dprime, close_dprime, distant_dprime) %>%
  2484. gather("dprime_type", "dprime_value", rec_dprime:distant_dprime) %>%
  2485. mutate(dprime_type = factor(dprime_type, levels = c("close_dprime", "rec_dprime", "distant_dprime"))) %>%
  2486. left_join(semmst_behav_data_rec_filtered %>% dplyr::select(ID, age, sex, education, Vocabulary, Digit_Symbol, NonWord_sum) %>% distinct(), by="ID")
  2487. semmst_behav_data_rec_LDI_ANCOVA = semmst_behav_data_rec_LDI_ANOVA %>%
  2488. left_join(semmst_behav_data_rec_filtered %>% dplyr::select(ID, age, sex, education, Vocabulary, Digit_Symbol, NonWord_sum) %>% distinct(), by="ID") %>%
  2489. mutate(Vocabulary_scale = scale(Vocabulary)) %>%
  2490. mutate(Digit_Symbol_scale = scale(Digit_Symbol)) %>%
  2491. mutate(NonWord_sum_scale = scale(NonWord_sum)) %>% drop_na()
  2492. # ASSUMPTION CHECKS
  2493. ## OUTLIER
  2494. semmst_behav_data_rec_LDI_ANOVA %>%
  2495. group_by(dprime_type) %>%
  2496. identify_outliers(dprime_value)
  2497. ##NORMALITY
  2498. semmst_behav_data_rec_LDI_ANOVA %>%
  2499. group_by(dprime_type) %>%
  2500. shapiro_test(dprime_value) %>% print(n=40)
  2501. ggqqplot(semmst_behav_data_rec_LDI_ANOVA, "dprime_value", ggtheme = theme_bw()) +
  2502. facet_grid(. ~ dprime_type, labeller = "label_both")
  2503. # ANOVA
  2504. ANOVA_behav_data_rec_LDI = semmst_behav_data_rec_LDI_ANOVA %>%
  2505. anova_test(dv = dprime_value, wid = ID, within = c(dprime_type), covariate = c(sex, age))
  2506. get_anova_table(ANOVA_behav_data_rec_LDI)
  2507. # REPORT
  2508. ## dprime_type 2 54 159.192 2.28e-23 * 0.493
  2509. ```
  2510. #### Supplementary Figure 6.
  2511. ```{r}
  2512. # 1) Prep & adjustment (remove linear effects of covariates)
  2513. figSI_outlier_plot_base <- semmst_behav_data_rec_LDI_ANOVA %>%
  2514. filter(!is.na(ID), !is.na(dprime_type), !is.na(dprime_value)) %>%
  2515. mutate(
  2516. dprime_type = factor(dprime_type),
  2517. sex = factor(sex)
  2518. )
  2519. # order as: Close, Distant, Target
  2520. figSI_outlier_contrast_order <- c("close_dprime", "distant_dprime", "rec_dprime")
  2521. # order as: Close, Distant, Target
  2522. figSI_outlier_contrast_order <- c("close_dprime", "distant_dprime", "rec_dprime")
  2523. figSI_outlier_plot_base <- figSI_outlier_plot_base %>%
  2524. mutate(dprime_type = fct_relevel(dprime_type, figSI_outlier_contrast_order))
  2525. # covariate-only model for adjustment
  2526. figSI_outlier_mod_cov <- lm(dprime_value ~ sex + age,
  2527. data = figSI_outlier_plot_base)
  2528. figSI_outlier_plot_base <- figSI_outlier_plot_base %>%
  2529. mutate(dprime_adj = dprime_value - predict(figSI_outlier_mod_cov, newdata = figSI_outlier_plot_base) + mean(dprime_value, na.rm = TRUE))
  2530. # 2) Labels & colors
  2531. figSI_outlier_x_lab_map <- c(
  2532. "close_dprime" = "Close\nlure",
  2533. "distant_dprime" = "Distant\nlure",
  2534. "rec_dprime" = "Foil"
  2535. )
  2536. # Paul Tol (muted): blue / yellow / red — CVD-safe, good in print & grayscale
  2537. figSI_outlier_fill_vals <- c("close_dprime"="#4477AA", "distant_dprime"="#DDCC77", "rec_dprime"="#117733")
  2538. # 3) Plot
  2539. behav_figSI_outlier <- ggplot(figSI_outlier_plot_base, aes(x = dprime_type, y = dprime_adj, fill = dprime_type)) +
  2540. geom_boxplot(width = 0.6, outlier.alpha = 0.4, linewidth = 1.8, color = "black") +
  2541. stat_summary(fun = mean, geom = "point", shape = 21, size = 2.2, fill = "white", color = "black") +
  2542. scale_x_discrete(labels = figSI_outlier_x_lab_map, drop = FALSE) +
  2543. scale_fill_manual(values = figSI_outlier_fill_vals, guide = "none") +
  2544. labs(
  2545. x = "Discriminability contrasts",
  2546. y = "Covariate-adjusted d'\n [p(\"old\"|target) - p(\"old\"|condition)]"
  2547. ) +
  2548. theme_classic(base_size = 14)
  2549. behav_figSI_outlier
  2550. ```
  2551. ```{r}
  2552. behav_figSI_outlier_export <- behav_figSI_outlier +
  2553. theme_classic(base_size = 40) +
  2554. theme(
  2555. axis.title = element_text(size = 42),
  2556. axis.text = element_text(size = 34)
  2557. ) + labs(x = NULL)
  2558. ggsave(
  2559. "derivatives/figures/SI_figures/Figure6SI.png",
  2560. plot = behav_figSI_outlier_export,
  2561. width = 16, height = 12, units = "in",
  2562. dpi = 300,
  2563. bg = "white",
  2564. device = "png" # base png
  2565. )
  2566. ```
  2567. ### Supplementary Results 6 - Encoding wo. covariates and outlier
  2568. #### ANOVA
  2569. ```{r}
  2570. # FILTER DATA
  2571. semmst_merged_data_H1 = semmst_merged_data_filtered %>%
  2572. filter(task == "Encoding") %>%
  2573. filter(cope_name %in% c("LureFirst", "ExactFirst", "LureRepeat", "ExactRepeat")) %>%
  2574. extract(
  2575. col = cope_name,
  2576. into = c("stim_type", "order"),
  2577. regex = "([A-Za-z]+?)(First|Repeat)$"
  2578. ) %>%
  2579. mutate(
  2580. stim_type = as.factor(stim_type),
  2581. order = as.factor(order)
  2582. ) %>%
  2583. mutate(stim_type = factor(stim_type, levels = c("Exact", "Lure")))
  2584. # ASSUMPTION CHECKS
  2585. ## OUTLIER
  2586. semmst_merged_data_H1 %>%
  2587. group_by(stim_type, order, roi_cluster) %>%
  2588. identify_outliers(estimate) %>%
  2589. dplyr::select(ID, is.outlier, is.extreme) %>%
  2590. filter(is.extreme==TRUE)
  2591. semmst_merged_data_H1SI_nooutlier = semmst_merged_data_H1 %>%
  2592. group_by(stim_type, order, roi_cluster) %>%
  2593. #filter(! ID %in% c("sub-434971")) %>% #, " sub-012421", "sub-632012", "sub-800472")) %>% # c("sub-434971", "sub-982347")) %>%
  2594. ungroup()
  2595. ##NORMALITY
  2596. semmst_merged_data_H1SI_nooutlier %>%
  2597. group_by(stim_type, order) %>%
  2598. shapiro_test(estimate)
  2599. # ANOVA
  2600. semmst_merged_data_H1SI_nooutlier_AOV = semmst_merged_data_H1SI_nooutlier %>%
  2601. anova_test(dv = estimate, wid = ID, within = c(stim_type, order, roi_cluster))#, covariate = c(NonWord_sum_scale, Digit_Symbol_scale))
  2602. get_anova_table(semmst_merged_data_H1SI_nooutlier_AOV)
  2603. # REPORT Effect DFn DFd F p p<.05 ges
  2604. ## 1 stim_type 1 29 16.545 3.33e-04 * 3.30e-02
  2605. ## 2 order 1 29 54.775 3.73e-08 * 1.07e-01
  2606. ## 4 stim_type:order 1 29 4.123 5.20e-02 1.60e-02
  2607. ```
  2608. #### Correlation
  2609. ```{r}
  2610. group_vars_H3SI <- c("ID", "mask_type", "space_type", "contrast", "roi_cluster")
  2611. # baseline
  2612. semmst_merged_data_H3SI_neural_ps = semmst_merged_data_filtered %>%
  2613. filter(task %in% c("Encoding")) %>%
  2614. filter(cope_name %in% c("ExactFirst", "ExactRepeat", "LureFirst","LureRepeat", "CloseFirst", "CloseRepeat", "DistantFirst", "DistantRepeat")) %>%
  2615. dplyr::select(ID, space_type, cope_name, hemisphere, roi, mask_type, cluster, voxels, contrast, task, roi_cluster, roi_hemisphere, estimate, NonWord_sum_scale, Digit_Symbol_scale, rec_dprime:lure_incorrect) %>%
  2616. group_by(across(all_of(group_vars_H3SI))) %>%
  2617. pivot_wider(names_from = cope_name, values_from = estimate, values_fill = NA_real_) %>%
  2618. mutate(
  2619. # --- Non-RS bias scores (Exact denominator) ---
  2620. LureBiasScore = LureRepeat - ExactRepeat,
  2621. CloseBiasScore = CloseRepeat - ExactRepeat,
  2622. DistantBiasScore = DistantRepeat - ExactRepeat
  2623. ) %>%
  2624. ungroup()
  2625. # 1) Filter to the Exact subset and define roi_group
  2626. df_corr_input_all_SI <- semmst_merged_data_H3SI_neural_ps %>%
  2627. mutate(roi_group = roi_cluster) %>%
  2628. dplyr::select(ID, roi_group, roi, hemisphere, cluster, voxels, LureBiasScore, lure_dprime, NonWord_sum_scale, Digit_Symbol_scale) %>%
  2629. ungroup()
  2630. df_corr_input_all_SI_nooutlier <- df_corr_input_all_SI %>%
  2631. droplevels() %>%
  2632. ungroup()
  2633. # Fisher-z CI for Pearson r (95% hard-coded)
  2634. pearson_ci <- function(r, n) {
  2635. z <- atanh(r)
  2636. se <- 1 / sqrt(n - 3)
  2637. zcrit <- qnorm(1 - (1 - 0.95) / 2)
  2638. c(low = tanh(z - zcrit * se), high = tanh(z + zcrit * se))
  2639. }
  2640. H3SI_corr_lure_simple <- df_corr_input_all_SI_nooutlier %>%
  2641. group_by(roi_group) %>%
  2642. group_modify(~{
  2643. d_all <- .x
  2644. # Pearson
  2645. d_s <- d_all %>% dplyr::select(LureBiasScore, lure_dprime) %>% tidyr::drop_na()
  2646. ct_s <- cor.test(d_s$LureBiasScore, d_s$lure_dprime, method = "pearson")
  2647. r_s <- unname(ct_s$estimate)
  2648. p_s <- ct_s$p.value
  2649. n_s <- nrow(d_s)
  2650. ci_s <- pearson_ci(r_s, n_s)
  2651. tibble::tibble(
  2652. n = n_s,
  2653. r = r_s,
  2654. p = p_s,
  2655. ci = sprintf("[%.3f, %.3f]", ci_s["low"], ci_s["high"])
  2656. )
  2657. }) %>%
  2658. ungroup() %>%
  2659. mutate(
  2660. p_adj = p.adjust(p, method = "bonferroni")
  2661. ) %>%
  2662. arrange(p_adj, p)
  2663. H3SI_corr_lure_simple
  2664. ```
  2665. #### Supplementary Figure 7B
  2666. ```{r}
  2667. fig_corroutlier_BSI_dodge <- 0.8
  2668. fig_corroutlier_BSI_barsum <- Rmisc::summarySEwithin(
  2669. data = semmst_merged_data_H1SI_nooutlier,
  2670. measurevar = "estimate",
  2671. withinvars = c("stim_type","order","roi_cluster"),
  2672. idvar = "ID",
  2673. na.rm = TRUE
  2674. ) %>%
  2675. mutate(stim_type = factor(as.character(stim_type),
  2676. levels = c("Exact","Lure"),
  2677. labels = c("Exact","Modified")))
  2678. fig_corroutlier_BSI_roi_lab <- semmst_merged_data_H1SI_nooutlier %>%
  2679. distinct(roi_cluster, hemisphere, roi, cluster, voxels) %>%
  2680. mutate(
  2681. roi_label = case_when(
  2682. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "R" ~ "Right DG-CA2/3-SUB cluster",
  2683. roi == "HEAD_DGCA23_HEAD_SUB" & hemisphere == "L" ~ "Left DG-CA2/3-SUB cluster",
  2684. TRUE ~ as.character(roi_cluster)
  2685. )
  2686. ) %>%
  2687. select(roi_cluster, roi_label)
  2688. fig_corroutlier_BSI_barsum <- fig_corroutlier_BSI_barsum %>%
  2689. left_join(fig_corroutlier_BSI_roi_lab, by = "roi_cluster")
  2690. # bar plot
  2691. fig_corroutlier_BSI <- ggplot(fig_corroutlier_BSI_barsum, aes(
  2692. x = stim_type, y = estimate,
  2693. fill = stim_type,
  2694. group = order
  2695. )) +
  2696. geom_col(aes(alpha = order),
  2697. position = position_dodge(width = fig_corroutlier_BSI_dodge),
  2698. width = 0.65) +
  2699. geom_errorbar(
  2700. aes(ymin = estimate - se, ymax = estimate + se),
  2701. position = position_dodge(width = fig_corroutlier_BSI_dodge),
  2702. width = 0.15,
  2703. size = 1.2
  2704. ) +
  2705. facet_wrap(~ roi_label) +
  2706. theme_classic(base_size = 13) +
  2707. labs(x = "Condition during encoding", y = "Mean % signal change", fill = "Stimulus", alpha = "Order") +
  2708. scale_fill_manual(values = c(
  2709. "Exact" = "#66BB6A",
  2710. "Modified" = "#9575CD"
  2711. )) +
  2712. scale_alpha_manual(values = c(
  2713. "First" = 0.95,
  2714. "Repeat" = 0.55
  2715. )) +
  2716. scale_y_continuous(expand = expansion(mult = c(0.02, 0.12))) +
  2717. custom_theme +
  2718. guides(fill = "none")
  2719. fig_corroutlier_BSI
  2720. ```
  2721. ```{r}
  2722. text_base_export <- 40
  2723. fig_corroutlier_BSI_export <- fig_corroutlier_BSI +
  2724. theme_classic(base_size = text_base_export) +
  2725. theme(
  2726. legend.position = c(0.88, 0.88),
  2727. legend.title = element_text(size = text_base_export * 0.88),
  2728. legend.text = element_text(size = text_base_export * 0.90),
  2729. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  2730. axis.text.y = element_text(size = text_base_export * 0.90),
  2731. axis.title = element_text(size = text_base_export * 1.05),
  2732. strip.text = element_blank(),
  2733. panel.spacing.x = unit(4, "in")
  2734. )
  2735. ggsave(
  2736. "derivatives/figures/SI_figures/Figure7BSI.png",
  2737. plot = fig_corroutlier_BSI_export,
  2738. width = 26, height = 13, units = "in",
  2739. dpi = 300, bg = "white", device = "png"
  2740. )
  2741. ```
  2742. #### Supplementary Figure 7A
  2743. ```{r}
  2744. ID_HI <- "sub-434971"
  2745. semmst_merged_data_H1SI_nooutlier_data <- semmst_merged_data_H1SI_nooutlier %>%
  2746. filter(!is.na(estimate)) %>%
  2747. mutate(
  2748. highlight = (ID == ID_HI),
  2749. # Column labels you asked for
  2750. stim_col = case_when(
  2751. stim_type == "Exact" ~ "Exact repeat",
  2752. TRUE ~ "Modified repeat" # e.g., Lure -> Modified
  2753. ),
  2754. # Row labels you asked for
  2755. roi_row = case_when(
  2756. str_detect(roi_cluster, "^L$|L") ~ "Left DG-CA2/3-Subiculum",
  2757. str_detect(roi_cluster, "^R$|R") ~ "Right DG-CA2/3-Subiculum",
  2758. TRUE ~ as.character(roi_cluster)
  2759. ),
  2760. order_row = case_when(
  2761. order == "First" ~ "First presentation",
  2762. order == "Repeat" ~ "Repeat",
  2763. TRUE ~ as.character(order)
  2764. )
  2765. ) %>%
  2766. mutate(
  2767. stim_col = factor(stim_col, levels = c("Exact repeat", "Modified repeat")),
  2768. roi_row = factor(roi_row, levels = c("Left DG-CA2/3-Subiculum", "Right DG-CA2/3-Subiculum")),
  2769. order_row = factor(order_row, levels = c("First presentation", "Repeat"))
  2770. )
  2771. # compute QQ points per facet (roi_row × order_row × stim_col) ---
  2772. semmst_merged_data_H1SI_nooutlier_qq <- semmst_merged_data_H1SI_nooutlier_data %>%
  2773. group_by(roi_row, order_row, stim_col) %>%
  2774. arrange(estimate, .by_group = TRUE) %>%
  2775. mutate(theoretical = qnorm(ppoints(n()))) %>%
  2776. ungroup()
  2777. # compute qqline (like R's qqline: through 25% and 75%) per facet ---
  2778. semmst_merged_data_H1SI_nooutlier_lines <- semmst_merged_data_H1SI_nooutlier_qq %>%
  2779. group_by(roi_row, order_row, stim_col) %>%
  2780. summarise(
  2781. y25 = quantile(estimate, 0.25, na.rm = TRUE),
  2782. y75 = quantile(estimate, 0.75, na.rm = TRUE),
  2783. x25 = qnorm(0.25),
  2784. x75 = qnorm(0.75),
  2785. slope = (y75 - y25) / (x75 - x25),
  2786. intercept = y25 - slope * x25,
  2787. .groups = "drop"
  2788. )
  2789. # plot (thin line, correct highlight, correct labels) ---
  2790. fig_corroutlier_SIA_figure = ggplot(semmst_merged_data_H1SI_nooutlier_qq, aes(x = theoretical, y = estimate)) +
  2791. geom_point(size = 2.4, alpha = 0.75, colour = "black") +
  2792. geom_point(
  2793. data = dplyr::filter(semmst_merged_data_H1SI_nooutlier_qq, highlight),
  2794. size = 2.6, alpha = 0.95, colour = "red"
  2795. ) +
  2796. geom_abline(
  2797. data = semmst_merged_data_H1SI_nooutlier_lines,
  2798. aes(slope = slope, intercept = intercept),
  2799. linewidth = 1.2,
  2800. colour = "black"
  2801. ) +
  2802. facet_grid(roi_row + order_row ~ stim_col, labeller = label_value) +
  2803. labs(
  2804. x = "Theoretical quantiles",
  2805. y = "Sample quantiles (% signal change)"
  2806. ) +
  2807. theme_bw()
  2808. fig_corroutlier_SIA_figure
  2809. ```
  2810. ```{r}
  2811. text_base_export <- 34 # tweak (28–40); 34 usually good for dense facet grids
  2812. fig_corroutlier_SIA_export <- fig_corroutlier_SIA_figure +
  2813. # bump dot sizes (both black + highlighted red)
  2814. geom_point(size = 7.1, alpha = 0.75, colour = "black") +
  2815. geom_point(
  2816. data = dplyr::filter(semmst_merged_data_H1SI_nooutlier_qq, highlight),
  2817. size = 10.3, alpha = 0.95, colour = "red"
  2818. ) +
  2819. # thicker qqline
  2820. geom_abline(
  2821. data = semmst_merged_data_H1SI_nooutlier_lines,
  2822. aes(slope = slope, intercept = intercept),
  2823. linewidth = 1.6,
  2824. colour = "black"
  2825. ) +
  2826. theme_bw(base_size = text_base_export) +
  2827. theme(
  2828. axis.text.x = element_text(size = text_base_export * 0.85, margin = margin(t = 6)),
  2829. axis.text.y = element_text(size = text_base_export * 0.85),
  2830. axis.title.x = element_text(size = text_base_export * 0.95, margin = margin(t = 10)),
  2831. axis.title.y = element_text(size = text_base_export * 0.95, margin = margin(r = 10)),
  2832. strip.text = element_text(size = text_base_export * 0.80, face = "bold"),
  2833. panel.spacing = unit(0.65, "lines"),
  2834. plot.margin = margin(12, 12, 12, 12)
  2835. )
  2836. # install.packages("ragg")
  2837. ggsave(
  2838. "derivatives/figures/SI_figures/Figure7ASI.png",
  2839. plot = fig_corroutlier_SIA_export,
  2840. width = 16, height = 26, units = "in",
  2841. dpi = 300, bg = "white", device = ragg::agg_png
  2842. )
  2843. ```
  2844. #### Supplementary Figure 7C
  2845. ```{r}
  2846. ID_HI <- "sub-434971"
  2847. # 1) Long table (ALL trials only)
  2848. fig_corroutlierCSI_df <- df_corr_input_all_SI_nooutlier %>%
  2849. dplyr::select(ID, roi_group, roi, hemisphere, bias = LureBiasScore, lure_dprime) %>%
  2850. tidyr::drop_na(bias, lure_dprime) %>%
  2851. dplyr::mutate(
  2852. hemi_lab = dplyr::recode(hemisphere, L = "Left", R = "Right"),
  2853. roi_clean = roi %>%
  2854. gsub("HEAD_", "", .) %>%
  2855. gsub("_", "-", .) %>%
  2856. gsub("2", "", .),
  2857. facet_lab = paste(hemi_lab, roi_clean)
  2858. )
  2859. # 2) Facet labels (no stats)
  2860. fig_corroutlierCSI_labs <- fig_corroutlierCSI_df %>%
  2861. dplyr::distinct(roi_group, facet_lab)
  2862. # 3) Plot (base)
  2863. fig_corroutlierCSI <- ggplot(
  2864. fig_corroutlierCSI_df %>% mutate(highlight = (ID == ID_HI)),
  2865. aes(bias, lure_dprime)
  2866. ) +
  2867. # all points
  2868. geom_point(
  2869. data = \(d) d %>% filter(!highlight),
  2870. alpha = 0.7, size = 4.5, colour = "grey20"
  2871. ) +
  2872. # highlighted point on top
  2873. geom_point(
  2874. data = \(d) d %>% filter(highlight),
  2875. alpha = 1, size = 6.2, colour = "red"
  2876. ) +
  2877. # regression line (single neutral colour)
  2878. geom_smooth(method = "lm", se = FALSE, linewidth = 1.1, colour = "grey20") +
  2879. facet_wrap(
  2880. ~ roi_group, scales = "free",
  2881. labeller = labeller(roi_group = setNames(fig_corroutlierCSI_labs$facet_lab, fig_corroutlierCSI_labs$roi_group))
  2882. ) +
  2883. labs(
  2884. x = "Modified repeat - exact repeat estimates",
  2885. y = "d' (lures vs targets)"
  2886. ) +
  2887. theme_classic(base_size = 18) +
  2888. custom_theme +
  2889. theme(
  2890. panel.spacing = unit(4, "in") # big gap like your 6A; reduce if too much
  2891. )
  2892. fig_corroutlierCSI
  2893. ```
  2894. ```{r}
  2895. text_base_export <- 40
  2896. fig_corroutlierCSI_export <- fig_corroutlierCSI +
  2897. theme_classic(base_size = text_base_export) +
  2898. theme(
  2899. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  2900. axis.text.y = element_text(size = text_base_export * 0.90),
  2901. axis.title = element_text(size = text_base_export * 1.05),
  2902. panel.spacing = unit(4, "in"),
  2903. strip.text = element_blank()
  2904. ) +
  2905. # make the line thicker for export (optional)
  2906. geom_smooth(method = "lm", se = FALSE, linewidth = 2.6, colour = "grey20")
  2907. ggsave(
  2908. "derivatives/figures/SI_figures/Figure7CSI.png",
  2909. plot = fig_corroutlierCSI_export,
  2910. width = 26, height = 12, units = "in",
  2911. dpi = 300, bg = "white", device = "png"
  2912. )
  2913. ```
  2914. ### Supplementary Results 7 - Recognition per encoding status
  2915. ```{r}
  2916. # FoilCR baseline per ID x roi_cluster
  2917. foil_baseline_df <- semmst_merged_data_filtered %>%
  2918. filter(task == "RecognitionRespAll",
  2919. cope_name == "FoilCR") %>%
  2920. select(ID, roi_cluster, foil_baseline = estimate)
  2921. # Target conditions + baseline correction
  2922. semmst_merged_data_filtered_supp4_REC_conditions_bc <- semmst_merged_data_supplement_filtered %>%
  2923. filter(task %in% c("RecognitionRespAllEncRepeat", "RecognitionRespAllEncSimilar"),
  2924. cope_name %in% c("LureCR", "TargetHIT")) %>%
  2925. left_join(foil_baseline_df, by = c("ID", "roi_cluster")) %>%
  2926. mutate(estimate_bc = estimate - foil_baseline)
  2927. # ASSUMPTION CHECKS
  2928. ## OUTLIER
  2929. semmst_merged_data_filtered_supp4_REC_conditions_bc %>%
  2930. group_by(cope_name, task, roi_cluster) %>%
  2931. identify_outliers(estimate) %>%
  2932. dplyr::select(ID, roi_cluster, cope_name, is.outlier, is.extreme) %>%
  2933. filter(is.extreme==TRUE)
  2934. semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier = semmst_merged_data_filtered_supp4_REC_conditions_bc %>%
  2935. #filter(!ID %in% c("sub-526930", "sub-468051")) %>%
  2936. ungroup()
  2937. semmst_merged_supp4_REC_conditions_bc_AOV = semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier %>%
  2938. droplevels() %>%
  2939. anova_test(dv = estimate, wid = ID, within = c(cope_name, task, roi_cluster))#, covariate = c(NonWord_sum_scale))
  2940. get_anova_table(semmst_merged_supp4_REC_conditions_bc_AOV)
  2941. # Effect DFn DFd F p p<.05 ges
  2942. ## 1 cope_name 1 29 2.597 0.118 8.00e-03
  2943. ## 2 task 1 29 0.236 0.631 5.39e-04
  2944. ## 4 cope_name:task 1 29 4.762 0.037 * 2.50e-02
  2945. semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier %>%
  2946. ggplot(aes(x=cope_name, fill=task, y=estimate)) +
  2947. geom_boxplot(position = position_dodge2()) +
  2948. facet_wrap(~roi_cluster)
  2949. # POST-HOC
  2950. # pairwise comparisons - stim_type * order
  2951. semmst_merged_supp4_REC_conditions_bc_PWC = semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier %>%
  2952. group_by(task) %>%
  2953. pairwise_t_test(
  2954. estimate ~ cope_name,
  2955. paired = TRUE,
  2956. detailed = TRUE,
  2957. ) %>%
  2958. ungroup() %>%
  2959. adjust_pvalue(method = "bonferroni") %>%
  2960. add_significance() %>%
  2961. ungroup() %>%
  2962. mutate(label = paste0("p=", signif(p.adj, 3)))
  2963. semmst_merged_supp4_REC_conditions_bc_PWC
  2964. ## roi_cluster estimate .y. group1 group2 n1 n2 statistic p df conf.low conf.high method alternative p.adj
  2965. ## 1 L_HEAD_DGCA23_HEAD_SUB_14 0.138 estimate FoilCR LureCR 30 30 3.43 0.002 29 0.0558 0.221 T-test two.sided 0.012 *
  2966. ## 4 R_HEAD_DGCA23_HEAD_SUB_15 0.103 estimate FoilCR LureCR 30 30 3.05 0.005 29 0.0341 0.173 T-test two.sided 0.03 *
  2967. # pairwise comparisons - stim_type * order
  2968. semmst_merged_supp4_REC_conditions_bc_PWC = semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier %>%
  2969. group_by(cope_name) %>%
  2970. mutate(task = forcats::fct_rev(task)) %>%
  2971. pairwise_t_test(
  2972. estimate ~ task,
  2973. paired = TRUE,
  2974. detailed = TRUE,
  2975. ) %>%
  2976. ungroup() %>%
  2977. adjust_pvalue(method = "bonferroni") %>%
  2978. add_significance() %>%
  2979. ungroup() %>%
  2980. mutate(label = paste0("p=", signif(p.adj, 3)))
  2981. semmst_merged_supp4_REC_conditions_bc_PWC
  2982. ```
  2983. #### Revision Figure 4 - REC status
  2984. ```{r}
  2985. #### Revision Figure 4 - REC conditions, baseline-corrected, collapsed across ROI ----
  2986. figR4_dodge <- 0.8
  2987. Figure4_revision_rec_bc_nofacet_level_map_task <- c(
  2988. "RecognitionRespAllEncRepeat",
  2989. "RecognitionRespAllEncSimilar"
  2990. )
  2991. Figure4_revision_rec_bc_nofacet_level_lab_task <- c(
  2992. "Exact repeat",
  2993. "Modified repeat"
  2994. )
  2995. Figure4_revision_rec_bc_nofacet_level_map_cope <- c(
  2996. "TargetHIT",
  2997. "LureCR"
  2998. )
  2999. Figure4_revision_rec_bc_nofacet_level_lab_cope <- c(
  3000. "Target hit",
  3001. "Lure correct rejection"
  3002. )
  3003. # 1) Base plotting data
  3004. Figure4_revision_rec_bc_nofacet_base <- semmst_merged_data_filtered_supp4_REC_conditions_bc_nooutlier %>%
  3005. filter(
  3006. task %in% Figure4_revision_rec_bc_nofacet_level_map_task,
  3007. cope_name %in% Figure4_revision_rec_bc_nofacet_level_map_cope,
  3008. roi_cluster %in% c("L_HEAD_DGCA23_HEAD_SUB_14", "R_HEAD_DGCA23_HEAD_SUB_15")
  3009. ) %>%
  3010. mutate(
  3011. task = factor(
  3012. task,
  3013. levels = Figure4_revision_rec_bc_nofacet_level_map_task,
  3014. labels = Figure4_revision_rec_bc_nofacet_level_lab_task
  3015. ),
  3016. cope_name = factor(
  3017. cope_name,
  3018. levels = Figure4_revision_rec_bc_nofacet_level_map_cope,
  3019. labels = Figure4_revision_rec_bc_nofacet_level_lab_cope
  3020. )
  3021. )
  3022. # 2) Collapse across ROI within participant
  3023. Figure4_revision_rec_bc_nofacet_idmean <- Figure4_revision_rec_bc_nofacet_base %>%
  3024. group_by(ID, task, cope_name) %>%
  3025. summarise(
  3026. estimate = mean(estimate, na.rm = TRUE),
  3027. .groups = "drop"
  3028. )
  3029. # 3) Within-subject summary for bar plot + SE
  3030. Figure4_revision_rec_bc_nofacet_barsum <- Rmisc::summarySEwithin(
  3031. data = Figure4_revision_rec_bc_nofacet_idmean,
  3032. measurevar = "estimate",
  3033. withinvars = c("task", "cope_name"),
  3034. idvar = "ID",
  3035. na.rm = TRUE
  3036. )
  3037. # 4A) Stats: Target vs Lure within each encoding condition
  3038. Figure4_revision_rec_bc_nofacet_stats_recwithinenc <- Figure4_revision_rec_bc_nofacet_idmean %>%
  3039. group_by(task) %>%
  3040. pairwise_t_test(
  3041. estimate ~ cope_name,
  3042. paired = TRUE,
  3043. p.adjust.method = "bonferroni"
  3044. ) %>%
  3045. ungroup() %>%
  3046. mutate(
  3047. label = case_when(
  3048. p.adj < 0.001 ~ "p < .001",
  3049. p.adj < 0.01 ~ "p < .01",
  3050. p.adj < 0.05 ~ "p < .05",
  3051. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p.adj)))
  3052. ),
  3053. x_num = case_when(
  3054. task == "Exact repeat" ~ 1,
  3055. task == "Modified repeat" ~ 2
  3056. )
  3057. )
  3058. Figure4_revision_rec_bc_nofacet_ypos_task <- Figure4_revision_rec_bc_nofacet_barsum %>%
  3059. group_by(task) %>%
  3060. summarise(
  3061. y_base = max(estimate + se, na.rm = TRUE),
  3062. .groups = "drop"
  3063. )
  3064. Figure4_revision_rec_bc_nofacet_stats_recwithinenc <- Figure4_revision_rec_bc_nofacet_stats_recwithinenc %>%
  3065. left_join(Figure4_revision_rec_bc_nofacet_ypos_task, by = "task") %>%
  3066. mutate(
  3067. xmin = x_num - 0.20,
  3068. xmax = x_num + 0.20,
  3069. y.position = y_base + 0.030
  3070. )
  3071. # 4B) Reverse stats: Repeat vs Similar within each recognition condition
  3072. Figure4_revision_rec_bc_nofacet_stats_encwithinrec <- Figure4_revision_rec_bc_nofacet_idmean %>%
  3073. group_by(cope_name) %>%
  3074. pairwise_t_test(
  3075. estimate ~ task,
  3076. paired = TRUE,
  3077. p.adjust.method = "bonferroni"
  3078. ) %>%
  3079. ungroup() %>%
  3080. mutate(
  3081. label = case_when(
  3082. p.adj < 0.001 ~ "p < .001",
  3083. p.adj < 0.01 ~ "p < .01",
  3084. p.adj < 0.05 ~ "p < .05",
  3085. TRUE ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p.adj)))
  3086. )
  3087. )
  3088. Figure4_revision_rec_bc_nofacet_ypos_cope <- Figure4_revision_rec_bc_nofacet_barsum %>%
  3089. group_by(cope_name) %>%
  3090. summarise(
  3091. y_base = max(estimate + se, na.rm = TRUE),
  3092. .groups = "drop"
  3093. )
  3094. Figure4_revision_rec_bc_nofacet_stats_encwithinrec <- Figure4_revision_rec_bc_nofacet_stats_encwithinrec %>%
  3095. left_join(Figure4_revision_rec_bc_nofacet_ypos_cope, by = "cope_name") %>%
  3096. mutate(
  3097. xmin = case_when(
  3098. cope_name == "Target hit" ~ 1 - 0.20,
  3099. cope_name == "Lure correct rejection" ~ 1 + 0.20
  3100. ),
  3101. xmax = case_when(
  3102. cope_name == "Target hit" ~ 2 - 0.20,
  3103. cope_name == "Lure correct rejection" ~ 2 + 0.20
  3104. ),
  3105. y.position = case_when(
  3106. cope_name == "Target hit" ~ y_base + 0.090,
  3107. cope_name == "Lure correct rejection" ~ y_base + 0.140
  3108. )
  3109. )
  3110. # 5) Plot
  3111. Figure4_revision_rec_bc_nofacet_plot <- ggplot(
  3112. Figure4_revision_rec_bc_nofacet_barsum,
  3113. aes(x = task, y = estimate, fill = cope_name)
  3114. ) +
  3115. geom_col(
  3116. position = position_dodge(width = figR4_dodge),
  3117. width = 0.65
  3118. ) +
  3119. geom_errorbar(
  3120. aes(ymin = estimate - se, ymax = estimate + se),
  3121. position = position_dodge(width = figR4_dodge),
  3122. width = 0.15,
  3123. linewidth = 1.2
  3124. ) +
  3125. stat_pvalue_manual(
  3126. Figure4_revision_rec_bc_nofacet_stats_recwithinenc,
  3127. label = "label",
  3128. xmin = "xmin",
  3129. xmax = "xmax",
  3130. y.position = "y.position",
  3131. tip.length = 0.01,
  3132. hide.ns = TRUE,
  3133. inherit.aes = FALSE,
  3134. bracket.size = 1.0,
  3135. size = 5.0
  3136. ) +
  3137. stat_pvalue_manual(
  3138. Figure4_revision_rec_bc_nofacet_stats_encwithinrec,
  3139. label = "label",
  3140. xmin = "xmin",
  3141. xmax = "xmax",
  3142. y.position = "y.position",
  3143. tip.length = 0.01,
  3144. hide.ns = TRUE,
  3145. inherit.aes = FALSE,
  3146. bracket.size = 1.0,
  3147. size = 5.0
  3148. ) +
  3149. scale_fill_manual(values = c(
  3150. "Target hit" = "#66BB6A",
  3151. "Lure correct rejection" = "#9575CD"
  3152. )) +
  3153. scale_y_continuous(expand = expansion(mult = c(0.02, 0.28))) +
  3154. labs(
  3155. x = "Encoding condition",
  3156. y = "Baseline-corrected mean % signal change",
  3157. fill = "Recognition condition"
  3158. ) +
  3159. theme_classic(base_size = 13) +
  3160. theme(
  3161. panel.grid.minor = element_blank()
  3162. )
  3163. Figure4_revision_rec_bc_nofacet_plot
  3164. ```
  3165. EXPORT
  3166. ```{r}
  3167. text_base_export <- 40
  3168. p_text_export <- text_base_export * 0.28
  3169. Figure4_revision_rec_bc_nofacet_plot_export <- ggplot(
  3170. Figure4_revision_rec_bc_nofacet_barsum,
  3171. aes(x = task, y = estimate, fill = cope_name)
  3172. ) +
  3173. geom_col(
  3174. position = position_dodge(width = figR4_dodge),
  3175. width = 0.65
  3176. ) +
  3177. geom_errorbar(
  3178. aes(ymin = estimate - se, ymax = estimate + se),
  3179. position = position_dodge(width = figR4_dodge),
  3180. width = 0.15,
  3181. linewidth = 2.0
  3182. ) +
  3183. stat_pvalue_manual(
  3184. Figure4_revision_rec_bc_nofacet_stats_recwithinenc,
  3185. label = "label",
  3186. xmin = "xmin",
  3187. xmax = "xmax",
  3188. y.position = "y.position",
  3189. tip.length = 0.01,
  3190. hide.ns = TRUE,
  3191. inherit.aes = FALSE,
  3192. bracket.size = 1.3,
  3193. size = p_text_export
  3194. ) +
  3195. stat_pvalue_manual(
  3196. Figure4_revision_rec_bc_nofacet_stats_encwithinrec,
  3197. label = "label",
  3198. xmin = "xmin",
  3199. xmax = "xmax",
  3200. y.position = "y.position",
  3201. tip.length = 0.01,
  3202. hide.ns = TRUE,
  3203. inherit.aes = FALSE,
  3204. bracket.size = 1.3,
  3205. size = p_text_export
  3206. ) +
  3207. scale_fill_manual(values = c(
  3208. "Target hit" = "#66BB6A",
  3209. "Lure correct rejection" = "#9575CD"
  3210. )) +
  3211. scale_y_continuous(expand = expansion(mult = c(0.02, 0.32))) +
  3212. labs(
  3213. x = "Encoding condition",
  3214. y = "Mean % signal change\nduring recognition",
  3215. fill = "Recognition condition"
  3216. ) +
  3217. theme_classic(base_size = text_base_export) +
  3218. theme(
  3219. axis.text.x = element_text(size = 34, margin = margin(t = 4)),
  3220. axis.text.y = element_text(size = 30),
  3221. axis.title.x = element_text(size = 38, margin = margin(t = 12)),
  3222. axis.title.y = element_text(size = 34, margin = margin(r = 18)),
  3223. legend.position = c(0.82, 0.96),
  3224. legend.justification = c(0.5, 1),
  3225. legend.title = element_text(size = 30),
  3226. legend.text = element_text(size = 26),
  3227. legend.background = element_rect(fill = scales::alpha("white", 0), color = NA),
  3228. plot.margin = margin(t = 24, r = 20, b = 20, l = 70)
  3229. )
  3230. ggsave(
  3231. "derivatives/figures/SI_figures/Figure8SI.png",
  3232. plot = Figure4_revision_rec_bc_nofacet_plot_export,
  3233. width = 16, height = 12, units = "in",
  3234. dpi = 300,
  3235. bg = "white",
  3236. device = "png"
  3237. )
  3238. ```
  3239. ### Revised Supplementary Figure 9 - Lure CR vs Lure FA
  3240. ```{r}
  3241. ## Figure H4 - Recognition specificity (Lure CR vs Lure FA)
  3242. # 1) Within-subject summary
  3243. FigureH4_barsum <- Rmisc::summarySEwithin(
  3244. data = semmst_merged_data_H4_lureFACR_nooutlier,
  3245. measurevar = "estimate",
  3246. withinvars = c("cope_name", "roi_cluster"),
  3247. idvar = "ID",
  3248. na.rm = TRUE
  3249. ) %>%
  3250. mutate(
  3251. cope_name = factor(
  3252. as.character(cope_name),
  3253. levels = c("LureCR", "LureFA"),
  3254. labels = c("Correct rejection", "False alarm")
  3255. )
  3256. )
  3257. # 2) ROI labels
  3258. FigureH4_roi_lab <- semmst_merged_data_H4_lureFACR_nooutlier %>%
  3259. dplyr::distinct(roi_cluster) %>%
  3260. dplyr::mutate(
  3261. roi_label = dplyr::case_when(
  3262. roi_cluster == "L_HEAD_DGCA23_HEAD_SUB_14" ~ "Left hippocampal head cluster",
  3263. roi_cluster == "R_HEAD_DGCA23_HEAD_SUB_15" ~ "Right hippocampal head cluster",
  3264. TRUE ~ as.character(roi_cluster)
  3265. )
  3266. )
  3267. FigureH4_barsum <- FigureH4_barsum %>%
  3268. dplyr::left_join(FigureH4_roi_lab, by = "roi_cluster")
  3269. # 3) Plot
  3270. FigureH4_lure_col <- "#9575CD"
  3271. FigureH4_plot <- ggplot(
  3272. FigureH4_barsum,
  3273. aes(x = cope_name, y = estimate, alpha = cope_name)
  3274. ) +
  3275. geom_col(
  3276. fill = FigureH4_lure_col,
  3277. width = 0.65,
  3278. show.legend = FALSE
  3279. ) +
  3280. geom_errorbar(
  3281. aes(ymin = estimate - se, ymax = estimate + se),
  3282. width = 0.15,
  3283. linewidth = 1.2
  3284. ) +
  3285. facet_wrap(~ roi_label) +
  3286. theme_classic(base_size = 13) +
  3287. labs(
  3288. x = "Response during recognition",
  3289. y = "Mean % signal change"
  3290. ) +
  3291. scale_alpha_manual(values = c(
  3292. "Correct rejection" = 0.95,
  3293. "False alarm" = 0.55
  3294. )) +
  3295. scale_y_continuous(expand = expansion(mult = c(0.02, 0.02))) +
  3296. custom_theme +
  3297. guides(alpha = "none")
  3298. FigureH4_plot
  3299. ```
  3300. EXPORT
  3301. ```{r}
  3302. text_base_export <- 40
  3303. FigureH4_plot_export <- FigureH4_plot +
  3304. theme_classic(base_size = text_base_export) +
  3305. theme(
  3306. axis.text.x = element_text(size = text_base_export * 0.82, margin = margin(t = 2)),
  3307. axis.text.y = element_text(size = text_base_export * 0.90),
  3308. axis.title = element_text(size = text_base_export * 1.05),
  3309. strip.text = element_text(size = text_base_export * 0.82, face = "bold"),
  3310. panel.spacing.x = unit(0.8, "lines")
  3311. )
  3312. ggsave(
  3313. "derivatives/figures/SI_figures/Figure9SI.png",
  3314. plot = FigureH4_plot_export,
  3315. width = 20, height = 12, units = "in",
  3316. dpi = 300,
  3317. bg = "white",
  3318. device = "png"
  3319. )
  3320. ```
  3321. # Revision Results
  3322. ### Revision Results 1 - Encoding trial-wise RS (Response to reviewers)
  3323. ```{r}
  3324. # 1) Neural data: keep all EncodingLSA* tasks, extract itemno from cope_name
  3325. semmst_merged_data_supplement_filtered_results3 = semmst_merged_data_supplement_filtered %>%
  3326. filter(stringr::str_detect(task, "^EncodingLSA")) %>%
  3327. mutate(
  3328. itemno = stringr::str_extract(as.character(cope_name), "\\d+"),
  3329. itemno = as.character(itemno)
  3330. ) %>%
  3331. filter(!is.na(itemno)) %>%
  3332. dplyr::select(ID, task, roi_cluster, estimate, itemno) %>%
  3333. distinct(ID, roi_cluster, itemno, .keep_all = TRUE)
  3334. # 2) Encoding item-level behavioral data: only order == 2
  3335. semmst_behav_data_enc_itemwise_GLMEM <- semmst_behav_data_filtered %>%
  3336. filter(task == "enc") %>%
  3337. filter(trial_type != "FILLER") %>%
  3338. mutate(
  3339. across(
  3340. all_of(c("Vocabulary", "Digit_Symbol", "NonWord_sum",
  3341. "arousal", "concreteness", "imageability", "meaningfulness")),
  3342. ~ as.numeric(scale(.x)),
  3343. .names = "{.col}_scale"
  3344. ),
  3345. itemno = as.character(itemno),
  3346. order = as.character(order),
  3347. trial_type = factor(trial_type)
  3348. ) %>%
  3349. filter(order == "2") %>%
  3350. drop_na(Vocabulary, itemno, cosine, trial_type) %>%
  3351. distinct(ID, itemno, .keep_all = TRUE)
  3352. # 3) Merge neural + encoding behavior by ID and itemno
  3353. semmst_merged_data_supplement_filtered_results3_glmem <- semmst_merged_data_supplement_filtered_results3 %>%
  3354. left_join(
  3355. semmst_behav_data_enc_itemwise_GLMEM %>%
  3356. dplyr::select(
  3357. ID, itemno,
  3358. noun, trial_type, cosine, order,
  3359. age, sex, education,
  3360. Vocabulary_scale, Digit_Symbol_scale, NonWord_sum_scale,
  3361. arousal_scale, meaningfulness_scale, concreteness_scale
  3362. ),
  3363. by = c("ID", "itemno")
  3364. ) %>%
  3365. drop_na(estimate, trial_type, cosine)
  3366. # quick checks
  3367. semmst_merged_data_supplement_filtered_results3_glmem %>%
  3368. count(roi_cluster, trial_type)
  3369. semmst_merged_data_supplement_filtered_results3_glmem %>%
  3370. dplyr::select(ID, itemno, task, roi_cluster, estimate, noun, trial_type, cosine, order) %>%
  3371. print()
  3372. ```
  3373. Models
  3374. ```{r}
  3375. model_simple_cosine_results3 <- lmer(
  3376. estimate ~ cosine * roi_cluster * trial_type +
  3377. (1 | ID) +
  3378. (1 | itemno),
  3379. data = semmst_merged_data_supplement_filtered_results3_glmem
  3380. )
  3381. model_complex_cosine_results3 <- lmer(
  3382. estimate ~ cosine * trial_type * roi_cluster +
  3383. Vocabulary_scale + Digit_Symbol_scale + NonWord_sum_scale +
  3384. arousal_scale + meaningfulness_scale + concreteness_scale +
  3385. (1 | ID) +
  3386. (1 | itemno),
  3387. data = semmst_merged_data_supplement_filtered_results3_glmem,
  3388. REML = FALSE,
  3389. control = lmerControl(
  3390. optimizer = "bobyqa",
  3391. optCtrl = list(maxfun = 20000)
  3392. )
  3393. )
  3394. anova(model_simple_cosine_results3, model_complex_cosine_results3)
  3395. summary(model_simple_cosine_results3)
  3396. ```
  3397. #### Revision Figure 1
  3398. ```{r}
  3399. # In this plot, we predict estimate by cosine similarity
  3400. FigureR1A_continuous_scatter_roi_base <- semmst_merged_data_supplement_filtered_results3_glmem %>%
  3401. filter(
  3402. roi_cluster %in% c("L_HEAD_DGCA23_HEAD_SUB_14", "R_HEAD_DGCA23_HEAD_SUB_15"),
  3403. trial_type %in% c("CLOSE", "DISTANT"),
  3404. !is.na(cosine),
  3405. !is.na(estimate)
  3406. ) %>%
  3407. mutate(
  3408. trial_type = factor(trial_type, levels = c("CLOSE", "DISTANT")),
  3409. roi_label = case_when(
  3410. roi_cluster == "L_HEAD_DGCA23_HEAD_SUB_14" ~ "Left HC head cluster",
  3411. roi_cluster == "R_HEAD_DGCA23_HEAD_SUB_15" ~ "Right HC head cluster",
  3412. TRUE ~ as.character(roi_cluster)
  3413. ),
  3414. roi_label = factor(
  3415. roi_label,
  3416. levels = c("Left HC head cluster", "Right HC head cluster")
  3417. )
  3418. )
  3419. # Okabe–Ito (CVD-safe): blue, yellow
  3420. FigureR1A_continuous_scatter_roi_cols <- c(
  3421. "CLOSE" = "#4477AA",
  3422. "DISTANT" = "#DDCC77"
  3423. )
  3424. FigureR1A_continuous_scatter_roi_plot <- ggplot(
  3425. FigureR1A_continuous_scatter_roi_base,
  3426. aes(x = cosine, y = estimate, color = trial_type, fill = trial_type)
  3427. ) +
  3428. geom_point(
  3429. alpha = 0.30,
  3430. size = 1.8
  3431. ) +
  3432. geom_smooth(
  3433. aes(group = trial_type),
  3434. method = "lm",
  3435. linewidth = 2,
  3436. se = TRUE,
  3437. alpha = 0.20
  3438. ) +
  3439. facet_wrap(~ roi_label, ncol = 1) +
  3440. scale_x_continuous(
  3441. breaks = function(lims) round(lims[1] + c(0.1, 0.5, 0.9) * (lims[2] - lims[1]), 2),
  3442. labels = function(x) sprintf("%.2f", x)
  3443. ) +
  3444. scale_color_manual(values = FigureR1A_continuous_scatter_roi_cols) +
  3445. scale_fill_manual(values = FigureR1A_continuous_scatter_roi_cols) +
  3446. labs(
  3447. x = "Cosine similarity",
  3448. y = "Mean % signal change",
  3449. color = "Encoding trial type",
  3450. fill = "Encoding trial type"
  3451. ) +
  3452. theme_classic(base_size = 14) +
  3453. theme(
  3454. panel.grid.minor = element_blank(),
  3455. strip.text = element_text(face = "bold"),
  3456. legend.position = c(0.87, 0.88)
  3457. )
  3458. FigureR1A_continuous_scatter_roi_plot
  3459. ```
  3460. ##### EXPORT
  3461. ```{r}
  3462. FigureR1A_continuous_scatter_roi_plot_export <- ggplot(
  3463. FigureR1A_continuous_scatter_roi_base,
  3464. aes(x = cosine, y = estimate, color = trial_type, fill = trial_type)
  3465. ) +
  3466. geom_point(
  3467. alpha = 0.30,
  3468. size = 3.8
  3469. ) +
  3470. geom_smooth(
  3471. aes(group = trial_type),
  3472. method = "lm",
  3473. linewidth = 3.2,
  3474. se = TRUE,
  3475. alpha = 0.20
  3476. ) +
  3477. facet_wrap(~ roi_label, ncol = 1) +
  3478. scale_x_continuous(
  3479. breaks = function(lims) round(lims[1] + c(0.1, 0.5, 0.9) * (lims[2] - lims[1]), 2),
  3480. labels = function(x) sprintf("%.2f", x)
  3481. ) +
  3482. scale_color_manual(values = FigureR1A_continuous_scatter_roi_cols) +
  3483. scale_fill_manual(values = FigureR1A_continuous_scatter_roi_cols) +
  3484. labs(
  3485. x = "Cosine similarity",
  3486. y = "Mean % signal change"
  3487. ) +
  3488. theme_classic(base_size = 32) +
  3489. theme(
  3490. axis.title = element_text(size = 34),
  3491. axis.text = element_text(size = 24),
  3492. strip.text = element_text(size = 24, face = "bold"),
  3493. legend.position = "none"
  3494. )
  3495. ggsave(
  3496. "derivatives/figures/revision_figures/FigureR1A.png",
  3497. plot = FigureR1A_continuous_scatter_roi_plot_export,
  3498. width = 12, height = 18, units = "in",
  3499. dpi = 300,
  3500. bg = "white",
  3501. device = "png"
  3502. )
  3503. ```
  3504. #### Revision Figure 1B
  3505. ```{r}
  3506. FigureR1B_itemwise_base <- semmst_merged_data_supplement_filtered_results3_glmem %>%
  3507. filter(
  3508. roi_cluster %in% c("L_HEAD_DGCA23_HEAD_SUB_14", "R_HEAD_DGCA23_HEAD_SUB_15"),
  3509. trial_type %in% c("CLOSE", "DISTANT"),
  3510. !is.na(itemno),
  3511. !is.na(trial_type),
  3512. !is.na(cosine),
  3513. !is.na(estimate)
  3514. ) %>%
  3515. mutate(
  3516. trial_type = factor(trial_type, levels = c("CLOSE", "DISTANT")),
  3517. roi_label = case_when(
  3518. roi_cluster == "L_HEAD_DGCA23_HEAD_SUB_14" ~ "Left HC head cluster",
  3519. roi_cluster == "R_HEAD_DGCA23_HEAD_SUB_15" ~ "Right HC head cluster",
  3520. TRUE ~ as.character(roi_cluster)
  3521. ),
  3522. roi_label = factor(
  3523. roi_label,
  3524. levels = c("Left HC head cluster", "Right HC head cluster")
  3525. ),
  3526. # Treat CLOSE and DISTANT versions of the same item separately
  3527. item_trial = paste(itemno, trial_type, sep = "_")
  3528. )
  3529. # Order x-axis categories by mean cosine similarity
  3530. FigureR1B_item_order <- FigureR1B_itemwise_base %>%
  3531. group_by(itemno, trial_type, item_trial) %>%
  3532. summarise(
  3533. mean_cosine = mean(cosine, na.rm = TRUE),
  3534. .groups = "drop"
  3535. ) %>%
  3536. arrange(mean_cosine, itemno, trial_type) %>%
  3537. pull(item_trial)
  3538. FigureR1B_itemwise_base <- FigureR1B_itemwise_base %>%
  3539. mutate(
  3540. item_trial = factor(item_trial, levels = FigureR1B_item_order)
  3541. )
  3542. # Colors
  3543. FigureR1B_itemwise_cols <- c(
  3544. "CLOSE" = "#4477AA",
  3545. "DISTANT" = "#DDCC77"
  3546. )
  3547. ## PLOT
  3548. FigureR1B_itemwise_plot <- ggplot(
  3549. FigureR1B_itemwise_base,
  3550. aes(x = item_trial, y = estimate, fill = trial_type, color = trial_type)
  3551. ) +
  3552. geom_boxplot(
  3553. width = 0.70,
  3554. alpha = 0.45,
  3555. outlier.shape = NA
  3556. ) +
  3557. geom_point(
  3558. aes(group = item_trial),
  3559. position = position_jitter(width = 0.16, height = 0),
  3560. alpha = 0.65,
  3561. size = 1.6
  3562. ) +
  3563. facet_wrap(~ roi_label, ncol = 1) +
  3564. scale_fill_manual(values = FigureR1B_itemwise_cols) +
  3565. scale_color_manual(values = FigureR1B_itemwise_cols) +
  3566. labs(
  3567. x = "Items in ascending order of cosine similarity",
  3568. y = "Mean % signal change",
  3569. fill = "Encoding trial type",
  3570. color = "Encoding trial type"
  3571. ) +
  3572. theme_classic(base_size = 14) +
  3573. theme(
  3574. panel.grid.minor = element_blank(),
  3575. strip.text = element_text(face = "bold"),
  3576. axis.text.x = element_blank(),
  3577. axis.ticks.x = element_blank(),
  3578. legend.position = "top"
  3579. )
  3580. FigureR1B_itemwise_plot
  3581. ```
  3582. ```{r}
  3583. FigureR1B_itemwise_plot_export <- ggplot(
  3584. FigureR1B_itemwise_base,
  3585. aes(x = item_trial, y = estimate, fill = trial_type, color = trial_type)
  3586. ) +
  3587. geom_boxplot(
  3588. width = 0.72,
  3589. alpha = 0.45,
  3590. outlier.shape = NA,
  3591. linewidth = 1.1
  3592. ) +
  3593. geom_point(
  3594. aes(group = item_trial),
  3595. position = position_jitter(width = 0.16, height = 0),
  3596. alpha = 0.70,
  3597. size = 2.8
  3598. ) +
  3599. facet_wrap(~ roi_label, ncol = 1) +
  3600. scale_fill_manual(values = FigureR1B_itemwise_cols) +
  3601. scale_color_manual(values = FigureR1B_itemwise_cols) +
  3602. labs(
  3603. x = "Items in ascending order of cosine similarity",
  3604. y = "Mean % signal change"
  3605. ) +
  3606. theme_classic(base_size = 32) +
  3607. theme(
  3608. axis.title = element_text(size = 34),
  3609. axis.text.x = element_blank(),
  3610. axis.ticks.x = element_blank(),
  3611. axis.text.y = element_text(size = 24),
  3612. strip.text = element_text(size = 24, face = "bold"),
  3613. legend.position = "none"
  3614. )
  3615. ggsave(
  3616. "derivatives/figures/revision_figures/FigureR1B.png",
  3617. plot = FigureR1B_itemwise_plot_export,
  3618. width = 16, height = 18, units = "in",
  3619. dpi = 300,
  3620. bg = "white",
  3621. device = "png"
  3622. )
  3623. ```
  3624. ### Revision Results 2 - yes vs no (Response to reviewers)
  3625. #### Behavioural yes-no results
  3626. ```{r}
  3627. # Behaviour
  3628. semmst_behav_data_filtered_revision_results2A = semmst_behav_data_filtered %>%
  3629. filter(task == "enc") %>%
  3630. filter(trial_type != "FILLER") %>%
  3631. drop_na(response, order) %>%
  3632. mutate(
  3633. trial_type = case_when(
  3634. trial_type %in% c("CLOSE", "DISTANT") ~ "LURE",
  3635. TRUE ~ as.character(trial_type)
  3636. ),
  3637. trial_type = factor(trial_type, levels = c("REPEAT", "LURE")),
  3638. response = tolower(as.character(response))
  3639. ) %>%
  3640. filter(response %in% c("no-good", "good")) %>%
  3641. mutate(
  3642. response = factor(response, levels = c("no-good", "good")),
  3643. order = factor(order, levels = c(1, 2))
  3644. ) %>%
  3645. droplevels()
  3646. # Percent response
  3647. semmst_behav_data_filtered_revision_results2A_good = semmst_behav_data_filtered_revision_results2A %>%
  3648. count(ID, trial_type, order, response, name = "n") %>%
  3649. tidyr::complete(ID, trial_type, order, response, fill = list(n = 0)) %>%
  3650. group_by(ID, trial_type, order) %>%
  3651. mutate(
  3652. percent_response = 100 * n / sum(n)
  3653. ) %>%
  3654. ungroup() %>%
  3655. filter(response == "good") %>%
  3656. droplevels()
  3657. # OUTLIERS
  3658. semmst_behav_data_filtered_revision_results2A_good %>%
  3659. group_by(trial_type, order) %>%
  3660. identify_outliers(percent_response) %>%
  3661. dplyr::select(ID, trial_type, order, is.outlier, is.extreme) %>%
  3662. filter(is.extreme == TRUE)
  3663. # NORMALITY
  3664. semmst_behav_data_filtered_revision_results2A_good %>%
  3665. group_by(trial_type, order) %>%
  3666. shapiro_test(percent_response)
  3667. ## ANOVA
  3668. semmst_behav_data_filtered_revision_results2A_AOV = semmst_behav_data_filtered_revision_results2A_good %>%
  3669. anova_test(
  3670. dv = percent_response,
  3671. wid = ID,
  3672. within = c(trial_type, order)
  3673. )
  3674. get_anova_table(semmst_behav_data_filtered_revision_results2A_AOV)
  3675. ## POST-HOC
  3676. semmst_behav_data_filtered_revision_results2A_PWC_response = semmst_behav_data_filtered_revision_results2A_good %>%
  3677. group_by(trial_type) %>%
  3678. pairwise_t_test(
  3679. percent_response ~ order,
  3680. paired = TRUE,
  3681. detailed = TRUE
  3682. ) %>%
  3683. ungroup() %>%
  3684. adjust_pvalue(method = "bonferroni") %>%
  3685. add_significance() %>%
  3686. mutate(label = paste0("p=", signif(p.adj, 3)))
  3687. semmst_behav_data_filtered_revision_results2A_PWC_response
  3688. ```
  3689. #### Neural results
  3690. ```{r}
  3691. # Prepare data
  3692. semmst_merged_data_revision_results2B = semmst_merged_data_supplement_filtered %>%
  3693. filter(task == "EncodingResp") %>%
  3694. filter(cope_name %in% c("YesFirst", "NoFirst", "YesRepeat", "NoRepeat")) %>%
  3695. filter(roi_cluster %in% c("L_HEAD_DGCA23_HEAD_SUB_14", "R_HEAD_DGCA23_HEAD_SUB_15")) %>%
  3696. filter(estimate != 0) %>%
  3697. extract(
  3698. col = cope_name,
  3699. into = c("response_type", "order"),
  3700. regex = "^(Yes|No)(First|Repeat)$"
  3701. ) %>%
  3702. mutate(
  3703. response_type = factor(response_type, levels = c("No", "Yes")),
  3704. order = factor(order, levels = c("First", "Repeat"))
  3705. ) %>%
  3706. droplevels()
  3707. # Keep only IDs with complete 2 x 2 x 2 cells
  3708. complete_ids_revision_results2B <- semmst_merged_data_revision_results2B %>%
  3709. count(ID, response_type, order, roi_cluster) %>%
  3710. tidyr::complete(
  3711. ID,
  3712. response_type = levels(semmst_merged_data_revision_results2B$response_type),
  3713. order = levels(semmst_merged_data_revision_results2B$order),
  3714. roi_cluster = unique(semmst_merged_data_revision_results2B$roi_cluster),
  3715. fill = list(n = 0)
  3716. ) %>%
  3717. group_by(ID) %>%
  3718. summarise(all_cells_present = all(n > 0), .groups = "drop") %>%
  3719. filter(all_cells_present) %>%
  3720. pull(ID)
  3721. semmst_merged_data_revision_results2B = semmst_merged_data_revision_results2B %>%
  3722. filter(ID %in% complete_ids_revision_results2B) %>%
  3723. droplevels()
  3724. # OUTLIER
  3725. semmst_merged_data_revision_results2B %>%
  3726. group_by(response_type, order, roi_cluster) %>%
  3727. identify_outliers(estimate) %>%
  3728. dplyr::select(ID, roi_cluster, response_type, order, is.outlier, is.extreme) %>%
  3729. filter(is.extreme == TRUE)
  3730. # Exclude outliers
  3731. semmst_merged_data_revision_results2B_nooutlier = semmst_merged_data_revision_results2B %>%
  3732. filter(!ID %in% c("sub-434971")) %>%
  3733. ungroup()
  3734. # NORMALITY
  3735. semmst_merged_data_revision_results2B_nooutlier %>%
  3736. group_by(response_type, order, roi_cluster) %>%
  3737. shapiro_test(estimate) %>%
  3738. print(n = 100)
  3739. ## ANOVA
  3740. semmst_merged_data_revision_results2B_AOV = semmst_merged_data_revision_results2B_nooutlier %>%
  3741. anova_test(
  3742. dv = estimate,
  3743. wid = ID,
  3744. within = c(response_type, order, roi_cluster)
  3745. )
  3746. get_anova_table(semmst_merged_data_revision_results2B_AOV)
  3747. ## POST-HOC
  3748. semmst_merged_data_revision_results2B_PWC = semmst_merged_data_revision_results2B_nooutlier %>%
  3749. group_by(ID, order, response_type) %>%
  3750. summarise(
  3751. estimate = mean(estimate, na.rm = TRUE),
  3752. .groups = "drop"
  3753. ) %>%
  3754. tidyr::pivot_wider(
  3755. names_from = response_type,
  3756. values_from = estimate
  3757. ) %>%
  3758. drop_na(No, Yes) %>%
  3759. tidyr::pivot_longer(
  3760. cols = c(No, Yes),
  3761. names_to = "response_type",
  3762. values_to = "estimate"
  3763. ) %>%
  3764. mutate(response_type = factor(response_type, levels = c("No", "Yes"))) %>%
  3765. group_by(response_type) %>%
  3766. pairwise_t_test(
  3767. estimate ~ order,
  3768. paired = TRUE,
  3769. detailed = TRUE
  3770. ) %>%
  3771. ungroup() %>%
  3772. adjust_pvalue(method = "bonferroni") %>%
  3773. add_significance() %>%
  3774. mutate(label = paste0("p=", signif(p.adj, 3)))
  3775. semmst_merged_data_revision_results2B_PWC
  3776. ```
  3777. #### Neural - behavioural correlation
  3778. ```{r}
  3779. # Participant-specific "yes" rate for LURE, second presentation
  3780. revision_results2C_behav_lure_yesrate <- semmst_behav_data_filtered_revision_results2A_good %>%
  3781. filter(
  3782. trial_type == "LURE",
  3783. order == 2
  3784. ) %>%
  3785. transmute(
  3786. ID,
  3787. lure_yes_rate = percent_response
  3788. ) %>%
  3789. droplevels()
  3790. ### Correlation - LURE second presentation
  3791. # 1) Filter / define roi_group
  3792. revision_results2C_corr_input_all <- semmst_merged_data_H3_neural_ps %>%
  3793. mutate(roi_group = roi_cluster) %>%
  3794. dplyr::select(
  3795. ID, roi_group, roi, hemisphere, cluster, voxels,
  3796. LureRepeat,
  3797. NonWord_sum_scale, Digit_Symbol_scale
  3798. ) %>%
  3799. left_join(revision_results2C_behav_lure_yesrate, by = "ID") %>%
  3800. dplyr::select(
  3801. ID, roi_group, roi, hemisphere, cluster, voxels,
  3802. LureRepeat, lure_yes_rate,
  3803. NonWord_sum_scale, Digit_Symbol_scale
  3804. ) %>%
  3805. ungroup()
  3806. # 2) Mahalanobis outlier detection (per ROI)
  3807. alpha <- 0.99 # cutoff for chi-square (df=2)
  3808. revision_results2C_md_tbl_all <- revision_results2C_corr_input_all %>%
  3809. dplyr::group_by(roi_group) %>%
  3810. dplyr::group_modify(~{
  3811. d <- .x %>%
  3812. dplyr::select(ID, LureRepeat, lure_yes_rate) %>%
  3813. tidyr::drop_na()
  3814. if (nrow(d) < 3) {
  3815. return(tibble::tibble(
  3816. ID = character(),
  3817. MD = numeric(),
  3818. p = numeric(),
  3819. cutoff = numeric(),
  3820. is_outlier = logical()
  3821. ))
  3822. }
  3823. X <- as.matrix(d[, c("LureRepeat", "lure_yes_rate")])
  3824. center <- colMeans(X)
  3825. covmat <- stats::cov(X)
  3826. md <- stats::mahalanobis(X, center = center, cov = covmat)
  3827. tibble::tibble(
  3828. ID = d$ID,
  3829. MD = md,
  3830. p = 1 - stats::pchisq(md, df = 2),
  3831. cutoff = stats::qchisq(alpha, df = 2),
  3832. is_outlier = md > stats::qchisq(alpha, df = 2)
  3833. )
  3834. }) %>%
  3835. ungroup()
  3836. revision_results2C_md_outliers_all <- revision_results2C_md_tbl_all %>%
  3837. dplyr::filter(is_outlier)
  3838. revision_results2C_md_multi_all <- revision_results2C_md_outliers_all %>%
  3839. dplyr::count(ID, sort = TRUE) %>%
  3840. dplyr::filter(n > 1)
  3841. revision_results2C_md_tbl_all
  3842. revision_results2C_md_outliers_all
  3843. revision_results2C_md_multi_all
  3844. # 3) Remove outlier ID
  3845. revision_results2C_corr_input_all_nooutlier <- revision_results2C_corr_input_all %>%
  3846. dplyr::filter(ID != "sub-434971") %>%
  3847. droplevels() %>%
  3848. ungroup()
  3849. # 4) Fisher-z CI for Pearson r (95% hard-coded)
  3850. pearson_ci <- function(r, n) {
  3851. z <- atanh(r)
  3852. se <- 1 / sqrt(n - 3)
  3853. zcrit <- stats::qnorm(1 - (1 - 0.95) / 2)
  3854. c(low = tanh(z - zcrit * se), high = tanh(z + zcrit * se))
  3855. }
  3856. # 5) SIMPLE correlations only (Pearson) per ROI + Bonferroni
  3857. revision_results2C_corr_lure_yesrate_simple <- revision_results2C_corr_input_all_nooutlier %>%
  3858. dplyr::group_by(roi_group) %>%
  3859. dplyr::group_modify(~{
  3860. d <- .x %>%
  3861. dplyr::select(LureRepeat, lure_yes_rate) %>%
  3862. tidyr::drop_na()
  3863. ct <- stats::cor.test(d$LureRepeat, d$lure_yes_rate, method = "pearson")
  3864. r <- unname(ct$estimate)
  3865. p <- ct$p.value
  3866. n <- nrow(d)
  3867. ci <- pearson_ci(r, n)
  3868. tibble::tibble(
  3869. n = n,
  3870. r = r,
  3871. p = p,
  3872. ci = sprintf("[%.3f, %.3f]", ci["low"], ci["high"])
  3873. )
  3874. }) %>%
  3875. ungroup() %>%
  3876. mutate(
  3877. p_adj = p.adjust(p, method = "bonferroni")
  3878. ) %>%
  3879. arrange(p_adj)
  3880. revision_results2C_corr_lure_yesrate_simple
  3881. ```
  3882. #### Revision Figure 2A - Behavioural revision
  3883. ```{r}
  3884. ## BEHAVIOURAL FIGURE ------------------------------------------------------------
  3885. FigureR2A_dodge <- 0.8
  3886. # 1) Within-subject summary
  3887. FigureR2A_barsum <- Rmisc::summarySEwithin(
  3888. data = semmst_behav_data_filtered_revision_results2A_good,
  3889. measurevar = "percent_response",
  3890. withinvars = c("trial_type", "order"),
  3891. idvar = "ID",
  3892. na.rm = TRUE
  3893. ) %>%
  3894. mutate(
  3895. trial_type = factor(
  3896. as.character(trial_type),
  3897. levels = c("REPEAT", "LURE"),
  3898. labels = c("Exact repeat", "Modified repeat")
  3899. ),
  3900. order = factor(
  3901. as.character(order),
  3902. levels = c("1", "2"),
  3903. labels = c("First", "Repeat")
  3904. )
  3905. )
  3906. # 2) Only theoretically matched pairwise post-hocs
  3907. FigureR2A_stats_all <- semmst_behav_data_filtered_revision_results2A_good %>%
  3908. mutate(
  3909. cond = factor(
  3910. paste(as.character(trial_type), as.character(order), sep = "_"),
  3911. levels = c("REPEAT_1", "REPEAT_2", "LURE_1", "LURE_2")
  3912. )
  3913. ) %>%
  3914. pairwise_t_test(
  3915. percent_response ~ cond,
  3916. paired = TRUE,
  3917. detailed = TRUE
  3918. ) %>%
  3919. adjust_pvalue(method = "bonferroni") %>%
  3920. add_significance() %>%
  3921. mutate(
  3922. comparison = paste(as.character(group1), as.character(group2), sep = "__"),
  3923. label = case_when(
  3924. p.adj < 0.001 ~ "p < .001",
  3925. p.adj < 0.05 ~ paste0("p = ", sub("^0", "", sprintf("%.3f", p.adj))),
  3926. TRUE ~ NA_character_
  3927. )
  3928. ) %>%
  3929. filter(comparison %in% c(
  3930. "REPEAT_1__REPEAT_2",
  3931. "LURE_1__LURE_2",
  3932. "REPEAT_1__LURE_1",
  3933. "REPEAT_2__LURE_2"
  3934. ))
  3935. # 3) Map comparisons onto dodged bar centers
  3936. FigureR2A_xpos <- c(
  3937. "REPEAT_1" = 1 - FigureR2A_dodge / 4,
  3938. "REPEAT_2" = 1 + FigureR2A_dodge / 4,
  3939. "LURE_1" = 2 - FigureR2A_dodge / 4,
  3940. "LURE_2" = 2 + FigureR2A_dodge / 4
  3941. )
  3942. FigureR2A_y_base <- max(
  3943. FigureR2A_barsum$percent_response + FigureR2A_barsum$se,
  3944. na.rm = TRUE
  3945. )
  3946. FigureR2A_y_step <- 6
  3947. FigureR2A_stats_all <- FigureR2A_stats_all %>%
  3948. mutate(
  3949. xmin_plot = unname(FigureR2A_xpos[as.character(group1)]),
  3950. xmax_plot = unname(FigureR2A_xpos[as.character(group2)]),
  3951. y.position = case_when(
  3952. comparison == "REPEAT_1__REPEAT_2" ~ FigureR2A_y_base + FigureR2A_y_step * 1,
  3953. comparison == "LURE_1__LURE_2" ~ FigureR2A_y_base + FigureR2A_y_step * 2,
  3954. comparison == "REPEAT_1__LURE_1" ~ FigureR2A_y_base + FigureR2A_y_step * 3,
  3955. comparison == "REPEAT_2__LURE_2" ~ FigureR2A_y_base + FigureR2A_y_step * 4
  3956. )
  3957. ) %>%
  3958. ungroup()
  3959. # only draw significant matched comparisons
  3960. FigureR2A_stats_draw <- FigureR2A_stats_all %>%
  3961. filter(!is.na(label), p.adj < 0.05) %>%
  3962. as.data.frame()
  3963. # 4) y-axis upper limit for brackets, but only label up to 100
  3964. FigureR2A_y_top <- max(
  3965. 105,
  3966. FigureR2A_stats_draw$y.position,
  3967. na.rm = TRUE
  3968. )
  3969. # 5) Base plot
  3970. FigureR2A_plot <- ggplot(
  3971. FigureR2A_barsum,
  3972. aes(
  3973. x = trial_type,
  3974. y = percent_response,
  3975. fill = trial_type,
  3976. group = order
  3977. )
  3978. ) +
  3979. geom_col(
  3980. aes(alpha = order),
  3981. position = position_dodge(width = FigureR2A_dodge),
  3982. width = 0.65
  3983. ) +
  3984. geom_errorbar(
  3985. aes(ymin = percent_response - se, ymax = percent_response + se),
  3986. position = position_dodge(width = FigureR2A_dodge),
  3987. width = 0.15,
  3988. linewidth = 1.2
  3989. ) +
  3990. ggpubr::stat_pvalue_manual(
  3991. FigureR2A_stats_draw,
  3992. label = "label",
  3993. xmin = "xmin_plot",
  3994. xmax = "xmax_plot",
  3995. y.position = "y.position",
  3996. tip.length = 0.01,
  3997. hide.ns = TRUE,
  3998. inherit.aes = FALSE,
  3999. bracket.shorten = 0,
  4000. bracket.size = 1.0,
  4001. size = 5.0,
  4002. step.increase = 0
  4003. ) +
  4004. theme_classic(base_size = 13) +
  4005. labs(
  4006. x = "Condition during encoding",
  4007. y = "Percentage of 'accept' responses",
  4008. fill = "Trial type",
  4009. alpha = "Order"
  4010. ) +
  4011. scale_fill_manual(values = c(
  4012. "Exact repeat" = "#66BB6A",
  4013. "Modified repeat" = "#9575CD"
  4014. )) +
  4015. scale_alpha_manual(values = c(
  4016. "First" = 0.95,
  4017. "Repeat" = 0.55
  4018. )) +
  4019. scale_y_continuous(
  4020. limits = c(0, FigureR2A_y_top),
  4021. breaks = c(0, 25, 50, 75, 100),
  4022. labels = c("0", "25", "50", "75", "100"),
  4023. expand = expansion(mult = c(0.02, 0))
  4024. ) +
  4025. custom_theme +
  4026. guides(fill = "none") +
  4027. theme(
  4028. legend.position = c(0.05, 0.98),
  4029. legend.justification = c(0, 1),
  4030. legend.background = element_rect(fill = scales::alpha("white", 0), color = NA)
  4031. )
  4032. FigureR2A_plot
  4033. ```
  4034. ```{r}
  4035. # 6) Export
  4036. text_base_export <- 40
  4037. FigureR2A_plot_export <- ggplot(
  4038. FigureR2A_barsum,
  4039. aes(
  4040. x = trial_type,
  4041. y = percent_response,
  4042. fill = trial_type,
  4043. group = order
  4044. )
  4045. ) +
  4046. geom_col(
  4047. aes(alpha = order),
  4048. position = position_dodge(width = FigureR2A_dodge),
  4049. width = 0.65
  4050. ) +
  4051. geom_errorbar(
  4052. aes(ymin = percent_response - se, ymax = percent_response + se),
  4053. position = position_dodge(width = FigureR2A_dodge),
  4054. width = 0.15,
  4055. linewidth = 2.0
  4056. ) +
  4057. ggpubr::stat_pvalue_manual(
  4058. FigureR2A_stats_draw,
  4059. label = "label",
  4060. xmin = "xmin_plot",
  4061. xmax = "xmax_plot",
  4062. y.position = "y.position",
  4063. tip.length = 0.015,
  4064. hide.ns = TRUE,
  4065. inherit.aes = FALSE,
  4066. bracket.shorten = 0,
  4067. bracket.size = 1.8,
  4068. size = text_base_export * 0.24,
  4069. step.increase = 0
  4070. ) +
  4071. labs(
  4072. x = "Condition during encoding",
  4073. y = "Percentage of 'accept' responses",
  4074. fill = "Trial type",
  4075. alpha = "Order"
  4076. ) +
  4077. scale_fill_manual(values = c(
  4078. "Exact repeat" = "#66BB6A",
  4079. "Modified repeat" = "#9575CD"
  4080. )) +
  4081. scale_alpha_manual(values = c(
  4082. "First" = 0.95,
  4083. "Repeat" = 0.55
  4084. )) +
  4085. scale_y_continuous(
  4086. limits = c(0, FigureR2A_y_top),
  4087. breaks = c(0, 25, 50, 75, 100),
  4088. labels = c("0", "25", "50", "75", "100"),
  4089. expand = expansion(mult = c(0.02, 0))
  4090. ) +
  4091. custom_theme +
  4092. guides(fill = "none") +
  4093. theme_classic(base_size = text_base_export) +
  4094. theme(
  4095. legend.position = c(0.05, 0.98),
  4096. legend.justification = c(0, 1),
  4097. legend.background = element_rect(fill = scales::alpha("white", 0), color = NA),
  4098. legend.title = element_text(size = text_base_export * 0.82),
  4099. legend.text = element_text(size = text_base_export * 0.78),
  4100. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  4101. axis.text.y = element_text(size = text_base_export * 0.90),
  4102. axis.title = element_text(size = text_base_export * 1.05)
  4103. )
  4104. ggsave(
  4105. "derivatives/figures/revision_figures/FigureR2A.png",
  4106. plot = FigureR2A_plot_export,
  4107. width = 16, height = 12, units = "in",
  4108. dpi = 300,
  4109. bg = "white",
  4110. device = "png"
  4111. )
  4112. ```
  4113. #### Revision Figure 2B - Nerual results
  4114. ```{r}
  4115. ## NEURAL FIGURE ----------------------------------------------------------------
  4116. FigureR2B_dodge <- 0.8
  4117. # 1) Collapse across the two HC head clusters (to match the post-hoc tests)
  4118. FigureR2B_plotdata <- semmst_merged_data_revision_results2B_nooutlier %>%
  4119. group_by(ID, response_type, order) %>%
  4120. summarise(
  4121. estimate = mean(estimate, na.rm = TRUE),
  4122. .groups = "drop"
  4123. ) %>%
  4124. mutate(
  4125. response_type = factor(
  4126. as.character(response_type),
  4127. levels = c("No", "Yes"),
  4128. labels = c("Not accept", "Accept")
  4129. ),
  4130. order = factor(
  4131. as.character(order),
  4132. levels = c("First", "Repeat"),
  4133. labels = c("First", "Repeat")
  4134. )
  4135. )
  4136. # 2) Within-subject summary
  4137. FigureR2B_barsum <- Rmisc::summarySEwithin(
  4138. data = FigureR2B_plotdata,
  4139. measurevar = "estimate",
  4140. withinvars = c("response_type", "order"),
  4141. idvar = "ID",
  4142. na.rm = TRUE
  4143. )
  4144. # 3) Planned comparison: Not accept vs Accept during FIRST presentation only
  4145. FigureR2B_first_comp <- FigureR2B_plotdata %>%
  4146. filter(order == "First") %>%
  4147. tidyr::pivot_wider(
  4148. names_from = response_type,
  4149. values_from = estimate
  4150. ) %>%
  4151. drop_na(`Not accept`, Accept)
  4152. FigureR2B_first_ttest <- t.test(
  4153. FigureR2B_first_comp$`Not accept`,
  4154. FigureR2B_first_comp$Accept,
  4155. paired = TRUE
  4156. )
  4157. FigureR2B_first_p <- FigureR2B_first_ttest$p.value
  4158. # 4) Bracket coordinates
  4159. FigureR2B_xpos <- c(
  4160. "Not accept_First" = 1 - FigureR2B_dodge / 4,
  4161. "Not accept_Repeat" = 1 + FigureR2B_dodge / 4,
  4162. "Accept_First" = 2 - FigureR2B_dodge / 4,
  4163. "Accept_Repeat" = 2 + FigureR2B_dodge / 4
  4164. )
  4165. FigureR2B_y_base <- max(
  4166. FigureR2B_barsum$estimate + FigureR2B_barsum$se,
  4167. na.rm = TRUE
  4168. )
  4169. FigureR2B_y_step <- 0.025
  4170. FigureR2B_stats_draw <- tibble::tibble(
  4171. group1 = "Not accept_First",
  4172. group2 = "Accept_First",
  4173. xmin_plot = unname(FigureR2B_xpos["Not accept_First"]),
  4174. xmax_plot = unname(FigureR2B_xpos["Accept_First"]),
  4175. y.position = FigureR2B_y_base + FigureR2B_y_step,
  4176. label = case_when(
  4177. FigureR2B_first_p < 0.001 ~ "p < .001",
  4178. FigureR2B_first_p < 0.1 ~ paste0("p = ", sub("^0", "", sprintf("%.3f", FigureR2B_first_p))),
  4179. TRUE ~ NA_character_
  4180. )
  4181. ) %>%
  4182. filter(!is.na(label))
  4183. # 5) y-axis upper limit for bracket
  4184. FigureR2B_y_top <- max(
  4185. c(
  4186. FigureR2B_barsum$estimate + FigureR2B_barsum$se,
  4187. FigureR2B_stats_draw$y.position
  4188. ),
  4189. na.rm = TRUE
  4190. ) + 0.01
  4191. # 6) Base plot
  4192. FigureR2B_plot <- ggplot(
  4193. FigureR2B_barsum,
  4194. aes(
  4195. x = response_type,
  4196. y = estimate,
  4197. fill = response_type,
  4198. group = order
  4199. )
  4200. ) +
  4201. geom_col(
  4202. aes(alpha = order),
  4203. position = position_dodge(width = FigureR2B_dodge),
  4204. width = 0.65
  4205. ) +
  4206. geom_errorbar(
  4207. aes(ymin = estimate - se, ymax = estimate + se),
  4208. position = position_dodge(width = FigureR2B_dodge),
  4209. width = 0.15,
  4210. linewidth = 1.2
  4211. ) +
  4212. theme_classic(base_size = 13) +
  4213. labs(
  4214. x = "Response during encoding",
  4215. y = "Mean % signal change",
  4216. fill = "Response type",
  4217. alpha = "Order"
  4218. ) +
  4219. scale_fill_manual(values = c(
  4220. "Accept" = "#4477AA",
  4221. "Not accept" = "#CC6677"
  4222. )) +
  4223. scale_alpha_manual(values = c(
  4224. "First" = 0.95,
  4225. "Repeat" = 0.55
  4226. )) +
  4227. scale_y_continuous(
  4228. limits = c(
  4229. min(0, min(FigureR2B_barsum$estimate - FigureR2B_barsum$se, na.rm = TRUE)),
  4230. FigureR2B_y_top
  4231. ),
  4232. expand = expansion(mult = c(0.02, 0))
  4233. ) +
  4234. custom_theme +
  4235. guides(fill = "none") +
  4236. theme(
  4237. legend.position = c(0.75, 0.98),
  4238. legend.justification = c(0, 1),
  4239. legend.background = element_rect(fill = scales::alpha("white", 0), color = NA)
  4240. )
  4241. # Add the single planned bracket only if p < .1
  4242. if (nrow(FigureR2B_stats_draw) > 0) {
  4243. FigureR2B_plot <- FigureR2B_plot +
  4244. ggpubr::stat_pvalue_manual(
  4245. FigureR2B_stats_draw,
  4246. label = "label",
  4247. xmin = "xmin_plot",
  4248. xmax = "xmax_plot",
  4249. y.position = "y.position",
  4250. tip.length = 0.01,
  4251. hide.ns = TRUE,
  4252. inherit.aes = FALSE,
  4253. bracket.shorten = 0,
  4254. bracket.size = 1.0,
  4255. size = 5.0
  4256. )
  4257. }
  4258. FigureR2B_plot
  4259. ```
  4260. ```{r}
  4261. # 7) Export
  4262. text_base_export <- 40
  4263. FigureR2B_plot_export <- ggplot(
  4264. FigureR2B_barsum,
  4265. aes(
  4266. x = response_type,
  4267. y = estimate,
  4268. fill = response_type,
  4269. group = order
  4270. )
  4271. ) +
  4272. geom_col(
  4273. aes(alpha = order),
  4274. position = position_dodge(width = FigureR2B_dodge),
  4275. width = 0.65
  4276. ) +
  4277. geom_errorbar(
  4278. aes(ymin = estimate - se, ymax = estimate + se),
  4279. position = position_dodge(width = FigureR2B_dodge),
  4280. width = 0.15,
  4281. linewidth = 2.0
  4282. ) +
  4283. labs(
  4284. x = "Response during encoding",
  4285. y = "Mean % signal change",
  4286. fill = "Response type",
  4287. alpha = "Order"
  4288. ) +
  4289. scale_fill_manual(values = c(
  4290. "Accept" = "#4477AA",
  4291. "Not accept" = "#CC6677"
  4292. )) +
  4293. scale_alpha_manual(values = c(
  4294. "First" = 0.95,
  4295. "Repeat" = 0.55
  4296. )) +
  4297. scale_y_continuous(
  4298. limits = c(
  4299. min(0, min(FigureR2B_barsum$estimate - FigureR2B_barsum$se, na.rm = TRUE)),
  4300. FigureR2B_y_top
  4301. ),
  4302. expand = expansion(mult = c(0.02, 0))
  4303. ) +
  4304. custom_theme +
  4305. guides(fill = "none") +
  4306. theme_classic(base_size = text_base_export) +
  4307. theme(
  4308. legend.position = c(0.75, 0.98),
  4309. legend.justification = c(0, 1),
  4310. legend.background = element_rect(fill = scales::alpha("white", 0), color = NA),
  4311. legend.title = element_text(size = text_base_export * 0.82),
  4312. legend.text = element_text(size = text_base_export * 0.78),
  4313. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  4314. axis.text.y = element_text(size = text_base_export * 0.90),
  4315. axis.title = element_text(size = text_base_export * 1.05)
  4316. )
  4317. # Add the single planned bracket only if p < .1
  4318. if (nrow(FigureR2B_stats_draw) > 0) {
  4319. FigureR2B_plot_export <- FigureR2B_plot_export +
  4320. ggpubr::stat_pvalue_manual(
  4321. FigureR2B_stats_draw,
  4322. label = "label",
  4323. xmin = "xmin_plot",
  4324. xmax = "xmax_plot",
  4325. y.position = "y.position",
  4326. tip.length = 0.015,
  4327. hide.ns = TRUE,
  4328. inherit.aes = FALSE,
  4329. bracket.shorten = 0,
  4330. bracket.size = 1.8,
  4331. size = text_base_export * 0.24
  4332. )
  4333. }
  4334. ggsave(
  4335. "derivatives/figures/revision_figures/FigureR2B.png",
  4336. plot = FigureR2B_plot_export,
  4337. width = 16, height = 12, units = "in",
  4338. dpi = 300,
  4339. bg = "white",
  4340. device = "png"
  4341. )
  4342. ```
  4343. #### Revision Figure 2C - Behavioural - neural correlation
  4344. ```{r}
  4345. ## Figure R2C - Neural response ~ behavioural "accept" rate
  4346. # 1) Long table
  4347. FigureR2C_df <- revision_results2C_corr_input_all_nooutlier %>%
  4348. dplyr::select(ID, roi_group, LureRepeat, lure_yes_rate) %>%
  4349. tidyr::drop_na(LureRepeat, lure_yes_rate) %>%
  4350. dplyr::mutate(
  4351. facet_lab = dplyr::case_when(
  4352. roi_group == "L_HEAD_DGCA23_HEAD_SUB_14" ~ "Left HC head cluster",
  4353. roi_group == "R_HEAD_DGCA23_HEAD_SUB_15" ~ "Right HC head cluster",
  4354. TRUE ~ as.character(roi_group)
  4355. )
  4356. )
  4357. # 2) Per-ROI correlation + Bonferroni + label text
  4358. FigureR2C_labs <- FigureR2C_df %>%
  4359. dplyr::group_by(roi_group, facet_lab) %>%
  4360. dplyr::summarise(
  4361. n = dplyr::n(),
  4362. r = unname(stats::cor(LureRepeat, lure_yes_rate, method = "pearson")),
  4363. p = stats::cor.test(LureRepeat, lure_yes_rate, method = "pearson")$p.value,
  4364. .groups = "drop"
  4365. ) %>%
  4366. dplyr::mutate(
  4367. p_adj = p.adjust(p, method = "bonferroni"),
  4368. stars = dplyr::case_when(
  4369. p_adj < .001 ~ "***",
  4370. p_adj < .01 ~ "**",
  4371. p_adj < .05 ~ "*",
  4372. TRUE ~ ""
  4373. ),
  4374. sig = if_else(p_adj < .05, "sig", "ns"),
  4375. ann = dplyr::case_when(
  4376. is.na(p_adj) ~ sprintf("r = %.2f, p = NA", r),
  4377. p_adj < .001 ~ sprintf("r = %.2f, p < .001%s", r, stars),
  4378. TRUE ~ sprintf("r = %.2f, p = %.3f%s", r, p_adj, stars)
  4379. )
  4380. )
  4381. FigureR2C_full <- FigureR2C_df %>%
  4382. left_join(FigureR2C_labs %>% dplyr::select(roi_group, sig), by = "roi_group")
  4383. # 3) Plot
  4384. FigureR2C_plot_base <- ggplot(FigureR2C_full, aes(LureRepeat, lure_yes_rate)) +
  4385. geom_point(alpha = 0.7, size = 4.5) +
  4386. scale_color_manual(values = c(ns = "grey30", sig = "red"), guide = "none") +
  4387. facet_wrap(
  4388. ~ roi_group,
  4389. scales = "free",
  4390. labeller = labeller(roi_group = setNames(FigureR2C_labs$facet_lab, FigureR2C_labs$roi_group))
  4391. ) +
  4392. labs(
  4393. x = "Neural % signal change to modified repeats",
  4394. y = "Percent rate of 'accept' responses\nfor modified repeats"
  4395. ) +
  4396. theme_classic(base_size = 18) +
  4397. custom_theme
  4398. FigureR2C_plot <- FigureR2C_plot_base +
  4399. geom_smooth(aes(color = sig), method = "lm", se = FALSE, linewidth = 1.1) +
  4400. geom_text(
  4401. data = FigureR2C_labs,
  4402. aes(x = -Inf, y = Inf, label = ann, color = sig),
  4403. inherit.aes = FALSE,
  4404. hjust = -0.05, vjust = 1.2, size = 5.5
  4405. )
  4406. FigureR2C_plot
  4407. ```
  4408. EXPORT
  4409. ```{r}
  4410. text_base_export <- 40
  4411. FigureR2C_plot_export <- FigureR2C_plot_base +
  4412. geom_smooth(aes(color = sig), method = "lm", se = FALSE, linewidth = 2.6) +
  4413. geom_text(
  4414. data = FigureR2C_labs,
  4415. aes(x = -Inf, y = Inf, label = ann, color = sig),
  4416. inherit.aes = FALSE,
  4417. hjust = -0.05, vjust = 1.2, size = 10.5
  4418. ) +
  4419. theme_classic(base_size = text_base_export) +
  4420. theme(
  4421. legend.title = element_text(size = text_base_export * 0.95),
  4422. legend.text = element_text(size = text_base_export * 0.90),
  4423. axis.text.x = element_text(size = text_base_export * 0.90, margin = margin(t = 2)),
  4424. axis.text.y = element_text(size = text_base_export * 0.90),
  4425. axis.title = element_text(size = text_base_export * 1.05)
  4426. )
  4427. ggsave(
  4428. "derivatives/figures/revision_figures/FigureR2C.png",
  4429. plot = FigureR2C_plot_export,
  4430. width = 20, height = 12, units = "in",
  4431. dpi = 300,
  4432. bg = "white",
  4433. device = "png"
  4434. )
  4435. ```

smst_mr1_analysis.Rmd at commit b1f8280, no license · at the source

Overview

Authors: Alex Ilyés1,2,3, Borbála Mónika Brosig2,4, György Mező5,6,7, Attila Keresztes2,3,8
  1. Doctoral School of Psychology, Eötvös Loránd University, Budapest 1075, Hungary
  2. Brain Imaging Centre, Hungarian Research Network, Research Centre for Natural Sciences, Budapest 1117, Hungary
  3. Institute of Psychology, Eötvös Loránd University, Budapest 1064, Hungary
  4. Mental Health Sciences Division, Doctoral School, Semmelweis University, Budapest 1085, Hungary
  5. Konkoly Observatory, Hungarian Research Network, Research Centre for Astronomy and Earth Sciences, Budapest 1121, Hungary
  6. Wigner Data Center, Hungarian Research Network, Wigner Research Centre for Physics, Budapest 1121, Hungary
  7. Hungarian Academy of Sciences Centre of Excellence, Hungarian Research Network, Research Centre for Astronomy and Earth Sciences, Budapest 1121, Hungary
  8. Max Planck Partner Group of the Max Planck Institute for Human Development, Hippocampal Circuit and Code for Cognition Lab, Budapest 1117, Hungary
Dates: received 28 January 2026; accepted 18 June 2026; published online 28 July 2026; in print 4 August 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1073/pnas.2603114123 · PMID 42520121 · PMCID PMC13438472 · OpenAlex W7171530764
Open access: hybrid, a free copy (OpenAlex)
Status: code verified
Categories: human (organism), cognitive (subfield)
Methods: Statistics, Preprocessing, fMRI & imaging
Keywords: pattern separation, episodic memory, semantic similarity, word2vec, repetition suppression
MeSH: Hippocampus*, Memory*, Adult, Brain Mapping, Female, Humans, Magnetic Resonance Imaging, Male, Memory, Episodic, Recognition, Psychology, Semantics, Young Adult (* major topic)
Journal subjects: Social Sciences, Psychological and Cognitive Sciences
Topic: Memory and Neural Mechanisms (Cognitive Neuroscience, Neuroscience), according to OpenAlex
Funding: Hungarian National Research, Development and Innovation Office (FK128648 and FK146496); Max Planck Society (Max Planck Partner Group); Hungarian Academy of Sciences (0708-21 515 AT); Eotvos Lorand University (Doctoral Projects 2022 Doctoral Projects 2023); University Research Scholarship Program of the Ministry for Culture and Innovation (EKÖP-25-4-I-ELTE-1020, EKÖP-24-2-I-ELTE-687)
Citations: not cited yet (Europe PMC); 144 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repositories

Its files are read in the Code ↔ Paper reader above, with 9 matches between paragraphs and lines of code.

Zenodo 18395407

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (6 files), PsychoPy (5 files), pandas (4 files)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
10 files
At the source:

Zenodo 20090797

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: the references
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Curve Fitting Toolbox (2 files), afex (1 file), broom (1 file), easystats (1 file), emmeans (1 file), FSL (1 file), ggplot2 (1 file), ggpubr (1 file), lme4 (1 file), lmerTest (1 file), Matplotlib (1 file), NiBabel (1 file), NumPy (1 file), pandas (1 file), patchwork (1 file), psych (1 file), rstatix (1 file), SciPy (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
6 files
At the source:

ilyesalex/smst2_mr_experiment

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: c73d841427fd9ac13d6434ad2bf9b971ab5c2e56, 27 January 2026
Languages: Python (9)
Size: 1,720 files, 9 scripts
Software Heritage: not archived
Found in: the Zenodo archive record
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (6 files), PsychoPy (5 files), pandas (4 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
10 files

ilyesalex/smst2_mr_analysis

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: b1f8280750fbe099b86224e616a30e5db4c0f85a, 8 May 2026
Languages: MATLAB (2), Shell (1), Python (1), R (1)
Size: 64 files, 5 scripts
Software Heritage: not archived
Found in: the Zenodo archive record
Holds: README, 1 notebook
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: Curve Fitting Toolbox (2 files), afex (1 file), broom (1 file), easystats (1 file), emmeans (1 file), FSL (1 file), ggplot2 (1 file), ggpubr (1 file), lme4 (1 file), lmerTest (1 file), Matplotlib (1 file), NiBabel (1 file), NumPy (1 file), pandas (1 file), patchwork (1 file), psych (1 file), rstatix (1 file), SciPy (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
6 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 4 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 28 scripts, each with its path and the digest of its content;
  • 9 matches between paragraphs of the paper and lines of the code (method lexical-v1);
  • neither the text of the paper nor the code itself.

Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.

Data

Datasets cited

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

  • no repository, dataset or request procedure was recognized in it

Read it in the paper: doi.org/10.1073/pnas.2603114123.

Versions

The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.

Version 1, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 4 authors, 5 keywords, 12 MeSH terms, 5 funders, 123 references.

Cite

This paper

Ilyés, A., Brosig, B. M., Mező, G., & Keresztes, A. (2026). The human hippocampus can pattern separate memories by meaning. Proceedings of the National Academy of Sciences of the United States of America, 123(31), e2603114123. https://doi.org/10.1073/pnas.2603114123

BibTeX

@article{ilyes2026human,
author = {Ilyés, Alex and Brosig, Borbála Mónika and Mező, György and Keresztes, Attila},
title = {{The human hippocampus can pattern separate memories by meaning}},
journal = {Proceedings of the National Academy of Sciences of the United States of America},
year = {2026},
month = jul,
volume = {123},
number = {31},
pages = {e2603114123},
publisher = {National Academy of Sciences},
issn = {0027-8424},
doi = {10.1073/pnas.2603114123},
url = {https://doi.org/10.1073/pnas.2603114123},
pmid = {42520121},
pmcid = {PMC13438472}
}

RIS

TY - JOUR
AU - Ilyés, Alex
AU - Brosig, Borbála Mónika
AU - Mező, György
AU - Keresztes, Attila
TI - The human hippocampus can pattern separate memories by meaning
T2 - Proceedings of the National Academy of Sciences of the United States of America
J2 - Proc Natl Acad Sci U S A
PY - 2026
DA - 2026/07/28
VL - 123
IS - 31
SP - e2603114123
SN - 0027-8424
PB - National Academy of Sciences
DO - 10.1073/pnas.2603114123
UR - https://doi.org/10.1073/pnas.2603114123
LA - en
ER -

CSL-JSON

{
"id": "10.1073/pnas.2603114123",
"type": "article-journal",
"title": "The human hippocampus can pattern separate memories by meaning",
"container-title": "Proceedings of the National Academy of Sciences of the United States of America",
"author": [
{
"family": "Ilyés",
"given": "Alex"
},
{
"family": "Brosig",
"given": "Borbála Mónika"
},
{
"family": "Mező",
"given": "György"
},
{
"family": "Keresztes",
"given": "Attila"
}
],
"container-title-short": "Proc Natl Acad Sci U S A",
"volume": "123",
"issue": "31",
"page": "e2603114123",
"DOI": "10.1073/pnas.2603114123",
"PMID": "42520121",
"PMCID": "PMC13438472",
"ISSN": "0027-8424",
"publisher": "National Academy of Sciences",
"URL": "https://doi.org/10.1073/pnas.2603114123",
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
28
]
]
}
}

The tracing map gets a citation of its own once an author has validated it and it has a DOI.

Similar papers

The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.

[1] doi:10.1038/s41467-026-73865-9 [code]
Histamine shapes the neurocomputational dynamics of human learning.
Journal: Nature communications
In common: PsychoPy, rstatix, easystats, 12 other tools, cognitive, 2 references
[2] doi:10.1093/braincomms/fcag255 [code]
Impaired consolidation of spatial memory during sleep in patients with leucine-rich glioma-inactivated 1-associated limbic encephalitis.
Journal: Brain communications
In common: afex, rstatix, emmeans, 6 other tools, cognitive, 6 references
[3] doi:10.1093/nc/niag029 [code]
A data-driven approach to identifying and evaluating connectivity-based neural correlates of conscious visual perception.
Journal: Neuroscience of consciousness
In common: afex, easystats, broom, 12 other tools, cognitive
[4] doi:10.1002/hbm.70512 [code]
Precision Imaging for Intraindividual Investigation of the Reward Response.
Journal: Human brain mapping
In common: PsychoPy, psych, easystats, 10 other tools, 2 references
[5] doi:10.1162/imag.a.1321 [code]
Phase similarity between similar objects indicates representational merging across retrieval training but not sleep.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: rstatix, easystats, broom, 10 other tools, cognitive, 1 reference
[6] doi:10.1126/sciadv.aec9291 [code]
Computational mechanisms of perception in autism revealed using games inspired by rodent operant tasks.
Journal: Science advances
In common: afex, psych, rstatix, 8 other tools, cognitive
[7] doi:10.1162/imag.a.1245 [code]
Towards precision EEG connectomics: Evaluating the benefits of dense sampling.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: psych, rstatix, lmerTest, 11 other tools, 1 reference
[8] doi:10.1016/j.nicl.2026.104012 [code]
Structural-functional multilayer brain network properties and outcome of combined repetitive transcranial magnetic stimulation and psychotherapy for obsessive-compulsive disorder.
Journal: NeuroImage. Clinical
In common: afex, psych, rstatix, 10 other tools, 1 reference
[9] doi:10.1038/s41398-026-04010-9 [code]
Bullying victimization and brain development: a longitudinal structural magnetic resonance imaging study from adolescence to early adulthood.
Journal: Translational psychiatry
In common: psych, rstatix, easystats, 8 other tools, 1 reference
[10] doi:10.1038/s41467-026-72934-3 [code]
Multi-focal ultrasound neuromodulation to the dorsal anterior cingulate cortex disrupts behavioural and neural pain processing.
Journal: Nature communications
In common: rstatix, broom, emmeans, 6 other tools, 4 references

Contribute

The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.

Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.

Request its removal

To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).

Discussion, reproductions, activity

Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.

Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.

Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.