OSCR

Convergent structural brain alterations in chronic pain: a multi-metric individual participant data meta-analysis.

Code ↔ Paper

5 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 5 matches
  1. [1] § Materials and methods › Subcortical segmentation ↔ Figure_making.R, lines 3228–3287 · score 0.94 · inferior lateral, ventral diencephalon, mid posterior, mid anterior, cerebellar white matter, cerebellar cortex
  2. [2] § Materials and methods › MRI preprocessing—cortical parcellation ↔ Meta_analysis.R, lines 3401–3460 · score 0.80 · GausCurv, ThickAvg, MeanCurv, Gaussian curvature, SurfArea, GrayVol
  3. [3] § Materials and methods › MRI preprocessing—cortical parcellation ↔ CP_OA.R, lines 744–774 · score 0.74 · CurvInd, FoldInd, NumVert, GausCurv
  4. [4] § Results › IPD meta-analysis results ↔ Meta_analysis.R, lines 2084–2144 · score 0.73 · left ventral diencephalon, right cerebellar white, left pallidum, brain stem, prediction intervals, meta
  5. [5] § Materials and methods › MRI preprocessing—cortical parcellation ↔ recon_all.sh, lines 1–54 · score 0.60 · FreeSurfer, T1 weighted, recon

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 · 4,084 lines · 138 KB · no license · 2 matches

  1. library(tidyverse)
  2. library(data.table)
  3. library(multcomp)
  4. library(effsize)
  5. library(writexl)
  6. library(ggpubr)
  7. library(plotly)
  8. library(reshape2)
  9. library(dplyr)
  10. library(tidyr)
  11. library(stringr)
  12. library(forcats)
  13. library(readr)
  14. library(glmnet)
  15. library(pROC)
  16. library(caret)
  17. library(pscl)
  18. library(corrplot)
  19. library(car)
  20. library(readxl)
  21. library(esvis)
  22. library(metafor)
  23. library(metaviz)
  24. library(smplot2)
  25. library(reshape2)
  26. library(ggridges)
  27. library(meta)
  28. library(introdataviz)
  29. library(smplot2)
  30. library(gghalves)
  31. library(ggdist)
  32. library(meta)
  33. library(forcats)
  34. library(tidytext)
  35. #Datasets used for meta analysis
  36. #Random effects model?
  37. migraine_summary_2_long
  38. clbp_summary_2_long
  39. FM_Summary_2_long
  40. OA_DK_Summary_2_long
  41. ptn_summary_long_2
  42. #Need to recalculate effect size and variance
  43. OA_escalc <- escalc(
  44. measure = 'SMDH',
  45. m1i = Mean_Group1,
  46. sd1i = SD_Group1,
  47. n1i = N_Group1,
  48. m2i = Mean_Group2,
  49. sd2i = SD_Group2,
  50. n2i = N_Group2,
  51. data = OA_DK_Summary_2_long
  52. )
  53. FM_escalc <- escalc(
  54. measure = 'SMDH',
  55. m1i = Mean_Group1,
  56. sd1i = SD_Group1,
  57. n1i = N_Group1,
  58. m2i = Mean_Group2,
  59. sd2i = SD_Group2,
  60. n2i = N_Group2,
  61. data = FM_Summary_2_long
  62. )
  63. CLBP_escalc <- escalc(
  64. measure = 'SMDH',
  65. m1i = Mean_Group1,
  66. sd1i = SD_Group1,
  67. n1i = N_Group1,
  68. m2i = Mean_Group2,
  69. sd2i = SD_Group2,
  70. n2i = N_Group2,
  71. data = clbp_summary_2_long
  72. )
  73. migraine_escalc <- escalc(
  74. measure = 'SMDH',
  75. m1i = Mean_Group1,
  76. sd1i = SD_Group1,
  77. n1i = N_Group1,
  78. m2i = Mean_Group2,
  79. sd2i = SD_Group2,
  80. n2i = N_Group2,
  81. data = migraine_summary_2_long
  82. )
  83. ptn_escalc <- escalc(
  84. measure = 'SMDH',
  85. m1i = Mean_Group1,
  86. sd1i = SD_Group1,
  87. n1i = N_Group1,
  88. m2i = Mean_Group2,
  89. sd2i = SD_Group2,
  90. n2i = N_Group2,
  91. data = ptn_summary_2_long
  92. )
  93. fm2_escalc <- escalc(
  94. measure = 'SMDH',
  95. m1i = Mean_Group1,
  96. sd1i = SD_Group1,
  97. n1i = N_Group1,
  98. m2i = Mean_Group2,
  99. sd2i = SD_Group2,
  100. n2i = N_Group2,
  101. data = fm2_summary_2_long
  102. )
  103. clbp2_s1_escalc <- escalc(
  104. measure = 'SMDH',
  105. m1i = Mean_Group1,
  106. sd1i = SD_Group1,
  107. n1i = N_Group1,
  108. m2i = Mean_Group2,
  109. sd2i = SD_Group2,
  110. n2i = N_Group2,
  111. data = clbp2_s1_summary_long
  112. )
  113. clbp2_s2_escalc <- escalc(
  114. measure = 'SMDH',
  115. m1i = Mean_Group1,
  116. sd1i = SD_Group1,
  117. n1i = N_Group1,
  118. m2i = Mean_Group2,
  119. sd2i = SD_Group2,
  120. n2i = N_Group2,
  121. data = clbp2_s2_summary_long
  122. )
  123. target_measures <- c("GrayVol","SurfArea","ThickAvg","MeanCurv","GausCurv")
  124. oa_filtered <- subset(OA_escalc, Measurement %in% target_measures)
  125. fm_filtered <- subset(FM_escalc, Measurement %in% target_measures)
  126. clbp_filtered <- subset(CLBP_escalc, Measurement %in% target_measures)
  127. m_filtered <- subset(migraine_escalc, Measurement %in% target_measures)
  128. ptn_filtered <- subset(ptn_escalc, Measurement %in% target_measures)
  129. fm2_filtered <- subset(fm2_escalc, Measurement %in% target_measures)
  130. clbp2_s1_filtered <- subset(clbp2_s1_escalc, Measurement %in% target_measures)
  131. clbp2_s2_filtered <- subset(clbp2_s2_escalc, Measurement %in% target_measures)
  132. oa_filtered$study <- 'OA'
  133. fm_filtered$study <- 'FM'
  134. clbp_filtered$study <- 'CLBP'
  135. m_filtered$study <- 'migraine'
  136. ptn_filtered$study <- 'PTN'
  137. fm2_filtered$study <- 'FM2'
  138. clbp2_s1_filtered$study <- 'CLBP2_S1'
  139. clbp2_s2_filtered$study <- 'CLBP2_S2'
  140. meta_df <- rbind(oa_filtered, fm_filtered, clbp_filtered, m_filtered)
  141. meta_results <- do.call(rbind, lapply(split(meta_df, list(meta_df$Region,
  142. meta_df$Measurement)),
  143. function(dfm){
  144. if(nrow(dfm) >=2) {
  145. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  146. data.frame(
  147. Region = dfm$Region[1],
  148. Measurement = dfm$Measurement[1],
  149. k = res$k,
  150. Estimate = res$b,
  151. SE = res$se,
  152. zval = res$zval,
  153. pval = res$pval,
  154. CI_lb = res$ci.lb,
  155. CI_ub = res$ci.ub,
  156. I2 = res$I2
  157. )
  158. } else {
  159. NULL
  160. }
  161. }))
  162. view(meta_results)
  163. meta_results <- meta_results %>%
  164. group_by(Measurement) %>%
  165. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  166. ungroup()
  167. write.csv(meta_results, "~/CP_Meta_Results.csv", row.names = F)
  168. #######################################################################
  169. #Visualization
  170. #######################################################################
  171. ggplot(meta_results, aes(x = Estimate, y = reorder(paste(Region, Measurement, sep = " - "), Estimate))) +
  172. geom_point(aes(color = FDR < 0.05), size = 3) +
  173. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  174. scale_color_manual(values = c("grey", "red")) +
  175. labs(x = "Meta-analytic Hedges' g", y = "Region - Measurement", color = "Significant") +
  176. theme_minimal()
  177. ggplot(meta_results, aes(x = Estimate, y = Region)) +
  178. geom_point(aes(color = FDR < 0.05), size = 3) +
  179. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  180. scale_color_manual(values = c("grey","red")) +
  181. labs(x = 'Meta-analytic Hedges g', y = "Region = Measurement", color = 'Significant') +
  182. theme_minimal() +
  183. facet_wrap(~Measurement, ncol = 5)
  184. ggplot(meta_results, aes(x = Estimate, y = I2)) +
  185. geom_point(aes(color = FDR < 0.05)) +
  186. geom_hline(yintercept = 50, linetype = "dashed", color = "gray") +
  187. labs(x = "Effect Size (Hedges' g)", y = "I² (%)", color = "Significant") +
  188. theme_minimal()
  189. ggplot(meta_results, aes(x = Estimate, y = -log10(pval))) +
  190. geom_point(aes(color = I2 > 50), size = 2) +
  191. geom_hline(yintercept = -log10(0.05), linetype = "dashed") +
  192. labs(x = "Effect Size", y = "-log10(p-value)", color = "High Heterogeneity (I² > 50)") +
  193. theme_minimal()
  194. ggplot(meta_results, aes(x = Estimate, y = reorder(Region, Estimate))) +
  195. geom_point(aes(color = pval < 0.05)) +
  196. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  197. facet_wrap(~Measurement, scales = "free_y") +
  198. theme_bw() +
  199. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  200. ggplot(meta_results, aes(x = Estimate, y = reorder(Region, Estimate))) +
  201. geom_point() +
  202. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.2) +
  203. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  204. labs(
  205. x = "Effect Size (Hedges' g)",
  206. y = "Region",
  207. title = "Forest Plot by Measurement"
  208. ) +
  209. theme_minimal()
  210. #######################################################################
  211. #Forest plots for meta results
  212. #######################################################################
  213. meta_filt <- meta_df[grepl("entorhinal", meta_df$Region), ]
  214. meta_filt <- subset(meta_filt, Measurement %in% c('SurfArea','GrayVol'))
  215. meta_visual <- data.frame(Hedges = meta_filt$yi, SE = sqrt(meta_filt$vi),
  216. Measurement = meta_filt$Measurement, Study = meta_filt$study,
  217. Region = meta_filt$Region)
  218. meta_SA <- subset(meta_visual, Measurement == 'SurfArea')
  219. meta_vol <- subset(meta_visual, Measurement == 'GrayVol')
  220. viz_forest(meta_SA, group = meta_SA$Region, study_labels = meta_SA$Study,
  221. annotate_CI = T, xlab = 'Hedges G')
  222. viz_forest(meta_vol, group = meta_vol$Region, study_labels = meta_vol$Study,
  223. annotate_CI = T, xlab = 'Hedges G')
  224. viz_forest(meta_visual, group = meta_visual$Measurement, study_labels = meta_visual$Region,
  225. annotate_CI = T, xlab = 'Hedges G')
  226. inftemp <- meta_df[grepl("inferiortemporal", meta_df$Region), ]
  227. inftemp <- subset(inftemp, Measurement %in% c('GausCurv'))
  228. inftemp_vis <- data.frame(Hedges = inftemp$yi, SE = sqrt(inftemp$vi),
  229. Measurement = inftemp$Measurement, Study = inftemp$study,
  230. Region = inftemp$Region)
  231. viz_forest(inftemp_vis, group = inftemp_vis$Region, study_labels = inftemp_vis$Study,
  232. annotate_CI = T, xlab = 'Hedges G')
  233. #######################################################################
  234. #Add PTN and redo
  235. #######################################################################
  236. meta_df_2 <- rbind(oa_filtered, fm_filtered, clbp_filtered, m_filtered, ptn_filtered)
  237. meta_results_2 <- do.call(rbind, lapply(split(meta_df_2, list(meta_df_2$Region,
  238. meta_df_2$Measurement)),
  239. function(dfm){
  240. if(nrow(dfm) >=2) {
  241. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  242. data.frame(
  243. Region = dfm$Region[1],
  244. Measurement = dfm$Measurement[1],
  245. k = res$k,
  246. Estimate = res$b,
  247. SE = res$se,
  248. zval = res$zval,
  249. pval = res$pval,
  250. CI_lb = res$ci.lb,
  251. CI_ub = res$ci.ub,
  252. I2 = res$I2
  253. )
  254. } else {
  255. NULL
  256. }
  257. }))
  258. view(meta_results_2)
  259. meta_results_2 <- meta_results_2 %>%
  260. group_by(Measurement) %>%
  261. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  262. ungroup()
  263. view(meta_results_2)
  264. meta_filt_2 <- meta_df_2[grepl("entorhinal", meta_df_2$Region), ]
  265. meta_filt_2 <- subset(meta_filt_2, Measurement %in% c('SurfArea','GrayVol'))
  266. meta_visual_2 <- data.frame(Hedges = meta_filt_2$yi, SE = sqrt(meta_filt_2$vi),
  267. Measurement = meta_filt_2$Measurement, Study = meta_filt_2$study,
  268. Region = meta_filt_2$Region)
  269. meta_SA_2 <- subset(meta_visual_2, Measurement == 'SurfArea')
  270. meta_vol_2 <- subset(meta_visual_2, Measurement == 'GrayVol')
  271. viz_forest(meta_SA_2, group = meta_SA_2$Region, study_labels = meta_SA_2$Study,
  272. annotate_CI = T, xlab = 'Hedges G')
  273. viz_forest(meta_vol_2, group = meta_vol_2$Region, study_labels = meta_vol_2$Study,
  274. annotate_CI = T, xlab = 'Hedges G')
  275. write.csv(meta_results_2, "~/CP_Meta_Results_2.csv", row.names = F)
  276. #######################################################################
  277. #Add FM2 and redo
  278. #######################################################################
  279. meta_df_3 <- rbind(oa_filtered, fm_filtered, clbp_filtered, m_filtered,
  280. ptn_filtered, fm2_filtered)
  281. meta_results_3 <- do.call(rbind, lapply(split(meta_df_3, list(meta_df_3$Region,
  282. meta_df_3$Measurement)),
  283. function(dfm){
  284. if(nrow(dfm) >=2) {
  285. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  286. data.frame(
  287. Region = dfm$Region[1],
  288. Measurement = dfm$Measurement[1],
  289. k = res$k,
  290. Estimate = res$b,
  291. SE = res$se,
  292. zval = res$zval,
  293. pval = res$pval,
  294. CI_lb = res$ci.lb,
  295. CI_ub = res$ci.ub,
  296. I2 = res$I2
  297. )
  298. } else {
  299. NULL
  300. }
  301. }))
  302. view(meta_results_3)
  303. meta_results_3 <- meta_results_3 %>%
  304. group_by(Measurement) %>%
  305. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  306. ungroup()
  307. view(meta_results_3)
  308. meta_filt_3 <- meta_df_3[grepl("entorhinal", meta_df_3$Region), ]
  309. meta_filt_3 <- subset(meta_filt_3, Measurement %in% c('SurfArea','GrayVol'))
  310. meta_visual_3 <- data.frame(Hedges = meta_filt_3$yi, SE = sqrt(meta_filt_3$vi),
  311. Measurement = meta_filt_3$Measurement, Study = meta_filt_3$study,
  312. Region = meta_filt_3$Region)
  313. meta_SA_3 <- subset(meta_visual_3, Measurement == 'SurfArea')
  314. meta_vol_3 <- subset(meta_visual_3, Measurement == 'GrayVol')
  315. viz_forest(meta_SA_3, group = meta_SA_3$Region, study_labels = meta_SA_3$Study,
  316. annotate_CI = T, xlab = 'Hedges G')
  317. viz_forest(meta_vol_3, group = meta_vol_3$Region, study_labels = meta_vol_3$Study,
  318. annotate_CI = T, xlab = 'Hedges G')
  319. ugh <- subset(meta_vol_3, Region == 'rh_entorhinal')
  320. viz_forest(ugh, study_labels =ugh$Study, xlab = 'Hedges G',
  321. text_size = 7)
  322. ggplot(meta_results_3, aes(x = Estimate, y = reorder(paste(Region, Measurement, sep = " - "), Estimate))) +
  323. geom_point(aes(color = FDR < 0.05), size = 3) +
  324. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  325. scale_color_manual(values = c("grey", "red")) +
  326. labs(x = "Meta-analytic Hedges' g", y = "Region - Measurement", color = "Significant") +
  327. theme_minimal()
  328. ggplot(meta_results_3, aes(x = Estimate, y = Region)) +
  329. geom_point(aes(color = FDR < 0.05), size = 3) +
  330. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  331. scale_color_manual(values = c("grey","red")) +
  332. labs(x = 'Meta-analytic Hedges g', y = "Region = Measurement", color = 'Significant') +
  333. theme_minimal() +
  334. facet_wrap(~Measurement, ncol = 5)
  335. ggplot(meta_results_3, aes(x = Estimate, y = I2)) +
  336. geom_point(aes(color = FDR < 0.05)) +
  337. geom_hline(yintercept = 50, linetype = "dashed", color = "gray") +
  338. labs(x = "Effect Size (Hedges' g)", y = "I² (%)", color = "Significant") +
  339. theme_minimal()
  340. ggplot(meta_results_3, aes(x = Estimate, y = -log10(pval))) +
  341. geom_point(aes(color = I2 > 50), size = 2) +
  342. geom_hline(yintercept = -log10(0.05), linetype = "dashed") +
  343. labs(x = "Effect Size", y = "-log10(p-value)", color = "High Heterogeneity (I² > 50)") +
  344. theme_minimal()
  345. ggplot(meta_results_3, aes(x = Estimate, y = reorder(Region, Estimate))) +
  346. geom_point(aes(color = pval < 0.05)) +
  347. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  348. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  349. theme_bw() +
  350. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  351. ggplot(meta_results_3, aes(x = Estimate, y = reorder(Region, Estimate))) +
  352. geom_point() +
  353. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.2) +
  354. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  355. labs(
  356. x = "Effect Size (Hedges' g)",
  357. y = "Region",
  358. title = "Forest Plot by Measurement"
  359. ) +
  360. theme_minimal()
  361. write.csv(meta_results_3, "~/CP_Meta_Results_3.csv", row.names = F)
  362. #######################################################################
  363. #Use M results and redo
  364. #######################################################################
  365. OA_m_escalc <- escalc(
  366. measure = 'SMDH',
  367. m1i = Mean_Group1,
  368. sd1i = SD_Group1,
  369. n1i = N_Group1,
  370. m2i = Mean_Group2,
  371. sd2i = SD_Group2,
  372. n2i = N_Group2,
  373. data = OA_M_summary_long
  374. )
  375. oa_m_filtered <- subset(OA_m_escalc, Measurement %in% target_measures)
  376. oa_m_filtered$study <- 'oa'
  377. CLBP_m_escalc <- escalc(
  378. measure = 'SMDH',
  379. m1i = Mean_Group1,
  380. sd1i = SD_Group1,
  381. n1i = N_Group1,
  382. m2i = Mean_Group2,
  383. sd2i = SD_Group2,
  384. n2i = N_Group2,
  385. data = clbp_m_summary_long
  386. )
  387. clbp_m_filtered <- subset(CLBP_m_escalc, Measurement %in% target_measures)
  388. clbp_m_filtered$study <- 'clbp'
  389. migraine_m_escalc <- escalc(
  390. measure = 'SMDH',
  391. m1i = Mean_Group1,
  392. sd1i = SD_Group1,
  393. n1i = N_Group1,
  394. m2i = Mean_Group2,
  395. sd2i = SD_Group2,
  396. n2i = N_Group2,
  397. data = migraine_m_summary_long
  398. )
  399. migraine_m_filtered <- subset(migraine_m_escalc, Measurement %in% target_measures)
  400. migraine_m_filtered$study <- 'migraine'
  401. ptn_m_escalc <- escalc(
  402. measure = 'SMDH',
  403. m1i = Mean_Group1,
  404. sd1i = SD_Group1,
  405. n1i = N_Group1,
  406. m2i = Mean_Group2,
  407. sd2i = SD_Group2,
  408. n2i = N_Group2,
  409. data = ptn_m_summary_long
  410. )
  411. ptn_m_filtered <- subset(ptn_m_escalc, Measurement %in% target_measures)
  412. ptn_m_filtered$study <- 'ptn'
  413. meta_df_m <- rbind(oa_m_filtered, clbp_m_filtered, migraine_m_filtered,
  414. ptn_m_filtered)
  415. meta_results_m <- do.call(rbind, lapply(split(meta_df_m, list(meta_df_m$Region,
  416. meta_df_m$Measurement)),
  417. function(dfm){
  418. if(nrow(dfm) >=2) {
  419. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  420. data.frame(
  421. Region = dfm$Region[1],
  422. Measurement = dfm$Measurement[1],
  423. k = res$k,
  424. Estimate = res$b,
  425. SE = res$se,
  426. zval = res$zval,
  427. pval = res$pval,
  428. CI_lb = res$ci.lb,
  429. CI_ub = res$ci.ub,
  430. I2 = res$I2
  431. )
  432. } else {
  433. NULL
  434. }
  435. }))
  436. view(meta_results_m)
  437. meta_results_m <- meta_results_m %>%
  438. group_by(Measurement) %>%
  439. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  440. ungroup()
  441. ggplot(meta_results_m, aes(x = Estimate, y = reorder(Region, Estimate))) +
  442. geom_point(aes(color = pval < 0.05)) +
  443. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  444. geom_vline(xintercept = 0, linetype = "solid", color = "black", size = 1) + # <-- bold line at 0
  445. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  446. theme_bw() +
  447. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  448. clbp2_s1_m_filtered <- escalc(
  449. measure = 'SMDH',
  450. m1i = Mean_Group1,
  451. sd1i = SD_Group1,
  452. n1i = N_Group1,
  453. m2i = Mean_Group2,
  454. sd2i = SD_Group2,
  455. n2i = N_Group2,
  456. data = clbp2_s1_dk_m_summary_long
  457. )
  458. clbp2_s1_m_filtered <- subset(clbp2_s1_m_filtered, Measurement %in% target_measures)
  459. clbp2_s1_m_filtered$study <- 'cbp2_s1'
  460. clbp2_s2_m_filtered <- escalc(
  461. measure = 'SMDH',
  462. m1i = Mean_Group1,
  463. sd1i = SD_Group1,
  464. n1i = N_Group1,
  465. m2i = Mean_Group2,
  466. sd2i = SD_Group2,
  467. n2i = N_Group2,
  468. data = clbp2_s2_dk_m_summary_long
  469. )
  470. clbp2_s2_m_filtered <- subset(clbp2_s2_m_filtered, Measurement %in% target_measures)
  471. clbp2_s2_m_filtered$study <- 'cbp2_s2'
  472. meta_df_m_2 <- rbind(oa_m_filtered, clbp_m_filtered, migraine_m_filtered,
  473. ptn_m_filtered, clbp2_s1_m_filtered, clbp2_s2_m_filtered)
  474. meta_results_m_2 <- do.call(rbind, lapply(split(meta_df_m_2, list(meta_df_m_2$Region,
  475. meta_df_m_2$Measurement)),
  476. function(dfm){
  477. if(nrow(dfm) >=2) {
  478. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  479. pred <- predict(res)
  480. data.frame(
  481. Region = dfm$Region[1],
  482. Measurement = dfm$Measurement[1],
  483. k = res$k,
  484. Estimate = res$b,
  485. SE = res$se,
  486. zval = res$zval,
  487. pval = res$pval,
  488. CI_lb = res$ci.lb,
  489. CI_ub = res$ci.ub,
  490. PI_lb = pred$pi.lb,
  491. PI_ub = pred$pi.ub,
  492. I2 = res$I2,
  493. Q = res$QE,
  494. pval_Q = res$QEp,
  495. t2 = res$tau2
  496. )
  497. } else {
  498. NULL
  499. }
  500. }))
  501. meta_results_m_2 <- meta_results_m_2 %>%
  502. group_by(Measurement) %>%
  503. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  504. ungroup()
  505. meta_results_m_2 <- meta_results_m_2 %>%
  506. group_by(Measurement) %>%
  507. mutate(Q_FDR_p = p.adjust(pval_Q, method = 'BH')) %>%
  508. ungroup()
  509. ggplot(meta_results_m_2, aes(x = Estimate, y = reorder(Region, Estimate))) +
  510. geom_point(aes(color = FDR < 0.05)) +
  511. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  512. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  513. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  514. theme_bw() +
  515. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  516. #######################################################################
  517. #Use F results and redo
  518. #######################################################################
  519. OA_f_escalc <- escalc(
  520. measure = 'SMDH',
  521. m1i = Mean_Group1,
  522. sd1i = SD_Group1,
  523. n1i = N_Group1,
  524. m2i = Mean_Group2,
  525. sd2i = SD_Group2,
  526. n2i = N_Group2,
  527. data = OA_F_summary_long
  528. )
  529. oa_f_filtered <- subset(OA_f_escalc, Measurement %in% target_measures)
  530. oa_f_filtered$study <- 'oa'
  531. CLBP_f_escalc <- escalc(
  532. measure = 'SMDH',
  533. m1i = Mean_Group1,
  534. sd1i = SD_Group1,
  535. n1i = N_Group1,
  536. m2i = Mean_Group2,
  537. sd2i = SD_Group2,
  538. n2i = N_Group2,
  539. data = clbp_f_summary_long
  540. )
  541. clbp_f_filtered <- subset(CLBP_f_escalc, Measurement %in% target_measures)
  542. clbp_f_filtered$study <- 'clbp'
  543. migraine_f_escalc <- escalc(
  544. measure = 'SMDH',
  545. m1i = Mean_Group1,
  546. sd1i = SD_Group1,
  547. n1i = N_Group1,
  548. m2i = Mean_Group2,
  549. sd2i = SD_Group2,
  550. n2i = N_Group2,
  551. data = migraine_f_summary_long
  552. )
  553. migraine_f_filtered <- subset(migraine_f_escalc, Measurement %in% target_measures)
  554. migraine_f_filtered$study <- 'migraine'
  555. ptn_f_escalc <- escalc(
  556. measure = 'SMDH',
  557. m1i = Mean_Group1,
  558. sd1i = SD_Group1,
  559. n1i = N_Group1,
  560. m2i = Mean_Group2,
  561. sd2i = SD_Group2,
  562. n2i = N_Group2,
  563. data = ptn_f_summary_long
  564. )
  565. ptn_f_filtered <- subset(ptn_f_escalc, Measurement %in% target_measures)
  566. ptn_f_filtered$study <- 'ptn'
  567. meta_df_f <- rbind(oa_m_filtered, clbp_m_filtered, migraine_m_filtered,
  568. ptn_m_filtered, fm_filtered, fm2_filtered)
  569. meta_results_f <- do.call(rbind, lapply(split(meta_df_f, list(meta_df_f$Region,
  570. meta_df_f$Measurement)),
  571. function(dfm){
  572. if(nrow(dfm) >=2) {
  573. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  574. data.frame(
  575. Region = dfm$Region[1],
  576. Measurement = dfm$Measurement[1],
  577. k = res$k,
  578. Estimate = res$b,
  579. SE = res$se,
  580. zval = res$zval,
  581. pval = res$pval,
  582. CI_lb = res$ci.lb,
  583. CI_ub = res$ci.ub,
  584. I2 = res$I2
  585. )
  586. } else {
  587. NULL
  588. }
  589. }))
  590. meta_results_f <- meta_results_f %>%
  591. group_by(Measurement) %>%
  592. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  593. ungroup()
  594. view(meta_results_f)
  595. write.csv(meta_results_m, "~/CP_Meta_Results_M.csv", row.names = F)
  596. write.csv(meta_results_f, "~/CP_Meta_Results_F.csv", row.names = F)
  597. entorhinal_m <- meta_df_m[grepl("entorhinal", meta_df_m$Region), ]
  598. entorhinal_m <- subset(entorhinal_m, Measurement %in% c('SurfArea','GrayVol'))
  599. entorinal_m_vis <- data.frame(Hedges = entorhinal_m$yi, SE = sqrt(entorhinal_m$vi),
  600. Measurement = entorhinal_m$Measurement, Study = entorhinal_m$study,
  601. Region = entorhinal_m$Region)
  602. entorhinal_sa_m <- subset(entorinal_m_vis, Measurement == 'SurfArea')
  603. entorhinal_vol_m <- subset(entorinal_m_vis, Measurement == 'GrayVol')
  604. viz_forest(entorhinal_sa_m, group = entorhinal_sa_m$Region, study_labels = entorhinal_sa_m$Study,
  605. annotate_CI = T, xlab = 'Hedges G')
  606. viz_forest(entorhinal_vol_m, group = entorhinal_vol_m$Region, study_labels = entorhinal_vol_m$Study,
  607. annotate_CI = T, xlab = 'Hedges G')
  608. entorhinal_f <- meta_df_f[grepl("entorhinal", meta_df_f$Region), ]
  609. entorhinal_f <- subset(entorhinal_f, Measurement %in% c('SurfArea','GrayVol'))
  610. entorinal_f_vis <- data.frame(Hedges = entorhinal_f$yi, SE = sqrt(entorhinal_f$vi),
  611. Measurement = entorhinal_f$Measurement, Study = entorhinal_f$study,
  612. Region = entorhinal_f$Region)
  613. entorhinal_sa_f <- subset(entorinal_f_vis, Measurement == 'SurfArea')
  614. entorhinal_vol_f <- subset(entorinal_f_vis, Measurement == 'GrayVol')
  615. viz_forest(entorhinal_sa_f, group = entorhinal_sa_f$Region, study_labels = entorhinal_sa_f$Study,
  616. annotate_CI = T, xlab = 'Hedges G')
  617. viz_forest(entorhinal_vol_f, group = entorhinal_vol_f$Region, study_labels = entorhinal_vol_f$Study,
  618. annotate_CI = T, xlab = 'Hedges G')
  619. clbp2_s1_f_filtered <- escalc(
  620. measure = 'SMDH',
  621. m1i = Mean_Group1,
  622. sd1i = SD_Group1,
  623. n1i = N_Group1,
  624. m2i = Mean_Group2,
  625. sd2i = SD_Group2,
  626. n2i = N_Group2,
  627. data = clbp2_s1_dk_f_summary_long
  628. )
  629. clbp2_s1_f_filtered <- subset(clbp2_s1_f_filtered, Measurement %in% target_measures)
  630. clbp2_s1_f_filtered$study <- 'cbp2_s1'
  631. clbp2_s2_f_filtered <- escalc(
  632. measure = 'SMDH',
  633. m1i = Mean_Group1,
  634. sd1i = SD_Group1,
  635. n1i = N_Group1,
  636. m2i = Mean_Group2,
  637. sd2i = SD_Group2,
  638. n2i = N_Group2,
  639. data = clbp2_s2_dk_f_summary_long
  640. )
  641. clbp2_s2_f_filtered <- subset(clbp2_s2_f_filtered, Measurement %in% target_measures)
  642. clbp2_s2_f_filtered$study <- 'cbp2_s2'
  643. meta_df_f_2 <- rbind(oa_f_filtered, clbp_f_filtered, migraine_f_filtered,
  644. ptn_f_filtered, fm_filtered, fm2_filtered, clbp2_s1_f_filtered,
  645. clbp2_s2_f_filtered)
  646. meta_results_f_2 <- do.call(rbind, lapply(split(meta_df_f_2, list(meta_df_f_2$Region,
  647. meta_df_f_2$Measurement)),
  648. function(dfm){
  649. if(nrow(dfm) >=2) {
  650. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  651. pred <- predict(res)
  652. data.frame(
  653. Region = dfm$Region[1],
  654. Measurement = dfm$Measurement[1],
  655. k = res$k,
  656. Estimate = res$b,
  657. SE = res$se,
  658. zval = res$zval,
  659. pval = res$pval,
  660. CI_lb = res$ci.lb,
  661. CI_ub = res$ci.ub,
  662. PI_lb = pred$pi.lb,
  663. PI_ub = pred$pi.ub,
  664. I2 = res$I2,
  665. Q = res$QE,
  666. pval_Q = res$QEp,
  667. t2 = res$tau2
  668. )
  669. } else {
  670. NULL
  671. }
  672. }))
  673. meta_results_f_2 <- meta_results_f_2 %>%
  674. group_by(Measurement) %>%
  675. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  676. ungroup()
  677. meta_results_f_2 <- meta_results_f_2 %>%
  678. group_by(Measurement) %>%
  679. mutate(Q_FDR_p = p.adjust(pval_Q, method = 'BH')) %>%
  680. ungroup()
  681. ggplot(meta_results_f_2, aes(x = Estimate, y = reorder(Region, Estimate))) +
  682. geom_point(aes(color = FDR < 0.05)) +
  683. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  684. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  685. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  686. theme_bw() +
  687. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  688. #######################################################################
  689. #Add CLBP2 S1 and S2 and redo
  690. #######################################################################
  691. meta_df_4 <- rbind(oa_filtered, fm_filtered, clbp_filtered, m_filtered,
  692. ptn_filtered, fm2_filtered, clbp2_s1_filtered, clbp2_s2_filtered)
  693. meta_results_4 <- do.call(rbind, lapply(split(meta_df_4, list(meta_df_4$Region,
  694. meta_df_4$Measurement)),
  695. function(dfm){
  696. if(nrow(dfm) >=2) {
  697. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  698. data.frame(
  699. Region = dfm$Region[1],
  700. Measurement = dfm$Measurement[1],
  701. k = res$k,
  702. Estimate = res$b,
  703. SE = res$se,
  704. zval = res$zval,
  705. pval = res$pval,
  706. CI_lb = res$ci.lb,
  707. CI_ub = res$ci.ub,
  708. I2 = res$I2
  709. )
  710. } else {
  711. NULL
  712. }
  713. }))
  714. meta_results_4 <- meta_results_4 %>%
  715. group_by(Measurement) %>%
  716. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  717. ungroup()
  718. view(meta_results_4)
  719. entorhinal_4 <- meta_df_4[grepl("entorhinal", meta_df_4$Region), ]
  720. entorhinal_4_vol <- subset(entorhinal_4, Measurement %in% 'GrayVol')
  721. entorhinal_4_vol_vis <- data.frame(Hedges = entorhinal_4_vol$yi, SE = sqrt(entorhinal_4_vol$vi),
  722. Measurement = entorhinal_4_vol$Measurement, Study = entorhinal_4_vol$study,
  723. Region = entorhinal_4_vol$Region)
  724. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'OA'] <- 'Tétreault, 2016, Osteoarthritis'
  725. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'FM'] <- 'Pando-Naude, 2019, Fibromyalgia'
  726. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'CLBP'] <- 'Makary, 2020, Chronic Lower Back Pain'
  727. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'migraine'] <- 'Seminowicz, 2020, Migraine'
  728. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'PTN'] <- 'Filimonova, 2025, Primary Trigeminal Neuralgia'
  729. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'FM2'] <- 'Balducci, 2022, Fibromyalgia'
  730. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'CLBP2_S1'] <- 'Mano, 2018, Chronic Lower Back Pain (UK Data)'
  731. entorhinal_4_vol_vis$Study[entorhinal_4_vol_vis$Study == 'CLBP2_S2'] <- 'Mano, 2018, Chronic Lower Back Pain (Japan Data)'
  732. entorhinal_4_vol_vis$Region[entorhinal_4_vol_vis$Region == 'lh_entorhinal'] <- 'Left Entorhinal Cortex Volume'
  733. entorhinal_4_vol_vis$Region[entorhinal_4_vol_vis$Region == 'rh_entorhinal'] <- 'Right Entorhinal Cortex Volume'
  734. viz_forest(entorhinal_4_vol_vis, group = entorhinal_4_vol_vis$Region,
  735. study_labels = entorhinal_4_vol_vis$Study,
  736. annotate_CI = T, xlab = 'Hedges G', variant = 'rain')
  737. ggplot(meta_results_4, aes(x = Estimate, y = Region)) +
  738. geom_point(aes(color = pval < 0.05)) +
  739. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  740. geom_vline(xintercept = 0, linetype = "solid", color = "black", size = 1) + # <-- bold line at 0
  741. facet_wrap(~Measurement, scales = "free_y", ncol = 5) +
  742. ggplot2::theme_bw() +
  743. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  744. #######################################################################
  745. #Subcortical Meta analysis
  746. #######################################################################
  747. OA_Sub_escalc <- escalc(
  748. measure = 'SMDH',
  749. m1i = Mean_Group1,
  750. sd1i = SD_Group1,
  751. n1i = N_Group1,
  752. m2i = Mean_Group2,
  753. sd2i = SD_Group2,
  754. n2i = N_Group2,
  755. data = OA_subcort_summary
  756. )
  757. OA_Sub_escalc$study <- 'OA'
  758. FM_Sub_escalc <- escalc(
  759. measure = 'SMDH',
  760. m1i = Mean_Group1,
  761. sd1i = SD_Group1,
  762. n1i = N_Group1,
  763. m2i = Mean_Group2,
  764. sd2i = SD_Group2,
  765. n2i = N_Group2,
  766. data = FM_sub_summary_2
  767. )
  768. FM_Sub_escalc$study <- 'FM'
  769. FM2_Sub_escalc <- escalc(
  770. measure = 'SMDH',
  771. m1i = Mean_Group1,
  772. sd1i = SD_Group1,
  773. n1i = N_Group1,
  774. m2i = Mean_Group2,
  775. sd2i = SD_Group2,
  776. n2i = N_Group2,
  777. data = fm2_sub_summary
  778. )
  779. FM2_Sub_escalc$significant <- NULL
  780. FM2_Sub_escalc$study <- 'FM2'
  781. CLBP_Sub_escalc <- escalc(
  782. measure = 'SMDH',
  783. m1i = Mean_Group1,
  784. sd1i = SD_Group1,
  785. n1i = N_Group1,
  786. m2i = Mean_Group2,
  787. sd2i = SD_Group2,
  788. n2i = N_Group2,
  789. data = clbp_sub_summary
  790. )
  791. CLBP_Sub_escalc$study <- 'CLBP'
  792. CLBP_Sub_s1_escalc <- escalc(
  793. measure = 'SMDH',
  794. m1i = Mean_Group1,
  795. sd1i = SD_Group1,
  796. n1i = N_Group1,
  797. m2i = Mean_Group2,
  798. sd2i = SD_Group2,
  799. n2i = N_Group2,
  800. data = clbp2_s1_sub_summary
  801. )
  802. CLBP_Sub_s1_escalc$study <- 'CLBP_s1'
  803. CLBP_Sub_s2_escalc <- escalc(
  804. measure = 'SMDH',
  805. m1i = Mean_Group1,
  806. sd1i = SD_Group1,
  807. n1i = N_Group1,
  808. m2i = Mean_Group2,
  809. sd2i = SD_Group2,
  810. n2i = N_Group2,
  811. data = clbp2_s2_sub_summary
  812. )
  813. CLBP_Sub_s2_escalc$study <- 'CLBP_s2'
  814. migraine_sub_escalc <- escalc(
  815. measure = 'SMDH',
  816. m1i = Mean_Group1,
  817. sd1i = SD_Group1,
  818. n1i = N_Group1,
  819. m2i = Mean_Group2,
  820. sd2i = SD_Group2,
  821. n2i = N_Group2,
  822. data = migraine_sub_summary
  823. )
  824. migraine_sub_escalc$study <- 'migraine'
  825. ptn_sub_escalc <-escalc(
  826. measure = 'SMDH',
  827. m1i = Mean_Group1,
  828. sd1i = SD_Group1,
  829. n1i = N_Group1,
  830. m2i = Mean_Group2,
  831. sd2i = SD_Group2,
  832. n2i = N_Group2,
  833. data = ptn_sub_summary
  834. )
  835. ptn_sub_escalc$study <- 'ptn'
  836. ###############################################################
  837. #Meta subcort
  838. ###############################################################
  839. meta_sub <- rbind(OA_Sub_escalc, FM_Sub_escalc, FM2_Sub_escalc, CLBP_Sub_escalc,
  840. CLBP_Sub_s1_escalc, CLBP_Sub_s2_escalc, migraine_sub_escalc,
  841. ptn_sub_escalc)
  842. meta_sub <- meta_sub %>%
  843. filter(!grepl("hypointensities", Region, ignore.case = T))
  844. meta_sub <- meta_sub %>%
  845. filter(!grepl("X5th",Region, ignore.case = T))
  846. meta_sub_results <- do.call(rbind, lapply(split(meta_sub, list(meta_sub$Region)),
  847. function(dfm){
  848. if(nrow(dfm) >=2) {
  849. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  850. data.frame(
  851. Region = dfm$Region[1],
  852. k = res$k,
  853. Estimate = res$b,
  854. SE = res$se,
  855. zval = res$zval,
  856. pval = res$pval,
  857. CI_lb = res$ci.lb,
  858. CI_ub = res$ci.ub,
  859. I2 = res$I2
  860. )
  861. } else {
  862. NULL
  863. }
  864. }))
  865. meta_sub_results <- meta_sub_results %>%
  866. mutate(FDR = p.adjust(pval, method = 'BH'))
  867. ggplot(meta_sub_results, aes(x = Estimate, y = reorder(Region, Estimate))) +
  868. geom_point(aes(color = pval < 0.05)) +
  869. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  870. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  871. theme_bw() +
  872. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  873. amygdala <- meta_sub[grepl("Amygdala", meta_sub$Region), ]
  874. amygdala_vis <- data.frame(Hedges = amygdala$yi, SE = sqrt(amygdala$vi),
  875. Study = amygdala$study,
  876. Region = amygdala$Region)
  877. viz_forest(amygdala_vis, group = amygdala_vis$Region,
  878. study_labels = amygdala_vis$Study,
  879. annotate_CI = T, xlab = 'Hedges G', variant = 'rain')
  880. #########################################################################
  881. #visual - group effect size
  882. #########################################################################
  883. visual <- meta_results_4
  884. visual$significant <- visual$pval < 0.05 & abs(visual$Estimate) > 0.2
  885. ggplot(visual, aes(x = Estimate, y = -log10(pval))) +
  886. geom_point(aes(color = significant), alpha = 0.7, size = 2) +
  887. geom_vline(xintercept = c(-0.8, 0.8), linetype = "dashed", color = "gray") +
  888. geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "gray") +
  889. geom_text(aes(label = ifelse(significant, Region, "")), hjust = 1.1, vjust = 0.5, size = 3) +
  890. scale_color_manual(values = c("grey", "red")) +
  891. labs(x = "Effect Size (Hedge's g)",
  892. y = "-log10(p-value)",
  893. title = "Volcano Plot of Brain Regions",
  894. color = "Significant") +
  895. theme_minimal() +
  896. theme(legend.position = "top") +
  897. facet_wrap(~Measurement) +
  898. xlim(-2,2)
  899. visual_long <- visual %>%
  900. separate(Region, into = c("hemi","region"), sep = "_") %>%
  901. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  902. regions <- visual_long$region %>% str_to_lower()
  903. miss <- setdiff(regions, atlas_regions)
  904. matching <- sapply(miss, function(regions){
  905. distances <- stringdist(regions, atlas_regions, method = 'jw')
  906. atlas_regions[which.min(distances)]
  907. })
  908. recoding <- setNames(matching, miss)
  909. recoding
  910. visual_long <- visual_long %>%
  911. mutate(region = str_to_lower(region),
  912. region = ifelse(region %in% names(recoding),
  913. recoding[region], region))
  914. visual_long$Measurement[visual_long$Measurement == 'SurfArea'] <- 'Surface Area'
  915. visual_long$Measurement[visual_long$Measurement == 'GrayVol'] <- 'Volume'
  916. visual_long$Measurement[visual_long$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  917. visual_long$Measurement[visual_long$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  918. visual_long$Measurement[visual_long$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  919. colnames(visual_long)[which(names(visual_long) == "Estimate")] <- "Hedge's G"
  920. visual_long$sig_outline <- ifelse(visual_long$FDR < 0.05, "sig", "ns")
  921. ggplot(visual_long, aes(fill = -`Hedge's G`)) +
  922. geom_brain(atlas = dk) +
  923. scale_fill_distiller(palette = 'RdBu',limits = c(-.5,.5)) +
  924. theme_void() +
  925. labs(title = "Regional Effect Sizes in Individuals with Chronic Pain vs Controls") +
  926. facet_wrap(~factor(Measurement, levels = c("Surface Area","Volume","Cortical Thickness",
  927. "Mean Curvature",'Gaussian Curvature')), ncol = 1) +
  928. theme(legend.position = 'bottom',
  929. legend.text = element_text(size = 12),
  930. legend.key.size = unit(1, 'cm'),
  931. legend.title = element_blank())
  932. #006f3c
  933. ggplot(visual_long, aes(fill = -`Hedge's G`, color = sig_outline, size = sig_outline)) +
  934. geom_brain(atlas = dk) +
  935. scale_fill_distiller(palette = 'RdBu', limits = c(-.5, .5)) +
  936. scale_color_manual(values = c("sig" = "black", "ns" = "grey70")) +
  937. scale_size_manual(values = c("sig" = 0.7, "ns" = 0.2)) + # thicker red, thinner black
  938. theme_void() +
  939. labs(title = "Regional Effect Sizes in Individuals with Chronic Pain vs Controls") +
  940. facet_wrap(~factor(Measurement, levels = c("Surface Area","Volume","Cortical Thickness",
  941. "Mean Curvature",'Gaussian Curvature')), ncol = 1) +
  942. theme(
  943. legend.position = 'bottom',
  944. legend.text = element_text(size = 12),
  945. legend.key.size = unit(1, 'cm'),
  946. legend.title = element_blank()
  947. )
  948. ggplot(visual_long, aes(fill = `Hedge's G`)) +
  949. geom_brain(atlas = dk) +
  950. scale_fill_distiller(palette = 'RdBu', limits = c(-.5, .5)) +
  951. theme_void() +
  952. labs(title = "Regional Effect Sizes in Individuals with Chronic Pain vs Controls") +
  953. facet_grid(rows = vars(hemi), cols = vars(factor(Measurement, levels = c(
  954. "Surface Area", "Volume", "Cortical Thickness", "Mean Curvature", "Gaussian Curvature"
  955. )))) +
  956. theme(
  957. legend.position = c(1.05, 0.6),
  958. strip.text = element_text(size = 9),
  959. plot.title = element_text(hjust = 0.5)
  960. )
  961. gaus_meta <- subset(visual, Measurement == 'GausCurv')
  962. gaus_meta <- subset(gaus_meta, FDR < 0.05)
  963. gaus_meta$hemi <- sub("^(lh|rh)_.*", "\\1", gaus_meta$Region)
  964. gaus_meta$BaseRegion <- sub("^(lh|rh)_(.*)", "\\2", gaus_meta$Region)
  965. region_counts <- table(gaus_meta$BaseRegion, gaus_meta$hemi)
  966. bilateral_regions <- rownames(region_counts)[rowSums(region_counts > 0) == 2]
  967. bilateral_regions
  968. gaus_filt <- subset(gaus_meta, BaseRegion %in% bilateral_regions)
  969. gaus_filt_long <- gaus_filt %>%
  970. separate(Region, into = c("hemi","region"), sep = "_") %>%
  971. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  972. regions <- gaus_filt_long$region %>% str_to_lower()
  973. miss <- setdiff(regions, atlas_regions)
  974. matching <- sapply(miss, function(regions){
  975. distances <- stringdist(regions, atlas_regions, method = 'jw')
  976. atlas_regions[which.min(distances)]
  977. })
  978. recoding <- setNames(matching, miss)
  979. recoding
  980. gaus_filt_long <- gaus_filt_long %>%
  981. mutate(region = str_to_lower(region),
  982. region = ifelse(region %in% names(recoding),
  983. recoding[region], region))
  984. gaus_filt_long$Measurement[gaus_filt_long$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  985. colnames(gaus_filt_long)[which(names(gaus_filt_long) == "Estimate")] <- "Hedge's G"
  986. ggplot(gaus_filt_long, aes(fill = `Hedge's G`)) +
  987. geom_brain(atlas = dk) +
  988. scale_fill_distiller(palette = 'RdBu',limits = c(-1, 1)) +
  989. theme_void() +
  990. labs(title = "Effect sizes the Gaussian Curvature of regions significantly associated with chronic pain") +
  991. theme(legend.position = 'bottom')
  992. ###################################################################
  993. visual_m <- meta_results_m_2
  994. visual_m$significant <- visual_m$pval < 0.05 & abs(visual_m$Estimate) > 0.2
  995. ggplot(visual_m, aes(x = Estimate, y = -log10(pval))) +
  996. geom_point(aes(color = significant), alpha = 0.7, size = 2) +
  997. geom_vline(xintercept = c(-0.8, 0.8), linetype = "dashed", color = "gray") +
  998. geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "gray") +
  999. geom_text(aes(label = ifelse(significant, Region, "")), hjust = 1.1, vjust = 0.5, size = 3) +
  1000. scale_color_manual(values = c("grey", "red")) +
  1001. labs(x = "Effect Size (Hedge's g)",
  1002. y = "-log10(p-value)",
  1003. title = "Volcano Plot of Brain Regions",
  1004. color = "Significant") +
  1005. theme_minimal() +
  1006. theme(legend.position = "top") +
  1007. facet_wrap(~Measurement) +
  1008. xlim(-2,2)
  1009. meta_results_m_2
  1010. meta_results_m_2 <- meta_results_m_2 %>%
  1011. separate(Region, into = c("hemi","region"), sep = "_") %>%
  1012. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  1013. regions <- meta_results_m_2$region %>% str_to_lower()
  1014. miss <- setdiff(regions, atlas_regions)
  1015. matching <- sapply(miss, function(regions){
  1016. distances <- stringdist(regions, atlas_regions, method = 'jw')
  1017. atlas_regions[which.min(distances)]
  1018. })
  1019. recoding <- setNames(matching, miss)
  1020. recoding
  1021. meta_results_m_2 <- meta_results_m_2 %>%
  1022. mutate(region = str_to_lower(region),
  1023. region = ifelse(region %in% names(recoding),
  1024. recoding[region], region))
  1025. write_xlsx(meta_results_m_2, "meta_m_results.xlsx")
  1026. visual_m <- visual_m %>%
  1027. separate(region, into = c("hemi","region"), sep = "_") %>%
  1028. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  1029. regions <- visual_m$region %>% str_to_lower()
  1030. miss <- setdiff(regions, atlas_regions)
  1031. matching <- sapply(miss, function(regions){
  1032. distances <- stringdist(regions, atlas_regions, method = 'jw')
  1033. atlas_regions[which.min(distances)]
  1034. })
  1035. recoding <- setNames(matching, miss)
  1036. recoding
  1037. visual_m <- visual_m %>%
  1038. mutate(region = str_to_lower(region),
  1039. region = ifelse(region %in% names(recoding),
  1040. recoding[region], region))
  1041. visual_m$Measurement[visual_m$Measurement == 'SurfArea'] <- 'Surface Area'
  1042. visual_m$Measurement[visual_m$Measurement == 'GrayVol'] <- 'Volume'
  1043. visual_m$Measurement[visual_m$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  1044. visual_m$Measurement[visual_m$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  1045. visual_m$Measurement[visual_m$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  1046. colnames(visual_m)[which(names(visual_m) == "Estimate")] <- "Hedge's G"
  1047. ggplot(visual_m_long, aes(fill = `Hedge's G`)) +
  1048. geom_brain(atlas = dk) +
  1049. scale_fill_distiller(palette = 'RdBu',limits = c(-1, 1)) +
  1050. theme_void() +
  1051. labs(title = "Regional Effect Sizes in CP vs Controls") +
  1052. facet_wrap(~factor(Measurement, levels = c("Surface Area","Volume","Cortical Thickness",
  1053. "Mean Curvature",'Gaussian Curvature')), ncol = 1) +
  1054. theme(legend.position = c(1.05,.60))
  1055. #########################################################################
  1056. visual_f <- meta_results_f_2
  1057. visual_f$significant <- visual_f$pval < 0.05 & abs(visual_f$Estimate) > 0.2
  1058. ggplot(visual_f, aes(x = Estimate, y = -log10(pval))) +
  1059. geom_point(aes(color = significant), alpha = 0.7, size = 2) +
  1060. geom_vline(xintercept = c(-0.8, 0.8), linetype = "dashed", color = "gray") +
  1061. geom_hline(yintercept = -log10(0.05), linetype = "dashed", color = "gray") +
  1062. geom_text(aes(label = ifelse(significant, region, "")), hjust = 1.1, vjust = 0.5, size = 3) +
  1063. scale_color_manual(values = c("grey", "red")) +
  1064. labs(x = "Effect Size (Hedge's g)",
  1065. y = "-log10(p-value)",
  1066. title = "Volcano Plot of Brain Regions",
  1067. color = "Significant") +
  1068. theme_minimal() +
  1069. theme(legend.position = "top") +
  1070. facet_wrap(~Measurement) +
  1071. xlim(-2,2)
  1072. meta_results_f_2
  1073. meta_results_f_2 <- meta_results_f_2 %>%
  1074. separate(Region, into = c("hemi","region"), sep = "_") %>%
  1075. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  1076. regions <- meta_results_f_2$region %>% str_to_lower()
  1077. miss <- setdiff(regions, atlas_regions)
  1078. matching <- sapply(miss, function(regions){
  1079. distances <- stringdist(regions, atlas_regions, method = 'jw')
  1080. atlas_regions[which.min(distances)]
  1081. })
  1082. recoding <- setNames(matching, miss)
  1083. recoding
  1084. meta_results_f_2 <- meta_results_f_2 %>%
  1085. mutate(region = str_to_lower(region),
  1086. region = ifelse(region %in% names(recoding),
  1087. recoding[region], region))
  1088. write_xlsx(meta_results_f_2, "meta_results_f.xlsx")
  1089. visual_f_long <- visual_f %>%
  1090. separate(region, into = c("hemi","region"), sep = "_") %>%
  1091. mutate(hemi = dplyr::recode(hemi, lh = 'left', rh = 'right'))
  1092. regions <- visual_f_long$region %>% str_to_lower()
  1093. miss <- setdiff(regions, atlas_regions)
  1094. matching <- sapply(miss, function(regions){
  1095. distances <- stringdist(regions, atlas_regions, method = 'jw')
  1096. atlas_regions[which.min(distances)]
  1097. })
  1098. recoding <- setNames(matching, miss)
  1099. recoding
  1100. visual_f_long <- visual_f_long %>%
  1101. mutate(region = str_to_lower(region),
  1102. region = ifelse(region %in% names(recoding),
  1103. recoding[region], region))
  1104. visual_f$Measurement[visual_f$Measurement == 'SurfArea'] <- 'Surface Area'
  1105. visual_f$Measurement[visual_f$Measurement == 'GrayVol'] <- 'Volume'
  1106. visual_f$Measurement[visual_f$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  1107. visual_f$Measurement[visual_f$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  1108. visual_f$Measurement[visual_f$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  1109. colnames(visual_f)[which(names(visual_f) == "Estimate")] <- "Hedge's G"
  1110. ggplot(visual_f_long, aes(fill = `Hedge's G`)) +
  1111. geom_brain(atlas = dk) +
  1112. scale_fill_distiller(palette = 'RdBu',limits = c(-1, 1)) +
  1113. theme_void() +
  1114. labs(title = "Regional Effect Sizes in CP vs Controls") +
  1115. facet_wrap(~factor(Measurement, levels = c("Surface Area","Volume","Cortical Thickness",
  1116. "Mean Curvature",'Gaussian Curvature')), ncol = 1) +
  1117. theme(legend.position = c(1.05,.60))
  1118. visual_f_long
  1119. identical(visual_f$region, visual_m$region)
  1120. identical(visual_f$Measurement, visual_m$Measurement)
  1121. identical(visual_f$hemi, visual_m$hemi)
  1122. x <- c(.2,-.2,-.3,.4-.68,.9)
  1123. t.test(x, mu = 0, alternative = 'two.sided')
  1124. meta_test <- data.frame(hemi = visual_m$hemi, Region = visual_m$region, Measurement = visual_m$Measurement,
  1125. estimate_m = visual_m$`Hedge's G`, estimate_f = visual_f$`Hedge's G`,
  1126. SE_m = visual_m$SE, SE_f = visual_f$SE, CI_lb_m = visual_m$CI_lb,
  1127. CI_ub_m = visual_m$CI_ub, CI_lb_f = visual_f$CI_lb, CI_ub_f = visual_f$CI_ub,
  1128. PI_lb_m = visual_m$PI_lb, PI_ub_m = visual_m$PI_ub, PI_ub_f = visual_f$PI_lb,
  1129. PI_lb_f = visual_f$PI_ub)
  1130. meta_test
  1131. meta_test$Measurement[meta_test$Measurement == 'SurfArea'] <- 'Surface Area'
  1132. meta_test$Measurement[meta_test$Measurement == 'GrayVol'] <- 'Volume'
  1133. meta_test$Measurement[meta_test$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  1134. meta_test$Measurement[meta_test$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  1135. meta_test$Measurement[meta_test$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  1136. meta_test_extended <- meta_test %>%
  1137. bind_rows(tibble(
  1138. estimate_m = NA,
  1139. estimate_f = NA,
  1140. Measurement = "Legend",
  1141. Region = NA,
  1142. SE_m = NA, SE_f = NA,
  1143. CI_lb_m = NA, CI_ub_m = NA,
  1144. CI_lb_f = NA, CI_ub_f = NA,
  1145. difference = NA
  1146. ))
  1147. meta_test_extended$Measurement <- factor(meta_test_extended$Measurement,
  1148. levels = c('Volume','Surface Area','Cortical Thickness',
  1149. 'Mean Curvature','Gaussian Curvature', "Legend"))
  1150. ioflickhuck <- ggplot(meta_test_extended, aes(x = estimate_m, y = estimate_f)) +
  1151. geom_point(alpha = .6) +
  1152. xlim(-0.75, 0.75) +
  1153. ylim(-0.75, 0.75) +
  1154. facet_wrap(~Measurement, ncol = 3) +
  1155. geom_abline(slope = 1, intercept = 0, linetype = 'longdash',color = '#e43c40') +
  1156. geom_smooth(method = 'lm', color = '#00b2a9') +
  1157. geom_hline(yintercept = 0, linetype = 'twodash', color = 'black') +
  1158. geom_vline(xintercept = 0, linetype = 'twodash', color = 'black') +
  1159. xlab('Male estimated effect size') +
  1160. ylab('Female estiamted effect size') +
  1161. theme_bw(base_size = 20) +
  1162. theme(
  1163. strip.text = element_text( size = 20),
  1164. panel.spacing = unit(3, 'lines')
  1165. )
  1166. ioflickhuck
  1167. ggsave("sex_diff.png", plot = ioflickhuck, dpi = 500, width = 18, height = 12, units = 'in', bg = 'white')
  1168. ggplot(meta_test, aes(x = estimate_m, y = estimate_f)) +
  1169. geom_point() +
  1170. theme_bw() +
  1171. geom_smooth(method = 'lm') +
  1172. facet_wrap(~Measurement) +
  1173. xlim(-1,1) +
  1174. ylim(-1,1) +
  1175. geom_abline(slope = 1, intercept = 0, linetype = 'longdash',color = '#e43c40') +
  1176. geom_abline(slope = -1, intercept = 0, linetype = 'longdash', color = '#e43c40') +
  1177. geom_smooth(method = 'lm', color = '#00b2a9') +
  1178. geom_hline(yintercept = 0, linetype = 'twodash', color = 'black') +
  1179. geom_vline(xintercept = 0, linetype = 'twodash', color = 'black') +
  1180. theme(panel.spacing = unit(2))
  1181. ggsave("sex_diff_estimates.png", plot = ioflickhuck, dpi = 500, width = 18, height = 12, units = 'in', bg = 'white')
  1182. ggplot() +
  1183. xlim(-1,1) +
  1184. ylim(-1,1) +
  1185. theme_minimal() +
  1186. geom_hline(yintercept = 0, linetype = 'twodash', color = 'black') +
  1187. geom_vline(xintercept = 0, linetype = 'twodash', color = 'black') +
  1188. geom_abline(slope = 1, intercept = 0, linetype = 'longdash', color = '#e43c40')
  1189. meta_test$difference <- meta_test$estimate_f - meta_test$estimate_m
  1190. meta_test$z_diff <- (meta_test$estimate_m - meta_test$estimate_f) / sqrt(meta_test$SE_m^2 + meta_test$SE_f^2)
  1191. meta_test$p <- 2*pnorm(abs(meta_test$z_diff), lower.tail = FALSE)
  1192. meta_test <- meta_test %>%
  1193. group_by(Measurement) %>%
  1194. mutate(FDR = p.adjust(p, method = 'BH')) %>%
  1195. ungroup()
  1196. write_xlsx(meta_test,"meta_sex_cortical.xlsx")
  1197. meta_test$effect_direction <- case_when(
  1198. sign(meta_test$estimate_m) == sign(meta_test$estimate_f) ~ "Same Direction",
  1199. sign(meta_test$estimate_m) != sign(meta_test$estimate_f) ~ "Opposite Direction",
  1200. TRUE ~ "Undefined"
  1201. )
  1202. meta_test$z_diff_mag <- (abs(meta_test$estimate_m) - abs(meta_test$estimate_f)) /
  1203. sqrt(meta_test$SE_m^2 + meta_test$SE_f^2)
  1204. ggplot(meta_test, aes(x = Measurement, y = z_diff_mag, color = effect_direction)) +
  1205. geom_boxplot(outlier.shape = NA) +
  1206. geom_jitter(width = 0.1, alpha = 0.7, size = 2) +
  1207. geom_hline(yintercept = 0, linetype = "dashed") +
  1208. scale_color_manual(values = c("Same Direction" = "black", "Opposite Direction" = "#e41a1c")) +
  1209. labs(
  1210. y = "Z-diff (|m| - |f|)",
  1211. title = "Effect Size Strength Difference (M > F = +)",
  1212. color = "Direction Match"
  1213. ) +
  1214. theme_minimal() +
  1215. facet_wrap(~effect_direction)
  1216. ggplot(meta_test, aes(x = Measurement, y = z_diff)) +
  1217. geom_boxplot() +
  1218. geom_jitter(width = .1)+
  1219. geom_hline(yintercept = 0, linetype = "dashed") +
  1220. labs(y = "Z-difference (m - f)", title = "Z-diff by Measurement (M > F = +)") +
  1221. theme_minimal()
  1222. ggplot(meta_test, aes(x = Measurement, y = difference)) +
  1223. theme_minimal() +
  1224. geom_boxplot() +
  1225. geom_jitter(width = .1)
  1226. write.csv(meta_test, "~/sex_differences_v462.csv", row.names = F)
  1227. gc_estimate <- subset(meta_test, Measurement == 'GausCurv')
  1228. shapiro.test(gc_estimate$difference)
  1229. t.test(gc_estimate$difference, mu = 0, alternative = 'two.sided')
  1230. t.test(gc_estimate$estimate_m, gc_estimate$estimate_f, alternative = 'two.sided')
  1231. mc_estimate <- subset(meta_test, Measurement == 'MeanCurv')
  1232. shapiro.test(mc_estimate$difference)
  1233. t.test(mc_estimate$difference, mu = 0, alternative = 'two.sided')
  1234. t.test(mc_estimate$estimate_m, mc_estimate$estimate_f, alternative = 'two.sided')
  1235. gv_estimate <- subset(meta_test, Measurement == 'GrayVol')
  1236. shapiro.test(gv_estimate$difference)
  1237. t.test(gv_estimate$difference, mu = 0, alternative = 'two.sided')
  1238. t.test(gv_estimate$estimate_m, gv_estimate$estimate_f, alternative = 'two.sided')
  1239. sa_estimate <- subset(meta_test, Measurement == 'SurfArea')
  1240. shapiro.test(sa_estimate$difference)
  1241. t.test(sa_estimate$difference, mu = 0, alternative = 'two.sided')
  1242. ct_estimate <- subset(meta_test, Measurement == 'ThickAvg')
  1243. shapiro.test(ct_estimate$difference)
  1244. t.test(ct_estimate$difference, mu = 0, alternative = 'two.sided')
  1245. ###########################################################################
  1246. #Sex analysis
  1247. ###########################################################################
  1248. meta_df_m_2
  1249. meta_df_f_2
  1250. meta_df_m_2$sex <- 'male'
  1251. meta_df_f_2$sex <- 'female'
  1252. meta_m <- meta_df_m_2[c("Region","Measurement","yi","vi","study","sex")]
  1253. meta_f <- meta_df_f_2[c("Region","Measurement","yi","vi","study","sex")]
  1254. meta_sex <- rbind(meta_m, meta_f)
  1255. res <- rma.mv(
  1256. yi = yi, V = vi,
  1257. mods = ~ sex,
  1258. random = ~1 | study,
  1259. data = meta_sex
  1260. )
  1261. summary(res)
  1262. collapsed_combined
  1263. collapsed_by_sex
  1264. gs <- data.frame()
  1265. #use meta_sex for old answers
  1266. for (m in unique(collapsed_by_sex$Measurement)) {
  1267. df_m <- subset(collapsed_by_sex, Measurement == m)
  1268. res_mes <- rma.mv(
  1269. yi = yi,
  1270. V = vi,
  1271. mods = ~ sex,
  1272. random = ~ 1 | study,
  1273. data = df_m
  1274. )
  1275. gs <- rbind(gs, data.frame(
  1276. Measurement = m,
  1277. Effect = res_mes$b,
  1278. SE = res_mes$se,
  1279. pval = res_mes$pval,
  1280. CI_lower = res_mes$ci.lb,
  1281. CI_upper = res_mes$ci.ub,
  1282. k = res_mes$k
  1283. ))
  1284. print(paste("Measurement:", m))
  1285. print(summary(res_mes))
  1286. }
  1287. measurements <- unique(meta_sex$Measurement)
  1288. group_summary <- data.frame()
  1289. group_summary_2 <- data.frame()
  1290. for (m in measurements) {
  1291. for (s in c("female", "male")) {
  1292. df_sub <- subset(meta_sex, Measurement == m & sex == s)
  1293. res_mes <- rma(
  1294. yi = yi,
  1295. vi = vi,
  1296. method = "REML",
  1297. data = df_sub
  1298. )
  1299. pred <- predict(res_mes)
  1300. group_summary_2 <- rbind(group_summary_2, data.frame(
  1301. Measurement = m,
  1302. Sex = s,
  1303. Effect = res_mes$b,
  1304. SE = res_mes$se,
  1305. CI_lower = res_mes$ci.lb,
  1306. CI_upper = res_mes$ci.ub,
  1307. PI_lb = pred$pi.lb,
  1308. PI_ub = pred$pi.ub,
  1309. tau2 = res_mes$tau2,
  1310. k = res_mes$k,
  1311. I2 = res_mes$I2,
  1312. Q = res_mes$QE,
  1313. pval_Q = res_mes$QEp
  1314. ))
  1315. }
  1316. # Combined effect across both sexes (optional)
  1317. df_all <- subset(meta_sex, Measurement == m)
  1318. res_all <- rma(
  1319. yi = yi,
  1320. vi = vi,
  1321. method = "REML",
  1322. data = df_all
  1323. )
  1324. pred_all <- predict(res_all)
  1325. group_summary_2 <- rbind(group_summary_2, data.frame(
  1326. Measurement = m,
  1327. Sex = "both",
  1328. Effect = res_all$b,
  1329. SE = res_all$se,
  1330. CI_lower = res_all$ci.lb,
  1331. CI_upper = res_all$ci.ub,
  1332. PI_lb = pred_all$pi.lb,
  1333. PI_ub = pred_all$pi.ub,
  1334. tau2 = res_all$tau2,
  1335. k = res_all$k,
  1336. I2 = res_all$I2,
  1337. Q = res_all$QE,
  1338. pval_Q = res_all$QEp
  1339. ))
  1340. }
  1341. group_summary$Measurement[group_summary$Measurement == 'SurfArea'] <- 'Surface Area'
  1342. group_summary$Measurement[group_summary$Measurement == 'GrayVol'] <- 'Volume'
  1343. group_summary$Measurement[group_summary$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  1344. group_summary$Measurement[group_summary$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  1345. group_summary$Measurement[group_summary$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  1346. group_summary$Sex[group_summary$Sex == 'female'] <- 'Female'
  1347. group_summary$Sex[group_summary$Sex == 'male'] <- 'Male'
  1348. group_summary$Measurement <- factor(group_summary$Measurement,
  1349. levels = c("Gaussian Curvature","Mean Curvature",
  1350. "Cortical Thickness","Surface Area","Volume" ))
  1351. ggplot(group_summary, aes(x = Measurement, y = Effect, color = Sex, group = Sex)) +
  1352. geom_point(position = position_dodge(width = 0.4), size = 3) +
  1353. geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
  1354. position = position_dodge(width = 0.4), width = 0.3) +
  1355. geom_hline(yintercept = 0, linetype = "dashed") +
  1356. labs(title = "Group-level Effect Size by Measurement and Sex",
  1357. y = "Hedges' g",
  1358. x = "Measurement Type") +
  1359. theme_minimal() +
  1360. scale_color_manual(values = c("Female" = "#00b2a9", "Male" = "#e43c40", "both" = "black")) +
  1361. coord_flip()
  1362. group_summary_2$Measurement[group_summary_2$Measurement == 'SurfArea'] <- 'Surface Area'
  1363. group_summary_2$Measurement[group_summary_2$Measurement == 'GrayVol'] <- 'Volume'
  1364. group_summary_2$Measurement[group_summary_2$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  1365. group_summary_2$Measurement[group_summary_2$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  1366. group_summary_2$Measurement[group_summary_2$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  1367. group_summary_2$Sex[group_summary_2$Sex == 'female'] <- 'Female'
  1368. group_summary_2$Sex[group_summary_2$Sex == 'male'] <- 'Male'
  1369. group_summary_2$Measurement <- factor(group_summary_2$Measurement,
  1370. levels = c("Gaussian Curvature","Mean Curvature",
  1371. "Cortical Thickness","Surface Area","Volume" ))
  1372. ggplot(group_summary_2, aes(x = Measurement, y = Effect, color = Sex, group = Sex)) +
  1373. geom_point(position = position_dodge(width = 0.4), size = 3) +
  1374. geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
  1375. position = position_dodge(width = 0.4), width = 0.3) +
  1376. geom_errorbar(aes(ymin = PI_lb, ymax = PI_ub),
  1377. position = position_dodge(width = 0.4), width = 0.2,
  1378. linetype = "dashed", alpha = .5, linewidth = 0.7) +
  1379. geom_hline(yintercept = 0, linetype = "dashed") +
  1380. labs(title = "Group-level Effect Size by Measurement and Sex",
  1381. y = "Hedges' g",
  1382. x = "Measurement Type") +
  1383. theme_minimal() +
  1384. scale_color_manual(values = c("Female" = "#00b2a9", "Male" = "#e43c40", "both" = "black")) +
  1385. coord_flip()
  1386. #Testing
  1387. testing_summary <- data.frame()
  1388. for (m in measurements) {
  1389. for (s in c("female", "male")) {
  1390. df_sub <- subset(meta_sex, Measurement == m & sex == s)
  1391. res_mes_stu <- rma.mv(
  1392. yi = yi,
  1393. V = vi,
  1394. method = "REML",
  1395. random = ~ 1 | study,
  1396. data = df_sub
  1397. )
  1398. testing_summary <- rbind(testing_summary, data.frame(
  1399. Measurement = m,
  1400. Sex = s,
  1401. Effect = res_mes$b,
  1402. SE = res_mes$se,
  1403. CI_lower = res_mes$ci.lb,
  1404. CI_upper = res_mes$ci.ub
  1405. ))
  1406. }
  1407. # Combined effect across both sexes (optional)
  1408. df_all <- subset(meta_sex, Measurement == m)
  1409. res_all <- rma.mv(
  1410. yi = yi,
  1411. V = vi,
  1412. method = "REML",
  1413. random = ~ 1 | study,
  1414. data = df_all
  1415. )
  1416. testing_summary <- rbind(testing_summary, data.frame(
  1417. Measurement = m,
  1418. Sex = "both",
  1419. Effect = res_all$b,
  1420. SE = res_all$se,
  1421. CI_lower = res_all$ci.lb,
  1422. CI_upper = res_all$ci.ub
  1423. ))
  1424. }
  1425. ggplot(testing_summary, aes(x = Measurement, y = Effect, color = Sex, group = Sex)) +
  1426. geom_point(position = position_dodge(width = 0.4), size = 3) +
  1427. geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
  1428. position = position_dodge(width = 0.4), width = 0.2) +
  1429. geom_hline(yintercept = 0, linetype = "dashed") +
  1430. labs(title = "Group-level Effect Size by Measurement and Sex",
  1431. y = "Hedges' g",
  1432. x = "Measurement Type") +
  1433. theme_minimal() +
  1434. scale_color_manual(values = c("female" = "#F8766D", "male" = "#00BFC4", "both" = "gray50")) +
  1435. coord_flip()
  1436. #########################################################################
  1437. #redo meta
  1438. #########################################################################
  1439. oasub_summary_2
  1440. fm1sub_summary_2
  1441. fm2sub_summary_2
  1442. clbpsub_summary_2
  1443. clbpsubs1_summary_2
  1444. clbpsubs2_summary_2
  1445. msub_summary_2
  1446. ptnsub_summary_2
  1447. oasubescalc_2 <- escalc(
  1448. measure = 'SMDH',
  1449. m1i = Mean_Group1,
  1450. sd1i = SD_Group1,
  1451. n1i = N_Group1,
  1452. m2i = Mean_Group2,
  1453. sd2i = SD_Group2,
  1454. n2i = N_Group2,
  1455. data = oasub_summary_2
  1456. )
  1457. oasubescalc_2$study <- 'OA'
  1458. fmsubescalc_2 <- escalc(
  1459. measure = 'SMDH',
  1460. m1i = Mean_Group1,
  1461. sd1i = SD_Group1,
  1462. n1i = N_Group1,
  1463. m2i = Mean_Group2,
  1464. sd2i = SD_Group2,
  1465. n2i = N_Group2,
  1466. data = fm1sub_summary_2
  1467. )
  1468. fmsubescalc_2$study <- 'FM1'
  1469. fm2subescalc_2 <- escalc(
  1470. measure = 'SMDH',
  1471. m1i = Mean_Group1,
  1472. sd1i = SD_Group1,
  1473. n1i = N_Group1,
  1474. m2i = Mean_Group2,
  1475. sd2i = SD_Group2,
  1476. n2i = N_Group2,
  1477. data = fm2sub_summary_2
  1478. )
  1479. fm2subescalc_2$study <- 'FM2'
  1480. clbpsubescalc_2 <- escalc(
  1481. measure = 'SMDH',
  1482. m1i = Mean_Group1,
  1483. sd1i = SD_Group1,
  1484. n1i = N_Group1,
  1485. m2i = Mean_Group2,
  1486. sd2i = SD_Group2,
  1487. n2i = N_Group2,
  1488. data = clbpsub_summary_2
  1489. )
  1490. clbpsubescalc_2$study <- 'CLBP1'
  1491. clbpsubs1escalc_2 <- escalc(
  1492. measure = 'SMDH',
  1493. m1i = Mean_Group1,
  1494. sd1i = SD_Group1,
  1495. n1i = N_Group1,
  1496. m2i = Mean_Group2,
  1497. sd2i = SD_Group2,
  1498. n2i = N_Group2,
  1499. data = clbpsubs1_summary_2
  1500. )
  1501. clbpsubs1escalc_2$study <- 'CLBP_S1'
  1502. clbpsubs2escalc_2 <- escalc(
  1503. measure = 'SMDH',
  1504. m1i = Mean_Group1,
  1505. sd1i = SD_Group1,
  1506. n1i = N_Group1,
  1507. m2i = Mean_Group2,
  1508. sd2i = SD_Group2,
  1509. n2i = N_Group2,
  1510. data = clbpsubs2_summary_2
  1511. )
  1512. clbpsubs2escalc_2$study <- 'CLBP_S2'
  1513. msubescalc_2 <- escalc(
  1514. measure = 'SMDH',
  1515. m1i = Mean_Group1,
  1516. sd1i = SD_Group1,
  1517. n1i = N_Group1,
  1518. m2i = Mean_Group2,
  1519. sd2i = SD_Group2,
  1520. n2i = N_Group2,
  1521. data = msub_summary_2
  1522. )
  1523. msubescalc_2$study <- 'migraine'
  1524. ptnsubescalc_2 <- escalc(
  1525. measure = 'SMDH',
  1526. m1i = Mean_Group1,
  1527. sd1i = SD_Group1,
  1528. n1i = N_Group1,
  1529. m2i = Mean_Group2,
  1530. sd2i = SD_Group2,
  1531. n2i = N_Group2,
  1532. data = ptnsub_summary_2
  1533. )
  1534. ptnsubescalc_2$study <- 'PTN'
  1535. meta_sub_2 <- rbind(oasubescalc_2, fmsubescalc_2, fm2subescalc_2, clbpsubescalc_2,
  1536. clbpsubs1escalc_2, clbpsubs2escalc_2, msubescalc_2,
  1537. ptnsubescalc_2)
  1538. meta_sub_3 <- rbind(oasubescalc_2, fmsubescalc_2, fm2subescalc_2, clbpsubescalc_2,
  1539. clbpsubs1escalc_2, clbpsubs2escalc_2, msubescalc_2,
  1540. ptnsubescalc_2)
  1541. meta_sub_results_2 <- do.call(rbind, lapply(split(meta_sub_2, list(meta_sub_2$Region)),
  1542. function(dfm){
  1543. if(nrow(dfm) >= 2) {
  1544. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  1545. pred <- predict(res)
  1546. data.frame(
  1547. Region = dfm$Region[1],
  1548. k = res$k,
  1549. Estimate = res$b,
  1550. SE = res$se,
  1551. zval = res$zval,
  1552. pval = res$pval,
  1553. CI_lb = res$ci.lb,
  1554. CI_ub = res$ci.ub,
  1555. PI_lb = pred$pi.lb,
  1556. PI_ub = pred$pi.ub,
  1557. I2 = res$I2,
  1558. Q = res$QE,
  1559. pval_Q = res$QEp,
  1560. t2 = res$tau2
  1561. )
  1562. } else {
  1563. NULL
  1564. }
  1565. }))
  1566. meta_sub_results_2 <- meta_sub_results_2 %>%
  1567. mutate(FDR = p.adjust(pval, method = 'BH'))
  1568. meta_sub_results_2 <- meta_sub_results_2 %>%
  1569. mutate(Q_FDR = p.adjust(pval_Q, method = 'BH'))
  1570. write_xlsx(meta_sub_results_2, "Meta_Subcortical_Results.xlsx")
  1571. meta_sub_results_3 <- do.call(rbind, lapply(split(meta_sub_3, list(meta_sub_3$Region)),
  1572. function(dfm){
  1573. if(nrow(dfm) >= 2) {
  1574. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  1575. pred <- predict(res)
  1576. data.frame(
  1577. Region = dfm$Region[1],
  1578. k = res$k,
  1579. Estimate = res$b,
  1580. SE = res$se,
  1581. zval = res$zval,
  1582. pval = res$pval,
  1583. CI_lb = res$ci.lb,
  1584. CI_ub = res$ci.ub,
  1585. PI_lb = pred$pi.lb,
  1586. PI_ub = pred$pi.ub,
  1587. I2 = res$I2,
  1588. Q = res$QE,
  1589. pval_Q = res$QEp,
  1590. t2 = res$tau2
  1591. )
  1592. } else {
  1593. NULL
  1594. }
  1595. }))
  1596. meta_sub_results_3 <- meta_sub_results_3 %>%
  1597. mutate(FDR = p.adjust(pval, method = 'BH'))
  1598. meta_sub_results_3 <- meta_sub_results_3 %>%
  1599. mutate(Q_FDR = p.adjust(pval_Q, method = 'BH'))
  1600. ggplot(meta_sub_results_2, aes(x = Estimate, y = reorder(Region, Estimate))) +
  1601. geom_point(aes(color = pval < 0.05)) +
  1602. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  1603. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  1604. theme_bw() +
  1605. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  1606. ggplot(meta_sub_results_2, aes(x = I2, y = Estimate, size = t2, color = Q_FDR < 0.05)) +
  1607. geom_point(alpha = 0.8) +
  1608. labs(title = "Effect Size vs. Heterogeneity",
  1609. x = "I² (%)", y = "Pooled Effect Size") +
  1610. scale_color_manual(values = c("gray", "red"), name = "Significant Heterogeneity") +
  1611. theme_minimal() +
  1612. ylim(-.5,.5) +
  1613. xlim(0,100) +
  1614. geom_hline(yintercept = 0, linetype = 'dashed', color = 'red') +
  1615. geom_text(aes(label = ifelse(Q_FDR < 0.05, Region, "")), hjust = 1.4, vjust = 0.5, size = 3)
  1616. meta_sub_results_2 <- meta_sub_results_2 %>%
  1617. mutate(Region = fct_reorder(Region, Estimate, .desc = TRUE))
  1618. meta_sub_results_2$Region_clean <- as.character(meta_sub_results_2$Region)
  1619. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Lateral.Ventricle'] <- 'Right Lateral Ventricle'
  1620. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Lateral.Ventricle'] <- 'Left Lateral Ventricle'
  1621. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Pallidum'] <- 'Left Pallidum'
  1622. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Pallidum'] <- 'Right Pallidum'
  1623. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Cerebellum.White.Matter'] <- 'Left Cerebellum White Matter'
  1624. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Cerebellum.White.Matter'] <- 'Right Cerebellum White Matter'
  1625. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'X3rd.Ventricle'] <- '3rd Ventricle'
  1626. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'CC_Anterior'] <- 'CC Anterior'
  1627. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'X4th.Ventricle'] <- '4th Ventricle'
  1628. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'CC_Mid_Anterior'] <- 'CC Mid Anterior'
  1629. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Caudate'] <- 'Left Caudate'
  1630. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Caudate'] <- 'Right Caudate'
  1631. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'CC_Posterior'] <- 'CC Posterior'
  1632. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'CC_Mid_Posterior'] <- 'CC Mid Posterior'
  1633. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'CC_Central'] <- 'CC Central'
  1634. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.VentralDC'] <- 'Left Ventral Diencephalon'
  1635. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.VentralDC'] <- 'Right Ventral Diencephalon'
  1636. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Brain.Stem'] <- 'Brain Stem'
  1637. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Putamen'] <- 'Left Putamen'
  1638. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Putamen'] <- 'Right Putamen'
  1639. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Thalamus'] <- 'Right Thalamus'
  1640. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Thalamus'] <- 'Left Thalamus'
  1641. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Hippocampus'] <- 'Left Hippocampus'
  1642. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Hippocampus'] <- 'Right Hippocampus'
  1643. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Cerebellum.Cortex'] <- 'Right Cerebellum Cortex'
  1644. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Cerebellum.Cortex'] <- 'Left Cerebellum Cortex'
  1645. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Amygdala'] <- 'Left Amygdala'
  1646. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Amygdala'] <- 'Right Amygdala'
  1647. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Cerebellum.Cortex'] <- 'Right Cerebellum Cortex'
  1648. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Cerebellum.Cortex'] <- 'Left Cerebellum Cortex'
  1649. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Left.Amygdala'] <- 'Left Amygdala'
  1650. meta_sub_results_2$Region_clean[meta_sub_results_2$Region_clean == 'Right.Amygdala'] <- 'Right Amygdala'
  1651. meta_sub_results_2 <- meta_sub_results_2 %>%
  1652. mutate(Region_clean = fct_reorder(Region_clean, Estimate, .desc = TRUE))
  1653. sub_graph <- ggplot(meta_sub_results_3, aes(y = Region_clean, x = Estimate)) +
  1654. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 1) +
  1655. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 0.8) +
  1656. geom_point(aes(color = FDR < 0.05), size = 2) +
  1657. geom_vline(xintercept = 0, linetype = "twodash") +
  1658. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  1659. labs(
  1660. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  1661. x = "Hedges' g", y = "Region",
  1662. color = "FDR < 0.05"
  1663. ) +
  1664. theme_bw(base_size = 19) +
  1665. theme(
  1666. strip.text = element_text(face = "bold", size = 19),
  1667. axis.text.y = element_text(size = 19),
  1668. legend.position = "bottom"
  1669. )
  1670. sub_graph
  1671. ggsave("sub_meta.png", plot = sub_graph, dpi = 500, width = 10, height = 12, units = 'in', bg = 'white')
  1672. ggplot(meta_sub_results_3, aes(x = Estimate, y = reorder(Region, Estimate))) +
  1673. geom_point(aes(color = FDR < 0.05)) +
  1674. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  1675. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  1676. theme_bw() +
  1677. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  1678. ggplot(meta_sub_results_3, aes(x = I2, y = Estimate, size = t2, color = Q_FDR < 0.05)) +
  1679. geom_point(alpha = 0.8) +
  1680. labs(title = "Effect Size vs. Heterogeneity",
  1681. x = "I² (%)", y = "Pooled Effect Size") +
  1682. scale_color_manual(values = c("gray", "red"), name = "Significant Heterogeneity") +
  1683. theme_minimal() +
  1684. ylim(-.5,.5) +
  1685. xlim(0,100) +
  1686. geom_hline(yintercept = 0, linetype = 'dashed', color = 'red') +
  1687. geom_text(aes(label = ifelse(Q_FDR < 0.05, Region, "")), hjust = 1.4, vjust = 0.5, size = 3)
  1688. meta_sub_results_3 <- meta_sub_results_3 %>%
  1689. mutate(Region = fct_reorder(Region, Estimate, .desc = TRUE))
  1690. meta_sub_results_3$Region_clean <- as.character(meta_sub_results_3$Region)
  1691. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Lateral.Ventricle'] <- 'Right Lateral Ventricle'
  1692. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Lateral.Ventricle'] <- 'Left Lateral Ventricle'
  1693. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Pallidum'] <- 'Left Pallidum'
  1694. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Pallidum'] <- 'Right Pallidum'
  1695. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Cerebellum.White.Matter'] <- 'Left Cerebellum White Matter'
  1696. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Cerebellum.White.Matter'] <- 'Right Cerebellum White Matter'
  1697. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'X3rd.Ventricle'] <- '3rd Ventricle'
  1698. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'CC_Anterior'] <- 'CC Anterior'
  1699. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'X4th.Ventricle'] <- '4th Ventricle'
  1700. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'CC_Mid_Anterior'] <- 'CC Mid Anterior'
  1701. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Caudate'] <- 'Left Caudate'
  1702. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Caudate'] <- 'Right Caudate'
  1703. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'CC_Posterior'] <- 'CC Posterior'
  1704. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'CC_Mid_Posterior'] <- 'CC Mid Posterior'
  1705. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'CC_Central'] <- 'CC Central'
  1706. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.VentralDC'] <- 'Left Ventral Diencephalon'
  1707. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.VentralDC'] <- 'Right Ventral Diencephalon'
  1708. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Brain.Stem'] <- 'Brain Stem'
  1709. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Putamen'] <- 'Left Putamen'
  1710. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Putamen'] <- 'Right Putamen'
  1711. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Thalamus'] <- 'Right Thalamus'
  1712. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Thalamus'] <- 'Left Thalamus'
  1713. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Hippocampus'] <- 'Left Hippocampus'
  1714. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Hippocampus'] <- 'Right Hippocampus'
  1715. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Cerebellum.Cortex'] <- 'Right Cerebellum Cortex'
  1716. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Cerebellum.Cortex'] <- 'Left Cerebellum Cortex'
  1717. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Amygdala'] <- 'Left Amygdala'
  1718. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Amygdala'] <- 'Right Amygdala'
  1719. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Accumbens.area'] <- 'Left Accumbens'
  1720. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Accumbens.area'] <- 'Right Accumbens'
  1721. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Right.Inf.Lat.Vent'] <- 'Right Inferior Lateral Ventricle'
  1722. meta_sub_results_3$Region_clean[meta_sub_results_3$Region_clean == 'Left.Inf.Lat.Vent'] <- 'Left Inferior Lateral Ventricle'
  1723. meta_sub_results_3 <- meta_sub_results_3 %>%
  1724. mutate(Region_clean = fct_reorder(Region_clean, Estimate, .desc = TRUE))
  1725. sub_graph_2 <- ggplot(meta_sub_results_3, aes(y = Region_clean, x = -Estimate)) +
  1726. geom_errorbarh(aes(xmin = -PI_ub, xmax = -PI_lb), color = "gray70", height = 0.4, size = 1) +
  1727. geom_errorbarh(aes(xmin = -CI_ub, xmax = -CI_lb), color = "black", height = 0.2, size = 0.8) +
  1728. geom_point(aes(color = FDR < 0.05), size = 2) +
  1729. geom_vline(xintercept = 0, linetype = "twodash") +
  1730. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  1731. labs(
  1732. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  1733. x = "Hedges' g", y = "Region",
  1734. color = "FDR < 0.05"
  1735. ) +
  1736. theme_bw(base_size = 19) +
  1737. theme(
  1738. strip.text = element_text(face = "bold", size = 19),
  1739. axis.text.y = element_text(size = 19),
  1740. legend.position = "bottom"
  1741. )
  1742. sub_graph_2
  1743. length(meta_sub_results_3$Region)
  1744. ggsave("sub_meta_2.png", plot = sub_graph_2, dpi = 500, width = 15, height = 12, units = 'in', bg = 'white')
  1745. meta_sub_results_3
  1746. # atlas keys we must match
  1747. atlas_keys <- aseg$data %>% distinct(hemi, region)
  1748. # canonical midline regions in aseg
  1749. cc_regions <- c("CC anterior","CC central","CC mid anterior",
  1750. "CC mid posterior","CC posterior")
  1751. other_midline <- c("3rd ventricle","4th ventricle","brain stem",
  1752. "cerebellum cortex","cerebellum white matter")
  1753. midline_rois <- c(cc_regions, other_midline)
  1754. # --- CLEAN & NORMALIZE -------------------------------------------------
  1755. clean_regions_2 <- meta_sub_results_3 %>%
  1756. mutate(
  1757. hemi = case_when(
  1758. str_detect(Region, "^Left[._ ]") ~ "left",
  1759. str_detect(Region, "^Right[._ ]") ~ "right",
  1760. TRUE ~ NA_character_
  1761. ),
  1762. region_base = Region %>%
  1763. str_remove("^Left[._ ]|^Right[._ ]") %>%
  1764. str_replace_all("[._-]", " ") %>%
  1765. str_squish() %>%
  1766. str_to_lower()
  1767. ) %>%
  1768. # map to aseg spellings
  1769. mutate(
  1770. region_base = str_replace(region_base, "^third ventricle$", "3rd ventricle"),
  1771. region_base = str_replace(region_base, "^fourth ventricle$", "4th ventricle"),
  1772. region_base = str_replace(region_base, "^x?3rd ventricle$", "3rd ventricle"),
  1773. region_base = str_replace(region_base, "^x?4th ventricle$", "4th ventricle"),
  1774. region_base = str_replace(region_base, "^brainstem$", "brain stem"),
  1775. region_base = str_replace(region_base, "^thalamus$", "thalamus proper"),
  1776. region_base = str_replace(region_base, "^ventral ?dc$", "ventral DC"),
  1777. region_base = str_replace(region_base, "^(nucleus )?accumbens( area)?$", "accumbens area"),
  1778. region_base = str_replace(region_base, "^cerebel+um cortex$|^cerebel+ar cortex$", "cerebellum cortex"),
  1779. region_base = str_replace(region_base, "^cerebel+um white matter$|^cerebel+ar white matter$", "cerebellum white matter"),
  1780. region_base = str_replace(region_base, "^cc ?ant(erior)?$", "CC anterior"),
  1781. region_base = str_replace(region_base, "^cc ?cent(ral)?$", "CC central"),
  1782. region_base = str_replace(region_base, "^cc ?mid ?ant(erior)?$", "CC mid anterior"),
  1783. region_base = str_replace(region_base, "^cc ?mid ?post(erior)?$", "CC mid posterior"),
  1784. region_base = str_replace(region_base, "^cc ?post(erior)?$", "CC posterior"),
  1785. # final region string
  1786. region = region_base %>%
  1787. # ensure "CC " stays caps and the rest is exactly as needed
  1788. (\(x) ifelse(str_starts(x, "cc "), str_replace(x, "^cc ", "CC "), x))() %>%
  1789. str_squish()
  1790. ) %>%
  1791. # set hemi="midline" for all midline ROIs (CC + others)
  1792. mutate(
  1793. hemi = if_else(region %in% midline_rois, "midline", hemi),
  1794. hemi = str_squish(hemi)
  1795. ) %>%
  1796. select(region, hemi, Estimate)
  1797. # --- DIAGNOSE: what still fails to match? ------------------------------
  1798. still_unmatched <- clean_regions %>%
  1799. anti_join(atlas_keys, by = c("hemi","region"))
  1800. if (nrow(still_unmatched)) {
  1801. message("Rows not matching atlas (hemi, region):")
  1802. print(still_unmatched)
  1803. }
  1804. # --- PLOT --------------------------------------------------------------
  1805. L <- max(abs(clean_regions_2$Estimate), na.rm = TRUE)
  1806. ggplot() +
  1807. geom_brain(
  1808. atlas = aseg,
  1809. data = clean_regions_2,
  1810. aes(fill = -Estimate),
  1811. colour = "grey60", # outline color
  1812. linewidth = 0.2 # or size = 0.2 if on older ggplot2
  1813. ) +
  1814. scale_fill_distiller(
  1815. palette = "RdBu",
  1816. limits = c(-L, L),
  1817. na.value = "grey90",
  1818. name = "Hedges g"
  1819. ) +
  1820. theme_void() +
  1821. theme(
  1822. legend.position = "bottom",
  1823. legend.text = element_text(size = 8)
  1824. ) +
  1825. guides(fill = guide_colorbar(barwidth = 10, barheight = 2))
  1826. sub_colors <- data.frame(Region = meta_sub_results_2$Region,
  1827. estimate = meta_sub_results_2$Estimate)
  1828. min_val <- -0.4
  1829. max_val <- 0.4
  1830. # Step 2: Define custom symmetric palette
  1831. my_palette <- colorRampPalette(c("#2569AE", "white", "#B31D2C"))
  1832. palette_n <- 100
  1833. custom_colors <- my_palette(palette_n)
  1834. # Step 3: Clip your effect sizes to stay within bounds
  1835. sub_colors$clipped_estimate <- pmax(pmin(sub_colors$estimate, max_val), min_val)
  1836. # Step 4: Convert effect sizes to indices (1 to 100)
  1837. sub_colors$scaled_index <- round(scales::rescale(sub_colors$clipped_estimate, to = c(1, palette_n), from = c(min_val, max_val)))
  1838. # Step 5: Get hex color
  1839. sub_colors$hex_code <- custom_colors[sub_colors$scaled_index]
  1840. # ✅ Done!
  1841. head(sub_colors)
  1842. RPall <- meta_sub_2[grepl("Right.Pallidum", meta_sub_2$Region), ]
  1843. LPall <- meta_sub_2[grepl("Left.Pallidum", meta_sub_2$Region), ]
  1844. Lamy <- meta_sub_2[grepl("Left.Amygdala", meta_sub_2$Region),]
  1845. Lamy_viz <- data.frame(Hedges = Lamy$yi, SE = sqrt(Lamy$vi), Study = Lamy$study,
  1846. Region = Lamy$Region)
  1847. rpall_viz <- data.frame(Hedges = RPall$yi, SE = sqrt(RPall$vi), Study = RPall$study,
  1848. Region = RPall$Region)
  1849. lpall_viz <- data.frame(Hedges = LPall$yi, SE = sqrt(LPall$vi), Study = LPall$study,
  1850. Region = LPall$Region)
  1851. rpall_viz$Study[rpall_viz$Study == 'OA'] <- 'Osteoarthritis: Tétreault, 2016'
  1852. rpall_viz$Study[rpall_viz$Study == 'FM1'] <- 'Fibromyalgia: Pando-Naude, 2019'
  1853. rpall_viz$Study[rpall_viz$Study == 'CLBP1'] <- 'Chronic Lower Back Pain: Makary, 2020'
  1854. rpall_viz$Study[rpall_viz$Study == 'migraine'] <- 'Migraine: Seminowicz, 2020'
  1855. rpall_viz$Study[rpall_viz$Study == 'PTN'] <- 'Primary Trigeminal Neuralgia: Filimonova, 2025'
  1856. rpall_viz$Study[rpall_viz$Study == 'FM2'] <- 'Balducci, 2022, Fibromyalgia'
  1857. rpall_viz$Study[rpall_viz$Study == 'CLBP_S1'] <- 'Chronic Lower Back Pain: Mano, 2018 (UK Data)'
  1858. rpall_viz$Study[rpall_viz$Study == 'CLBP_S2'] <- 'Chronic Lower Back Pain: Mano, 2018 (Japan Data)'
  1859. rpall_viz$Study[rpall_viz$Study == 'Balducci, 2022, Fibromyalgia'] <- 'Fibromyalgia: Balducci, 2022'
  1860. lpall_viz$Study[lpall_viz$Study == 'OA'] <- 'Osteoarthritis: Tétreault, 2016'
  1861. lpall_viz$Study[lpall_viz$Study == 'FM1'] <- 'Fibromyalgia: Pando-Naude, 2019'
  1862. lpall_viz$Study[lpall_viz$Study == 'CLBP1'] <- 'Chronic Lower Back Pain: Makary, 2020'
  1863. lpall_viz$Study[lpall_viz$Study == 'migraine'] <- 'Migraine: Seminowicz, 2020'
  1864. lpall_viz$Study[lpall_viz$Study == 'PTN'] <- 'Primary Trigeminal Neuralgia: Filimonova, 2025'
  1865. lpall_viz$Study[lpall_viz$Study == 'FM2'] <- 'Balducci, 2022, Fibromyalgia'
  1866. lpall_viz$Study[lpall_viz$Study == 'CLBP_S1'] <- 'Chronic Lower Back Pain: Mano, 2018 (UK Data)'
  1867. lpall_viz$Study[lpall_viz$Study == 'CLBP_S2'] <- 'Chronic Lower Back Pain: Mano, 2018 (Japan Data)'
  1868. lpall_viz$Study[lpall_viz$Study == 'Balducci, 2022, Fibromyalgia'] <- 'Fibromyalgia: Balducci, 2022'
  1869. res_lamy <- metagen(TE = Hedges,
  1870. seTE = SE,
  1871. studlab = Study,
  1872. data = Lamy_viz,
  1873. method.tau = "REML",
  1874. prediction = TRUE,
  1875. random = TRUE)
  1876. forest(res_lamy,
  1877. main = "Left Amygdala Volume",
  1878. prediction = TRUE,
  1879. col.predict = "gray50",
  1880. col.random = "navy",
  1881. xlab = "Hedges' g",
  1882. common = F,
  1883. print.common = F,
  1884. leftlabs = c("Study","Estimated effect size","Standard Error"))
  1885. res_rpall <- metagen(TE = Hedges,
  1886. seTE = SE,
  1887. studlab = Study,
  1888. data = rpall_viz,
  1889. method.tau = "REML",
  1890. prediction = TRUE,
  1891. random = TRUE)
  1892. res_lpall <- metagen(TE = Hedges,
  1893. seTE = SE,
  1894. studlab = Study,
  1895. data = lpall_viz,
  1896. method.tau = "REML",
  1897. prediction = TRUE,
  1898. random = TRUE)
  1899. forest(res_rpall,
  1900. main = "Right Pallidum Volume",
  1901. prediction = TRUE,
  1902. col.predict = "gray50",
  1903. col.random = "navy",
  1904. xlab = "Hedges' g",
  1905. common = F,
  1906. print.common = F,
  1907. leftlabs = c("Study","Estimated effect size","Standard Error"))
  1908. forest(res_lpall,
  1909. main = "Left Pallidum Volume",
  1910. prediction = TRUE,
  1911. col.predict = "gray50",
  1912. col.random = "navy",
  1913. xlab = "Hedges' g",
  1914. common = F,
  1915. print.common = F,
  1916. leftlabs = c("Study","Estimated effect size","Standard Error"))
  1917. #########################################################################
  1918. #meta with sex
  1919. #########################################################################
  1920. oasub_m_summary_2
  1921. clbpsub_m_summary_2
  1922. clbpsubs1_m_summary_2
  1923. clbpsubs2_m_summary_2
  1924. msub_m_summary_2
  1925. ptnsub_m_summary_2
  1926. oasub_m_escalc <- escalc(
  1927. measure = 'SMDH',
  1928. m1i = Mean_Group1,
  1929. sd1i = SD_Group1,
  1930. n1i = N_Group1,
  1931. m2i = Mean_Group2,
  1932. sd2i = SD_Group2,
  1933. n2i = N_Group2,
  1934. data = oasub_m_summary_2
  1935. )
  1936. oasub_m_escalc$study <- 'OA'
  1937. clbpsub_m_escalc <- escalc(
  1938. measure = 'SMDH',
  1939. m1i = Mean_Group1,
  1940. sd1i = SD_Group1,
  1941. n1i = N_Group1,
  1942. m2i = Mean_Group2,
  1943. sd2i = SD_Group2,
  1944. n2i = N_Group2,
  1945. data = clbpsub_m_summary_2
  1946. )
  1947. clbpsub_m_escalc$study <- 'CLBP1'
  1948. clbpsubs1_m_escalc <- escalc(
  1949. measure = 'SMDH',
  1950. m1i = Mean_Group1,
  1951. sd1i = SD_Group1,
  1952. n1i = N_Group1,
  1953. m2i = Mean_Group2,
  1954. sd2i = SD_Group2,
  1955. n2i = N_Group2,
  1956. data = clbpsubs1_m_summary_2
  1957. )
  1958. clbpsubs1_m_escalc$study <- 'CLBP_S1'
  1959. clbpsubs2_m_escalc <- escalc(
  1960. measure = 'SMDH',
  1961. m1i = Mean_Group1,
  1962. sd1i = SD_Group1,
  1963. n1i = N_Group1,
  1964. m2i = Mean_Group2,
  1965. sd2i = SD_Group2,
  1966. n2i = N_Group2,
  1967. data = clbpsubs2_m_summary_2
  1968. )
  1969. clbpsubs2_m_escalc$study <- 'CLBP_S2'
  1970. msub_m_escalc <- escalc(
  1971. measure = 'SMDH',
  1972. m1i = Mean_Group1,
  1973. sd1i = SD_Group1,
  1974. n1i = N_Group1,
  1975. m2i = Mean_Group2,
  1976. sd2i = SD_Group2,
  1977. n2i = N_Group2,
  1978. data = msub_m_summary_2
  1979. )
  1980. msub_m_escalc$study <- 'migraine'
  1981. ptnsub_m_escalc <- escalc(
  1982. measure = 'SMDH',
  1983. m1i = Mean_Group1,
  1984. sd1i = SD_Group1,
  1985. n1i = N_Group1,
  1986. m2i = Mean_Group2,
  1987. sd2i = SD_Group2,
  1988. n2i = N_Group2,
  1989. data = ptnsub_m_summary_2
  1990. )
  1991. ptnsub_m_escalc$study <- 'PTN'
  1992. meta_sub_m <- rbind(oasub_m_escalc, clbpsub_m_escalc,
  1993. clbpsubs1_m_escalc, clbpsubs2_m_escalc, msub_m_escalc,
  1994. ptnsub_m_escalc)
  1995. meta_sub_m_results <- do.call(rbind, lapply(split(meta_sub_m, list(meta_sub_m$Region)),
  1996. function(dfm){
  1997. if(nrow(dfm) >=2) {
  1998. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  1999. pred <- predict(res)
  2000. data.frame(
  2001. Region = dfm$Region[1],
  2002. k = res$k,
  2003. Estimate = res$b,
  2004. SE = res$se,
  2005. zval = res$zval,
  2006. pval = res$pval,
  2007. CI_lb = res$ci.lb,
  2008. CI_ub = res$ci.ub,
  2009. PI_lb = pred$pi.lb,
  2010. PI_ub = pred$pi.ub,
  2011. I2 = res$I2,
  2012. Q = res$QE,
  2013. pval_Q = res$QEp,
  2014. t2 = res$tau2
  2015. )
  2016. } else {
  2017. NULL
  2018. }
  2019. }))
  2020. meta_sub_m_results <- meta_sub_m_results %>%
  2021. mutate(FDR = p.adjust(pval, method = 'BH'))
  2022. meta_sub_m_results <- meta_sub_m_results %>%
  2023. mutate(Q_FDR_p = p.adjust(pval_Q, method = 'BH'))
  2024. ggplot(meta_sub_m_results, aes(x = -Estimate, y = reorder(Region, Estimate))) +
  2025. geom_point(aes(color = FDR < 0.05)) +
  2026. geom_errorbarh(aes(xmax = -CI_lb, xmin = -CI_ub), height = 0.3) +
  2027. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  2028. theme_bw() +
  2029. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  2030. ################################################################in F now
  2031. oasub_f_escalc <- escalc(
  2032. measure = 'SMDH',
  2033. m1i = Mean_Group1,
  2034. sd1i = SD_Group1,
  2035. n1i = N_Group1,
  2036. m2i = Mean_Group2,
  2037. sd2i = SD_Group2,
  2038. n2i = N_Group2,
  2039. data = oasub_f_summary_2
  2040. )
  2041. oasub_f_escalc$study <- 'OA'
  2042. clbpsub_f_escalc <- escalc(
  2043. measure = 'SMDH',
  2044. m1i = Mean_Group1,
  2045. sd1i = SD_Group1,
  2046. n1i = N_Group1,
  2047. m2i = Mean_Group2,
  2048. sd2i = SD_Group2,
  2049. n2i = N_Group2,
  2050. data = clbpsub_f_summary_2
  2051. )
  2052. clbpsub_f_escalc$study <- 'CLBP1'
  2053. clbpsubs1_f_escalc <- escalc(
  2054. measure = 'SMDH',
  2055. m1i = Mean_Group1,
  2056. sd1i = SD_Group1,
  2057. n1i = N_Group1,
  2058. m2i = Mean_Group2,
  2059. sd2i = SD_Group2,
  2060. n2i = N_Group2,
  2061. data = clbpsubs1_f_summary_2
  2062. )
  2063. clbpsubs1_f_escalc$study <- 'CLBP_S1'
  2064. clbpsubs2_f_escalc <- escalc(
  2065. measure = 'SMDH',
  2066. m1i = Mean_Group1,
  2067. sd1i = SD_Group1,
  2068. n1i = N_Group1,
  2069. m2i = Mean_Group2,
  2070. sd2i = SD_Group2,
  2071. n2i = N_Group2,
  2072. data = clbpsubs2_f_summary_2
  2073. )
  2074. clbpsubs2_f_escalc$study <- 'CLBP_S2'
  2075. msub_f_escalc <- escalc(
  2076. measure = 'SMDH',
  2077. m1i = Mean_Group1,
  2078. sd1i = SD_Group1,
  2079. n1i = N_Group1,
  2080. m2i = Mean_Group2,
  2081. sd2i = SD_Group2,
  2082. n2i = N_Group2,
  2083. data = msub_f_summary_2
  2084. )
  2085. msub_f_escalc$study <- 'migraine'
  2086. ptnsub_f_escalc <- escalc(
  2087. measure = 'SMDH',
  2088. m1i = Mean_Group1,
  2089. sd1i = SD_Group1,
  2090. n1i = N_Group1,
  2091. m2i = Mean_Group2,
  2092. sd2i = SD_Group2,
  2093. n2i = N_Group2,
  2094. data = ptnsub_f_summary_2
  2095. )
  2096. ptnsub_f_escalc$study <- 'PTN'
  2097. fmsubescalc_2$sex <- 'female'
  2098. fm2subescalc_2$sex <- 'female'
  2099. msub_f_escalc$sex <- 'female'
  2100. meta_sub_f <- rbind(oasub_f_escalc, clbpsub_f_escalc,
  2101. clbpsubs1_f_escalc, clbpsubs2_f_escalc, msub_f_escalc,
  2102. ptnsub_f_escalc, fmsubescalc_2, fm2subescalc_2)
  2103. meta_sub_f_results <- do.call(rbind, lapply(split(meta_sub_f, list(meta_sub_f$Region)),
  2104. function(dfm){
  2105. if(nrow(dfm) >=2) {
  2106. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  2107. pred <- predict(res)
  2108. data.frame(
  2109. Region = dfm$Region[1],
  2110. k = res$k,
  2111. Estimate = res$b,
  2112. SE = res$se,
  2113. zval = res$zval,
  2114. pval = res$pval,
  2115. CI_lb = res$ci.lb,
  2116. CI_ub = res$ci.ub,
  2117. PI_lb = pred$pi.lb,
  2118. PI_ub = pred$pi.ub,
  2119. I2 = res$I2,
  2120. Q = res$QE,
  2121. pval_Q = res$QEp,
  2122. t2 = res$tau2
  2123. )
  2124. } else {
  2125. NULL
  2126. }
  2127. }))
  2128. meta_sub_f_results <- meta_sub_f_results %>%
  2129. mutate(FDR = p.adjust(pval, method = 'BH'))
  2130. meta_sub_f_results <- meta_sub_f_results %>%
  2131. mutate(Q_FDR_p = p.adjust(pval_Q, method = 'BH'))
  2132. ggplot(meta_sub_f_results, aes(x = Estimate, y = reorder(Region, Estimate))) +
  2133. geom_point(aes(color = FDR < 0.05)) +
  2134. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), height = 0.3) +
  2135. geom_vline(xintercept = 0, linetype = "solid", color = "black", linewidth = 1) + # <-- bold line at 0
  2136. theme_bw() +
  2137. labs(x = "Meta-analytic Effect Size (Hedges' g)", y = "Region", color = "Significant")
  2138. ########################################################################
  2139. meta_sub_m_results
  2140. meta_sub_f_results
  2141. sub_sex_result <- data.frame(Region = meta_sub_m_results$Region,
  2142. estimate_m = meta_sub_m_results$Estimate, estimate_f = meta_sub_f_results$Estimate,
  2143. SE_m = meta_sub_m_results$SE, SE_f = meta_sub_f_results$SE, CI_lb_m = meta_sub_m_results$CI_lb,
  2144. CI_ub_m = meta_sub_m_results$CI_ub, CI_lb_f = meta_sub_f_results$CI_lb, CI_ub_f = meta_sub_f_results$CI_ub)
  2145. ggplot(sub_sex_result, aes(x = estimate_m, y = estimate_f)) +
  2146. geom_point() +
  2147. theme_minimal() +
  2148. xlim(-1,1) +
  2149. ylim(-1,1) +
  2150. geom_hline(yintercept = 0, linetype = 'twodash', color = 'black') +
  2151. geom_vline(xintercept = 0, linetype = 'twodash', color = 'black') +geom_abline(slope = 1, intercept = 0, linetype = 'longdash',color = '#e43c40') +
  2152. geom_abline(slope = -1, intercept = 0, linetype = 'longdash', color = '#e43c40') +
  2153. sm_statCorr(method = 'lm', color = '#00b2a9') +
  2154. ylab("Female estimated effect size") +
  2155. xlab("Male estimated effect size")
  2156. ###############################################################################
  2157. meta_sub_m
  2158. meta_sub_f
  2159. meta_sub_f_results
  2160. meta_sub_m_results
  2161. write_xlsx(meta_sub_f_results, "meta_sub_f.xlsx")
  2162. write_xlsx(meta_sub_m_results, "meta_sub_m.xlsx")
  2163. identical(meta_sub_f_results$Region, meta_sub_m_results$Region)
  2164. meta_sub_df <- data.frame(Region = meta_sub_m_results$Region, estimate_m = meta_sub_m_results$Estimate,
  2165. estimate_f = meta_sub_f_results$Estimate, SE_m = meta_sub_m_results$SE,
  2166. SE_f = meta_sub_f_results$SE, CI_l_m = meta_sub_m_results$CI_lb,
  2167. CI_u_m = meta_sub_m_results$CI_ub, CI_l_f = meta_sub_f_results$CI_lb,
  2168. CI_u_f = meta_sub_f_results$CI_ub, PI_lb_m = meta_sub_m_results$PI_lb,
  2169. PI_ub_m = meta_sub_m_results$PI_ub, PI_lb_f = meta_sub_f_results$PI_lb,
  2170. PI_ub_f = meta_sub_f_results$PI_ub)
  2171. meta_sub_df$z_diff <- (meta_sub_df$estimate_m - meta_sub_df$estimate_f) / sqrt(meta_sub_df$SE_m^2 + meta_sub_df$SE_f^2)
  2172. write_xlsx(meta_sub_df, "meta_sub_sex_analysis.xlsx")
  2173. meta_sub_df$effect_direction <- case_when(
  2174. sign(meta_sub_df$estimate_m) == sign(meta_sub_df$estimate_f) ~ "Same Direction",
  2175. sign(meta_sub_df$estimate_m) != sign(meta_sub_df$estimate_f) ~ "Opposite Direction",
  2176. TRUE ~ "Undefined"
  2177. )
  2178. meta_sub_df
  2179. meta_sub_m$sex <- 'male'
  2180. meta_sub_f$sex <- 'female'
  2181. sub_m <- meta_sub_m[c("Region","yi","vi","study","sex")]
  2182. sub_f <- meta_sub_f[c("Region","yi","vi","study","sex")]
  2183. meta_sub_sex <- rbind(sub_m, sub_f)
  2184. meta_sub_sex$sex <- factor(meta_sub_sex$sex)
  2185. meta_sub_sex$study <- factor(meta_sub_sex$study)
  2186. res_sex <- rma(yi = yi, vi = vi, mods = ~ sex, data = meta_sub_sex, method = "REML")
  2187. summary(res_sex)
  2188. res_sex_region <- rma(yi, vi, mods = ~ sex + Region, data = meta_sub_sex, method = "REML")
  2189. summary(res_sex_region)
  2190. res_3lvl <- rma.mv(yi = yi,
  2191. V = vi,
  2192. mods = ~ sex,
  2193. random = ~ 1 | study,
  2194. data = meta_sub_sex,
  2195. method = "REML")
  2196. summary(res_3lvl)
  2197. res_3lvl
  2198. group_summary_sub <- data.frame()
  2199. for (s in c("female", "male")) {
  2200. df_sub <- subset(meta_sub_sex, sex == s)
  2201. res_sub <- rma(
  2202. yi = yi,
  2203. vi = vi,
  2204. method = "REML",
  2205. data = df_sub
  2206. )
  2207. group_summary_sub <- rbind(group_summary_sub, data.frame(
  2208. Sex = s,
  2209. Effect = res_sub$b,
  2210. SE = res_sub$se,
  2211. CI_lower = res_sub$ci.lb,
  2212. CI_upper = res_sub$ci.ub,
  2213. ))
  2214. }
  2215. # Combined effect across both sexes (optional)
  2216. sub_collapsed <- meta_sub_sex %>%
  2217. group_by(study, sex) %>%
  2218. summarise(
  2219. yi = sum(yi / vi) / sum(1 / vi),
  2220. vi = 1 / sum(1 / vi),
  2221. .groups = "drop"
  2222. )
  2223. res_all_sub <- rma(
  2224. yi = yi,
  2225. vi = vi,
  2226. method = "REML",
  2227. data = meta_sub_sex
  2228. )
  2229. res_sub2 <- rma(
  2230. yi = yi,
  2231. vi = vi,
  2232. method = "REML",
  2233. data = sub_collapsed
  2234. )
  2235. res_sub2_df <- rbind(res_sub2, data.frame(
  2236. Effect = res_sub2$b,
  2237. SE = res_sub2$se,
  2238. CI_lower = res_sub2$ci.lb,
  2239. CI_upper = res_sub2$ci.ub
  2240. ))
  2241. group_summary_sub <- rbind(group_summary_sub, data.frame(
  2242. Sex = "both",
  2243. Effect = res_all_sub$b,
  2244. SE = res_all_sub$se,
  2245. CI_lower = res_all_sub$ci.lb,
  2246. CI_upper = res_all_sub$ci.ub
  2247. ))
  2248. ggplot(group_summary_sub, aes(x = Sex, y = Effect, color = Sex, group = Sex)) +
  2249. geom_point(position = position_dodge(width = 0.4), size = 3) +
  2250. geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
  2251. position = position_dodge(width = 0.4), width = 0.3) +
  2252. geom_hline(yintercept = 0, linetype = "dashed") +
  2253. labs(title = "Group-level Effect Size by Sex for Subcortical Volumes",
  2254. y = "Hedges' g",
  2255. x = "Sex") +
  2256. theme_minimal() +
  2257. scale_color_manual(values = c("female" = "#00b2a9", "male" = "#e43c40", "both" = "black")) +
  2258. coord_flip() +
  2259. ylim(-.1,.25)
  2260. ##################################################################
  2261. #Visualizing Meta results
  2262. ##################################################################
  2263. heatmap_data2 <- meta_results_4[, c("Region", "Measurement", "Estimate")]
  2264. heatmap_data2 <- dcast(heatmap_data2, Region ~ Measurement, value.var = "Estimate")
  2265. # Convert to long format for ggplot
  2266. long_heatmap <- melt(heatmap_data2, id.vars = "Region")
  2267. # Plot
  2268. ggplot(long_heatmap, aes(x = variable, y = Region, fill = value)) +
  2269. geom_tile() +
  2270. scale_fill_gradient2(low = "blue", mid = "white", high = "red", midpoint = 0,
  2271. name = "Effect Size") +
  2272. theme_minimal() +
  2273. labs(x = "Measurement", y = "Region", title = "Effect Sizes by Region and Measurement") +
  2274. theme(axis.text.y = element_text(size = 6)) +
  2275. coord_flip() +
  2276. theme(axis.text.x = element_text(angle = 60, vjust = .5))
  2277. ggplot(meta_results_4, aes(x = Estimate, y = Measurement, fill = Measurement)) +
  2278. geom_density_ridges(alpha = 0.8, scale = 1.2) +
  2279. theme_ridges() +
  2280. theme(legend.position = "none") +
  2281. labs(title = "Distribution of Effect Sizes by Measurement",
  2282. x = "Effect Size Estimate",
  2283. y = "Measurement")
  2284. ggplot(meta_results_4, aes(x = Estimate, y = I2, color = Measurement)) +
  2285. geom_point(alpha = 0.8, size = 3) +
  2286. theme_minimal() +
  2287. labs(title = "Effect Size vs. Heterogeneity (I²)",
  2288. x = "Effect Size Estimate",
  2289. y = "I² (%)") +
  2290. geom_vline(xintercept = 0, linetype = "dashed") +
  2291. scale_color_brewer(palette = "Set1") +
  2292. facet_wrap(~Measurement, ncol = 5) +
  2293. ylim(0,100) +
  2294. geom_hline(yintercept = c(25,50,75), linetype = 'dashed', color = 'red')
  2295. ggplot(meta_results_4, aes(x = Estimate, y = I2, color = Measurement)) +
  2296. ggdist::stat_halfeye(
  2297. adjust = .5,
  2298. width = .6,
  2299. .width = 0,
  2300. justification = -.3
  2301. ) +
  2302. geom_boxplot(width = .25, outlier.shape = NA) +
  2303. geom_point(alpha = 0.8, size = 3,
  2304. position = position_jitter(seed = 1, width = .1)) +
  2305. theme_minimal() +
  2306. labs(title = "Effect Size vs. Heterogeneity (I²)",
  2307. x = "Effect Size Estimate",
  2308. y = "I² (%)") +
  2309. geom_vline(xintercept = 0, linetype = "dashed") +
  2310. facet_wrap(~Measurement, ncol = 5) +
  2311. geom_hline(yintercept = 50, linetype = "dashed", color = 'red')
  2312. funnel(metagen(TE = meta_results_4$Estimate, seTE = meta_results_4$SE))
  2313. meta_gaus <- subset(meta_results_4, Measurement %in% "GausCurv")
  2314. meta_gaus$significant <- meta_gaus$FDR < 0.05
  2315. ggplot(meta_gaus, aes(x = Estimate, y = I2)) +
  2316. geom_point(aes(color = significant), alpha = .7, size = 2) +
  2317. geom_hline(yintercept = c(25,50,75), linetype = 'dashed', color = 'red') +
  2318. geom_vline(xintercept = 0, linetype = 'solid', color = 'black') +
  2319. geom_text(aes(label = ifelse(significant, Region, "")) , hjust = 1.1, vjust = 0.5, size = 3) +
  2320. labs(x = "Effect Size (Hedge's g)",
  2321. y = "Heterogeneity I2 (%)",
  2322. title = "Effect size vs heterogeneity of Gaussian curve measurements",
  2323. color = "Significant") +
  2324. theme_minimal() +
  2325. xlim(-.75,.75) +
  2326. ylim(0,100)
  2327. meta_df_4
  2328. meta_results_5 <- do.call(rbind, lapply(split(meta_df_4, list(meta_df_4$Region,
  2329. meta_df_4$Measurement)),
  2330. function(dfm){
  2331. if(nrow(dfm) >= 2) {
  2332. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  2333. pred <- predict(res)
  2334. data.frame(
  2335. Region = dfm$Region[1],
  2336. Measurement = dfm$Measurement[1],
  2337. k = res$k,
  2338. Estimate = res$b,
  2339. SE = res$se,
  2340. zval = res$zval,
  2341. pval = res$pval,
  2342. CI_lb = res$ci.lb,
  2343. CI_ub = res$ci.ub,
  2344. PI_lb = pred$pi.lb,
  2345. PI_ub = pred$pi.ub,
  2346. I2 = res$I2,
  2347. Q = res$QE,
  2348. pval_Q = res$QEp,
  2349. t2 = res$tau2
  2350. )
  2351. } else {
  2352. NULL
  2353. }
  2354. }))
  2355. meta_results_5 <- meta_results_5 %>%
  2356. group_by(Measurement) %>%
  2357. mutate(FDR = p.adjust(pval, method = 'BH')) %>%
  2358. ungroup()
  2359. meta_results_5 <- meta_results_5 %>%
  2360. group_by(Measurement) %>%
  2361. mutate(FDR_Q = p.adjust(pval_Q, method = 'BH')) %>%
  2362. ungroup()
  2363. meta_results_5 <- meta_results_5 %>%
  2364. mutate(Region_facet = paste(Measurement, Region, sep = "__")) %>% # unique within facet
  2365. group_by(Measurement) %>%
  2366. mutate(Region_facet = fct_reorder(Region_facet, Estimate, .desc = TRUE)) %>%
  2367. ungroup()
  2368. m5v <- meta_results_5
  2369. flipped <- data.frame(Region = m5v$Region, Measurement = m5v$Measurement, k = m5v$k,
  2370. Estimate = -m5v$Estimate, SE = m5v$SE, zval = m5v$zval,
  2371. pval = m5v$pval, CI_lb = -m5v$CI_ub, CI_ub = -m5v$CI_lb,
  2372. PI_lb = -m5v$PI_ub, PI_ub = -m5v$PI_lb, I2 = m5v$I2, Q = m5v$Q,
  2373. pval_Q = m5v$pval_Q, t2 = m5v$t2, FDR = m5v$FDR, Region_facet = m5v$Region_facet,
  2374. FDR_Q = m5v$FDR_Q)
  2375. m5v$Measurement[m5v$Measurement == 'SurfArea'] <- 'Surface Area'
  2376. m5v$Measurement[m5v$Measurement == 'GrayVol'] <- 'Gray Matter Volume'
  2377. m5v$Measurement[m5v$Measurement == 'ThickAvg'] <- 'Average Thickness'
  2378. m5v$Measurement[m5v$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  2379. m5v$Measurement[m5v$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  2380. flipped$Measurement[flipped$Measurement == 'SurfArea'] <- 'Surface Area'
  2381. flipped$Measurement[flipped$Measurement == 'GrayVol'] <- 'Gray Matter Volume'
  2382. flipped$Measurement[flipped$Measurement == 'ThickAvg'] <- 'Average Thickness'
  2383. flipped$Measurement[flipped$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  2384. flipped$Measurement[flipped$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  2385. flipped <- flipped %>%
  2386. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2387. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2388. regions <- flipped$region %>% str_to_lower()
  2389. miss <- setdiff(regions, atlas_regions)
  2390. matching <- sapply(miss, function(regions){
  2391. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2392. atlas_regions[which.min(distances)]
  2393. })
  2394. recoding <- setNames(matching, miss)
  2395. recoding
  2396. flipped <- flipped %>%
  2397. mutate(region = str_to_lower(region),
  2398. region = ifelse(region %in% names(recoding),
  2399. recoding[region], region))
  2400. write_xlsx(flipped, "Cortical_meta_results.xlsx")
  2401. metaplot <- ggplot(m5v, aes(y = Region_facet, x = Estimate)) +
  2402. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 1) +
  2403. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 0.8) +
  2404. geom_point(aes(color = FDR < 0.05), size = 2) +
  2405. geom_vline(xintercept = 0, linetype = "twodash") +
  2406. facet_wrap(~factor(Measurement, levels = c("Surface Area","Gray Matter Volume","Average Thickness",
  2407. "Gaussian Curvature",'Mean Curvature')),
  2408. scales = "free_y", ncol = 5) +
  2409. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2410. scale_y_discrete(labels = function(x) gsub(".*__", "", x)) +
  2411. labs(
  2412. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2413. x = "Hedges' g", y = "Region",
  2414. color = "FDR < 0.05"
  2415. ) +
  2416. ggplot2::theme_bw(base_size = 12) +
  2417. theme(
  2418. strip.text = element_text(face = "bold", size = 12),
  2419. axis.text.y = element_text(size = 12),
  2420. legend.position = "bottom"
  2421. )
  2422. metaplot
  2423. metaplot2 <- ggplot(flipped, aes(y = Region_facet, x = Estimate)) +
  2424. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 1) +
  2425. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 0.8) +
  2426. geom_point(aes(color = FDR < 0.05), size = 2) +
  2427. geom_vline(xintercept = 0, linetype = "twodash") +
  2428. facet_wrap(~factor(Measurement, levels = c("Surface Area","Gray Matter Volume","Average Thickness",
  2429. "Gaussian Curvature",'Mean Curvature')),
  2430. scales = "free_y", ncol = 5) +
  2431. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2432. scale_y_discrete(labels = function(x) gsub(".*__", "", x)) +
  2433. labs(
  2434. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2435. x = "Hedges' g", y = "Region",
  2436. color = "FDR < 0.05"
  2437. ) +
  2438. ggplot2::theme_bw(base_size = 12) +
  2439. theme(
  2440. strip.text = element_text(face = "bold", size = 12),
  2441. axis.text.y = element_text(size = 12),
  2442. legend.position = "bottom"
  2443. )
  2444. metaplot2
  2445. ggsave("meta_forest.png", plot = metaplot, dpi = 500, width = 26, height = 15, units = 'in', bg = 'white')
  2446. flipped_gc <- subset(flipped, Measurement %in% 'Gaussian Curvature')
  2447. gc_long <- flipped_gc %>%
  2448. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2449. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2450. regions <- gc_long$region %>% str_to_lower()
  2451. miss <- setdiff(regions, atlas_regions)
  2452. matching <- sapply(miss, function(regions){
  2453. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2454. atlas_regions[which.min(distances)]
  2455. })
  2456. recoding <- setNames(matching, miss)
  2457. recoding
  2458. gc_long <- gc_long %>%
  2459. mutate(region = str_to_lower(region),
  2460. region = ifelse(region %in% names(recoding),
  2461. recoding[region], region))
  2462. gc_long <- gc_long %>%
  2463. mutate(region = reorder_within(region, Estimate, hemi))
  2464. gc_meta <- ggplot(gc_long, aes(y = region, x = Estimate)) +
  2465. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2466. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2467. geom_point(aes(color = FDR < 0.05), size = 5) +
  2468. geom_vline(xintercept = 0, linetype = "twodash") +
  2469. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2470. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2471. labs(
  2472. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2473. x = "Hedges' g", y = "Region",
  2474. color = "FDR < 0.05"
  2475. ) +
  2476. ggplot2::theme_bw(base_size = 28) +
  2477. theme(
  2478. strip.text = element_text(face = "bold", size = 28),
  2479. axis.text.y = element_text(size = 28),
  2480. legend.position = "bottom"
  2481. ) +
  2482. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  2483. tidytext::scale_y_reordered(position = 'left')
  2484. gc_meta
  2485. ggplot(gc_long, aes(y = region, x = Estimate)) +
  2486. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2487. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2488. geom_point(aes(color = FDR < 0.05), size = 5) +
  2489. geom_vline(xintercept = 0, linetype = "twodash") +
  2490. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2491. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2492. labs(
  2493. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2494. x = "Hedges' g", y = "Region",
  2495. color = "FDR < 0.05"
  2496. ) +
  2497. ggplot2::theme_bw(base_size = 12) +
  2498. theme(
  2499. strip.text = element_text(face = "bold", size = 12),
  2500. axis.text.y = element_text(size = 12),
  2501. legend.position = "bottom"
  2502. ) +
  2503. facet_wrap(~hemi, scales = "free_y", ncol = 2) +
  2504. tidytext::scale_y_reordered(position = 'right')
  2505. ggsave("gc_meta.png", plot = gc_meta, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  2506. flipped_mc <- subset(flipped, Measurement %in% 'Mean Curvature')
  2507. mc_long <- flipped_mc %>%
  2508. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2509. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2510. regions <- mc_long$region %>% str_to_lower()
  2511. miss <- setdiff(regions, atlas_regions)
  2512. matching <- sapply(miss, function(regions){
  2513. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2514. atlas_regions[which.min(distances)]
  2515. })
  2516. recoding <- setNames(matching, miss)
  2517. recoding
  2518. mc_long <- mc_long %>%
  2519. mutate(region = str_to_lower(region),
  2520. region = ifelse(region %in% names(recoding),
  2521. recoding[region], region))
  2522. mc_long <- mc_long %>%
  2523. mutate(region = reorder_within(region, Estimate, hemi))
  2524. mc_meta <- ggplot(mc_long, aes(y = region, x = Estimate)) +
  2525. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2526. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2527. geom_point(aes(color = FDR < 0.05), size = 5) +
  2528. geom_vline(xintercept = 0, linetype = "twodash") +
  2529. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2530. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2531. labs(
  2532. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2533. x = "Hedges' g", y = "Region",
  2534. color = "FDR < 0.05"
  2535. ) +
  2536. ggplot2::theme_bw(base_size = 28) +
  2537. theme(
  2538. strip.text = element_text(face = "bold", size = 28),
  2539. axis.text.y = element_text(size = 28),
  2540. legend.position = "bottom"
  2541. ) +
  2542. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  2543. tidytext::scale_y_reordered(position = 'left')
  2544. mc_meta
  2545. ggsave("mc_meta.png", plot = mc_meta, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  2546. flipped_gmv <- subset(flipped, Measurement %in% 'Gray Matter Volume')
  2547. gm_long <- flipped_gmv %>%
  2548. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2549. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2550. regions <- gm_long$region %>% str_to_lower()
  2551. miss <- setdiff(regions, atlas_regions)
  2552. matching <- sapply(miss, function(regions){
  2553. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2554. atlas_regions[which.min(distances)]
  2555. })
  2556. recoding <- setNames(matching, miss)
  2557. recoding
  2558. gm_long <- gm_long %>%
  2559. mutate(region = str_to_lower(region),
  2560. region = ifelse(region %in% names(recoding),
  2561. recoding[region], region))
  2562. gm_long <- gm_long %>%
  2563. mutate(region = reorder_within(region, Estimate, hemi))
  2564. gmv_meta <- ggplot(gm_long, aes(y = region, x = Estimate)) +
  2565. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2566. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2567. geom_point(aes(color = FDR < 0.05), size = 5) +
  2568. geom_vline(xintercept = 0, linetype = "twodash") +
  2569. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2570. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2571. labs(
  2572. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2573. x = "Hedges' g", y = "Region",
  2574. color = "FDR < 0.05"
  2575. ) +
  2576. ggplot2::theme_bw(base_size = 28) +
  2577. theme(
  2578. strip.text = element_text(face = "bold", size = 28),
  2579. axis.text.y = element_text(size = 28),
  2580. legend.position = "bottom"
  2581. ) +
  2582. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  2583. tidytext::scale_y_reordered(position = 'left')
  2584. gmv_meta
  2585. ggsave("gmv_meta.png", plot = gmv_meta, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  2586. flipped_sa <- subset(flipped, Measurement %in% 'Surface Area')
  2587. sa_long <- flipped_sa %>%
  2588. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2589. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2590. regions <- sa_long$region %>% str_to_lower()
  2591. miss <- setdiff(regions, atlas_regions)
  2592. matching <- sapply(miss, function(regions){
  2593. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2594. atlas_regions[which.min(distances)]
  2595. })
  2596. recoding <- setNames(matching, miss)
  2597. recoding
  2598. sa_long <- sa_long %>%
  2599. mutate(region = str_to_lower(region),
  2600. region = ifelse(region %in% names(recoding),
  2601. recoding[region], region))
  2602. sa_long <- sa_long %>%
  2603. mutate(region = reorder_within(region, Estimate, hemi))
  2604. sa_meta <- ggplot(sa_long, aes(y = region, x = Estimate)) +
  2605. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2606. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2607. geom_point(aes(color = FDR < 0.05), size = 5) +
  2608. geom_vline(xintercept = 0, linetype = "twodash") +
  2609. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2610. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2611. labs(
  2612. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2613. x = "Hedges' g", y = "Region",
  2614. color = "FDR < 0.05"
  2615. ) +
  2616. ggplot2::theme_bw(base_size = 28) +
  2617. theme(
  2618. strip.text = element_text(face = "bold", size = 28),
  2619. axis.text.y = element_text(size = 28),
  2620. legend.position = "bottom"
  2621. ) +
  2622. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  2623. tidytext::scale_y_reordered(position = 'right')
  2624. sa_meta
  2625. ggsave("gmv_meta.png", plot = gmv_meta, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  2626. flipped_ct <- subset(flipped, Measurement %in% 'Average Thickness')
  2627. ct_long <- flipped_ct %>%
  2628. separate(Region, into = c("hemi","region"), sep = "_") %>%
  2629. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  2630. regions <- ct_long$region %>% str_to_lower()
  2631. miss <- setdiff(regions, atlas_regions)
  2632. matching <- sapply(miss, function(regions){
  2633. distances <- stringdist(regions, atlas_regions, method = 'jw')
  2634. atlas_regions[which.min(distances)]
  2635. })
  2636. recoding <- setNames(matching, miss)
  2637. recoding
  2638. ct_long <- ct_long %>%
  2639. mutate(region = str_to_lower(region),
  2640. region = ifelse(region %in% names(recoding),
  2641. recoding[region], region))
  2642. ct_long <- ct_long %>%
  2643. mutate(region = reorder_within(region, Estimate, hemi))
  2644. ct_meta <- ggplot(ct_long, aes(y = region, x = Estimate)) +
  2645. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2646. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2647. geom_point(aes(color = FDR < 0.05), size = 5) +
  2648. geom_vline(xintercept = 0, linetype = "twodash") +
  2649. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2650. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  2651. labs(
  2652. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2653. x = "Hedges' g", y = "Region",
  2654. color = "FDR < 0.05"
  2655. ) +
  2656. ggplot2::theme_bw(base_size = 28) +
  2657. theme(
  2658. strip.text = element_text(face = "bold", size = 28),
  2659. axis.text.y = element_text(size = 28),
  2660. legend.position = "bottom"
  2661. ) +
  2662. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  2663. tidytext::scale_y_reordered(position = 'left')
  2664. ct_meta
  2665. ggsave("ct_meta.png", plot = ct_meta, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  2666. left_ent <- ent_test %>%
  2667. filter(grepl("Left", Region))
  2668. gmv_ent <- m5v_gmv %>%
  2669. filter(grepl("entorhinal", Region))
  2670. gmv_ent$Region[gmv_ent$Region == 'lh_entorhinal'] <- 'Left Entorhinal Cortex'
  2671. gmv_ent$Region[gmv_ent$Region == 'rh_entorhinal'] <- 'Right Entorhinal Cortex'
  2672. ent_plot <- ggplot(gmv_ent, aes(y = Region, x = Estimate)) +
  2673. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  2674. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  2675. geom_point(aes(color = FDR < 0.05), size = 5) +
  2676. geom_vline(xintercept = 0, linetype = "twodash", size = 1) +
  2677. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  2678. scale_y_discrete(labels = function(x) gsub(".*__", "", x)) +
  2679. labs(
  2680. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  2681. x = "Hedges' g", y = "Region",
  2682. color = "FDR < 0.05"
  2683. ) +
  2684. ggplot2::theme_bw(base_size = 20) +
  2685. theme(
  2686. strip.text = element_text(face = "bold", size = 20),
  2687. axis.text.y = element_text(size = 20),
  2688. legend.position = "bottom",
  2689. legend.background = element_rect(fill = "transparent", color = NA),
  2690. panel.background = element_rect(fill = "transparent", color = NA),
  2691. plot.background = element_rect(fill = "transparent", color = NA),
  2692. panel.grid = element_blank()
  2693. ) +
  2694. xlim(-.3,.6)
  2695. ent_plot
  2696. ggsave(
  2697. "forest_plot_transparent.png",
  2698. plot = ent_plot,
  2699. width = 10,
  2700. height = 4,
  2701. dpi = 300,
  2702. bg = "transparent"
  2703. )
  2704. ent_test <- entorhinal_4_vol_vis
  2705. ent_test$Study[ent_test$Study == 'Tétreault, 2016, Osteoarthritis'] <- 'Osteoarthritis: Tétreault, 2016'
  2706. ent_test$Study[ent_test$Study == 'Pando-Naude, 2019, Fibromyalgia'] <- 'Fibromyalgia: Pando-Naude, 2019'
  2707. ent_test$Study[ent_test$Study == 'Makary, 2020, Chronic Lower Back Pain'] <- 'Chronic Lower Back Pain: Makary, 2020'
  2708. ent_test$Study[ent_test$Study == 'Seminowicz, 2020, Migraine'] <- 'Migraine: Seminowicz, 2020'
  2709. ent_test$Study[ent_test$Study == 'Filimonova, 2025, Primary Trigeminal Neuralgia'] <- 'Primary Trigeminal Neuralgia: Filimonova, 2025'
  2710. ent_test$Study[ent_test$Study == 'Balducci, 2022, Fibromyalgia'] <- 'Balducci, 2022, Fibromyalgia'
  2711. ent_test$Study[ent_test$Study == 'Mano, 2018, Chronic Lower Back Pain (UK Data)'] <- 'Chronic Lower Back Pain: Mano, 2018 (UK Data)'
  2712. ent_test$Study[ent_test$Study == 'Mano, 2018, Chronic Lower Back Pain (Japan Data)'] <- 'Chronic Lower Back Pain: Mano, 2018 (Japan Data)'
  2713. ent_test$Study[ent_test$Study == 'Balducci, 2022, Fibromyalgia'] <- 'Fibromyalgia: Balducci, 2022'
  2714. left_ent <- ent_test %>%
  2715. filter(grepl("Left", Region))
  2716. right_ent <- ent_test %>%
  2717. filter(grepl("Right", Region))
  2718. left_ent <- left_ent %>%
  2719. separate(Study, into = c("Condition","Author"), sep = ":")
  2720. right_ent <- right_ent %>%
  2721. separate(Study, into = c("Condition","Author"), sep = ":")
  2722. res_left <- metagen(TE = Hedges,
  2723. seTE = SE,
  2724. studlab = Author,
  2725. data = left_ent,
  2726. method.tau = "REML",
  2727. prediction = TRUE,
  2728. random = TRUE)
  2729. res_right <- metagen(TE = Hedges,
  2730. seTE = SE,
  2731. studlab = Author,
  2732. data = right_ent,
  2733. method.tau = "REML",
  2734. prediction = TRUE,
  2735. random = TRUE)
  2736. forest(res_left,
  2737. prediction = TRUE,
  2738. col.predict = "gray50",
  2739. col.random = "navy",
  2740. xlab = "Hedges' g",
  2741. common = FALSE,
  2742. print.common = FALSE,
  2743. leftcols = c("Condition","studlab", "TE", "seTE"),
  2744. leftlabs = c("Condition","Author", "Effect Size", "SE"),
  2745. sortvar = Condition)
  2746. forest(res_right,
  2747. prediction = TRUE,
  2748. col.predict = "gray50",
  2749. col.random = "navy",
  2750. xlab = "Hedges' g",
  2751. common = FALSE,
  2752. print.common = FALSE,
  2753. leftcols = c("Condition","studlab", "TE", "seTE"),
  2754. leftlabs = c("Condition","Author", "Effect Size", "SE"),
  2755. sortvar = Condition)
  2756. #Visualize lh precentral gyrus gaus curv
  2757. precentral <- meta_df_4[grepl("precentral", meta_df_4$Region), ]
  2758. precentral <- subset(precentral, Measurement %in% 'GausCurv')
  2759. precentral_viz <- data.frame(Hedges = precentral$yi, SE = sqrt(precentral$vi),
  2760. Measurement = precentral$Measurement, Study = precentral$study,
  2761. Region = precentral$Region)
  2762. left_precentral <- precentral_viz %>%
  2763. filter(grepl("lh", Region))
  2764. right_precentral <- precentral_viz %>%
  2765. filter(grepl("rh", Region))
  2766. res_lhpre <- metagen(TE = Hedges,
  2767. seTE = SE,
  2768. studlab = Study,
  2769. data = left_precentral,
  2770. method.tau = "REML",
  2771. prediction = TRUE,
  2772. random = TRUE)
  2773. res_rhpre <- metagen(TE = Hedges,
  2774. seTE = SE,
  2775. studlab = Study,
  2776. data = right_precentral,
  2777. method.tau = "REML",
  2778. prediction = TRUE,
  2779. random = TRUE)
  2780. forest(res_lhpre,
  2781. main = "Left Precentral Gyrus Gaussian Curve",
  2782. prediction = TRUE,
  2783. col.predict = "gray50",
  2784. col.random = "navy",
  2785. xlab = "Hedges' g",
  2786. common = F,
  2787. print.common = F,
  2788. leftlabs = c("Study","Estimated effect size","Standard Error"))
  2789. forest(res_rhpre,
  2790. main = "Right Precentral Gyrus Gaussian Curve",
  2791. prediction = TRUE,
  2792. col.predict = "gray50",
  2793. col.random = "navy",
  2794. xlab = "Hedges' g",
  2795. common = F,
  2796. print.common = F,
  2797. leftlabs = c("Study","Estimated effect size","Standard Error"))
  2798. subsetting_meta5 <- meta_results_5
  2799. subsetting_meta5$Measurement[subsetting_meta5$Measurement == 'SurfArea'] <- 'Surface Area'
  2800. subsetting_meta5$Measurement[subsetting_meta5$Measurement == 'GrayVol'] <- 'Gray Matter Volume'
  2801. subsetting_meta5$Measurement[subsetting_meta5$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  2802. subsetting_meta5$Measurement[subsetting_meta5$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  2803. subsetting_meta5$Measurement[subsetting_meta5$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  2804. ggplot(subsetting_meta5, aes(x = I2, y = Estimate, size = t2, color = FDR_Q < 0.05)) +
  2805. geom_point(alpha = 0.8) +
  2806. labs(title = "Effect Size vs. Heterogeneity",
  2807. x = "I² (%)", y = "Pooled Effect Size") +
  2808. scale_color_manual(values = c("gray", "red"), name = "Significant Heterogeneity") +
  2809. facet_wrap(~factor(Measurement, levels = c("Surface Area","Gray Matter Volume","Cortical Thickness",
  2810. "Mean Curvature",'Gaussian Curvature')), ncol = 1) +
  2811. theme_minimal() +
  2812. ylim(-.5,.5) +
  2813. xlim(0,100) +
  2814. geom_hline(yintercept = 0, linetype = 'solid', color = 'black') +
  2815. geom_vline(xintercept = c(25,50,75), linetype = 'twodash',color = 'red') +
  2816. geom_text(aes(label = ifelse(FDR_Q < 0.05, Region, "")), hjust = 1.4, vjust = 0.5, size = 3)
  2817. ggplot(meta_results_5, aes(x = Estimate, y = -log10(FDR_Q), color = FDR_Q < 0.05)) +
  2818. geom_point() +
  2819. geom_hline(yintercept = -log10(0.05), linetype = "dashed") +
  2820. labs(title = "Effect Size vs. Heterogeneity Significance",
  2821. x = "Effect Estimate", y = "-log10(FDR_Q)")+
  2822. facet_wrap(~Measurement, ncol = 5) +
  2823. theme_minimal()
  2824. ggplot(meta_results_5, aes(x = t2)) +
  2825. geom_histogram(bins = 20, fill = "steelblue", color = "white") +
  2826. labs(title = "Distribution of Between-Study Variance (τ²)") +
  2827. facet_wrap(~Measurement) +
  2828. theme_minimal()
  2829. testing <- subset(meta_results_5, Measurement %in% "ThickAvg")
  2830. densityPlot(testing$t2)
  2831. #########################################################################
  2832. #Group level summary again
  2833. #########################################################################
  2834. meta_sex
  2835. rma(yi, vi, mods = ~ sex + Region, data = meta_sex, method = "REML")
  2836. collapsed_by_sex <- meta_sex %>%
  2837. group_by(study, sex, Measurement) %>%
  2838. summarise(
  2839. yi = sum(yi / vi) / sum(1 / vi),
  2840. vi = 1 / sum(1 / vi),
  2841. .groups = "drop"
  2842. )
  2843. collapsed_combined <- collapsed_by_sex %>%
  2844. group_by(study, Measurement) %>%
  2845. summarise(
  2846. yi = if(n() == 2) sum(yi / vi) / sum(1 / vi) else yi,
  2847. vi = if(n() == 2) 1 / sum(1 / vi) else vi,
  2848. .groups = "drop"
  2849. )
  2850. group_summary_3 <- data.frame()
  2851. for (m in measurements) {
  2852. for (s in c("female", "male")) {
  2853. df_sub <- subset(collapsed_meta, Measurement == m & sex == s)
  2854. res_mes <- rma(yi = yi, vi = vi, method = "REML", data = df_sub)
  2855. pred <- predict(res_mes)
  2856. group_summary_3 <- rbind(group_summary_3, data.frame(
  2857. Measurement = m,
  2858. Sex = s,
  2859. Effect = res_mes$b,
  2860. SE = res_mes$se,
  2861. CI_lower = res_mes$ci.lb,
  2862. CI_upper = res_mes$ci.ub,
  2863. PI_lb = pred$pi.lb,
  2864. PI_ub = pred$pi.ub,
  2865. tau2 = res_mes$tau2,
  2866. k = res_mes$k,
  2867. I2 = res_mes$I2,
  2868. Q = res_mes$QE,
  2869. pval_Q = res_mes$QEp
  2870. ))
  2871. }
  2872. # Combined (both sexes)
  2873. df_all <- subset(collapsed_combined, Measurement == m)
  2874. res_all <- rma(yi = yi, vi = vi, method = "REML", data = df_all)
  2875. pred_all <- predict(res_all)
  2876. group_summary_3 <- rbind(group_summary_3, data.frame(
  2877. Measurement = m,
  2878. Sex = "both",
  2879. Effect = res_all$b,
  2880. SE = res_all$se,
  2881. CI_lower = res_all$ci.lb,
  2882. CI_upper = res_all$ci.ub,
  2883. PI_lb = pred_all$pi.lb,
  2884. PI_ub = pred_all$pi.ub,
  2885. tau2 = res_all$tau2,
  2886. k = res_all$k,
  2887. I2 = res_all$I2,
  2888. Q = res_all$QE,
  2889. pval_Q = res_all$QEp
  2890. ))
  2891. }
  2892. group_summary_3$Measurement[group_summary_3$Measurement == 'SurfArea'] <- 'Surface Area'
  2893. group_summary_3$Measurement[group_summary_3$Measurement == 'GrayVol'] <- 'Gray Matter Volume'
  2894. group_summary_3$Measurement[group_summary_3$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  2895. group_summary_3$Measurement[group_summary_3$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  2896. group_summary_3$Measurement[group_summary_3$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  2897. group_summary_3$Measurement <- factor(group_summary_3$Measurement,
  2898. levels = c('Gaussian Curvature','Mean Curvature','Surface Area',
  2899. 'Cortical Thickness','Gray Matter Volume'))
  2900. pfaff <- ggplot(group_summary_3, aes(x = Measurement, y = Effect, color = Sex, group = Sex)) +
  2901. geom_point(position = position_dodge(width = 0.7), size = 3) +
  2902. geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
  2903. position = position_dodge(width = 0.7), width = 0.6) +
  2904. geom_hline(yintercept = 0, linetype = "dashed") +
  2905. labs(title = "Group-level Effect Size by Measurement and Sex",
  2906. y = "Hedges' g",
  2907. x = "Measurement Type") +
  2908. scale_color_manual(values = c("female" = "#00b2a9", "male" = "#e43c40", "both" = "black")) +
  2909. coord_flip() +
  2910. ggplot2::theme_bw(base_size = 16) +
  2911. theme(
  2912. strip.text = element_text(face = "bold", size = 16),
  2913. axis.text.y = element_text(size = 16),
  2914. legend.position = "bottom"
  2915. )
  2916. pfaff
  2917. ggsave("meta_sex_groupestimate.png", plot = pfaff, dpi = 500, width = 15, height = 6, units = 'in', bg = 'white')
  2918. meta_sex$sex <- factor(meta_sex$sex)
  2919. meta_sex$Region <- factor(meta_sex$Region)
  2920. meta_function <- function(data) {
  2921. rma.mv(yi, vi, random = ~1 | study/Region, method = 'REML', data = data)
  2922. }
  2923. meta_test_results <- meta_sex %>%
  2924. group_by(Measurement, sex) %>%
  2925. filter(n() >= 2) %>% # only analyze if you have enough data
  2926. group_modify(~ {
  2927. tryCatch({
  2928. model <- rma.mv(yi, vi, random = ~1 | study/Region, method = "REML", data = .x)
  2929. tibble(g = model$b[1], se = model$se, ci.lb = model$ci.lb, ci.ub = model$ci.ub)
  2930. }, error = function(e) {
  2931. tibble(g = NA, se = NA, ci.lb = NA, ci.ub = NA)
  2932. })
  2933. }) %>%
  2934. ungroup()
  2935. meta_test_results$Measurement[meta_test_results$Measurement == 'SurfArea'] <- 'Surface Area'
  2936. meta_test_results$Measurement[meta_test_results$Measurement == 'GrayVol'] <- 'Gray Matter Volume'
  2937. meta_test_results$Measurement[meta_test_results$Measurement == 'ThickAvg'] <- 'Cortical Thickness'
  2938. meta_test_results$Measurement[meta_test_results$Measurement == 'GausCurv'] <- 'Gaussian Curvature'
  2939. meta_test_results$Measurement[meta_test_results$Measurement == 'MeanCurv'] <- 'Mean Curvature'
  2940. meta_test_results$Measurement <- factor(meta_test_results$Measurement,
  2941. levels = c('Gaussian Curvature','Mean Curvature','Cortical Thickness',
  2942. 'Surface Area','Gray Matter Volume'))
  2943. pfaff <- ggplot(meta_test_results, aes(x = g, y = Measurement, color = sex)) +
  2944. geom_point(position = position_dodge(width = 0.5), size = 3) +
  2945. geom_errorbar(aes(xmin = ci.lb, xmax = ci.ub),
  2946. position = position_dodge(width = 0.5), width = 0.2) +
  2947. geom_vline(xintercept = 0, linetype = "dashed") +
  2948. scale_color_manual(values = c("red", "turquoise3")) +
  2949. labs(
  2950. x = "Hedges' g",
  2951. y = "Measurement Type",
  2952. title = "Group-level Effect Size by Measurement and Sex"
  2953. ) +
  2954. theme_minimal(base_size = 14)
  2955. pfaff
  2956. ############################################################################
  2957. #Redo more shit
  2958. ############################################################################
  2959. measurement_types <- unique(meta_sex$Measurement)
  2960. # Create empty list to store results
  2961. results_list <- list()
  2962. # Loop
  2963. for (m in measurement_types) {
  2964. cat("\n\n-------------------\nAnalyzing:", m, "\n-------------------\n")
  2965. # Subset data
  2966. sub_df <- subset(meta_sex, Measurement == m)
  2967. # Run 3-level meta-regression with sex as moderator, random intercept by study
  2968. model <- rma.mv(yi = yi,
  2969. V = vi,
  2970. mods = ~ sex,
  2971. random = ~ 1 | study,
  2972. data = sub_df,
  2973. method = "REML")
  2974. print(summary(model))
  2975. # Optionally store result
  2976. results_list[[as.character(m)]] <- model
  2977. }
  2978. ######################################################################
  2979. oa_vol_summary
  2980. fm_vol_summary
  2981. fm2_vol_summary
  2982. clbp_vol_summary
  2983. clbps1_vol_summary
  2984. clbps2_vol_summary
  2985. mvol_summary
  2986. pvol_summary
  2987. oa_vol_summary$study <- 'OA'
  2988. fm_vol_summary$study <- 'FM'
  2989. fm2_vol_summary$study <- 'FM2'
  2990. clbp_vol_summary$study <- 'CLBP'
  2991. clbps1_vol_summary$study <- 'CLBP_S1'
  2992. clbps2_vol_summary$study <- 'CLBP_S2'
  2993. mvol_summary$study <- 'migraine'
  2994. pvol_summary$study <- 'PTN'
  2995. meta_vol_df <- rbind(oa_vol_summary, fm_vol_summary, fm2_vol_summary,
  2996. clbp_vol_summary, clbps1_vol_summary, clbps2_vol_summary,
  2997. mvol_summary, pvol_summary)
  2998. meta_vol_df$yi <- meta_vol_df$Hedges_g
  2999. meta_vol_df$vi <- meta_vol_df$SE_Hedges_g^2
  3000. meta_vol_results <- do.call(rbind, lapply(split(meta_vol_df, meta_vol_df$Region),
  3001. function(dfm){
  3002. if(nrow(dfm) >= 2) {
  3003. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  3004. pred <- predict(res)
  3005. data.frame(
  3006. Region = dfm$Region[1],
  3007. k = res$k,
  3008. Estimate = res$b,
  3009. SE = res$se,
  3010. zval = res$zval,
  3011. pval = res$pval,
  3012. CI_lb = res$ci.lb,
  3013. CI_ub = res$ci.ub,
  3014. PI_lb = pred$pi.lb,
  3015. PI_ub = pred$pi.ub,
  3016. I2 = res$I2,
  3017. Q = res$QE,
  3018. pval_Q = res$QEp,
  3019. t2 = res$tau2
  3020. )
  3021. } else {
  3022. NULL
  3023. }
  3024. }))
  3025. meta_vol_results <- meta_vol_results %>%
  3026. mutate(FDR = p.adjust(pval, method = 'BH'))
  3027. oasub_summary
  3028. fm1sub_summary
  3029. fm2sub_summary
  3030. clbpsub_summary
  3031. clbpsubs1_summary
  3032. clbpsubs2_summary
  3033. msub_summary
  3034. ptnsub_summary
  3035. oasub_summary$study <- 'OA'
  3036. fm1sub_summary$study <- 'FM'
  3037. fm2sub_summary$study <- 'FM2'
  3038. clbpsub_summary$study <- 'CLBP'
  3039. clbpsubs1_summary$study <- 'CLBP_S1'
  3040. clbpsubs2_summary$study <- 'CLBP_S2'
  3041. msub_summary$study <- 'migraine'
  3042. ptnsub_summary$study <- 'PTN'
  3043. meta_sub_df <- rbind(oasub_summary, fm1sub_summary, fm2sub_summary,
  3044. clbpsub_summary, clbpsubs1_summary, clbpsubs2_summary,
  3045. msub_summary, ptnsub_summary)
  3046. meta_sub_df$yi <- meta_sub_df$Hedges_g
  3047. meta_sub_df$vi <- meta_sub_df$SE_Hedges_g^2
  3048. meta_sub_results <- do.call(rbind, lapply(split(meta_sub_df, meta_sub_df$Region),
  3049. function(dfm){
  3050. if(nrow(dfm) >= 2) {
  3051. res <- rma(yi = dfm$yi, vi = dfm$vi, method = 'REML')
  3052. pred <- predict(res)
  3053. data.frame(
  3054. Region = dfm$Region[1],
  3055. k = res$k,
  3056. Estimate = res$b,
  3057. SE = res$se,
  3058. zval = res$zval,
  3059. pval = res$pval,
  3060. CI_lb = res$ci.lb,
  3061. CI_ub = res$ci.ub,
  3062. PI_lb = pred$pi.lb,
  3063. PI_ub = pred$pi.ub,
  3064. I2 = res$I2,
  3065. Q = res$QE,
  3066. pval_Q = res$QEp,
  3067. t2 = res$tau2
  3068. )
  3069. } else {
  3070. NULL
  3071. }
  3072. }))
  3073. meta_sub_results <- meta_sub_results %>%
  3074. mutate(FDR = p.adjust(pval, method = 'BH'))
  3075. meta_sub_results <- meta_sub_results %>%
  3076. mutate(FDR_Q = p.adjust(pval_Q, method = 'BH'))
  3077. meta_sub_results$Region_clean <- as.character(meta_sub_results$Region)
  3078. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Lateral.Ventricle'] <- 'Right Lateral Ventricle'
  3079. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Lateral.Ventricle'] <- 'Left Lateral Ventricle'
  3080. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Pallidum'] <- 'Left Pallidum'
  3081. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Pallidum'] <- 'Right Pallidum'
  3082. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Cerebellum.White.Matter'] <- 'Left Cerebellum White Matter'
  3083. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Cerebellum.White.Matter'] <- 'Right Cerebellum White Matter'
  3084. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'X3rd.Ventricle'] <- '3rd Ventricle'
  3085. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'CC_Anterior'] <- 'CC Anterior'
  3086. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'X4th.Ventricle'] <- '4th Ventricle'
  3087. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'CC_Mid_Anterior'] <- 'CC Mid Anterior'
  3088. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Caudate'] <- 'Left Caudate'
  3089. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Caudate'] <- 'Right Caudate'
  3090. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'CC_Posterior'] <- 'CC Posterior'
  3091. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'CC_Mid_Posterior'] <- 'CC Mid Posterior'
  3092. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'CC_Central'] <- 'CC Central'
  3093. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.VentralDC'] <- 'Left Ventral Diencephalon'
  3094. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.VentralDC'] <- 'Right Ventral Diencephalon'
  3095. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Brain.Stem'] <- 'Brain Stem'
  3096. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Putamen'] <- 'Left Putamen'
  3097. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Putamen'] <- 'Right Putamen'
  3098. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Thalamus'] <- 'Right Thalamus'
  3099. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Thalamus'] <- 'Left Thalamus'
  3100. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Hippocampus'] <- 'Left Hippocampus'
  3101. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Hippocampus'] <- 'Right Hippocampus'
  3102. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Cerebellum.Cortex'] <- 'Right Cerebellum Cortex'
  3103. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Cerebellum.Cortex'] <- 'Left Cerebellum Cortex'
  3104. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Amygdala'] <- 'Left Amygdala'
  3105. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Amygdala'] <- 'Right Amygdala'
  3106. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Cerebellum.Cortex'] <- 'Right Cerebellum Cortex'
  3107. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Cerebellum.Cortex'] <- 'Left Cerebellum Cortex'
  3108. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Amygdala'] <- 'Left Amygdala'
  3109. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Amygdala'] <- 'Right Amygdala'
  3110. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Inf.Lat.Vent'] <- 'Left Inferior Lateral Ventricle'
  3111. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Inf.Lat.Vent'] <- 'Right Inferior Lateral Ventricle'
  3112. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Left.Accumbens.area'] <- 'Left Accumbens'
  3113. meta_sub_results$Region_clean[meta_sub_results$Region_clean == 'Right.Accumbens.area'] <- 'Right Accumbens'
  3114. meta_sub_results <- meta_sub_results %>%
  3115. mutate(Region_clean = fct_reorder(Region_clean, Estimate, .desc = TRUE))
  3116. sub_graph_adjusted <- ggplot(meta_sub_results, aes(y = Region_clean, x = -Estimate)) +
  3117. geom_errorbarh(aes(xmin = -PI_ub, xmax = -PI_lb), color = "gray70", height = 0.4, size = 1) +
  3118. geom_errorbarh(aes(xmin = -CI_ub, xmax = -CI_lb), color = "black", height = 0.2, size = 0.8) +
  3119. geom_point(aes(color = FDR < 0.05), size = 2) +
  3120. geom_vline(xintercept = 0, linetype = "twodash") +
  3121. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  3122. labs(
  3123. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  3124. x = "Hedges' g", y = "Region",
  3125. color = "FDR < 0.05"
  3126. ) +
  3127. theme_bw(base_size = 24) +
  3128. theme(
  3129. strip.text = element_text(face = "bold", size = 24),
  3130. axis.text.y = element_text(size = 24),
  3131. legend.position = "bottom"
  3132. ) +
  3133. tidytext::scale_y_reordered(position = 'right')
  3134. sub_graph_adjusted
  3135. ggsave("sub_meta_adjusted.png", plot = sub_graph_adjusted, dpi = 500, width = 9, height = 12, units = 'in', bg = 'white')
  3136. identical(meta_sub_results$Region, meta_sub_results_462$Region)
  3137. meta_diff <- data.frame(Region = meta_sub_results$Region_clean, unadjusted_estimate = meta_sub_results_462$Estimate,
  3138. adjusted = meta_sub_results$Estimate)
  3139. meta_diff$difference <- meta_diff$unadjusted_estimate - meta_diff$adjusted
  3140. meta_diff$Region_name <- meta_sub_results$Region
  3141. ilovegraphs <- ggplot(meta_diff, aes(x = difference, y = Region)) +
  3142. geom_point(size = 5) +
  3143. theme_bw() +
  3144. geom_vline(xintercept = 0, linetype = 'dotdash') +
  3145. xlim(-0.02, 0.02) +
  3146. ggplot2::theme_bw(base_size = 28) +
  3147. theme(
  3148. strip.text = element_text(face = "bold", size = 28),
  3149. axis.text.y = element_text(size = 28),
  3150. legend.position = "bottom"
  3151. )
  3152. ilovegraphs
  3153. ggsave("submeta_diff.png", plot = ilovegraphs, dpi = 500, width = 9, height = 12, units = 'in', bg = 'white')
  3154. clean_regions <- meta_diff %>%
  3155. mutate(
  3156. hemi = case_when(
  3157. str_detect(Region_name, "^Left[._ ]") ~ "left",
  3158. str_detect(Region_name, "^Right[._ ]") ~ "right",
  3159. TRUE ~ NA_character_
  3160. ),
  3161. region_base = Region_name %>%
  3162. str_remove("^Left[._ ]|^Right[._ ]") %>%
  3163. str_replace_all("[._-]", " ") %>%
  3164. str_squish() %>%
  3165. str_to_lower()
  3166. ) %>%
  3167. # map to aseg spellings
  3168. mutate(
  3169. region_base = str_replace(region_base, "^third ventricle$", "3rd ventricle"),
  3170. region_base = str_replace(region_base, "^fourth ventricle$", "4th ventricle"),
  3171. region_base = str_replace(region_base, "^x?3rd ventricle$", "3rd ventricle"),
  3172. region_base = str_replace(region_base, "^x?4th ventricle$", "4th ventricle"),
  3173. region_base = str_replace(region_base, "^brainstem$", "brain stem"),
  3174. region_base = str_replace(region_base, "^thalamus$", "thalamus proper"),
  3175. region_base = str_replace(region_base, "^ventral ?dc$", "ventral DC"),
  3176. region_base = str_replace(region_base, "^(nucleus )?accumbens( area)?$", "accumbens area"),
  3177. region_base = str_replace(region_base, "^cerebel+um cortex$|^cerebel+ar cortex$", "cerebellum cortex"),
  3178. region_base = str_replace(region_base, "^cerebel+um white matter$|^cerebel+ar white matter$", "cerebellum white matter"),
  3179. region_base = str_replace(region_base, "^cc ?ant(erior)?$", "CC anterior"),
  3180. region_base = str_replace(region_base, "^cc ?cent(ral)?$", "CC central"),
  3181. region_base = str_replace(region_base, "^cc ?mid ?ant(erior)?$", "CC mid anterior"),
  3182. region_base = str_replace(region_base, "^cc ?mid ?post(erior)?$", "CC mid posterior"),
  3183. region_base = str_replace(region_base, "^cc ?post(erior)?$", "CC posterior"),
  3184. # final region string
  3185. region = region_base %>%
  3186. # ensure "CC " stays caps and the rest is exactly as needed
  3187. (\(x) ifelse(str_starts(x, "cc "), str_replace(x, "^cc ", "CC "), x))() %>%
  3188. str_squish()
  3189. ) %>%
  3190. # set hemi="midline" for all midline ROIs (CC + others)
  3191. mutate(
  3192. hemi = if_else(region %in% midline_rois, "midline", hemi),
  3193. hemi = str_squish(hemi)
  3194. ) %>%
  3195. select(region, hemi, difference)
  3196. # --- DIAGNOSE: what still fails to match? ------------------------------
  3197. still_unmatched <- clean_regions %>%
  3198. anti_join(atlas_keys, by = c("hemi","region"))
  3199. if (nrow(still_unmatched)) {
  3200. message("Rows not matching atlas (hemi, region):")
  3201. print(still_unmatched)
  3202. }
  3203. # --- PLOT --------------------------------------------------------------
  3204. L <- max(abs(clean_regions$difference), na.rm = TRUE)
  3205. ggplot() +
  3206. geom_brain(
  3207. atlas = aseg,
  3208. data = clean_regions,
  3209. aes(fill = difference),
  3210. colour = "grey60" # or size = 0.2 if on older ggplot2
  3211. ) +
  3212. scale_fill_distiller(
  3213. palette = "PRGn",
  3214. limits = c(-L,L),
  3215. na.value = "grey90"
  3216. ) +
  3217. theme(
  3218. legend.position = "bottom",
  3219. legend.text = element_text(size = 8)
  3220. ) +
  3221. guides(fill = guide_colorbar(barwidth = 2, barheight = 10)) +
  3222. theme_void()
  3223. head(meta_vol_results)
  3224. metavol_flip <- data.frame(Region = meta_vol_results$Region, k = meta_vol_results$k,
  3225. Estimate = -meta_vol_results$Estimate, SE = meta_vol_results$SE,
  3226. zval = meta_vol_results$zval, pval = meta_vol_results$pval,
  3227. CI_lb = -meta_vol_results$CI_ub, CI_ub = -meta_vol_results$CI_lb,
  3228. PI_lb = -meta_vol_results$PI_ub, PI_ub = -meta_vol_results$PI_lb,
  3229. I2 = meta_vol_results$I2, Q = meta_vol_results$Q,
  3230. pval_Q = meta_vol_results$pval_Q, t2 = meta_vol_results$t2,
  3231. FDR = meta_vol_results$FDR)
  3232. metavol_flip <- metavol_flip %>%
  3233. mutate(FDR_Q = p.adjust(pval_Q, method = 'BH'))
  3234. gm_long2 <- metavol_flip %>%
  3235. separate(Region, into = c("hemi","region","measurement"), sep = "_") %>%
  3236. mutate(hemi = dplyr::recode(hemi, lh = 'Left', rh = 'Right'))
  3237. regions <- gm_long2$region %>% str_to_lower()
  3238. miss <- setdiff(regions, atlas_regions)
  3239. matching <- sapply(miss, function(regions){
  3240. distances <- stringdist(regions, atlas_regions, method = 'jw')
  3241. atlas_regions[which.min(distances)]
  3242. })
  3243. recoding <- setNames(matching, miss)
  3244. recoding
  3245. gm_long2 <- gm_long2 %>%
  3246. mutate(region = str_to_lower(region),
  3247. region = ifelse(region %in% names(recoding),
  3248. recoding[region], region))
  3249. gm_long2 <- gm_long2 %>%
  3250. mutate(region = reorder_within(region, Estimate, hemi))
  3251. gmv_meta2 <- ggplot(gm_long2, aes(y = region, x = Estimate)) +
  3252. geom_errorbarh(aes(xmin = PI_lb, xmax = PI_ub), color = "gray70", height = 0.4, size = 3) +
  3253. geom_errorbarh(aes(xmin = CI_lb, xmax = CI_ub), color = "black", height = 0.2, size = 2) +
  3254. geom_point(aes(color = FDR < 0.05), size = 5) +
  3255. geom_vline(xintercept = 0, linetype = "twodash") +
  3256. scale_color_manual(values = c("TRUE" = "#e41a1c", "FALSE" = "black")) +
  3257. scale_y_discrete(labels = function(x) gsub("__.+", "", x)) + # clean label if needed
  3258. labs(
  3259. title = "Forest Plot of Effect Sizes with Prediction Intervals",
  3260. x = "Hedges' g", y = "Region",
  3261. color = "FDR < 0.05"
  3262. ) +
  3263. ggplot2::theme_bw(base_size = 28) +
  3264. theme(
  3265. strip.text = element_text(face = "bold", size = 28),
  3266. axis.text.y = element_text(size = 28),
  3267. legend.position = "bottom"
  3268. ) +
  3269. facet_wrap(~hemi, scales = "free_y", ncol = 1) +
  3270. tidytext::scale_y_reordered(position = 'right')
  3271. gmv_meta2
  3272. ggsave("gmv_meta2.png", plot = gmv_meta2, dpi = 500, width = 8, height = 26, units = 'in', bg = 'white')
  3273. gm_long2$unadjusted_estimate <- flipped_gmv$Estimate
  3274. gm_long2$difference <- gm_long2$unadjusted_estimate - gm_long2$Estimate
  3275. gm_long2$percent_reduction <- abs((gm_long2$unadjusted_estimate - gm_long2$Estimate))/abs(gm_long2$unadjusted_estimate)
  3276. head(gm_long2)
  3277. gm_figure <- data.frame(hemi = gm_long2$hemi, region = flipped_gmv$region, estimate = gm_long2$Estimate,
  3278. unadjusted_estimate = gm_long2$unadjusted_estimate)
  3279. head(gm_figure)
  3280. rawdata <- rawdata %>%
  3281. pivot_longer(cols = !c(subject, cohort, sex, study),
  3282. names_to = c('hemi','region','measurement'),
  3283. names_sep = "_",
  3284. values_to = 'value'
  3285. )
  3286. gm_figure <- gm_figure %>%
  3287. pivot_longer(cols = c(estimate, unadjusted_estimate),
  3288. names_to = 'type',
  3289. values_to = 'estimate')
  3290. gm_figure <- gm_figure %>%
  3291. pivot_wider(names_from = type,
  3292. values_from = estimate)
  3293. gm_figure$difference <- gm_figure$unadjusted_estimate - gm_figure$estimate
  3294. gm_figure <- gm_figure %>%
  3295. pivot_longer(cols = c(estimate, unadjusted_estimate),
  3296. names_to = 'type',
  3297. values_to = 'estimate')
  3298. gmv_difference <- ggplot(gm_figure, aes(x = difference, y = region)) +
  3299. geom_point(size = 5) +
  3300. theme_bw() +
  3301. facet_wrap(~hemi) +
  3302. geom_vline(xintercept = 0, linetype = 'dotdash') +
  3303. xlim(-0.1, 0.1) +
  3304. ggplot2::theme_bw(base_size = 28) +
  3305. theme(
  3306. strip.text = element_text(face = "bold", size = 28),
  3307. axis.text.y = element_text(size = 28),
  3308. legend.position = "bottom"
  3309. )
  3310. gmv_difference
  3311. ggsave("gmv_difference.png", plot = gmv_difference, dpi = 500, width = 10, height = 12, units = 'in', bg = 'white')
  3312. gm_figure$hemi[gm_figure$hemi == 'Left'] <- 'left'
  3313. gm_figure$hemi[gm_figure$hemi == 'Right'] <- 'right'
  3314. ggplot(gm_figure, aes(fill = difference)) +
  3315. geom_brain(atlas = dk, position = position_brain(hemi~side)) +
  3316. scale_fill_distiller(palette = 'PRGn',
  3317. limits = c(-.1,.1)) +
  3318. theme(legend.text = element_text(size = 12), plot.title = element_text(size = 20)) +
  3319. theme_void()

Meta_analysis.R at commit d81138d, no license · at the source

Overview

Authors: Ryan W J Loke1,2, Oscar Ortiz2,3, Sylvia M Gustin4,5, Michèle Hubli6,7, Clas Linnman8, Abigail Livny9,10, Yann Quidé4,5, Paulina S Scheuren1,2, John L K Kramer1,2
  1. Department of Anesthesiology, Pharmacology, and Therapeutics, Faculty of Medicine, University of British Columbia, Vancouver, Bc v6t 1z3, Canada
  2. International Collaborations on Repair Discoveries (ICORD), University of British Columbia, Vancouver, Bc v5z 1n1, Canada
  3. School of Biomedical Engineering, Faculty of Applied Sciences, University of British Columbia, Vancouver, Bc v6t 2b9, Canada
  4. NeuroRecovery Research Hub, School of Psychology, The University of New South Wales (UNSW) Sydney, Sydney, NSW 2052, Australia
  5. Centre for Pain IMPACT, Neuroscience Research Australia, Randwick, NSW 2031, Australia
  6. Spinal Cord Injury Center, Balgrist University Hospital, University of Zurich, Zurich 8008, Switzerland
  7. Neuroscience Center Zurich, ETH Zurich and University of Zurich, Zurich 8057, Switzerland
  8. Department of Psychiatry, Massachusetts General Brigham & Harvard Medical School, Boston, MA 02115, USA
  9. Clinical Brain Imaging R&D Center, Sheba Medical Center, Tel Aviv 5262000, Israel
  10. Sagol School of Neuroscience, Faculty of Medical and Health Sciences, Tel Aviv University, Tel Aviv 6997801, Israel
Journal: Brain communications, volume 8, issue 3, article fcag146
Dates: received 22 October 2025; accepted 21 April 2026; published online 24 April 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.1093/braincomms/fcag146 · PMID 42099305 · PMCID PMC13148768 · OpenAlex W4414077135
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), human (organism), pain (population)
Methods: Statistics, Preprocessing, fMRI & imaging
Keywords: chronic pain, brain morphology, individual participant data meta-analysis, curvature
Topic: Musculoskeletal pain and rehabilitation (Pharmacology, Medicine), according to OpenAlex
Funding: Natural Sciences and Engineering Research Council of Canada
Citations: cited by 3 papers (Europe PMC); 61 references in the paper

Abstract

Chronic pain is a leading contributor to all-cause morbidity and disability, encompassing numerous biopsychosocial dimensions that persistently engage complex networks of brain regions. Meta-analyses have advanced our understanding of structural brain differences in chronic pain but rely exclusively on summary statistics which may introduce heterogeneity related to completeness of reporting and differences in methodological approaches. To address these limitations, we conducted the first individual participant data (IPD) meta-analysis of brain structure alterations in chronic pain. Using traditional morphometric measures (i.e. volume, cortical thickness, and surface area) and differential-geometric shape metrics (i.e. intrinsic and extrinsic curvature), we aimed to reveal alterations in brain structure convergent across chronic pain conditions. We hypothesized that chronic pain would be associated with region-specific grey matter reductions in regions previously implicated in chronic pain (e.g. parahippocampal gyrus and insula) and explored whether curvature metrics would reveal additional structural changes. Anatomical MRI images from eight publicly available datasets spanning five conditions and 401 individuals with chronic pain (and 245 age- and sex- matched healthy controls) were analysed: (i) knee osteoarthritis, (ii) chronic low back pain, (iii) fibromyalgia, (iv) migraine, and (v) primary trigeminal neuralgia. FreeSurfer was used to parcellate T1-weighted anatomical images, and metrics for cortical and subcortical regions were extracted. Meta-analysis revealed a range of structural changes in the brain associated with chronic pain. Cortical thinning and volume loss were small and localized to the temporo-occipital regions, including bilateral volumetric reductions in the entorhinal cortex in individuals with chronic pain. Increases in intrinsic curvature were widespread, involving 49 out of 68 cortical regions. No significant alterations were detected in subcortical volumes. Intrinsic curvature and subcortical volumetric estimates had higher levels of inter-study heterogeneity compared to other metrics, reflecting potential condition and sample-specific variability. Leveraging harmonized processing across a large sample size, our novel IPD meta-analysis highlights both widespread and region-specific structural remodelling of chronic pain-related neuroanatomy.

Reproduced under the paper's license (CC BY), from the paper cited above.

Repository

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

lokeryan/ChronicPainIPD

License: none: the authors keep all their rights
State: the link answers, verified on 29 September 2026
Evidence: files inventoried
Commit: d81138d3bc2bd0455c701187ca4ff41e7ad54b97, 22 January 2026
Languages: R (8), Shell (2)
Size: 12 files, 10 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: tidyverse (8 files), data.table (7 files), ggpubr (7 files), multcomp (7 files), Plotly (7 files), reshape2 (7 files), car (6 files), caret (6 files), glmnet (6 files), pROC (6 files), ggplot2 (2 files), broom (1 file), FreeSurfer (1 file), ggseg (1 file), metafor (1 file), psych (1 file)
Availability: 1 check, the latest on 29 September 2026: the link answers
  • 29 September 2026: the link answers
11 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:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 10 scripts, each with its path and the digest of its content;
  • 5 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

No dataset and no data link were found in the paper.

Data availability

Beyond current analyses, we developed an RShiny application (https://lokeryan.shinyapps.io/chronic_pain/) that enables interactive visualization of the raw data, study-level comparisons, and meta-analyses. This tool can allow other researchers to explore analyses in detail and facilitate new lines of inquiry beyond the scope of the present study. All MRI images analysed for this study were obtained from OpenNeuro (https://openneuro.org/), an open-science neuroinformatics database, or OpenPain (https://openpain.org/), an open access data sharing platform for brain imaging studies of human pain. All study-level and sex analysis results can be found in the Supplementary material and can be explored in the RShiny application. In-house bash scripts used to process the MRI images have been uploaded to our GitHub repository, along with the generated R scripts used to analyse all datasets. Additionally, code generated for the purpose of data processing and statistical analysis is included in the following GitHub: https://github.com/lokeryan/ChronicPainIPD. Upon reasonable request, the corresponding author can provide additional information and data to interested researchers for the purpose of academic research and further scientific investigations. If interested, other investigators may contribute their data to our study by reaching out to the corresponding author, which we can implement into our RShiny application. All study-level and meta-analysis results can be found in the supplementary. Please refer to the supplementary table dictionary for descriptions of all sheet names and column variables.

Reproduced under the paper's license (CC BY), from the paper cited above.

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, 29 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 9 authors, 4 keywords, 1 funder, 56 references.

Cite

This paper

Loke, R. W. J., Ortiz, O., Gustin, S. M., Hubli, M., Linnman, C., Livny, A., Quidé, Y., Scheuren, P. S., & Kramer, J. L. K. (2026). Convergent structural brain alterations in chronic pain: a multi-metric individual participant data meta-analysis. Brain communications, 8(3), fcag146. https://doi.org/10.1093/braincomms/fcag146

BibTeX

@article{loke2026convergent,
author = {Loke, Ryan W J and Ortiz, Oscar and Gustin, Sylvia M and Hubli, Michèle and Linnman, Clas and Livny, Abigail and Quidé, Yann and Scheuren, Paulina S and Kramer, John L K},
title = {{Convergent structural brain alterations in chronic pain: a multi-metric individual participant data meta-analysis}},
journal = {Brain communications},
year = {2026},
month = apr,
volume = {8},
number = {3},
pages = {fcag146},
publisher = {Oxford University Press},
issn = {2632-1297},
doi = {10.1093/braincomms/fcag146},
url = {https://doi.org/10.1093/braincomms/fcag146},
pmid = {42099305},
pmcid = {PMC13148768}
}

RIS

TY - JOUR
AU - Loke, Ryan W J
AU - Ortiz, Oscar
AU - Gustin, Sylvia M
AU - Hubli, Michèle
AU - Linnman, Clas
AU - Livny, Abigail
AU - Quidé, Yann
AU - Scheuren, Paulina S
AU - Kramer, John L K
TI - Convergent structural brain alterations in chronic pain: a multi-metric individual participant data meta-analysis
T2 - Brain communications
J2 - Brain Commun
PY - 2026
DA - 2026/04/24
VL - 8
IS - 3
SP - fcag146
SN - 2632-1297
PB - Oxford University Press
DO - 10.1093/braincomms/fcag146
UR - https://doi.org/10.1093/braincomms/fcag146
LA - en
ER -

CSL-JSON

{
"id": "10.1093/braincomms/fcag146",
"type": "article-journal",
"title": "Convergent structural brain alterations in chronic pain: a multi-metric individual participant data meta-analysis",
"container-title": "Brain communications",
"author": [
{
"family": "Loke",
"given": "Ryan W J"
},
{
"family": "Ortiz",
"given": "Oscar"
},
{
"family": "Gustin",
"given": "Sylvia M"
},
{
"family": "Hubli",
"given": "Michèle"
},
{
"family": "Linnman",
"given": "Clas"
},
{
"family": "Livny",
"given": "Abigail"
},
{
"family": "Quidé",
"given": "Yann"
},
{
"family": "Scheuren",
"given": "Paulina S"
},
{
"family": "Kramer",
"given": "John L K"
}
],
"container-title-short": "Brain Commun",
"volume": "8",
"issue": "3",
"page": "fcag146",
"DOI": "10.1093/braincomms/fcag146",
"PMID": "42099305",
"PMCID": "PMC13148768",
"ISSN": "2632-1297",
"publisher": "Oxford University Press",
"URL": "https://doi.org/10.1093/braincomms/fcag146",
"language": "en",
"issued": {
"date-parts": [
[
2026,
4,
24
]
]
}
}

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.1126/sciadv.aec9291 [code]
Computational mechanisms of perception in autism revealed using games inspired by rodent operant tasks.
Journal: Science advances
In common: pROC, glmnet, caret, 8 other tools
[2] doi:10.1093/braincomms/fcag121 [code]
Anterior insular co-activation patterns associated with stress markers in chronic primary pain.
Journal: Brain communications
In common: multcomp, psych, car, 5 other tools, pain, 2 references
[3] doi:10.1093/bioinformatics/btag592 [code]
Network-based stratification of allele-specific expression reveals patient subgroups in Huntington's disease.
Journal: Bioinformatics (Oxford, England)
In common: multcomp, pROC, car, 7 other tools
[4] doi:10.1016/j.isci.2026.115657 [code]
Integration of machine learning to develop a disulfidptosis model for predicting glioma prognosis, immunotherapy response, and drug.
Journal: iScience
In common: pROC, glmnet, psych, 7 other tools
[5] doi:10.1093/braincomms/fcag236 [code]
Dynamic, state-dependent characteristics of cognitive fluctuations in Lewy body dementia: a magnetoencephalography study.
Journal: Brain communications
In common: ggseg, pROC, caret, 6 other tools
[6] doi:10.1080/20002297.2026.2705667 [code]
Oral microbiota dysbiosis related to the cortical thinning and cognitive impairment in cerebral small vessel disease.
Journal: Journal of oral microbiology
In common: ggseg, pROC, glmnet, 5 other tools, structural MRI / diffusion
[7] doi:10.3390/ijms27104466 [code]
Uncovering the Key Circuit FOSL2/FOS/EGR3/EGR1, Contributing to the Hyperexcitability of Excitatory Neurons in the Epileptic Temporal Cortex and Hippocampus.
Journal: International journal of molecular sciences
In common: pROC, glmnet, caret, 6 other tools
[8] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: glmnet, caret, broom, 6 other tools
[9] doi:10.1038/s41398-026-04131-1 [code]
Multimodal phenotypic classification of generalized anxiety and panic using structural MRI data and psychosocial factors: machine learning results from the German National Cohort (NAKO) study.
Journal: Translational psychiatry
In common: pROC, caret, psych, 5 other tools, structural MRI / diffusion
[10] doi:10.1038/s41467-026-73262-2 [code]
Robust but independent sex differences in human brain function, structure, and behavior.
Journal: Nature communications
In common: caret, car, broom, 5 other tools, structural MRI / diffusion, 1 reference

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.