OSCR

Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers.

Code ↔ Paper

11 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 11 matches
  1. [1] § MATERIALS AND METHODS › Statistical analysis—Cox regression proportional hazard models ↔ Cox_regression_analysis.R, lines 605–631 · score 0.80 · cox.zph, proportional hazards assumptions, fitted Cox, Cox models, coxph, survival
  2. [2] § MATERIALS AND METHODS › Experimental design ↔ Cox_regression_analysis.R, lines 503–558 · score 0.76 · chi square, categorical variables, MCI diagnosis, biomarker collection, MEG, tau
  3. [3] § RESULTS › Neurophysiological and proteinopathy biomarkers predict MCI progression ↔ Cox_regression_analysis.R, lines 1264–1337 · score 0.66 · MEG alpha power, gamma power, risk scores, hippocampal volume, entorhinal tau, FDR
  4. [4] § MATERIALS AND METHODS › Statistical analysis—Cox regression proportional hazard models ↔ Cox_regression_analysis.R, lines 1629–1679 · score 0.64 · linear predictor, Cox model, relative risk, covariates, baseline, interaction
  5. [5] § RESULTS › Time-varying effects of neurophysiology and neocortical Aβ ↔ Cox_regression_analysis.R, lines 1681–1723 · score 0.61 · relative risk scores, gamma power, alpha power, beta, HR, delta
  6. [6] § MATERIALS AND METHODS › Statistical analysis—Cox regression proportional hazard models ↔ Cox_regression_analysis.R, lines 42–121 · score 0.57 · feature selection, parsimonious, AIC, interactions, regression, neurophysiological
  7. [7] § MATERIALS AND METHODS › Statistical analysis—Cox regression proportional hazard models ↔ Cox_regression_analysis.R, lines 763–820 · score 0.56 · Kaplan Meier survival, curves, Cox, regression, models
  8. [8] § RESULTS › Neurophysiological dynamics complement proteinopathy biomarkers ↔ Cox_regression_analysis.R, lines 42–121 · score 0.56 · parsimonious model, feature selection, AIC, interaction, predicting, Neurophysiological
  9. [9] § RESULTS › Neurophysiological and proteinopathy biomarkers predict MCI progression ↔ Cox_regression_analysis.R, lines 1264–1337 · score 0.56 · risk scores, Cox model, hippocampal volume, entorhinal tau, MEG alpha, FDR
  10. [10] § MATERIALS AND METHODS › Neuropsychological assessment and MCI diagnosis ↔ Cox_regression_analysis.R, lines 503–558 · score 0.53 · biomarker collection, MCI status, diagnosis, SD, APOE
  11. [11] § MATERIALS AND METHODS › Magnetoencephalography ↔ Cox_regression_analysis.R, lines 635–700 · score 0.53 · Cox regression, Cox models, beta, delta, theta, MRI

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 · 2,408 lines · 101 KB · no license · 11 matches

  1. ############### Script generated by Jonathan Gallego Rudolf ####################
  2. ##### Progression to MCI analysis
  3. ##### 1) Cox regression models
  4. ### Survival analysis based on Cox regression proportional hazard models
  5. ### to estimate the risk of progression to MCI and the added value of incorporating
  6. ### neurophysiological, brain structure, plasma and PET biomarkers to clinical information
  7. ### The script is used to fit Cox regression model and obtain survival curves
  8. ### for each model. Likelihood ratio tests are used to assess whether
  9. ### the model performed significantly better than a null model including no
  10. ### features, and also compared to the reference clinical model
  11. ### The following models were defined
  12. ### Model 1: Clinical (age, sex, education, apoe)
  13. ### Model 2: Clinical + MEG (initially relative alpha power, but was extended to other frequency bands)
  14. ### Model 3: Clinical + MRI (hippocampal volume norm to TIV)
  15. ### Model 4: Clinical + plasma (Ab42/40 ratio, p-tau217)
  16. ### Model 5: Clinical + Ab PET uptake (neocortical)
  17. ### Model 6: Clinical + Tau PET uptake (entorhinal)
  18. ### Model 7: All features
  19. ### Additional model comparisons are performed by calculating the difference in AIC
  20. ### and the concordance index of the models
  21. ### Risk scores estimated for each participants are used to stratify individuals
  22. ### into low-, medium- and high-risk groups, to visualize whether the survival curves
  23. ### changes as a function of a given biomarker
  24. ### Linear models and independent t-test were implemented to assess the contribution
  25. ### of different biomarkers by estimating their correlation with the risk scores
  26. ### estimated derived from its corresponding model
  27. ### Run models including time interactions to assess whether the hazard risk
  28. ### associated with a given biomarker changes over time. Plot the change in
  29. ### hazard ratio over time, calculating the CIs while accounting for the clinical covariates
  30. ### Plot the change in the linear predictor (calculated for each participant) as a function of time, to show
  31. ### the time interaction effect on the association between a biomarker and the risk of progression
  32. ### Compare whether adding the neurophysiological activity time-varying effects
  33. ### improved the performance of the models combining clinical information and plasma/PET biomarkers
  34. ### Step AIC model feature selection regression analysis was performed on the model
  35. ### including all biomarkers to select a parsimonious model including a set of features that would
  36. ### maximize model performance while accounting for model complexity based on AIC values
  37. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  38. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  39. ### 1. Prepare data and environment----
  40. ##### Generate data_meg_pet_cogn spreadsheet (n=104) using the prepare_data script
  41. ### Load required packages
  42. library(scico)
  43. library(survival)
  44. library(survminer)
  45. require(ggplot2)
  46. require(nlme)
  47. require(MuMIn)
  48. library(lubridate)
  49. library(ggsurvfit)
  50. library(gtsummary)
  51. library(tidycmprsk)
  52. library(condsurv)
  53. library(dplyr)
  54. library(ggpubr)
  55. library(see)
  56. library(scales)
  57. library(tidyr)
  58. ### Set path to save data
  59. path="C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Figures/MCI_review"
  60. ### Load Dk atlas labels
  61. dk_labels=readLines('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/labels_dk.csv')
  62. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  63. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  64. ### 2. Get dates and calculate time diff, scale variables, add lobes (EXTRA)----
  65. ### Load dates for the scanning visits, plasma measures and MCI diagnosis
  66. scan_dates=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Spreadsheets/multimodal_scan_dates.csv",header=T)
  67. ### Change format to dates (needed for computing time diff)
  68. scan_dates=scan_dates[,c(seq(1,9))]
  69. scan_dates$MEG_date=as.Date(as.character(scan_dates$MEG_date), format = "%Y%m%d")
  70. scan_dates$MRI_date=as.Date(as.character(scan_dates$MRI_date), format = "%Y%m%d")
  71. scan_dates$Ab_date=as.Date(as.character(scan_dates$Ab_date), format = "%Y%m%d")
  72. scan_dates$Tau_date=as.Date(as.character(scan_dates$Tau_date), format = "%Y%m%d")
  73. scan_dates$First_cogn=as.Date(as.character(scan_dates$First_cogn), format = "%Y%m%d")
  74. scan_dates$MEG_cogn=as.Date(as.character(scan_dates$MEG_cogn), format = "%Y%m%d")
  75. scan_dates$MCI_date=as.Date(as.character(scan_dates$MCI_date_july_24), format = "%Y%m%d")
  76. scan_dates$Plasma_date=as.Date(as.character(scan_dates$Plasma_date), format = "%Y%m%d")
  77. scan_dates$ids=data_meg_pet_cogn$pscid
  78. ### Add dates to spreadsheet
  79. data_meg_pet_cogn=cbind(data_meg_pet_cogn,scan_dates)
  80. ### Generate function to compute difference in dates
  81. compute_date_diff <- function(df, date_col1, date_col2, new_col_name) {
  82. # Ensure date columns are in POSIXct format
  83. date1 <- as.POSIXct(df[[date_col1]], tz = "UTC")
  84. date2 <- as.POSIXct(df[[date_col2]], tz = "UTC")
  85. # Compute difference in days
  86. diff_days <- as.numeric(difftime(date1, date2, units = "days"))
  87. # Add to dataframe
  88. df[[new_col_name]] <- diff_days
  89. return(df)
  90. }
  91. ### Function to compute days to cutoff date (July 31, 2024)
  92. fill_missing_diff_days <- function(df, date_col, diff_col, cutoff = "2024-07-31", new_col = "diff_years") {
  93. # Ensure cutoff is Date
  94. cutoff_date <- as.Date(cutoff)
  95. # Loop through rows where diff_col is NA
  96. for (i in seq_len(nrow(df))) {
  97. if (is.na(df[[diff_col]][i])) {
  98. date_val <- df[[date_col]][i]
  99. if (!is.na(date_val)) {
  100. diff_days <- as.numeric(difftime(as.POSIXct(cutoff_date), as.POSIXct(date_val, tz = "UTC"), units = "days"))
  101. df[[diff_col]][i] <- diff_days
  102. }
  103. }
  104. }
  105. # Convert to years
  106. df[[new_col]] <- df[[diff_col]] / 365
  107. return(df)
  108. }
  109. ### Calculate difference in dates
  110. data_meg_pet_cogn=compute_date_diff(data_meg_pet_cogn, "MCI_date", "MEG_date", "diff_days_meg_mci")
  111. ### Format variables of interest
  112. ### Ensure MCI status, apoe, and sex are defined as factors as factors
  113. data_meg_pet_cogn$mci_status=as.factor(data_meg_pet_cogn$mci_status)
  114. data_meg_pet_cogn$apoe=as.factor(data_meg_pet_cogn$apoe)
  115. data_meg_pet_cogn$sex=as.factor(data_meg_pet_cogn$sex)
  116. data_meg_pet_cogn$sex=factor(data_meg_pet_cogn$sex, levels = c("Male", "Female"))
  117. ### Z-score biomarker measurements
  118. data_meg_pet_cogn$age_z=scale(data_meg_pet_cogn$age);
  119. data_meg_pet_cogn$education_z=scale(data_meg_pet_cogn$education)
  120. data_meg_pet_cogn$hippocampal_volume_z=scale(data_meg_pet_cogn$hippocampal_volume)
  121. data_meg_pet_cogn$tau_meta_roi_meg_delta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_delta)
  122. data_meg_pet_cogn$tau_meta_roi_meg_theta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_theta)
  123. data_meg_pet_cogn$tau_meta_roi_meg_alpha_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_alpha)
  124. data_meg_pet_cogn$tau_meta_roi_meg_beta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_beta)
  125. data_meg_pet_cogn$tau_meta_roi_meg_gamma1_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_gamma1)
  126. data_meg_pet_cogn$tau_meta_roi_meg_gamma2_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_gamma2)
  127. data_meg_pet_cogn$Ab42_40_ratio_z=scale(data_meg_pet_cogn$Ab42_40_ratio)
  128. data_meg_pet_cogn$ptau_217_z=scale(data_meg_pet_cogn$ptau_217)
  129. data_meg_pet_cogn$amyloid_index_z=scale(data_meg_pet_cogn$amyloid_index)
  130. data_meg_pet_cogn$tau_entorhinal_z=scale(data_meg_pet_cogn$tau_entorhinal)
  131. ### Generate new dataset removing the subjects that do not have plasma data
  132. no_plasma=c("MTL0139","MTL0224")
  133. data_meg_pet_cogn3=data_meg_pet_cogn %>% filter(!pscid %in% no_plasma)
  134. ### Compute additional differences in dates
  135. data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MEG_date", "Plasma_date", "diff_days_meg_plasma")
  136. data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "Ab_date", "Tau_date", "diff_days_ab_tau")
  137. data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MEG_date", "MRI_date", "diff_days_meg_mri")
  138. data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "Ab_date", "MEG_date", "diff_days_ab_meg")
  139. data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MCI_date", "Plasma_date", "diff_days_plasma_mci")
  140. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  141. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  142. ### 3. Load extended Ab and tau PET and plasma datasets----
  143. ### Load full PET dataset
  144. Ab_data_all_subs=read.csv('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/PAD_PET_NAV_suvr_space-anat_ref-cerebellumCortex_time-4070_Nov2023.tsv',header=T, sep="")
  145. Tau_data_all_subs=read.csv('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/PAD_PET_TAU_suvr_space-anat_ref-infcereg_time-4070_Nov2023.tsv',header=T, sep="")
  146. ### Separate BL and FU
  147. Ab_data_bl_all_subs <- filter(Ab_data_all_subs, Visit_label_PET == "ses-01")
  148. Ab_data_fu_all_subs <- filter(Ab_data_all_subs, Visit_label_PET == "ses-02")
  149. Tau_data_bl_all_subs <- filter(Tau_data_all_subs, Visit_label_PET == "ses-01")
  150. Tau_data_fu_all_subs <- filter(Tau_data_all_subs, Visit_label_PET == "ses-02")
  151. ### Get subset of individuals with MEG (n=104)
  152. Ab_data_bl_NN_subs=filter(Ab_data_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  153. Ab_data_fu_NN_subs=filter(Ab_data_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  154. Tau_data_bl_NN_subs=filter(Tau_data_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  155. Tau_data_fu_NN_subs=filter(Tau_data_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  156. ### Keep subjects with both Ab and tau FU (N=86)
  157. Ab_data_bl_both=filter(Ab_data_bl_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
  158. Ab_data_fu_both=filter(Ab_data_fu_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
  159. Tau_data_bl_both=filter(Tau_data_bl_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
  160. Tau_data_fu_both=Tau_data_fu_NN_subs
  161. ### Load demographic information
  162. demographics_387=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Cognition/MCI_progression/new_consensus_and_petersen_july_2024/july_2024_updated_plasma/Demographics_387.csv",header=T)
  163. demographics_ab_243=filter(demographics_387,pscid%in%Ab_data_bl_all_subs$PSCID)
  164. demographics_tau_240=demographics_387 %>% filter(pscid %in% Tau_data_bl_all_subs$PSCID)
  165. ### Get subset of individuals with BL Ab and Tau PET
  166. Ab_data_bl_243=filter(Ab_data_bl_all_subs,PSCID%in%demographics_ab_243$pscid); amyloid_index_243=Ab_data_bl_243$thresh_amyl_index
  167. Tau_data_bl_240=filter(Tau_data_bl_all_subs,PSCID%in%demographics_tau_240$pscid); tau_meta_roi_240=Tau_data_bl_240$thresh_meta_roi_bilat
  168. ### Load MCI dates
  169. MCI_dates_all=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Cognition/MCI_progression/new_consensus_and_petersen_july_2024/mci_dates_july_24_all.csv",header=T, sep=","); colnames(MCI_dates_all)=c("pscid", "mci_status")
  170. ### Subset MCI list for individuals with Ab PET (n=255)
  171. MCI_dates_ab_255=data.frame(pscid=Ab_data_bl_all_subs$PSCID)
  172. MCI_dates_ab_255$mci_status=as.integer(Ab_data_bl_all_subs$PSCID %in% MCI_dates_all$pscid)
  173. MCI_dates_ab_255=merge(MCI_dates_ab_255, MCI_dates_all, by.x = "pscid", by.y = "pscid", all.x = TRUE); colnames(MCI_dates_ab_255)=c("pscid","mci_status","mci_date")
  174. ### Subset MCI list for individuals with Ab PET (n=255)
  175. MCI_dates_tau_252=data.frame(pscid=Tau_data_bl_all_subs$PSCID)
  176. MCI_dates_tau_252$mci_status=as.integer(Tau_data_bl_all_subs$PSCID %in% MCI_dates_all$pscid)
  177. MCI_dates_tau_252=merge(MCI_dates_tau_252, MCI_dates_all, by.x = "pscid", by.y = "pscid", all.x = TRUE); colnames(MCI_dates_tau_252)=c("pscid","mci_status","mci_date")
  178. MCI_dates_ab_243=filter(MCI_dates_ab_255,pscid%in%demographics_ab_243$pscid);
  179. MCI_dates_tau_240=filter(MCI_dates_tau_252,pscid%in%demographics_tau_240$pscid);
  180. ### Load PET scans dates
  181. PET_dates_all_subs=read.csv('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/PAD_PET_info_Nov2023.csv',header=T, sep=",")
  182. PET_dates_all_subs$date_scan=as.Date(as.character(PET_dates_all_subs$date_scan), format = "%Y%m%d")
  183. ### Get dates for BL and FU Ab and tau PET scans
  184. Ab_dates_bl_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-01" & Tracer_PET == "NAV")
  185. Ab_dates_fu_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-02" & Tracer_PET == "NAV")
  186. Tau_dates_bl_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-01" & Tracer_PET == "TAU")
  187. Tau_dates_fu_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-02" & Tracer_PET == "TAU")
  188. ### Get subset of individuals with MEG (used for predictive model analysis)
  189. Ab_dates_bl_NN_subs=filter(Ab_dates_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  190. Ab_dates_fu_NN_subs=filter(Ab_dates_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  191. Tau_dates_bl_NN_subs=filter(Tau_dates_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  192. Tau_dates_fu_NN_subs=filter(Tau_dates_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
  193. ### Keep subjects with both Ab and tau FU (N=86; used for predictive model analysis)
  194. Ab_dates_bl_both=filter(Ab_dates_bl_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
  195. Ab_dates_fu_both=filter(Ab_dates_fu_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
  196. Tau_dates_bl_both=filter(Tau_dates_bl_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
  197. Tau_dates_fu_both=Tau_dates_fu_NN_subs
  198. Ab_dates_bl_243=filter(Ab_dates_bl_all_subs,PSCID%in%Ab_data_bl_243$PSCID)
  199. Tau_dates_bl_240=filter(Tau_dates_bl_all_subs,PSCID%in%Tau_data_bl_240$PSCID)
  200. ### Create df to run Cox regression models on full Ab sample
  201. df_ab_243=data.frame(cbind(pscid=Ab_data_bl_243$PSCID, age=Ab_dates_bl_243$age_PET, sex=demographics_ab_243$Sex,
  202. education=demographics_ab_243$Education, apoe=demographics_ab_243$apoe,
  203. amyloid_index=amyloid_index_243,PET_date=Ab_dates_bl_243$Date_PET,
  204. mci_status=MCI_dates_ab_243$mci_status,
  205. mci_date=MCI_dates_ab_243$mci_date))
  206. df_ab_243$mci_date=as.Date(as.character(df_ab_243$mci_date), format = "%Y%m%d")
  207. df_ab_243$amyloid_index=as.double(df_ab_243$amyloid_index)
  208. df_ab_243$amyloid_index_z=scale(df_ab_243$amyloid_index); df_ab_243$amyloid_index_z=df_ab_243$amyloid_index_z[,1]
  209. ### Create df to run Cox regression models on full Ab sample
  210. df_tau_240=data.frame(cbind(pscid=Tau_data_bl_240$PSCID, age=Tau_dates_bl_240$age_PET, sex=demographics_tau_240$Sex,
  211. education=demographics_tau_240$Education, apoe=demographics_tau_240$apoe,
  212. tau_meta_roi=tau_meta_roi_240,PET_date=Tau_dates_bl_240$Date_PET,
  213. mci_status=MCI_dates_tau_240$mci_status,
  214. mci_date=MCI_dates_tau_240$mci_date))
  215. df_tau_240$mci_date=as.Date(as.character(df_tau_240$mci_date), format = "%Y%m%d")
  216. df_tau_240$tau_meta_roi=as.double(df_tau_240$tau_meta_roi)
  217. df_tau_240$tau_meta_roi_z=scale(df_tau_240$tau_meta_roi); df_tau_240$tau_meta_roi_z=df_tau_240$tau_meta_roi_z[,1]
  218. ### Calculate entorhinal cortex SUVR
  219. tau_entorhinal=vector()
  220. for (i in 1:nrow(Tau_data_bl_240)){
  221. tau_entorhinal_s=mean(Tau_data_bl_240$ctx.lh.entorhinal[i],Tau_data_bl_240$ctx.rh.entorhinal[i])
  222. tau_entorhinal=rbind(tau_entorhinal,tau_entorhinal_s)
  223. }
  224. tau_entorhinal=data.frame(tau_entorhinal=tau_entorhinal)
  225. df_tau_240$tau_entorhinal=as.double(tau_entorhinal$tau_entorhinal)
  226. df_tau_240$tau_entorhinal_z=scale(df_tau_240$tau_entorhinal); df_tau_240$tau_entorhinal_z=df_tau_240$tau_entorhinal_z[,1]
  227. ### Calculate difference in dates between Ab PET and MCI diagnosis
  228. df_ab_243=compute_date_diff(df_ab_243, "mci_date", "PET_date", "diff_days_pet_mci")
  229. ### Get MCI date for Cox regression models for full Ab sample
  230. df_ab_243$diff_days_pet_mci_cox=df_ab_243$diff_days_pet_mci
  231. df_ab_243 <- fill_missing_diff_days(df_ab_243, "PET_date", "diff_days_pet_mci_cox", cutoff = "2024-07-31", new_col = "diff_years_pet_mci_cox")
  232. ### Transform age and education to z scores
  233. df_ab_243$age=as.double(df_ab_243$age); df_ab_243$age_z=scale(df_ab_243$age); df_ab_243$age_z=df_ab_243$age_z[,1]
  234. df_ab_243$education=as.double(df_ab_243$education); df_ab_243$education_z=scale(df_ab_243$education); df_ab_243$education_z=df_ab_243$education_z[,1]
  235. df_ab_243$mci_status=as.double(df_ab_243$mci_status)+1
  236. df_ab_243$mci_status_cox=factor(df_ab_243$mci_status, levels = c(1, 2), labels = c(0, 1)) # Define the factor with levels 0 and 1
  237. df_ab_243$mci_status_cox=as.double(df_ab_243$mci_status_cox)
  238. df_ab_243$mci_status_cox=df_ab_243$mci_status_cox-1
  239. ### Remove subjects that had the PET after being diagnoses as MCI
  240. df_ab_226=df_ab_243[df_ab_243$diff_years_pet_mci_cox >= 0, ]
  241. ### Create df with only the additional subjects
  242. df_ab_124=filter(df_ab_226,!pscid%in%data_meg_pet_cogn3$pscid)
  243. ### Calculate difference in dates between Tau PET and MCI diagnosis
  244. df_tau_240=compute_date_diff(df_tau_240, "mci_date", "PET_date", "diff_days_pet_mci")
  245. ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
  246. df_tau_240$diff_days_pet_mci_cox=df_tau_240$diff_days_pet_mci
  247. df_tau_240 <- fill_missing_diff_days(df_tau_240, "PET_date", "diff_days_pet_mci_cox", cutoff = "2024-07-31", new_col = "diff_years_pet_mci_cox")
  248. ### Transform age and education to z scores
  249. df_tau_240$age=as.double(df_tau_240$age); df_tau_240$age_z=scale(df_tau_240$age); df_tau_240$age_z=df_tau_240$age_z[,1]
  250. df_tau_240$education=as.double(df_tau_240$education); df_tau_240$education_z=scale(df_tau_240$education); df_tau_240$education_z=df_tau_240$education_z[,1]
  251. ### Add MCI vars for Cox
  252. df_tau_240$mci_status=as.double(df_tau_240$mci_status)+1
  253. df_tau_240$mci_status_cox=factor(df_tau_240$mci_status, levels = c(1, 2), labels = c(0, 1)) # Define the factor with levels 0 and 1
  254. df_tau_240$mci_status_cox=as.double(df_tau_240$mci_status_cox)
  255. df_tau_240$mci_status_cox=df_tau_240$mci_status_cox-1
  256. ### Remove subjects that had the PET after being diagnoses as MCI
  257. df_tau_224=df_tau_240[df_tau_240$diff_years_pet_mci_cox >= 0, ]
  258. ### Create df with only the additional subjects
  259. df_tau_122=filter(df_tau_224,!pscid%in%data_meg_pet_cogn3$pscid)
  260. ### Load full plasma data spreadsheets
  261. plasma_ab4240=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Plasma/PREVENT-AD_n387_IPMS_totaltau_ptau_4plex.tsv",header=T, sep="");
  262. plasma_ptau_217=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Plasma/PREVENT-AD_n387_ptau217_UGOT.tsv",header=T, sep="");
  263. plasma_ab4240$Candidate_Age=plasma_ab4240$Candidate_Age/12; plasma_ptau_217$Candidate_Age=plasma_ptau_217$Candidate_Age/12
  264. plasma_ab4240_216=plasma_ab4240[!is.na(plasma_ab4240$AB_ratio_MS_UGOT), ]
  265. plasma_ptau_217_216=filter(plasma_ptau_217,PSCID %in% plasma_ab4240_216$PSCID);
  266. plasma_ptau_217_216=inner_join(plasma_ptau_217_216, plasma_ab4240_216, by = c("PSCID", "Study_visit_label"))
  267. demographics_plasma_216=filter(demographics_387,pscid%in%plasma_ptau_217_216$PSCID)
  268. ### Subset MCI list for individuals with Ab PET (n=255)
  269. MCI_dates_plasma_216=data.frame(pscid=plasma_ptau_217_216$PSCID)
  270. MCI_dates_plasma_216$mci_status=as.integer(plasma_ptau_217_216$PSCID %in% MCI_dates_all$pscid)
  271. MCI_dates_plasma_216=merge(MCI_dates_plasma_216, MCI_dates_all, by.x = "pscid", by.y = "pscid", all.x = TRUE); colnames(MCI_dates_plasma_216)=c("pscid","mci_status","mci_date")
  272. ### Create df to run Cox regression models on full Ab sample
  273. df_plasma_216=data.frame(cbind(pscid=plasma_ab4240_216$PSCID, age=plasma_ab4240_216$Candidate_Age, sex=demographics_plasma_216$Sex,
  274. education=demographics_plasma_216$Education, apoe=demographics_plasma_216$apoe,
  275. plasma_date=plasma_ab4240_216$Date_taken,
  276. mci_status=MCI_dates_plasma_216$mci_status,
  277. mci_date=MCI_dates_plasma_216$mci_date,
  278. Ab42_40_ratio=plasma_ab4240_216$AB_ratio_MS_UGOT,
  279. ptau_217=plasma_ptau_217_216$UGOT_ptau217))
  280. df_plasma_216$mci_date=as.Date(as.character(df_plasma_216$mci_date), format = "%Y%m%d")
  281. ### Calculate difference in dates between plasma and MCI diagnosis
  282. df_plasma_216=compute_date_diff(df_plasma_216, "mci_date", "plasma_date", "diff_days_plasma_mci")
  283. ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
  284. df_plasma_216$diff_days_plasma_mci_cox=df_plasma_216$diff_days_plasma_mci
  285. df_plasma_216 <- fill_missing_diff_days(df_plasma_216, "plasma_date", "diff_days_plasma_mci_cox", cutoff = "2024-07-31", new_col = "diff_years_plasma_mci_cox")
  286. ### Transform age and education to z scores
  287. df_plasma_216$age=as.double(df_plasma_216$age); df_plasma_216$age_z=scale(df_plasma_216$age); df_plasma_216$age_z=df_plasma_216$age_z[,1]
  288. df_plasma_216$education=as.double(df_plasma_216$education); df_plasma_216$education_z=scale(df_plasma_216$education); df_plasma_216$education_z=df_plasma_216$education_z[,1]
  289. df_plasma_216$Ab42_40_ratio=as.double(df_plasma_216$Ab42_40_ratio); df_plasma_216$Ab42_40_ratio_z=scale(df_plasma_216$Ab42_40_ratio); df_plasma_216$Ab42_40_ratio_z=df_plasma_216$Ab42_40_ratio_z[,1]
  290. df_plasma_216$ptau_217=as.double(df_plasma_216$ptau_217); df_plasma_216$ptau_217_z=scale(df_plasma_216$ptau_217); df_plasma_216$ptau_217_z=df_plasma_216$ptau_217_z[,1]
  291. df_plasma_216$mci_status=as.double(df_plasma_216$mci_status)+1
  292. df_plasma_216$mci_status_cox=factor(df_plasma_216$mci_status, levels = c(1, 2), labels = c(0, 1)) # Define the factor with levels 0 and 1
  293. df_plasma_216$mci_status_cox=as.double(df_plasma_216$mci_status_cox)
  294. df_plasma_216$mci_status_cox=df_plasma_216$mci_status_cox-1
  295. ### Remove subjects that had the PET after being diagnoses as MCI and missing ptau217 from the same visit as the Ab4240 ratio
  296. df_plasma_211=df_plasma_216[df_plasma_216$diff_years_plasma_mci_cox >= 0, ];
  297. df_plasma_211=df_plasma_211[!is.na(df_plasma_211$ptau_217), ]
  298. ### Create df with only the additional subjects
  299. df_plasma_109=filter(df_plasma_211,!pscid%in%data_meg_pet_cogn3$pscid)
  300. df_plasma_211_mci <- df_plasma_211[df_plasma_211$mci_status == 2, ]
  301. df_ab_226_mci <- df_ab_226[df_ab_226$mci_status == 2, ]
  302. df_tau_224_mci <- df_tau_224[df_tau_224$mci_status == 2, ]
  303. mean(df_plasma_211_mci$diff_years_plasma_mci_cox); sd(df_plasma_211_mci$diff_years_plasma_mci_cox)
  304. min(df_plasma_211_mci$diff_years_plasma_mci_cox); max(df_plasma_211_mci$diff_years_plasma_mci_cox);
  305. mean(df_ab_226_mci$diff_years_pet_mci_cox); sd(df_ab_226_mci$diff_years_pet_mci_cox)
  306. min(df_ab_226_mci$diff_years_pet_mci_cox); max(df_ab_226_mci$diff_years_pet_mci_cox);
  307. mean(df_tau_224_mci$diff_years_pet_mci_cox); sd(df_tau_224_mci$diff_years_pet_mci_cox)
  308. min(df_tau_224_mci$diff_years_pet_mci_cox); max(df_tau_224_mci$diff_years_pet_mci_cox);
  309. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  310. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  311. ### 4. Plot MCI proportion and compare progressors vs non-progressors----
  312. ### Numbers updated up to July 2024
  313. mci_chart <- data.frame(mci_status = c("CU", "MCI"), num_sub = c(71, 31), perc_sub = c(69.6, 30.4))
  314. ### Create a basic bar chart
  315. pie_chart <- ggplot(mci_chart, aes(x = "", y = num_sub, fill = mci_status)) +
  316. geom_bar(stat = "identity") + coord_polar(theta = "y") +
  317. scale_fill_manual(values = c("#ACD8A7","#E0B0FF")) + theme_void()
  318. ### Print the pie chart
  319. print(pie_chart)
  320. ### Plot variability in the time bw biomarker collection and MCI diagnosis
  321. ggplot(data_meg_pet_cogn3, aes(x = mci_status, y = diff_days_meg_mci, fill = mci_status)) +
  322. geom_boxplot() + geom_jitter(width = 0.2, color = "black", alpha = 0.7) + # Adds jittered points
  323. scale_fill_manual(values = c("white", "#E0B0FF")) + # Example colors
  324. labs(title = "Boxplot with Modern Theme", x = "Group", y = "Value") +
  325. theme_minimal(base_size = 15) + theme_modern()
  326. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  327. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  328. ### 5. Group comparison between MCI progressors and non-progressors----
  329. ### Calculate group means and sd for MCI progressors and non-progressors
  330. aggregate(data_meg_pet_cogn3$tau_entorhinal, list(data_meg_pet_cogn3$mci_status), FUN=mean)
  331. aggregate(data_meg_pet_cogn3$tau_entorhinal, list(data_meg_pet_cogn3$mci_status), FUN=sd)
  332. ### Run Wilcox test
  333. wilcox_age=wilcox.test(data_meg_pet_cogn3$age~data_meg_pet_cogn3$mci_status)
  334. wilcox_education=wilcox.test(data_meg_pet_cogn3$education~data_meg_pet_cogn3$mci_status)
  335. wilcox_tau_meta_roi_meg_delta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_delta~data_meg_pet_cogn3$mci_status)
  336. wilcox_tau_meta_roi_meg_theta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_theta~data_meg_pet_cogn3$mci_status)
  337. wilcox_tau_meta_roi_meg_alpha=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_alpha~data_meg_pet_cogn3$mci_status)
  338. wilcox_tau_meta_roi_meg_beta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_beta~data_meg_pet_cogn3$mci_status)
  339. wilcox_tau_meta_roi_meg_gamma1=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_gamma1~data_meg_pet_cogn3$mci_status)
  340. wilcox_hipp=wilcox.test(data_meg_pet_cogn3$hippocampal_volume~data_meg_pet_cogn3$mci_status)
  341. wilcox_ab4240=wilcox.test(data_meg_pet_cogn3$Ab42_40_ratio~data_meg_pet_cogn3$mci_status)
  342. wilcox_ptau217=wilcox.test(data_meg_pet_cogn3$ptau_217~data_meg_pet_cogn3$mci_status)
  343. wilcox_ab=wilcox.test(data_meg_pet_cogn3$amyloid_index~data_meg_pet_cogn3$mci_status)
  344. wilcox_tau=wilcox.test(data_meg_pet_cogn3$tau_entorhinal~data_meg_pet_cogn3$mci_status)
  345. ### Check proportion of subjects for categorical variables (sex and APOE) and run chi squared
  346. tt=table(data_meg_pet_cogn3$mci_status,data_meg_pet_cogn3$sex); tt; chi_sex=chisq.test(tt)
  347. tt=table(data_meg_pet_cogn3$mci_status,data_meg_pet_cogn3$apoe); tt; chi_apoe=chisq.test(tt)
  348. wil_chi_stats=c(wilcox_age$statistic, chi_sex$statistic, wilcox_education$statistic, chi_apoe$statistic,
  349. wilcox_tau_meta_roi_meg_delta$statistic, wilcox_tau_meta_roi_meg_theta$statistic, wilcox_tau_meta_roi_meg_alpha$statistic,
  350. wilcox_tau_meta_roi_meg_beta$statistic, wilcox_tau_meta_roi_meg_gamma1$statistic,
  351. wilcox_hipp$statistic, wilcox_ab4240$statistic, wilcox_ptau217$statistic, wilcox_ab$statistic, wilcox_tau$statistic)
  352. p_vals_wilcox=c(wilcox_age$p.value, chi_sex$p.value, wilcox_education$p.value, chi_apoe$p.value,
  353. wilcox_tau_meta_roi_meg_delta$p.value, wilcox_tau_meta_roi_meg_theta$p.value, wilcox_tau_meta_roi_meg_alpha$p.value,
  354. wilcox_tau_meta_roi_meg_beta$p.value, wilcox_tau_meta_roi_meg_gamma1$p.value,
  355. wilcox_hipp$p.value, wilcox_ab4240$p.value, wilcox_ptau217$p.value, wilcox_ab$p.value, wilcox_tau$p.value)
  356. p_vals_wilcox_fdr=p.adjust(p_vals_wilcox, method = "fdr")
  357. sprintf("%.6f", p_vals_wilcox_fdr)
  358. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  359. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  360. ### 6. MCI Cox regression survival analysis (no time interactions)----
  361. ### 6.1 Prepare data----
  362. ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
  363. data_meg_pet_cogn3$diff_days_meg_mci_cox=data_meg_pet_cogn3$diff_days_meg_mci
  364. data_meg_pet_cogn3=fill_missing_diff_days(data_meg_pet_cogn3, "MEG_date", "diff_days_meg_mci_cox", cutoff = "2024-07-31", new_col = "diff_years_meg_mci_cox")
  365. ### Define MCI list as 0s and 1s to run cox models
  366. data_meg_pet_cogn3$mci_status_cox=factor(data_meg_pet_cogn3$mci_status, levels = c(1, 2), labels = c(0, 1)) # Define the factor with levels 0 and 1
  367. data_meg_pet_cogn3$mci_status_cox=as.double(data_meg_pet_cogn3$mci_status_cox)
  368. data_meg_pet_cogn3$mci_status_cox=data_meg_pet_cogn3$mci_status_cox-1
  369. ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
  370. data_meg_pet_cogn3$diff_days_plasma_mci_cox=data_meg_pet_cogn3$diff_days_plasma_mci
  371. data_meg_pet_cogn3=fill_missing_diff_days(data_meg_pet_cogn3, "Plasma_date", "diff_days_plasma_mci_cox", cutoff = "2024-07-31", new_col = "diff_years_plasma_mci_cox")
  372. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  373. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  374. ### 6.2 Run Cox regression models----
  375. ### Generate function that computes Cox regression models, get summary,
  376. ### compute c-index, check PH assumption, fit survival curves and plot them
  377. ### Only works for models that do not include a time interaction
  378. run_cox_model <- function(formula, data, model_name) {
  379. ### Fit Cox model
  380. formula <- as.formula(formula)
  381. model <- coxph(formula, data = data); model_summary <- summary(model)
  382. ### Check proportional hazard assumption
  383. if (model_name != "null_model") {
  384. pha=cox.zph(model)}
  385. ### Compute C-index
  386. c_index <- model_summary$concordance
  387. ### Fit survival curves
  388. surv_fit <- survfit(model)
  389. ### Log-rank test & p-value
  390. lr_test <- model_summary$logtest[1]; p_val <- model_summary$logtest[3]
  391. ### Plot survival curve
  392. plot(surv_fit, xlab = "Time (years)", ylab = "Survival Probability", ylim = c(0, 1))
  393. ### Save model summary to a text file
  394. summary_filename <- paste0("Cox_model_", model_name, "_summary.txt")
  395. #capture.output(model_summary, file = summary_filename)
  396. ### Return results for null model (no PH assumption)
  397. if (model_name == "null_model") {
  398. return(list(model = model, summary = model_summary, c_index = c_index,
  399. log_rank_test = lr_test, p_value = p_val))}
  400. if (model_name != "null_model") {
  401. return(list(model = model, summary = model_summary, ph_assumption=pha, c_index = c_index,
  402. log_rank_test = lr_test, p_value = p_val))}
  403. }
  404. ### Run Cox regressions for each model (main models)
  405. ### Clinical
  406. results_clin = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe", data_meg_pet_cogn3, "clin")
  407. ### MRI
  408. results_clin_mri = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+hippocampal_volume_z", data=data_meg_pet_cogn3, "clin_mri")
  409. ### MEG
  410. results_clin_meg_delta = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_delta_z", data_meg_pet_cogn3, "clin_meg_delta")
  411. results_clin_meg_theta = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_theta_z", data_meg_pet_cogn3, "clin_meg_theta")
  412. results_clin_meg_alpha = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_alpha_z", data_meg_pet_cogn3, "clin_meg_alpha")
  413. results_clin_meg_beta = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_beta_z", data_meg_pet_cogn3, "clin_meg_beta")
  414. results_clin_meg_gamma1 = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_gamma1_z", data_meg_pet_cogn3, "clin_meg_gamma1")
  415. results_clin_meg_gamma2 = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_gamma2_z", data_meg_pet_cogn3, "clin_meg_gamma2")
  416. results_clin_meg_alpha_gamma1 = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_meta_roi_meg_alpha_z+tau_meta_roi_meg_gamma1_z", data_meg_pet_cogn3, "clin_meg_alpha_gamma1")
  417. ### Plasma
  418. results_clin_plasma = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+Ab42_40_ratio_z+ptau_217_z", data=data_meg_pet_cogn3, "clin_plasma")
  419. ### PET
  420. results_clin_ab = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+amyloid_index_z", data=data_meg_pet_cogn3, "clin_ab")
  421. results_clin_tau = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_entorhinal_z", data=data_meg_pet_cogn3, "clin_tau")
  422. ### All biomarkers
  423. results_all = run_cox_model("Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+hippocampal_volume_z+tau_meta_roi_meg_alpha_z+tau_meta_roi_meg_gamma1_z+Ab42_40_ratio_z+ptau_217_z+amyloid_index_z+tau_entorhinal_z", data=data_meg_pet_cogn3, "all")
  424. ### Results using full plasma (n=211) Ab (n=226) and Tau PET (n=224) samples
  425. results_clin_211 = run_cox_model("Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe", data=df_plasma_211, "clin_211")
  426. results_clin_plasma_211 = run_cox_model("Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+Ab42_40_ratio_z+ptau_217_z", data=df_plasma_211, "clin_plasma_211")
  427. results_clin_226 = run_cox_model("Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe", data=df_ab_226, "clin_226")
  428. results_clin_ab_226 = run_cox_model("Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+amyloid_index_z", data=df_ab_226, "clin_ab_226")
  429. results_clin_224= run_cox_model("Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe", data=df_tau_224, "clin_224")
  430. results_clin_tau_224= run_cox_model("Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z+sex+education_z+apoe+tau_entorhinal_z", data=df_tau_224, "clin_tau_224")
  431. ### Get LRT statistic and pvalues from the main models
  432. ### Extract LRT statistic values (vs null model)
  433. stats_lrt=c(results_clin$log_rank_test, results_clin_meg_alpha_gamma1$log_rank_test, results_clin_mri$log_rank_test, results_clin_plasma$log_rank_test,
  434. results_clin_ab$log_rank_test, results_clin_tau$log_rank_test, results_all$log_rank_test)
  435. ### Extract p-values and correct for multiple comparisons
  436. p_vals_lrt=c(results_clin$p_value, results_clin_meg_alpha_gamma1$p_value, results_clin_mri$p_value, results_clin_plasma$p_value,
  437. results_clin_ab$p_value, results_clin_tau$p_value, results_all$p_value)
  438. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  439. sprintf("%.6f", p_vals_fdr_lrt)
  440. ### Get LRT statistic and pvalues from the extended models
  441. ### Extract LRT statistic values (vs null model)
  442. stats_lrt=c(results_clin_plasma_211$log_rank_test, results_clin_ab_226$log_rank_test, results_clin_tau_224$log_rank_test)
  443. ### Extract p-values and correct for multiple comparisons
  444. p_vals_lrt=c(results_clin_plasma_211$p_value, results_clin_ab_226$p_value, results_clin_tau_224$p_value)
  445. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  446. sprintf("%.6f", p_vals_fdr_lrt)
  447. ### Generate function to compare two models using LRT
  448. compare_models_lrt <- function(model1, model2) {
  449. log_like_1 <- logLik(model1)
  450. log_like_2 <- logLik(model2)
  451. lr_test <- -2 * (as.numeric(log_like_1) - as.numeric(log_like_2))
  452. df <- attr(log_like_2, "df") - attr(log_like_1, "df")
  453. p_value <- pchisq(lr_test, df = df, lower.tail = FALSE)
  454. return(list(
  455. lr_test_statistic = lr_test,
  456. df = df,
  457. p_value = p_value
  458. ))
  459. }
  460. ### Compare each model against the reference clinical model
  461. clin_vs_clin_meg_alpha_gamma1 <- compare_models_lrt(results_clin$model, results_clin_meg_alpha_gamma1$model)
  462. clin_vs_clin_mri <- compare_models_lrt(results_clin$model, results_clin_mri$model)
  463. clin_vs_clin_plasma <- compare_models_lrt(results_clin$model, results_clin_plasma$model)
  464. clin_vs_clin_ab <- compare_models_lrt(results_clin$model, results_clin_ab$model)
  465. clin_vs_clin_tau <- compare_models_lrt(results_clin$model, results_clin_tau$model)
  466. clin_vs_all <- compare_models_lrt(results_clin$model, results_all$model)
  467. #
  468. # ### Extract LRT statistic values (vs clinical model)
  469. stats_lrt=c(clin_vs_clin_meg_alpha_gamma1$lr_test_statistic, clin_vs_clin_mri$lr_test_statistic, clin_vs_clin_plasma$lr_test_statistic,
  470. clin_vs_clin_ab$lr_test_statistic, clin_vs_clin_tau$lr_test_statistic, clin_vs_all$lr_test_statistic)
  471. ### Extract p-values and correct for multiple comparisons
  472. p_vals_lrt=c(clin_vs_clin_meg_alpha_gamma1$p_value, clin_vs_clin_mri$p_value, clin_vs_clin_plasma$p_value,
  473. clin_vs_clin_ab$p_value, clin_vs_clin_tau$p_value, clin_vs_all$p_value)
  474. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  475. sprintf("%.6f", p_vals_fdr_lrt)
  476. ### Comparisons of proteinopathy models using the extnded sample
  477. clin_vs_clin_plasma_211 <- compare_models_lrt(results_clin_211$model, results_clin_plasma_211$model)
  478. clin_vs_clin_ab_226 <- compare_models_lrt(results_clin_226$model, results_clin_ab_226$model)
  479. clin_vs_clin_tau_224 <- compare_models_lrt(results_clin_224$model, results_clin_tau_224$model)
  480. ### Extract LRT statistic values (vs clinical model)
  481. stats_lrt=c(clin_vs_clin_plasma_211$lr_test_statistic, clin_vs_clin_ab_226$lr_test_statistic, clin_vs_clin_tau_224$lr_test_statistic)
  482. ### Extract p-values and correct for multiple comparisons
  483. p_vals_lrt=c(clin_vs_clin_plasma_211$p_value, clin_vs_clin_ab_226$p_value, clin_vs_clin_tau_224$p_value)
  484. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  485. sprintf("%.6f", p_vals_fdr_lrt)
  486. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  487. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  488. ### 6.3 Generate Kaplan-Meier survival curves----
  489. ### SVG figures
  490. for (i in seq(1,10)) {
  491. ### Models
  492. model_options = list(results_clin$model, results_clin_mri$model, results_clin_meg_alpha_gamma1$model,
  493. results_clin_plasma$model, results_clin_ab$model, results_clin_tau$model,
  494. results_all$model, results_clin_plasma_211$model,
  495. results_clin_ab_226$model, results_clin_tau_224$model)
  496. model_colors = c("gray5", "gray50","#F23F43","#FF7F50", "#1E98DD","#7600BC", "#00BA38",
  497. "#FF7F50","#1E98DD","#7600BC")
  498. model_names = c("clin","clin_mri","clin_meg","clin_plasma","clin_ab","clin_tau",
  499. "all","clin_plasma_211","clin_ab_226","clin_tau_224")
  500. ### Select model
  501. model = model_options[[i]]
  502. model_color = model_colors[i]
  503. model_name = model_names[i]
  504. ### Axis settings
  505. if (i %in% seq(1, 7)) {
  506. df_plot = data_meg_pet_cogn3
  507. x_limits = c(0, 7.5)
  508. y_limits = c(0.6, 1)
  509. y_breaks = seq(0.6, 1, by = 0.1)
  510. }
  511. if (i %in% c(8)) {
  512. df_plot = df_plasma_211
  513. x_limits = c(0, 11.2)
  514. y_limits = c(0.45, 1)
  515. y_breaks = seq(0.5, 1, by = 0.1)
  516. }
  517. if (i %in% c(9)) {
  518. df_plot = df_ab_226
  519. x_limits = c(0, 7.5)
  520. y_limits = c(0.45, 1)
  521. y_breaks = seq(0.5, 1, by = 0.1)
  522. }
  523. if (i %in% c(10)) {
  524. df_plot = df_tau_224
  525. x_limits = c(0, 7.5)
  526. y_limits = c(0.45, 1)
  527. y_breaks = seq(0.5, 1, by = 0.1)
  528. }
  529. ### Generate base plot
  530. p = ggsurvplot(
  531. fit = survfit(model),
  532. data = data_meg_pet_cogn3,
  533. xlab = "Time (years)",
  534. ylab = "Survival probability",
  535. xlim = x_limits,
  536. ylim = y_limits,
  537. palette = model_color,
  538. size = 0.3, # ???? thinner main survival curve
  539. conf.int = TRUE,
  540. conf.int.fill = model_color,
  541. conf.int.alpha = 0.15,
  542. conf.int.style = "ribbon",
  543. surv.median.line = "none",
  544. risk.table = FALSE,
  545. legend = "none",
  546. censor.size = 1, # ???? smaller + symbols
  547. ggtheme = theme_classic(base_family = "Arial") +
  548. theme(
  549. axis.title = element_text(size = 8),
  550. axis.text = element_text(size = 8),
  551. axis.line = element_line(linewidth = 0.4),
  552. axis.ticks = element_line(linewidth = 0.4),
  553. plot.margin = margin(2, 2, 2, 2, "mm")
  554. )
  555. )
  556. ### Refine plot (CI boundary lines slightly thinner)
  557. g = p$plot +
  558. geom_step(aes(y = upper), color = model_color, linewidth = 0.2, alpha = 1) +
  559. geom_step(aes(y = lower), color = model_color, linewidth = 0.2, alpha = 1) +
  560. scale_x_continuous(
  561. limits = x_limits,
  562. breaks = seq(0, ceiling(max(x_limits)), by = 1)
  563. ) +
  564. scale_y_continuous(
  565. limits = y_limits,
  566. breaks = y_breaks
  567. )
  568. ### Save as SVG (Illustrator-ready, editable text)
  569. svg_filename <- file.path(path, paste0("cox_surv_curve_", model_name, "_panel.svg"))
  570. svglite::svglite(
  571. file = svg_filename,
  572. width = 4.6 / 2.54, # cm ??? inches
  573. height = 4.6 / 2.54
  574. )
  575. print(g)
  576. dev.off()
  577. ### Display in R
  578. print(g)
  579. }
  580. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  581. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  582. ### 6.4 Plot survival curves stratified by risk group----
  583. ##### Manually change features
  584. library(survival)
  585. library(survminer)
  586. library(svglite)
  587. library(grid)
  588. ### Compute risk groups
  589. risk_scores <- predict(results_all$model, type = "lp")
  590. risk_group <- cut(
  591. risk_scores,
  592. breaks = quantile(risk_scores, probs = c(0, 1/3, 2/3, 1)),
  593. labels = c("Low", "Medium", "High"),
  594. include.lowest = TRUE
  595. )
  596. data_meg_pet_cogn3$risk_group <- risk_group
  597. ### Fit survival model
  598. fit <- survfit(
  599. Surv(diff_years_meg_mci_cox, mci_status_cox) ~ risk_group,
  600. data = data_meg_pet_cogn3
  601. )
  602. ### Generate survival plot + risk table
  603. p <- ggsurvplot(
  604. fit,
  605. data = data_meg_pet_cogn3,
  606. xlab = "Time (years)",
  607. ylab = "Survival probability",
  608. legend.labs = c("L", "M", "H"),
  609. palette = c("#49beaa", "#ffbe0b", "#f27a7d"),
  610. size = 0.4,
  611. censor.size = 1,
  612. conf.int = TRUE,
  613. conf.int.alpha = 0.15,
  614. conf.int.style = "ribbon",
  615. risk.table = TRUE,
  616. risk.table.col = "strata",
  617. break.time.by = 1,
  618. xlim = c(0, 7.5),
  619. tables.theme = theme_classic(base_family = "Arial"),
  620. ggtheme = theme_classic(base_family = "Arial"),
  621. legend = "none"
  622. )
  623. ### Style MAIN plot
  624. p$plot <- p$plot +
  625. scale_x_continuous(limits = c(0, 7.5), breaks = seq(0, 7, by = 1)) +
  626. scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = 0.2)) +
  627. theme(
  628. axis.title = element_text(size = 8),
  629. axis.text = element_text(size = 8),
  630. axis.line = element_line(linewidth = 0.4),
  631. axis.ticks = element_line(linewidth = 0.4),
  632. plot.margin = margin(2, 2, 2, 2, "mm")
  633. )
  634. ### Style RISK TABLE (clean)
  635. # Set font sizes for numbers inside table
  636. p$table <- p$table +
  637. theme_classic(base_family = "Arial") +
  638. theme(
  639. axis.title = element_blank(),
  640. axis.text.x = element_text(size = 8),
  641. axis.text.y = element_text(size = 8), # row labels (Low, Medium, High)
  642. axis.ticks = element_line(linewidth = 0.4),
  643. axis.line = element_line(linewidth = 0.4),
  644. plot.margin = margin(1, 2, 2, 2, "mm"),
  645. )
  646. # Remove "Number at risk" title
  647. g <- ggplotGrob(p$table)
  648. title_index <- which(g$layout$name == "title")
  649. if(length(title_index) > 0) g$grobs[[title_index]] <- nullGrob()
  650. p$table <- g # replace table with cleaned grob
  651. ### SAVE FILES (SVG ??? Illustrator)
  652. # Survival panel
  653. svglite::svglite(
  654. file = file.path(path, "survival_curve_all.svg"),
  655. width = 4.6 / 2.54,
  656. height = 4.6 / 2.54
  657. )
  658. print(p$plot)
  659. dev.off()
  660. # Risk table panel
  661. svglite::svglite(
  662. file = file.path(path, "risk_table_all.svg"),
  663. width = 5.9 / 2.54,
  664. height = 4.6 / 2.54
  665. )
  666. grid.draw(p$table) # ???? Use grid.draw for grob objects
  667. dev.off()
  668. ### Optional: print in R
  669. print(p$plot)
  670. grid.draw(p$table)
  671. # ### Log-rank test
  672. # survdiff(
  673. # Surv(diff_years_meg_mci_cox, mci_status_cox) ~ risk_group,
  674. # data = data_meg_pet_cogn3
  675. # )
  676. create_time_table <- function(
  677. model,
  678. data,
  679. time_var,
  680. event_var,
  681. time_points = seq(0, 7, by = 1),
  682. output_name = "time_table",
  683. path = "."
  684. ) {
  685. # Ensure proper format
  686. data <- as.data.frame(data)
  687. ### Risk groups
  688. risk_scores <- predict(model, type = "lp")
  689. risk_group <- cut(
  690. risk_scores,
  691. breaks = quantile(risk_scores, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE),
  692. labels = c("Low", "Medium", "High"),
  693. include.lowest = TRUE
  694. )
  695. data$risk_group <- factor(risk_group, levels = c("Low", "Medium", "High"))
  696. ### Survival fit
  697. surv_formula <- as.formula(
  698. paste0("Surv(", time_var, ", ", event_var, ") ~ risk_group")
  699. )
  700. fit <- survfit(surv_formula, data = data)
  701. ### Fixed time table
  702. fit_summary_fixed <- summary(fit, times = time_points)
  703. time_table_fixed <- data.frame(
  704. strata = fit_summary_fixed$strata,
  705. time = fit_summary_fixed$time,
  706. n_risk = fit_summary_fixed$n.risk,
  707. n_event = fit_summary_fixed$n.event,
  708. n_censor = fit_summary_fixed$n.censor
  709. )
  710. ### Cumulative values
  711. time_table_cum <- time_table_fixed %>%
  712. group_by(strata) %>%
  713. arrange(time) %>%
  714. mutate(
  715. cum_events = cumsum(n_event),
  716. cum_censor = cumsum(n_censor)
  717. ) %>%
  718. ungroup() %>%
  719. arrange(strata, time)
  720. ### Save CSV
  721. write.csv(
  722. time_table_cum,
  723. file = file.path(path, paste0(output_name, ".csv")),
  724. row.names = FALSE
  725. )
  726. ### Return table
  727. return(time_table_cum)
  728. }
  729. time_table_clin <- create_time_table(model = results_clin$model, data = data_meg_pet_cogn3,
  730. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  731. output_name = "time_table_clin", path = path)
  732. time_table_clin_meg <- create_time_table(model = results_clin_meg_alpha_gamma1$model, data = data_meg_pet_cogn3,
  733. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  734. output_name = "time_table_clin_meg", path = path)
  735. time_table_clin_mri <- create_time_table(model = results_clin_mri$model, data = data_meg_pet_cogn3,
  736. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  737. output_name = "time_table_clin_mri", path = path)
  738. time_table_clin_plasma <- create_time_table(model = results_clin_plasma$model, data = data_meg_pet_cogn3,
  739. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  740. output_name = "time_table_clin_plasma", path = path)
  741. time_table_clin_ab <- create_time_table(model = results_clin_ab$model, data = data_meg_pet_cogn3,
  742. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  743. output_name = "time_table_clin_ab", path = path)
  744. time_table_clin_tau <- create_time_table(model = results_clin_tau$model, data = data_meg_pet_cogn3,
  745. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  746. output_name = "time_table_clin_tau", path = path)
  747. time_table_all <- create_time_table(model = results_all$model, data = data_meg_pet_cogn3,
  748. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  749. output_name = "time_table_all", path = path)
  750. time_table_clin_plasma_211 <- create_time_table(model = results_clin_plasma_211$model, data = df_plasma_211,
  751. time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 11, by = 1),
  752. output_name = "time_table_clin_plasma_211", path = path)
  753. time_table_clin_ab_226 <- create_time_table(model = results_clin_ab_226$model, data = df_ab_226,
  754. time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  755. output_name = "time_table_clin_ab_226", path = path)
  756. time_table_clin_tau_224 <- create_time_table(model = results_clin_tau_224$model, data = df_tau_224,
  757. time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
  758. output_name = "time_table_clin_tau_224", path = path)
  759. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  760. #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  761. ### Assess correlation between risk scores and biomarkers----
  762. ### Change labels for APOE
  763. data_meg_pet_cogn3$apoe <- factor(data_meg_pet_cogn3$apoe, levels = c(0, 1), labels = c("Non-carrier", "Carrier"))
  764. model_options=list(results_clin$model, results_clin_meg_alpha_gamma1$model, results_clin_mri$model, results_clin_plasma$model,
  765. results_clin_ab$model, results_clin_tau$model, results_all$model,
  766. results_clin_plasma_211$model, results_clin_ab_226$model, results_clin_tau_224$model)
  767. compare_risk_simple <- function(variable_name, variable_type, model_idx,
  768. model_options, data, y_label, custom_colors, path,integer_y=FALSE) {
  769. # Compute risk scores
  770. risk_scores <- predict(model_options[[model_idx]], type="lp")
  771. data$risk_scores <- risk_scores
  772. # Continuous variables
  773. if(variable_type == "continuous") {
  774. formula <- as.formula(paste("risk_scores ~", variable_name))
  775. lm_res <- summary(lm(formula, data=data))
  776. t_val <- lm_res$coefficients[variable_name, "t value"]
  777. p_val <- lm_res$coefficients[variable_name, "Pr(>|t|)"]
  778. # Scatterplot with regression line
  779. p_scatter <- ggplot(data, aes(x=.data[[variable_name]], y=risk_scores)) +
  780. geom_point(color=custom_colors[1], alpha=0.5, size=1.5, stroke=0.1) +
  781. geom_smooth(method="lm", se=TRUE, color=custom_colors[1], fill=custom_colors[1], alpha=0.15, linewidth=0.3) +
  782. labs(x=y_label, y="Relative risk score") +
  783. theme_classic(base_family="Arial") +
  784. theme(
  785. axis.title = element_text(size = 8),
  786. axis.text = element_text(size = 8),
  787. axis.line = element_line(linewidth = 0.4),
  788. axis.ticks = element_line(linewidth = 0.4),
  789. plot.margin = margin(2, 2, 2, 2, "mm")
  790. )
  791. if(integer_y) {
  792. p_scatter <- p_scatter +
  793. scale_y_continuous(
  794. breaks = seq(
  795. floor(min(data[[variable_name]], na.rm = TRUE)),
  796. ceiling(max(data[[variable_name]], na.rm = TRUE)),
  797. by = 2
  798. )
  799. )
  800. }
  801. svglite::svglite(file=file.path(path, paste0("scatter_risk_score_", variable_name, ".svg")),
  802. width=4.6/2.54, height=4.6/2.54)
  803. print(p_scatter); dev.off()
  804. return(list(lm_summary=lm_res, t_val=t_val, p_val=p_val))
  805. }
  806. # Categorical variables (sex, apoe)
  807. if(variable_type == "categorical") {
  808. formula <- as.formula(paste("risk_scores ~", variable_name))
  809. t_res <- t.test(formula, data=data)
  810. # Box + violin plot
  811. p_box <- ggplot(data, aes(x=.data[[variable_name]], y=risk_scores, fill=.data[[variable_name]])) +
  812. geom_violin(trim=FALSE, linewidth=0.3) +
  813. geom_boxplot(width=0.3, outlier.shape=NA, linewidth=0.3) +
  814. geom_jitter(width=0.15, color="black", alpha=0.25, size=1.5, stroke = 0.1) +
  815. scale_fill_manual(values=custom_colors) +
  816. labs(x=y_label, y="Relative risk score") +
  817. theme_classic(base_family="Arial") +
  818. theme(
  819. axis.title = element_text(size = 8),
  820. axis.text = element_text(size = 8),
  821. axis.line = element_line(linewidth = 0.4),
  822. axis.ticks = element_line(linewidth = 0.4),
  823. plot.margin = margin(2, 2, 2, 2, "mm"),
  824. legend.position="none")
  825. svglite::svglite(file=file.path(path, paste0("box_risk_score_", variable_name, ".svg")),
  826. width=4.6/2.54, height=4.6/2.54)
  827. print(p_box); dev.off()
  828. return(list(t_test=t_res))
  829. }
  830. }
  831. ### Define parameters for running the models
  832. variables = c("age_z", "sex","education_z", "apoe", "tau_meta_roi_meg_alpha_z","tau_meta_roi_meg_gamma1_z", "hippocampal_volume_z",
  833. "Ab42_40_ratio_z","ptau_217_z","amyloid_index_z","tau_entorhinal_z")
  834. y_labels=c("Age (z-score)", "Sex", "Education (z-score)", "APOE ??4 carrier status",
  835. "Alpha power (z-score)", "Gamma power (z-score)", "Hipp. volume (z-score)",
  836. "A??42/40 ratio (z-score)", "p-tau217 (z-score)", "Neocortical A?? (z-score)", "Entorhinal tau (z-score)")
  837. model=c("clin","clin_meg","clin_mri","clin_plasma","clin_ab","clin_tau","all","clin_plasma_211","clin_ab_226","clin_tau_224")
  838. custom_colors_con=c("#49beaa","#ffbe0b","#f27a7d")
  839. custom_colors_cat=c("#c8f0e7","#ffecb3","#fcdfe0","#49beaa","#ffbe0b","#f27a7d")
  840. model_colors=c("gray5", "#F23F43", "gray50", "#FF7F50", "#1E98DD","#7600BC", "#00BA38", "#FF7F50", "#1E98DD","#7600BC")
  841. My_Theme = theme(plot.title = element_text(hjust = 0.5, size = 16, family = "TT Arial"), axis.text.x = element_text(family = "TT Arial"),
  842. axis.text.y = element_text(family = "TT Arial"), axis.title.x = element_text(family = "TT Arial"),
  843. axis.title.y = element_text(family = "TT Arial"), legend.text=element_text(family = "TT Arial"),
  844. legend.title =element_text(family = "TT Arial"))
  845. # For sex (categorical)
  846. res_sex <- compare_risk_simple(variable_name = "sex", variable_type = "categorical", model_idx = 7,
  847. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Sex",
  848. custom_colors = c("gray80","gray80"), path = path)
  849. # For sex (categorical)
  850. res_apoe <- compare_risk_simple(variable_name = "apoe", variable_type = "categorical", model_idx = 7,
  851. model_options = model_options, data = data_meg_pet_cogn3, y_label = "APOE e4 status",
  852. custom_colors = c("gray80","gray80"), path = path)
  853. # For age_z (continuous)
  854. res_age <- compare_risk_simple(variable_name = "age_z", variable_type = "continuous", model_idx = 7,
  855. model_options = model_options, data = data_meg_pet_cogn3$apoe, y_label = "Age (z-score)",
  856. custom_colors = c("gray5"), path = path)
  857. # For age_z (continuous)
  858. res_age <- compare_risk_simple(variable_name = "education_z", variable_type = "continuous", model_idx = 7,
  859. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Education (z-score)",
  860. custom_colors = c("gray5"), path = path)
  861. # For age_z (continuous)
  862. res_age <- compare_risk_simple(variable_name = "tau_meta_roi_meg_alpha_z", variable_type = "continuous", model_idx = 7,
  863. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Alpha power (z-score)",
  864. custom_colors = c("#F23F43"), path = path)
  865. # For age_z (continuous)
  866. res_age <- compare_risk_simple(variable_name = "tau_meta_roi_meg_gamma1_z", variable_type = "continuous", model_idx = 7,
  867. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Gamma power (z-score)",
  868. custom_colors = c("#F23F43"), path = path)
  869. # For age_z (continuous)
  870. res_age <- compare_risk_simple(variable_name = "hippocampal_volume_z", variable_type = "continuous", model_idx = 7,
  871. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Hipp. volume (z-score)",
  872. custom_colors = c("gray50"), path = path)
  873. # For age_z (continuous)
  874. res_age <- compare_risk_simple(variable_name = "Ab42_40_ratio_z", variable_type = "continuous", model_idx = 7,
  875. model_options = model_options, data = data_meg_pet_cogn3, y_label = "A??42/40 ratio (z-score)",
  876. custom_colors = c("#FF7F50"), path = path)
  877. # For age_z (continuous)
  878. res_age <- compare_risk_simple(variable_name = "ptau_217_z", variable_type = "continuous", model_idx = 7,
  879. model_options = model_options, data = data_meg_pet_cogn3, y_label = "p-tau217 (z-score)",
  880. custom_colors = c("#FF7F50"), path = path, integer_y = TRUE)
  881. # For age_z (continuous)
  882. res_age <- compare_risk_simple(variable_name = "amyloid_index_z", variable_type = "continuous", model_idx = 7,
  883. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Neocortical A?? (z-score)",
  884. custom_colors = c("#1E98DD"), path = path)
  885. # For age_z (continuous)
  886. res_age <- compare_risk_simple(variable_name = "tau_entorhinal_z", variable_type = "continuous", model_idx = 7,
  887. model_options = model_options, data = data_meg_pet_cogn3, y_label = "Entorhinal tau (z-score)",
  888. custom_colors = c("#7600BC"), path = path)
  889. ### Extract t/t statistic values (risk scores)
  890. stats_t_all=c(results_risk_group_age_all$t_val_risk_score,results_risk_group_sex_all$t_value_risk_score,results_risk_group_edu_all$t_val_risk_score,
  891. results_risk_group_apoe_all$t_value_risk_score, results_risk_group_alpha_all$t_val_risk_score, results_risk_group_gamma_all$t_val_risk_score,
  892. results_risk_group_hipp_all$t_val_risk_score, results_risk_group_ab4240_all$t_val_risk_score,results_risk_group_ptau217_all$t_val_risk_score,
  893. results_risk_group_ab_all$t_val_risk_score,results_risk_group_tau_all$t_val_risk_score)
  894. ### Correct p-values for multiple comparisons
  895. p_vals_t_all=c(results_risk_group_age_all$p_value_risk_score,results_risk_group_sex_all$p_value_risk_score,results_risk_group_edu_all$p_value_risk_score,
  896. results_risk_group_apoe_all$p_value_risk_score,results_risk_group_alpha_all$p_value_risk_score, results_risk_group_gamma_all$p_value_risk_score,
  897. results_risk_group_hipp_all$p_value_risk_score, results_risk_group_ab4240_all$p_value_risk_score,results_risk_group_ptau217_all$p_value_risk_score,
  898. results_risk_group_ab_all$p_value_risk_score,results_risk_group_tau_all$p_value_risk_score)
  899. p_vals_fdr_t_all=p.adjust(p_vals_t_all, method = "fdr")
  900. sprintf("%.6f", p_vals_fdr_t_all)
  901. ### 8. MCI Cox regression survival analysis with time interactions----
  902. ### 8.1 Run Cox regressions including time interactions----
  903. ### Generate function to run a Cox model including an interaction term
  904. run_cox_model_time_int <- function(formula, data, model_name) {
  905. formula <- as.formula(formula)
  906. model <- coxph(formula, data = data, tt = function(x, time, ...) x * time)
  907. model_summary <- summary(model)
  908. c_index <- model_summary$concordance
  909. lr_test <- model_summary$logtest[1]
  910. p_val <- model_summary$logtest[3]
  911. summary_filename <- paste0("Cox_model_", model_name, "_summary.txt")
  912. #capture.output(model_summary, file = summary_filename)
  913. return(list(model = model, summary = model_summary, c_index = c_index, log_rank_test = lr_test, p_value = p_val))
  914. }
  915. ### Run Cox regressions including time interactions for each biomarker
  916. ### Age
  917. results_clin_age_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  918. education_z + apoe + tt(age_z)", data = data_meg_pet_cogn3, model_name = "age_tt")
  919. ### Education
  920. results_clin_edu_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  921. education_z + apoe + tt(education_z)", data = data_meg_pet_cogn3, model_name = "edu_tt")
  922. ### MEG delta
  923. results_clin_meg_delta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  924. education_z + apoe + tau_meta_roi_meg_delta_z + tt(tau_meta_roi_meg_delta_z)", data = data_meg_pet_cogn3, model_name = "delta_tt")
  925. ### MEG theta
  926. results_clin_meg_theta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  927. education_z + apoe + tau_meta_roi_meg_theta_z + tt(tau_meta_roi_meg_theta_z)", data = data_meg_pet_cogn3, model_name = "theta_tt")
  928. ### MEG alpha
  929. results_clin_meg_alpha_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  930. education_z + apoe + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z)", data = data_meg_pet_cogn3, model_name = "alpha_tt")
  931. ### MEG beta
  932. results_clin_meg_beta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  933. education_z + apoe + tau_meta_roi_meg_beta_z + tt(tau_meta_roi_meg_beta_z)", data = data_meg_pet_cogn3, model_name = "beta_tt")
  934. ### MEG gamma1
  935. results_clin_meg_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  936. education_z + apoe + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z)", data = data_meg_pet_cogn3, model_name = "gamma1_tt")
  937. ### MEG gamma2
  938. results_clin_meg_gamma2_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  939. education_z + apoe + tau_meta_roi_meg_gamma2_z + tt(tau_meta_roi_meg_gamma2_z)", data = data_meg_pet_cogn3, model_name = "gamma2_tt")
  940. ### MEG alpha and gamma1
  941. results_clin_meg_alpha_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  942. education_z + apoe + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z) + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z)",
  943. data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
  944. ### MRI hipp vol
  945. results_clin_mri_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  946. education_z + apoe + hippocampal_volume_z + tt(hippocampal_volume_z)", data = data_meg_pet_cogn3, model_name = "hipp_tt")
  947. ### Plasma Ab42/40 ratio
  948. results_clin_Ab4240_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  949. education_z + apoe + Ab42_40_ratio_z + tt(Ab42_40_ratio_z)", data = data_meg_pet_cogn3, model_name = "Ab42_40_tt")
  950. ### Plasma p-tau217
  951. results_clin_ptau_217_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  952. education_z + apoe + ptau_217_z + tt(ptau_217_z)", data = data_meg_pet_cogn3, model_name = "ptau_217_tt")
  953. ### Ab PET
  954. results_clin_ab_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  955. education_z + apoe + amyloid_index_z + tt(amyloid_index_z)", data = data_meg_pet_cogn3, model_name = "amyloid_index_tt")
  956. ### Tau PET
  957. results_clin_tau_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  958. education_z + apoe + tau_entorhinal_z + tt(tau_entorhinal_z)", data = data_meg_pet_cogn3, model_name = "tau_entorhinal_tt")
  959. ### Run Cox regression models with time interaction for the extended samples
  960. ### Plasma Ab42/40 ratio (n=211)
  961. results_clin_Ab4240_211_nl=run_cox_model_time_int(formula = "Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z + sex +
  962. education_z + apoe + Ab42_40_ratio_z + tt(Ab42_40_ratio_z)", data = df_plasma_211, model_name = "ab4240_211_tt")
  963. ### Plasma p-tau217 (n=211)
  964. results_clin_ptau_217_211_nl=run_cox_model_time_int(formula = "Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z + sex +
  965. education_z + apoe + ptau_217_z + tt(ptau_217_z)", data = df_plasma_211, model_name = "ptau_217_211_tt")
  966. ### Ab PET (n=226)
  967. results_clin_ab_226_nl=run_cox_model_time_int(formula = "Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z + sex +
  968. education_z + apoe + amyloid_index_z + tt(amyloid_index_z)", data = df_ab_226, model_name = "ab_226_tt")
  969. ### Tau PET (n=224)
  970. results_clin_tau_224_nl=run_cox_model_time_int(formula = "Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z + sex +
  971. education_z + apoe + tau_entorhinal_z + tt(tau_entorhinal_z)", data = df_tau_224, model_name = "tau_224_tt")
  972. ### Combine MEG nl and proteinopathy
  973. ### MEG nl and plasma
  974. results_clin_plasma_meg_alpha_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  975. education_z + apoe + Ab42_40_ratio_z + ptau_217_z + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z) + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z)",
  976. data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
  977. ### MEG nl and Ab PET
  978. results_clin_ab_meg_alpha_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  979. education_z + apoe + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z) + amyloid_index_z + tt(amyloid_index_z) + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z)",
  980. data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
  981. ### MEG nl and tau PET
  982. results_clin_tau_meg_alpha_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
  983. education_z + apoe + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z) + tau_entorhinal_z + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z)",
  984. data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
  985. ### Comparisons of proteinopathy models including the gamma1 time interaction
  986. clin_plasma_vs_clin_plasma_meg_alpha_gamma1 <- compare_models_lrt(results_clin_plasma$model, results_clin_plasma_meg_alpha_gamma1_nl$model)
  987. clin_ab_vs_clin_ab_meg_alpha_gamma1 <- compare_models_lrt(results_clin_ab_nl$model, results_clin_ab_meg_alpha_gamma1_nl$model)
  988. clin_tau_vs_clin_tau_meg_alpha_gamma1 <- compare_models_lrt(results_clin_tau$model, results_clin_tau_meg_alpha_gamma1_nl$model)
  989. clin_plasma_vs_clin_plasma_meg_alpha_gamma1=anova(results_clin_plasma$model, results_clin_plasma_meg_alpha_gamma1_nl$model, test = "LRT")
  990. ### Extract p-values and correct for multiple comparisons
  991. stats_lrt=c(clin_plasma_vs_clin_plasma_meg_alpha_gamma1$lr_test_statistic,clin_ab_vs_clin_ab_meg_alpha_gamma1$lr_test_statistic,clin_tau_vs_clin_tau_meg_alpha_gamma1$lr_test_statistic)
  992. p_vals_lrt=c(clin_plasma_vs_clin_plasma_meg_alpha_gamma1$p_value,clin_ab_vs_clin_ab_meg_alpha_gamma1$p_value,clin_tau_vs_clin_tau_meg_alpha_gamma1$p_value)
  993. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  994. sprintf("%.6f", p_vals_fdr_lrt)
  995. ### Get LRT statistic and pvalues from the models including a time interaction
  996. ### Extract LRT statistic values (vs null model)
  997. stats_lrt=c(results_clin_age_nl$log_rank_test, results_clin_education_nl$log_rank_test, results_clin_meg_nl$log_rank_test, results_clin_mri_nl$log_rank_test,
  998. results_clin_Ab4240_nl$log_rank_test, results_clin_ptau_217_nl$log_rank_test, results_clin_ab_nl$log_rank_test, results_clin_tau_nl$log_rank_test)
  999. ### Extract p-values and correct for multiple comparisons
  1000. p_vals_lrt=c(results_clin_age_nl$p_value, results_clin_education$p_value, results_clin_meg_nl$p_value, results_clin_mri_nl$p_value,
  1001. results_clin_Ab4240_nl$p_value, results_clin_ptau_217_nl$p_value, results_clin_ab_nl$p_value, results_clin_tau_nl$p_value)
  1002. p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
  1003. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1004. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1005. ### 8.2 Plot interaction effect: Hazard ratio change over time----
  1006. ### Generate function to plot change in Hazard ratio over time from the interaction coefficients
  1007. library(ggplot2)
  1008. library(svglite)
  1009. plot_time_varying_hr_with_ci <- function(cox_model, var_name, time_max = 7, n_points = 100,
  1010. color = "#ffbe0b", path = ".", ylim_fixed = "no") {
  1011. # Extract coefficients
  1012. coef_main <- coef(cox_model)[[var_name]]
  1013. coef_tt <- coef(cox_model)[[paste0("tt(", var_name, ")")]]
  1014. # Variance-covariance
  1015. vcov_matrix <- vcov(cox_model)
  1016. var_main <- vcov_matrix[var_name, var_name]
  1017. var_tt <- vcov_matrix[paste0("tt(", var_name, ")"), paste0("tt(", var_name, ")")]
  1018. cov_main_tt <- vcov_matrix[var_name, paste0("tt(", var_name, ")")]
  1019. # Time sequence
  1020. time_seq <- seq(0, time_max, length.out = n_points)
  1021. # Linear predictor and variance
  1022. lp_time <- coef_main + coef_tt * time_seq
  1023. var_lp_time <- var_main + (time_seq^2) * var_tt + 2 * time_seq * cov_main_tt
  1024. # Confidence intervals
  1025. se_lp_time <- sqrt(var_lp_time)
  1026. lower_lp <- lp_time - 1.96 * se_lp_time
  1027. upper_lp <- lp_time + 1.96 * se_lp_time
  1028. # Dataframe
  1029. df_hr <- data.frame(
  1030. Time = time_seq,
  1031. HR = exp(lp_time),
  1032. HR_lower = exp(lower_lp),
  1033. HR_upper = exp(upper_lp)
  1034. )
  1035. # HR=1 crossing
  1036. time_hr1 <- if (coef_tt != 0) -coef_main / coef_tt else NA
  1037. y_max <- if (ylim_fixed == "yes") 2 else max(df_hr$HR_upper, na.rm = TRUE)
  1038. y_seg_end <- y_max / 3
  1039. # Create ggplot
  1040. p <- ggplot(df_hr, aes(x = Time, y = HR)) +
  1041. geom_line(color = color, linewidth = 0.6) +
  1042. geom_ribbon(aes(ymin = HR_lower, ymax = HR_upper), fill = color, alpha = 0.2) +
  1043. geom_hline(yintercept = 1, linetype = "dashed", color = "gray50", linewidth = 0.4) +
  1044. labs(x = "Years from biomarker", y = "Hazard ratio") +
  1045. theme_classic(base_family = "Arial") + # Use system font
  1046. theme(
  1047. axis.title = element_text(size = 8),
  1048. axis.text = element_text(size = 8),
  1049. axis.line = element_line(linewidth = 0.4),
  1050. axis.ticks = element_line(linewidth = 0.4),
  1051. plot.margin = margin(2, 2, 2, 2, "mm")
  1052. )
  1053. # Y-axis breaks
  1054. if (ylim_fixed == "yes") {
  1055. p <- p + scale_y_continuous(limits = c(0, 2), breaks = 0:2)
  1056. } else {
  1057. p <- p + scale_y_continuous(breaks = seq(0, ceiling(max(df_hr$HR_upper)), by = 1))
  1058. }
  1059. # HR=1 annotation
  1060. if (!is.na(time_hr1) && time_hr1 >= 0 && time_hr1 <= time_max) {
  1061. p <- p +
  1062. annotate("segment",
  1063. x = time_hr1, xend = time_hr1,
  1064. y = 1, yend = 1 - (y_seg_end * 0.4),
  1065. linetype = "dashed", color = "gray50", linewidth = 0.4) +
  1066. annotate("text",
  1067. x = time_hr1,
  1068. y = 1 - (y_seg_end * 0.4),
  1069. label = "HR=1",
  1070. size = 2.2, # ??? 8 pt
  1071. vjust = 1.2)
  1072. }
  1073. # Save as SVG (editable text in Illustrator)
  1074. svg_filename <- paste0(path, "/", var_name, "_panel.svg")
  1075. svglite::svglite(file = svg_filename, width = 4.6/2.54, height = 4.6/2.54) # cm ??? inches
  1076. print(p)
  1077. dev.off()
  1078. return(p)
  1079. }
  1080. ## Generate plots for each biomarker
  1081. plot_time_varying_hr_with_ci(results_clin_age_nl$model, "age_z", 7, 100, "#ffbe0b", path, "no")
  1082. plot_time_varying_hr_with_ci(results_clin_edu_nl$model, "education_z", 7, 100, "#ffbe0b", path, "no")
  1083. plot_time_varying_hr_with_ci(results_clin_meg_delta_nl$model, "tau_meta_roi_meg_delta_z", 7, 100, "#ffbe0b", path, "no")
  1084. plot_time_varying_hr_with_ci(results_clin_meg_theta_nl$model, "tau_meta_roi_meg_theta_z", 7, 100, "#ffbe0b", path, "no")
  1085. plot_time_varying_hr_with_ci(results_clin_meg_alpha_nl$model, "tau_meta_roi_meg_alpha_z", 7, 100, "#ffbe0b", path, "no")
  1086. plot_time_varying_hr_with_ci(results_clin_meg_beta_nl$model, "tau_meta_roi_meg_beta_z", 7, 100, "#ffbe0b", path, "no")
  1087. plot_time_varying_hr_with_ci(results_clin_meg_gamma1_nl$model, "tau_meta_roi_meg_gamma1_z", 7, 100, "#ffbe0b", path, "no")
  1088. plot_time_varying_hr_with_ci(results_clin_meg_gamma2_nl$model, "tau_meta_roi_meg_gamma2_z", 7, 100, "#ffbe0b", path, "no")
  1089. plot_time_varying_hr_with_ci(results_clin_mri_nl$model, "hippocampal_volume_z", 7, 100, "#ffbe0b", path, "no")
  1090. plot_time_varying_hr_with_ci(results_clin_Ab4240_nl$model, "Ab42_40_ratio_z", 7, 100, "#ffbe0b", path,"yes")
  1091. plot_time_varying_hr_with_ci(results_clin_ptau_217_nl$model, "ptau_217_z", 7, 100, "#ffbe0b", path, "no")
  1092. plot_time_varying_hr_with_ci(results_clin_ab_nl$model, "amyloid_index_z", 7, 100, "#ffbe0b", path, "no")
  1093. plot_time_varying_hr_with_ci(results_clin_tau_nl$model, "tau_entorhinal_z", 7, 100, "#ffbe0b", path, "no")
  1094. ### Generate plots for the extended samples for plasma and PET
  1095. plot_time_varying_hr_with_ci(results_clin_Ab4240_211_nl$model, "Ab42_40_ratio_z", 11, 100, "#ffbe0b", path,"no")
  1096. plot_time_varying_hr_with_ci(results_clin_ptau_217_211_nl$model, "ptau_217_z", 11, 100, "#ffbe0b", path,"no")
  1097. plot_time_varying_hr_with_ci(results_clin_ab_226_nl$model, "amyloid_index_z", 7, 100, "#ffbe0b", path, "no")
  1098. plot_time_varying_hr_with_ci(results_clin_tau_224_nl$model, "tau_entorhinal_z", 7, 100, "#ffbe0b", path, "no")
  1099. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1100. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1101. ### 8.3 Compute relative risk and plot linear predictor as a function of time----
  1102. ### Function: Compute relative risks + Illustrator-friendly plotting
  1103. compute_relative_risks <- function(data, biomarker, time_var, event_var, covariates = NULL,
  1104. time_points = 1:7, id_var = "pscid", plot = TRUE, model_name = NULL,
  1105. path = ".", include_ci = TRUE, overlay_baseline = FALSE,
  1106. save_csv = FALSE, csv_filename = NULL, scale = "log") {
  1107. # Check scale
  1108. if (!scale %in% c("log", "exp")) stop("scale must be 'log' or 'exp'")
  1109. # Build Cox formula
  1110. all_covariates <- paste(c(covariates, biomarker, paste0("tt(", biomarker, ")")), collapse = " + ")
  1111. cox_formula <- as.formula(paste0("Surv(", time_var, ", ", event_var, ") ~ ", all_covariates))
  1112. # Fit Cox model with time interaction
  1113. cox_model <- coxph(cox_formula, data = data, tt = function(x, time, ...) x * time)
  1114. # Compute linear predictors
  1115. linear_predictor <- predict(cox_model, type = "lp", newdata = data)
  1116. coefs <- coef(cox_model)
  1117. vcov_mat <- vcov(cox_model)
  1118. biom_coef <- coefs[biomarker]
  1119. interaction_name <- paste0("tt(", biomarker, ")")
  1120. interaction_coef <- coefs[interaction_name]
  1121. se_biom <- sqrt(vcov_mat[biomarker, biomarker])
  1122. se_inter <- sqrt(vcov_mat[interaction_name, interaction_name])
  1123. cov_biom_inter <- vcov_mat[biomarker, interaction_name]
  1124. lp_no_main <- linear_predictor - data[[biomarker]] * biom_coef
  1125. # Compute relative risks for each time point
  1126. relative_risks <- lapply(time_points, function(t) {
  1127. biom <- data[[biomarker]]
  1128. lp <- lp_no_main + biom * (biom_coef + interaction_coef * t)
  1129. if (include_ci) {
  1130. se_lp <- sqrt((biom^2) * (se_biom^2 + t^2 * se_inter^2 + 2 * t * cov_biom_inter))
  1131. lower <- lp - 1.96 * se_lp
  1132. upper <- lp + 1.96 * se_lp
  1133. if (scale == "exp") {
  1134. return(data.frame(lp = exp(lp), lower = exp(lower), upper = exp(upper)))
  1135. } else {
  1136. return(data.frame(lp = lp, lower = lower, upper = upper))
  1137. }
  1138. } else {
  1139. return(data.frame(lp = if (scale == "exp") exp(lp) else lp))
  1140. }
  1141. })
  1142. # Combine results
  1143. rr_df <- do.call(rbind, lapply(seq_along(time_points), function(i) {
  1144. df <- relative_risks[[i]]
  1145. df[[id_var]] <- data[[id_var]]
  1146. df$biomarker_value <- data[[biomarker]]
  1147. df$time <- time_points[i]
  1148. return(df)
  1149. }))
  1150. rr_df <- rr_df[, c(id_var, "time", "biomarker_value", "lp", if (include_ci) c("lower", "upper") else NULL)]
  1151. names(rr_df)[names(rr_df) == "lp"] <- "relative_risk"
  1152. # Save CSV if requested
  1153. if (save_csv && !is.null(csv_filename)) {
  1154. write.csv(rr_df, file = file.path(path, csv_filename), row.names = FALSE)
  1155. }
  1156. # Plotting (Illustrator-friendly)
  1157. if (plot && !is.null(model_name)) {
  1158. # Colors
  1159. parula_colors <- c("#352A87", "#3B52A1", "#3F7FBA", "#469BBA", "#58B89E", "#84CA79", "#D9D93A")
  1160. if (length(time_points) != length(parula_colors)) stop("Number of time points must match parula_colors length")
  1161. names(parula_colors) <- as.character(time_points)
  1162. # Labels
  1163. xlab <- switch(biomarker,
  1164. "age_z" = "Age (z-score)",
  1165. "education_z" = "Education (z-score)",
  1166. "tau_meta_roi_meg_delta_z" = "Delta power (z-score)",
  1167. "tau_meta_roi_meg_theta_z" = "Theta power (z-score)",
  1168. "tau_meta_roi_meg_alpha_z" = "Alpha power (z-score)",
  1169. "tau_meta_roi_meg_beta_z" = "Beta power (z-score)",
  1170. "tau_meta_roi_meg_gamma1_z" = "Gamma power (z-score)",
  1171. "tau_meta_roi_meg_gamma2_z" = "Gamma2 power (z-score)",
  1172. "hippocampal_volume_z" = "Hipp. volume (z-score)",
  1173. "Ab42_40_ratio_z" = "A??42/40 ratio (z-score)",
  1174. "ptau_217_z" = "p-tau217 (z-score)",
  1175. "amyloid_index_z" = "Neocortical A?? (z-score)",
  1176. "tau_entorhinal_z" = "Entorhinal tau (z-score)",
  1177. biomarker)
  1178. ylab <- if (scale == "exp") "Relative Risk (HR)" else "Relative risk score"
  1179. # Base plot
  1180. p <- ggplot(rr_df, aes(x = biomarker_value, y = relative_risk, color = factor(time))) +
  1181. geom_point(size = 0.6, alpha = 0.7) +
  1182. geom_smooth(method = "lm", aes(group = time), se = FALSE, linewidth = 0.6) +
  1183. scale_color_manual(values = parula_colors, name = "Time") +
  1184. guides(color = guide_legend(nrow = 1))
  1185. # CI ribbons
  1186. if (include_ci) {
  1187. p <- p + geom_ribbon(aes(ymin = lower, ymax = upper, fill = factor(time)), alpha = 0.15, color = NA) +
  1188. scale_fill_manual(values = parula_colors, guide = "none")
  1189. }
  1190. # Adaptive integer y-axis
  1191. p <- p + scale_y_continuous(
  1192. breaks = function(x) {
  1193. bks <- pretty(x)
  1194. bks_int <- unique(round(bks))
  1195. bks_int[bks_int >= min(x) & bks_int <= max(x)]
  1196. },
  1197. labels = scales::label_number(accuracy = 1)
  1198. )
  1199. # Color scale
  1200. p <- p + scale_color_manual(values = parula_colors, name = "Time")
  1201. # Theme
  1202. p <- p +
  1203. labs(x = xlab, y = ylab) +
  1204. theme_classic(base_family = "Arial") +
  1205. theme(
  1206. axis.title = element_text(size = 8),
  1207. axis.text = element_text(size = 8),
  1208. axis.line = element_line(linewidth = 0.4),
  1209. axis.ticks = element_line(linewidth = 0.4),
  1210. legend.position = "none",
  1211. legend.title = element_text(size = 8),
  1212. legend.text = element_text(size = 7),
  1213. legend.key.width = unit(0.4, "cm"),
  1214. legend.spacing.x = unit(0.05, "cm"),
  1215. legend.margin = margin(0, 0, 0, 0),
  1216. legend.box.margin = margin(0, 0, 0, 0),
  1217. plot.margin = margin(2, 2, 2, 2, "mm")
  1218. )
  1219. # Save as SVG for Illustrator
  1220. svg_filename <- file.path(path, paste0(model_name, "_panel.svg"))
  1221. svglite::svglite(file = svg_filename, width = 4.6/2.54, height = 4.6/2.54) # cm ??? inches
  1222. print(p)
  1223. dev.off()
  1224. # Also show plot in R
  1225. print(p)
  1226. }
  1227. return(rr_df)
  1228. }
  1229. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "age_z",
  1230. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("sex", "education_z", "apoe"),
  1231. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_age", path = path,
  1232. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_age.csv")
  1233. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "education_z",
  1234. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "apoe"),
  1235. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_education", path = path,
  1236. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_education.csv")
  1237. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_delta_z",
  1238. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1239. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_delta", path = path,
  1240. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_delta.csv")
  1241. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_theta_z",
  1242. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1243. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_theta", path = path,
  1244. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_theta.csv")
  1245. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_alpha_z",
  1246. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1247. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_alpha", path = path,
  1248. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_alpha.csv")
  1249. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_beta_z",
  1250. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1251. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_beta", path = path,
  1252. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_beta.csv")
  1253. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_gamma1_z",
  1254. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1255. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_gamma1", path = path,
  1256. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_gamma1.csv")
  1257. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_gamma2_z",
  1258. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1259. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_gamma2", path = path,
  1260. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_gamma2.csv")
  1261. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "hippocampal_volume_z",
  1262. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1263. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_hipp_vol", path = path,
  1264. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_hipp_vol.csv")
  1265. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "Ab42_40_ratio_z",
  1266. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1267. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_Ab4240", path = path,
  1268. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_Ab4240.csv")
  1269. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "ptau_217_z",
  1270. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1271. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_ptau217", path = path,
  1272. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_ptau217.csv")
  1273. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "amyloid_index_z",
  1274. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1275. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_amyloid_idx", path = path,
  1276. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_amyloid_idx.csv")
  1277. rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_entorhinal_z",
  1278. time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1279. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_tau_ent", path = path,
  1280. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_tau_ent.csv")
  1281. rr_df <- compute_relative_risks(data = df_plasma_211, biomarker = "Ab42_40_ratio_z",
  1282. time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1283. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_Ab4240_211", path = path,
  1284. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_Ab4240_211.csv")
  1285. rr_df <- compute_relative_risks(data = df_plasma_211, biomarker = "ptau_217_z",
  1286. time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1287. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_ptau217_211", path = path,
  1288. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_ptau217_211.csv")
  1289. rr_df <- compute_relative_risks(data = df_ab_226, biomarker = "amyloid_index_z",
  1290. time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1291. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_amyloid_idx_226", path = path,
  1292. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_amyloid_idx_226.csv")
  1293. rr_df <- compute_relative_risks(data = df_tau_224, biomarker = "tau_entorhinal_z",
  1294. time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
  1295. time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_tau_ent_224", path = path,
  1296. include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_tau_ent_224.csv")
  1297. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1298. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1299. ### 9. Run stepwise Cox regression----
  1300. ### including alpha and gamma1 and amyloid time interactions and hippocampal volume
  1301. step_cox=step(coxph(Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex + education_z + apoe +
  1302. hippocampal_volume_z + tau_meta_roi_meg_alpha_z + tt(tau_meta_roi_meg_alpha_z) + tau_meta_roi_meg_gamma1_z + tt(tau_meta_roi_meg_gamma1_z) +
  1303. Ab42_40_ratio_z + ptau_217_z + amyloid_index_z + tt(amyloid_index_z) + tau_entorhinal_z, data=data_meg_pet_cogn3))
  1304. ### Selected model
  1305. final_cox <- coxph(Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex + hippocampal_volume_z + tau_meta_roi_meg_gamma1_z +
  1306. tt(tau_meta_roi_meg_alpha_z) + amyloid_index_z + tt(amyloid_index_z) + tau_entorhinal_z, data = data_meg_pet_cogn3)
  1307. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1308. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1309. ### Supplementary materials----
  1310. ### 10. AIC-based model comparison----
  1311. ### Set color palette for the diff matrix
  1312. color_scale <- colorRampPalette(c("#71bfff","white", "#fc9c9c"))(100)
  1313. ### Get AIC values
  1314. AIC_1=AIC(results_clin$model); AIC_2=AIC(results_clin_meg_alpha_gamma1$model);
  1315. AIC_3=AIC(results_clin_meg_alpha_nl$model); AIC_4=AIC(results_clin_meg_gamma1_nl$model);
  1316. AIC_5=AIC(results_clin_mri$model); AIC_6=AIC(results_clin_plasma$model);
  1317. AIC_7=AIC(results_clin_ab$model); AIC_8=AIC(results_clin_ab_nl$model);
  1318. AIC_9=AIC(results_clin_tau$model); AIC_10=AIC(results_all$model);
  1319. values_AIC <- c(AIC_1, AIC_2, AIC_3, AIC_4, AIC_5, AIC_6, AIC_7,AIC_8, AIC_9, AIC_10)
  1320. ##### Calculate differences in AIC/BIC values between models
  1321. diff_matrix <- outer(values_AIC, values_AIC, "-"); diag(diff_matrix) <- NA
  1322. models=c("Ref","Ref MEG","Ref MEG A (t)","Ref MEG G (t)","Ref MRI","Ref plasma","Ref A??","Ref A?? (t)","Ref tau","All");
  1323. colnames(diff_matrix)=models; rownames(diff_matrix)=models; print(diff_matrix)
  1324. diff_matrix=diff_matrix*(-1)
  1325. library(corrplot)
  1326. library(svglite)
  1327. plot_aic_matrix <- function(
  1328. model_list,
  1329. model_names,
  1330. output_name = "AIC_matrix",
  1331. path = "."
  1332. ) {
  1333. # Compute AIC values
  1334. values_AIC <- sapply(model_list, AIC)
  1335. # Difference matrix
  1336. diff_matrix <- outer(values_AIC, values_AIC, "-")
  1337. diff_matrix <- diff_matrix * (-1)
  1338. colnames(diff_matrix) <- model_names
  1339. rownames(diff_matrix) <- model_names
  1340. diff_vals <- diff_matrix
  1341. diag(diff_matrix) <- 0
  1342. # Create star matrix (LOWER TRIANGLE ONLY)
  1343. star_matrix <- matrix("", nrow = nrow(diff_vals), ncol = ncol(diff_vals))
  1344. for(i in 1:nrow(diff_vals)) {
  1345. for(j in 1:ncol(diff_vals)) {
  1346. if(i > j) { # ???? only lower triangle
  1347. val <- diff_vals[i, j]
  1348. if(!is.na(val)) {
  1349. if(abs(val) > 10) {
  1350. star_matrix[i, j] <- "**"
  1351. } else if(abs(val) > 2) {
  1352. star_matrix[i, j] <- "*"
  1353. }
  1354. }
  1355. }
  1356. }
  1357. }
  1358. # Color scale
  1359. color_scale <- colorRampPalette(c("#71bfff", "white", "#fc9c9c"))(100)
  1360. # Save SVG (slightly larger ??? better scaling)
  1361. svglite::svglite(
  1362. file = file.path(path, paste0(output_name, ".svg")),
  1363. width = 7 / 2.54,
  1364. height = 7 / 2.54
  1365. )
  1366. # ???? remove ALL margins (outer + inner)
  1367. par(mar = c(0, 0, 0, 0), mai = c(0, 0, 0, 0))
  1368. # Plot matrix
  1369. corrplot(
  1370. corr = diff_matrix,
  1371. method = "color",
  1372. col = color_scale,
  1373. is.corr = FALSE,
  1374. type = "lower",
  1375. diag = FALSE,
  1376. outline = FALSE,
  1377. addCoef.col = NULL,
  1378. tl.cex = 0.6, # ~8 pt labels
  1379. tl.col = "black",
  1380. tl.srt = 45,
  1381. tl.offset = 0.1, # ???? tighter ??? reduces top whitespace
  1382. cl.pos = "n", # no legend
  1383. bg = "white",
  1384. addgrid.col = "black"
  1385. )
  1386. # Add stars (lower triangle only)
  1387. n <- nrow(diff_matrix)
  1388. for(i in 1:n) {
  1389. for(j in 1:n) {
  1390. if(i > j) { # ???? enforce lower triangle
  1391. label <- star_matrix[i, j]
  1392. if(label != "") {
  1393. text(
  1394. x = j,
  1395. y = n - i + 1,
  1396. labels = label,
  1397. cex = 0.6 # ~8 pt
  1398. )
  1399. }
  1400. }
  1401. }
  1402. }
  1403. dev.off()
  1404. return(diff_vals)
  1405. }
  1406. model_list <- list(
  1407. results_clin$model,
  1408. results_clin_meg_alpha_gamma1$model,
  1409. results_clin_meg_alpha_nl$model,
  1410. results_clin_meg_gamma1_nl$model,
  1411. results_clin_mri$model,
  1412. results_clin_plasma$model,
  1413. results_clin_ab$model,
  1414. results_clin_ab_nl$model,
  1415. results_clin_tau$model,
  1416. results_all$model
  1417. )
  1418. model_names <- c(
  1419. "Ref","Ref MEG","Ref MEG A (t)","Ref MEG G (t)",
  1420. "Ref MRI","Ref plasma","Ref A??","Ref A?? (t)",
  1421. "Ref tau","All"
  1422. )
  1423. diff_matrix <- plot_aic_matrix(
  1424. model_list = model_list,
  1425. model_names = model_names,
  1426. output_name = "AIC_difference_models",
  1427. path = path
  1428. )
  1429. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1430. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1431. ### 11. Concordance index----
  1432. ### Set plot colors
  1433. # Define model labels and colors
  1434. models <- c("Ref", "Ref MEG", "Ref MEG A (t)", "Ref MEG G (t)", "Ref MRI", "Ref plasma",
  1435. "Ref A??", "Ref A?? (t)", "Ref tau", "All biomarkers")
  1436. # Create dataframe with custom model labels
  1437. df_c_index <- data.frame(
  1438. Model = factor(models, levels = models), # Set desired order and labels
  1439. C = c(results_clin$c_index[1], results_clin_meg_alpha_gamma1$c_index[1], results_clin_meg_alpha_nl$c_index[1], results_clin_meg_gamma1_nl$c_index[1],
  1440. results_clin_mri$c_index[1], results_clin_plasma$c_index[1], results_clin_ab$c_index[1],
  1441. results_clin_ab_nl$c_index[1], results_clin_tau$c_index[1], results_all$c_index[1]),
  1442. seC = c(results_clin$c_index[2], results_clin_meg_alpha_gamma1$c_index[2], results_clin_meg_alpha_nl$c_index[2], results_clin_meg_gamma1_nl$c_index[2],
  1443. results_clin_mri$c_index[2], results_clin_plasma$c_index[2], results_clin_ab$c_index[2],
  1444. results_clin_ab_nl$c_index[2], results_clin_tau$c_index[2], results_all$c_index[2])
  1445. )
  1446. c_index_colors <- c("gray20", "#F23F43","#F23F43","#F23F43", "gray50", "#FF7F50",
  1447. "#1E98DD","#1E98DD", "#7600BC", "#00BA38")
  1448. plot_c_index <- function(
  1449. df_c_index,
  1450. colors,
  1451. output_name = "c_index_models",
  1452. path = "."
  1453. ) {
  1454. # Plot
  1455. p <- ggplot(df_c_index, aes(x = Model, y = C, fill = Model)) +
  1456. geom_bar(
  1457. stat = "identity",
  1458. color = "black",
  1459. linewidth = 0.3 # ???? thin outline
  1460. ) +
  1461. geom_errorbar(
  1462. aes(ymin = C - seC, ymax = C + seC),
  1463. width = 0.15,
  1464. linewidth = 0.3 # ???? thin error bars
  1465. ) +
  1466. scale_fill_manual(values = colors) +
  1467. labs(
  1468. x = NULL,
  1469. y = "Concordance index"
  1470. ) +
  1471. coord_cartesian(ylim = c(0.5, 0.85)) +
  1472. theme_classic(base_family = "Arial") +
  1473. theme(
  1474. legend.position = "none",
  1475. axis.text.x = element_text(
  1476. angle = 70,
  1477. hjust = 1,
  1478. size = 6
  1479. ),
  1480. axis.text.y = element_text(size = 8),
  1481. axis.title.y = element_text(size = 8),
  1482. axis.title.x = element_blank(),
  1483. axis.line = element_line(linewidth = 0.4),
  1484. axis.ticks = element_line(linewidth = 0.4),
  1485. plot.margin = margin(2, 2, 2, 2, "mm") # ???? tight margins
  1486. )
  1487. # Save SVG
  1488. svglite::svglite(
  1489. file = file.path(path, paste0(output_name, ".svg")),
  1490. width = 4.6 / 2.54, # ???? match your other panels
  1491. height = 4.6 / 2.54
  1492. )
  1493. print(p)
  1494. dev.off()
  1495. return(p)
  1496. }
  1497. models <- c("Ref", "Ref MEG", "Ref MEG A (t)", "Ref MEG G (t)",
  1498. "Ref MRI", "Ref plasma", "Ref A??", "Ref A?? (t)",
  1499. "Ref tau", "All biomarkers")
  1500. df_c_index <- data.frame(
  1501. Model = factor(models, levels = models),
  1502. C = c(
  1503. results_clin$c_index[1],
  1504. results_clin_meg_alpha_gamma1$c_index[1],
  1505. results_clin_meg_alpha_nl$c_index[1],
  1506. results_clin_meg_gamma1_nl$c_index[1],
  1507. results_clin_mri$c_index[1],
  1508. results_clin_plasma$c_index[1],
  1509. results_clin_ab$c_index[1],
  1510. results_clin_ab_nl$c_index[1],
  1511. results_clin_tau$c_index[1],
  1512. results_all$c_index[1]
  1513. ),
  1514. seC = c(
  1515. results_clin$c_index[2],
  1516. results_clin_meg_alpha_gamma1$c_index[2],
  1517. results_clin_meg_alpha_nl$c_index[2],
  1518. results_clin_meg_gamma1_nl$c_index[2],
  1519. results_clin_mri$c_index[2],
  1520. results_clin_plasma$c_index[2],
  1521. results_clin_ab$c_index[2],
  1522. results_clin_ab_nl$c_index[2],
  1523. results_clin_tau$c_index[2],
  1524. results_all$c_index[2]
  1525. )
  1526. )
  1527. plot_c_index(
  1528. df_c_index = df_c_index,
  1529. colors = c_index_colors,
  1530. output_name = "c_index_models",
  1531. path = path
  1532. )
  1533. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1534. ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
  1535. ### 12. Plot correlations coefficients between model features----
  1536. ### Function to compute p-values for correlations
  1537. cor_pvalues <- function(mat) {
  1538. p_mat <- matrix(NA, ncol = ncol(mat), nrow = nrow(mat))
  1539. colnames(p_mat) <- colnames(mat)
  1540. rownames(p_mat) <- rownames(mat)
  1541. for (i in 1:(ncol(mat) - 1)) {
  1542. for (j in (i + 1):ncol(mat)) {
  1543. test <- cor.test(mat[, i], mat[, j], use = "complete.obs")
  1544. p_mat[i, j] <- test$p.value
  1545. p_mat[j, i] <- test$p.value # Symmetric matrix
  1546. }
  1547. }
  1548. diag(p_mat) <- NA # No p-values on the diagonal
  1549. return(p_mat)
  1550. }
  1551. # Compute correlation matrix
  1552. cor_matrix <- cor(data_meg_pet_cogn3[, c("age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1","hippocampal_volume",
  1553. "Ab42_40_ratio","ptau_217","amyloid_index",
  1554. "tau_entorhinal")], use = "complete.obs")
  1555. # Compute p-value matrix
  1556. p_matrix <- cor_pvalues(data_meg_pet_cogn3[, c("age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1","hippocampal_volume",
  1557. "Ab42_40_ratio","ptau_217","amyloid_index",
  1558. "tau_entorhinal")])
  1559. p_matrix=p_matrix[c(seq(1,7)),seq(1,7)]
  1560. # Labels
  1561. labels <- c("Age", "Education", "Alpha power", "Gamma power", "Hipp. vol.",
  1562. "A??42/40 ratio", "p-tau217", "Necortical A??", "Entorhinal tau")
  1563. rownames(cor_matrix) <- labels
  1564. colnames(cor_matrix) <- labels
  1565. rownames(p_matrix) <- labels
  1566. colnames(p_matrix) <- labels
  1567. # Color scale for correlation matrix
  1568. #color_scale_corr <- colorRampPalette(c("#3A97A3", "white", "#ff84e1"))(100)
  1569. color_scale_corr <- colorRampPalette(c("#71bfff","white", "#fc9c9c"))(100)
  1570. # Color scale for p-values (e.g., significant values darker)
  1571. color_scale_pval <- colorRampPalette(c("white", "red"))(100)
  1572. plot_correlation_matrix <- function(
  1573. data,
  1574. variables,
  1575. labels = NULL,
  1576. output_name = "correlation_matrix",
  1577. path = "."
  1578. ) {
  1579. library(corrplot)
  1580. library(svglite)
  1581. # Subset data
  1582. mat <- data[, variables]
  1583. # Compute correlation matrix
  1584. cor_matrix <- cor(mat, use = "complete.obs")
  1585. # Labels
  1586. if(!is.null(labels)) {
  1587. rownames(cor_matrix) <- labels
  1588. colnames(cor_matrix) <- labels
  1589. }
  1590. # Create star matrix (LOWER TRIANGLE ONLY)
  1591. star_matrix <- matrix("", nrow = nrow(cor_matrix), ncol = ncol(cor_matrix))
  1592. for(i in 1:nrow(cor_matrix)) {
  1593. for(j in 1:ncol(cor_matrix)) {
  1594. if(i > j) {
  1595. val <- cor_matrix[i, j]
  1596. if(!is.na(val)) {
  1597. if(abs(val) > 0.5) {
  1598. star_matrix[i, j] <- "**"
  1599. } else if(abs(val) > 0.3) {
  1600. star_matrix[i, j] <- "*"
  1601. }
  1602. }
  1603. }
  1604. }
  1605. }
  1606. # Color scale
  1607. color_scale <- colorRampPalette(c("#71bfff","white","#fc9c9c"))(100)
  1608. # Save SVG
  1609. svglite::svglite(
  1610. file = file.path(path, paste0(output_name, ".svg")),
  1611. width = 7 / 2.54,
  1612. height = 7 / 2.54
  1613. )
  1614. par(mar = c(0, 0, 0, 0), mai = c(0, 0, 0, 0))
  1615. # Plot matrix (no numbers)
  1616. corrplot(
  1617. cor_matrix,
  1618. method = "color",
  1619. col = color_scale,
  1620. is.corr = TRUE,
  1621. type = "lower",
  1622. diag = FALSE,
  1623. outline = FALSE,
  1624. addCoef.col = NULL,
  1625. tl.cex = 0.6, # ~8 pt labels
  1626. tl.col = "black",
  1627. tl.srt = 45,
  1628. tl.offset = 0.1,
  1629. cl.pos = "b", # ???? color legend at bottom
  1630. cl.cex = 0.6, # ~8 pt legend text
  1631. cl.length = 5, # cleaner ticks
  1632. cl.align.text = "c",
  1633. bg = "white",
  1634. addgrid.col = "black"
  1635. )
  1636. # Add stars (lower triangle only)
  1637. n <- nrow(cor_matrix)
  1638. for(i in 1:n) {
  1639. for(j in 1:n) {
  1640. if(i > j) {
  1641. label <- star_matrix[i, j]
  1642. if(label != "") {
  1643. text(
  1644. x = j,
  1645. y = n - i + 1,
  1646. labels = label,
  1647. cex = 0.6 # ~8 pt
  1648. )
  1649. }
  1650. }
  1651. }
  1652. }
  1653. dev.off()
  1654. return(cor_matrix)
  1655. }
  1656. vars <- c(
  1657. "age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1",
  1658. "hippocampal_volume","Ab42_40_ratio","ptau_217",
  1659. "amyloid_index","tau_entorhinal"
  1660. )
  1661. labels <- c(
  1662. "Age", "Education", "Alpha power", "Gamma power",
  1663. "Hipp. vol.", "A??42/40 ratio", "p-tau217",
  1664. "Neocortical A??", "Entorhinal tau"
  1665. )
  1666. plot_correlation_matrix(
  1667. data = data_meg_pet_cogn3,
  1668. variables = vars,
  1669. labels = labels,
  1670. output_name = "corr_features_stars",
  1671. path = path
  1672. )

Cox_regression_analysis.R at commit 179a41c, no license · at the source

Overview

Authors: Jonathan Gallego-Rudolf1,2, Alex I Wiesman2,3, Yara Yakoub1, Henrik Zetterberg4,5,6,7,8,9,10,11, Kaj Blennow12, Sylvain Baillet2,13,14, Sylvia Villeneuve1,2, The PREVENT-AD Research Group
14 affiliations
  1. Douglas Research Centre, McGill University, Montreal, Canada
  2. McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University, Montreal, Canada
  3. Department of Biomedical Physiology and Kinesiology, Simon Fraser University, Burnaby, Canada
  4. Department of Psychiatry and Neurochemistry, Institute of Neuroscience and Physiology, The Sahlgrenska Academy, University of Gothenburg, Gothenburg, Sweden
  5. Department of Neurodegenerative Disease, UCL Queen Square Institute of Neurology, University College London, London, UK
  6. Clinical Neurochemistry Laboratory, Sahlgrenska University Hospital, Mölndal, Sweden
  7. UK Dementia Research Institute at UCL, London, UK
  8. Hong Kong Center for Neurodegenerative Diseases, Hong Kong, Hong Kong
  9. UW Department of Medicine, School of Medicine and Public Health, Madison, WI, USA
  10. UW Department of Pathology and Laboratory of Medicine, School of Medicine and Public Health, Madison, WI, USA
  11. Centre for Brain Research, Indian Institute of Science, Bangalore, India
  12. Institute of Neuroscience and Physiology, University of Gothenburg, Mölndal, Sweden
  13. Centre of Research of University of Montreal Health Centre (CRCHUM), Montreal, Canada
  14. Department of Neuroscience, University of Montreal, Montreal, Canada
Journal: Science advances, volume 12, issue 33, article eaee2305
Dates: received 25 November 2025; accepted 6 July 2026; published online 12 August 2026; in print August 2026
Type: Research article · Language: English
License: CC BY-NC
Identifiers: DOI 10.1126/sciadv.aee2305 · PMID 42585316 · PMCID PMC13464481 · OpenAlex W4414410838
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: structural MRI / diffusion (modality), MEG (modality), PET / SPECT (modality), human (organism), Alzheimer's / dementia (population), clinical / translational (subfield)
Methods: Preprocessing, Spectral & time-frequency, Statistics, Connectivity, Machine learning
MeSH: Biomarkers*, Cognitive Dysfunction*, Aged, Alzheimer Disease, Disease Progression, Female, Humans, Magnetic Resonance Imaging, Magnetoencephalography, Male, Positron-Emission Tomography, tau Proteins (* major topic)
Topic: Dementia and Cognitive Impairment Research (Psychiatry and Mental health, Medicine), according to OpenAlex
Funding: NIBIB NIH HHS (R01 EB026299); NINDS NIH HHS (F32 NS119375); NIA NIH HHS (R01 AG068563)
Citations: not cited yet (Europe PMC); 83 references in the paper

Abstract

Alzheimer’s disease (AD) develops silently for years before symptoms emerge, making early identification of at-risk individuals essential for prevention trials and early intervention. We tested whether combining neurophysiological, imaging, and blood biomarkers improves prediction of progression to mild cognitive impairment (MCI) in cognitively unimpaired older adults with a family history of AD (n = 102; 31 progressors; mean follow-up of 5.9 years). Magnetoencephalography, magnetic resonance imaging, plasma biomarkers, and amyloid and tau positron emission tomography each captured complementary aspects of disease biology. Multimodal models predicted progression more accurately than demographic and genetic factors alone. Higher MEG alpha power was associated with increased near-term risk, whereas higher gamma activity predicted lower near-term risk; both effects weakened over time. Higher neocortical amyloid burden predicted increasing risk over follow-up, whereas plasma biomarkers and entorhinal tau predicted higher risk without significant time-varying effects. These findings support a time-sensitive multimodal framework for identifying cognitively unimpaired individuals at risk of MCI due to AD.

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

Repositories

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

Zenodo 19897907

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data, code, and materials availability:”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: SPM (8 files), NiBabel (3 files), NumPy (2 files), ANTs (1 file), Matplotlib (1 file), pandas (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
37 files

openpreventad.loris.ca

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: “Data, code, and materials availability:”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)

mcgill.ca/bic/neuroinformatics

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: the link answers
Software Heritage: not checked
Found in: “Data, code, and materials availability:”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)

ggsegverse/ggseg

License: other
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: ccb3e8e98faa0fee4ff1edf6fb1bb3be76d25b4d, 11 September 2026
Languages: R (51)
Size: 149 files, 51 scripts
Software Heritage: not archived
Found in: “Data, code, and materials availability:”
Holds: README, license file, environment (DESCRIPTION), tests, continuous integration, documentation, 7 notebooks
Not found: CITATION.cff
Tools: ggplot2 (26 files), tidyverse (14 files), ggseg (9 files)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
54 files

Zenodo 19411014

License: CC-BY-4.0
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Size: 1 file
Software Heritage: not checked
Found in: “Data, code, and materials availability:”
Not found: README, license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: easystats (1 file), ggplot2 (1 file), ggpubr (1 file), nlme (1 file), survival (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
  • 27 September 2026: the link answers (HTTP 200)
2 files

jogaru1818/progression_to_mci

License: none: the authors keep all their rights
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 179a41c360792b31a87d6ff8e121c09454c02133, 3 April 2026
Languages: R (1)
Size: 2 files, 1 script
Software Heritage: not archived
Found in: the Zenodo archive record
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: easystats (1 file), ggplot2 (1 file), ggpubr (1 file), nlme (1 file), survival (1 file), tidyverse (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
2 files

villeneuvelab/vlpp

License: MIT
State: the link answers, verified on 27 September 2026
Evidence: files inventoried
Commit: 88d3cc43742e594237153c9cdd98efc345836287, 11 June 2019
Languages: Python (23), MATLAB (8), Shell (3), JavaScript (1)
Size: 53 files, 35 scripts
Software Heritage: archived
Found in: the Zenodo archive record
Holds: README, license file, environment (requirements.txt, setup.cfg, setup.py)
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: SPM (8 files), NiBabel (3 files), NumPy (2 files), ANTs (1 file), Matplotlib (1 file), pandas (1 file)
Availability: 1 check, the latest on 27 September 2026: the link answers
  • 27 September 2026: the link answers
37 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:

  • 7 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 123 scripts, each with its path and the digest of its content;
  • 11 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, code, and materials availability

Data used in the preparation of this manuscript were obtained from the PREVENT-AD study (https://douglas.research.mcgill.ca/prevent-alzheimer-program/). All data and code needed to evaluate and reproduce the results in the paper are present in the paper and/or the Supplementary Materials and are openly available through the following platforms: https://openpreventad.loris.ca/ and www.mcgill.ca/bic/neuroinformatics/omega. Detailed instructions for accessing the PREVENT-AD dataset are available at www.centre-stopad.com/en/data/. This study did not generate new materials. MRI processing and parcellation were performed using FreeSurfer version 5.3 (https://surfer.nmr.mgh.harvard.edu/fswiki/CorticalParcellation). PET analysis was implemented using an in-house pipeline developed by the Villeneuve lab available in an open-access published Zenodo repository (https://zenodo.org/records/19897907). MEG data analysis was conducted using the openly available Brainstorm3 software (https://neuroimage.usc.edu/brainstorm/Introduction), running on Matlab version R2021b (www.mathworks.com/products/matlab.html). All statistical analyses reported here were performed using R version 4.1.1 (www.r-project.org/). Cox proportional hazard regression models were implemented using the cox.ph function from R’s survival package version 3.5.0 (https://cran.r-project.org/web/packages/survival/index.html). Data visualization was conducted with ggplot2 version 3.4.0 (https://cran.r-project.org/web/packages/ggplot2/index.html) using survminer version 0.5.0 for plotting the survival curves (https://cran.r-project.org/web/packages/survminer/index.html) and ggseg version 1.6.5 for generating the brain plots (https://github.com/ggsegverse/ggseg). Results from the correlation analyses were visualized with the corrplot R package version 0.92 (https://cran.r-project.org/web/packages/corrplot/index.html). The code used to implement the statistical analyses presented here is available in an open-access published Zenodo repository: https://doi.org/10.5281/zenodo.19411014.

Reproduced under the paper's license (CC BY-NC), 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, 27 September 2026: the first record

Recorded: type, language, journal, volume, issue, pages, dates, 8 authors, 12 MeSH terms, 3 funders, 71 references.

Cite

This paper

Gallego-Rudolf, J., Wiesman, A. I., Yakoub, Y., Zetterberg, H., Blennow, K., Baillet, S., Villeneuve, S., & The PREVENT-AD Research Group. (2026). Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers. Science advances, 12(33), eaee2305. https://doi.org/10.1126/sciadv.aee2305

BibTeX

@article{gallegorudolf2026prediction,
author = {Gallego-Rudolf, Jonathan and Wiesman, Alex I and Yakoub, Yara and Zetterberg, Henrik and Blennow, Kaj and Baillet, Sylvain and Villeneuve, Sylvia and {The PREVENT-AD Research Group}},
title = {{Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers}},
journal = {Science advances},
year = {2026},
month = aug,
volume = {12},
number = {33},
pages = {eaee2305},
publisher = {American Association for the Advancement of Science},
issn = {2375-2548},
doi = {10.1126/sciadv.aee2305},
url = {https://doi.org/10.1126/sciadv.aee2305},
pmid = {42585316},
pmcid = {PMC13464481}
}

RIS

TY - JOUR
AU - Gallego-Rudolf, Jonathan
AU - Wiesman, Alex I
AU - Yakoub, Yara
AU - Zetterberg, Henrik
AU - Blennow, Kaj
AU - Baillet, Sylvain
AU - Villeneuve, Sylvia
AU - The PREVENT-AD Research Group
TI - Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers
T2 - Science advances
J2 - Sci Adv
PY - 2026
DA - 2026/08/12
VL - 12
IS - 33
SP - eaee2305
SN - 2375-2548
PB - American Association for the Advancement of Science
DO - 10.1126/sciadv.aee2305
UR - https://doi.org/10.1126/sciadv.aee2305
LA - en
ER -

CSL-JSON

{
"id": "10.1126/sciadv.aee2305",
"type": "article-journal",
"title": "Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers",
"container-title": "Science advances",
"author": [
{
"family": "Gallego-Rudolf",
"given": "Jonathan"
},
{
"family": "Wiesman",
"given": "Alex I"
},
{
"family": "Yakoub",
"given": "Yara"
},
{
"family": "Zetterberg",
"given": "Henrik"
},
{
"family": "Blennow",
"given": "Kaj"
},
{
"family": "Baillet",
"given": "Sylvain"
},
{
"family": "Villeneuve",
"given": "Sylvia"
},
{
"literal": "The PREVENT-AD Research Group"
}
],
"container-title-short": "Sci Adv",
"volume": "12",
"issue": "33",
"page": "eaee2305",
"DOI": "10.1126/sciadv.aee2305",
"PMID": "42585316",
"PMCID": "PMC13464481",
"ISSN": "2375-2548",
"publisher": "American Association for the Advancement of Science",
"URL": "https://doi.org/10.1126/sciadv.aee2305",
"language": "en",
"issued": {
"date-parts": [
[
2026,
8,
12
]
]
}
}

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.1093/brain/awaf413 [code]
Estimating the time course of biomarker changes in Alzheimer's disease.
Journal: Brain : a journal of neurology
In common: nlme, PET / SPECT, Alzheimer's / dementia, structural MRI / diffusion, 1 other category, 5 references, author Henrik Zetterberg
[2] doi:10.1002/hbm.70605 [code]
BrainEnrich: Revealing Biological Insights for Imaging-Derived Phenotypes Through Transcriptomic Enrichment.
Journal: Human brain mapping
In common: ggseg, nlme, easystats, 8 other tools, 2 references
[3] doi:10.1038/s41467-026-71682-8 [code]
GWAS meta-analysis of cerebrospinal fluid Alzheimer's biomarkers reveals loci regulating lipids, brain volume and autophagy.
Journal: Nature communications
In common: ggplot2, tidyverse, pandas, 1 other tool, Alzheimer's / dementia, structural MRI / diffusion, 2 authors
[4] doi:10.1016/j.nicl.2026.104012 [code]
Structural-functional multilayer brain network properties and outcome of combined repetitive transcranial magnetic stimulation and psychotherapy for obsessive-compulsive disorder.
Journal: NeuroImage. Clinical
In common: survival, nlme, SPM, 7 other tools, structural MRI / diffusion, 1 reference
[5] doi:10.1126/sciadv.aea3919 [code]
Hierarchical brain dynamics supporting visual perceptual transitions.
Journal: Science advances
In common: ggplot2, pandas, Matplotlib, 1 other tool, MEG, 4 references, author Sylvain Baillet
[6] doi:10.1093/nc/niag029 [code]
A data-driven approach to identifying and evaluating connectivity-based neural correlates of conscious visual perception.
Journal: Neuroscience of consciousness
In common: ggseg, easystats, SPM, 7 other tools, MEG
[7] doi:10.1002/hipo.70100 [code]
Dissociable Mechanisms Underlie Differences Between Memory and Metamemory in Older Adults: The Differentiating Role of Anxiety and Depression Symptoms.
Journal: Hippocampus
In common: easystats, ggpubr, ggplot2, 1 other tool, PET / SPECT, Alzheimer's / dementia, structural MRI / diffusion, 4 references
[8] doi:10.1038/s41593-026-02363-4 [code]
Cortical thickness changes precede high levels of amyloid by at least 7 years.
Journal: Nature neuroscience
In common: ggseg, easystats, ggpubr, 2 other tools, PET / SPECT, Alzheimer's / dementia, structural MRI / diffusion, 2 references
[9] doi:10.1038/s41467-026-76812-w [code]
Assessing molecular, cellular and transcriptomic bases of laminar perfusion and cytoarchitecture coupling in the human cortex.
Journal: Nature communications
In common: ggseg, ANTs, SPM, 6 other tools, structural MRI / diffusion
[10] doi:10.1038/s42003-026-10957-8 [code]
Brain defence by the extracellular matrix protein Cochlin.
Journal: Communications biology
In common: survival, nlme, SPM, 6 other tools

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.