Prediction of mild cognitive impairment progression using time-sensitive multimodal biomarkers.
The 11 matches
- [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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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] § 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
- ############### Script generated by Jonathan Gallego Rudolf ####################
- ##### Progression to MCI analysis
- ##### 1) Cox regression models
- ### Survival analysis based on Cox regression proportional hazard models
- ### to estimate the risk of progression to MCI and the added value of incorporating
- ### neurophysiological, brain structure, plasma and PET biomarkers to clinical information
- ### The script is used to fit Cox regression model and obtain survival curves
- ### for each model. Likelihood ratio tests are used to assess whether
- ### the model performed significantly better than a null model including no
- ### features, and also compared to the reference clinical model
- ### The following models were defined
- ### Model 1: Clinical (age, sex, education, apoe)
- ### Model 2: Clinical + MEG (initially relative alpha power, but was extended to other frequency bands)
- ### Model 3: Clinical + MRI (hippocampal volume norm to TIV)
- ### Model 4: Clinical + plasma (Ab42/40 ratio, p-tau217)
- ### Model 5: Clinical + Ab PET uptake (neocortical)
- ### Model 6: Clinical + Tau PET uptake (entorhinal)
- ### Model 7: All features
- ### Additional model comparisons are performed by calculating the difference in AIC
- ### and the concordance index of the models
- ### Risk scores estimated for each participants are used to stratify individuals
- ### into low-, medium- and high-risk groups, to visualize whether the survival curves
- ### changes as a function of a given biomarker
- ### Linear models and independent t-test were implemented to assess the contribution
- ### of different biomarkers by estimating their correlation with the risk scores
- ### estimated derived from its corresponding model
- ### Run models including time interactions to assess whether the hazard risk
- ### associated with a given biomarker changes over time. Plot the change in
- ### hazard ratio over time, calculating the CIs while accounting for the clinical covariates
- ### Plot the change in the linear predictor (calculated for each participant) as a function of time, to show
- ### the time interaction effect on the association between a biomarker and the risk of progression
- ### Compare whether adding the neurophysiological activity time-varying effects
- ### improved the performance of the models combining clinical information and plasma/PET biomarkers
- ### Step AIC model feature selection regression analysis was performed on the model
- ### including all biomarkers to select a parsimonious model including a set of features that would
- ### maximize model performance while accounting for model complexity based on AIC values
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 1. Prepare data and environment----
- ##### Generate data_meg_pet_cogn spreadsheet (n=104) using the prepare_data script
- ### Load required packages
- library(scico)
- library(survival)
- library(survminer)
- require(ggplot2)
- require(nlme)
- require(MuMIn)
- library(lubridate)
- library(ggsurvfit)
- library(gtsummary)
- library(tidycmprsk)
- library(condsurv)
- library(dplyr)
- library(ggpubr)
- library(see)
- library(scales)
- library(tidyr)
- ### Set path to save data
- path="C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Figures/MCI_review"
- ### Load Dk atlas labels
- dk_labels=readLines('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/labels_dk.csv')
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 2. Get dates and calculate time diff, scale variables, add lobes (EXTRA)----
- ### Load dates for the scanning visits, plasma measures and MCI diagnosis
- scan_dates=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Spreadsheets/multimodal_scan_dates.csv",header=T)
- ### Change format to dates (needed for computing time diff)
- scan_dates=scan_dates[,c(seq(1,9))]
- scan_dates$MEG_date=as.Date(as.character(scan_dates$MEG_date), format = "%Y%m%d")
- scan_dates$MRI_date=as.Date(as.character(scan_dates$MRI_date), format = "%Y%m%d")
- scan_dates$Ab_date=as.Date(as.character(scan_dates$Ab_date), format = "%Y%m%d")
- scan_dates$Tau_date=as.Date(as.character(scan_dates$Tau_date), format = "%Y%m%d")
- scan_dates$First_cogn=as.Date(as.character(scan_dates$First_cogn), format = "%Y%m%d")
- scan_dates$MEG_cogn=as.Date(as.character(scan_dates$MEG_cogn), format = "%Y%m%d")
- scan_dates$MCI_date=as.Date(as.character(scan_dates$MCI_date_july_24), format = "%Y%m%d")
- scan_dates$Plasma_date=as.Date(as.character(scan_dates$Plasma_date), format = "%Y%m%d")
- scan_dates$ids=data_meg_pet_cogn$pscid
- ### Add dates to spreadsheet
- data_meg_pet_cogn=cbind(data_meg_pet_cogn,scan_dates)
- ### Generate function to compute difference in dates
- compute_date_diff <- function(df, date_col1, date_col2, new_col_name) {
- # Ensure date columns are in POSIXct format
- date1 <- as.POSIXct(df[[date_col1]], tz = "UTC")
- date2 <- as.POSIXct(df[[date_col2]], tz = "UTC")
- # Compute difference in days
- diff_days <- as.numeric(difftime(date1, date2, units = "days"))
- # Add to dataframe
- df[[new_col_name]] <- diff_days
- return(df)
- }
- ### Function to compute days to cutoff date (July 31, 2024)
- fill_missing_diff_days <- function(df, date_col, diff_col, cutoff = "2024-07-31", new_col = "diff_years") {
- # Ensure cutoff is Date
- cutoff_date <- as.Date(cutoff)
- # Loop through rows where diff_col is NA
- for (i in seq_len(nrow(df))) {
- if (is.na(df[[diff_col]][i])) {
- date_val <- df[[date_col]][i]
- if (!is.na(date_val)) {
- diff_days <- as.numeric(difftime(as.POSIXct(cutoff_date), as.POSIXct(date_val, tz = "UTC"), units = "days"))
- df[[diff_col]][i] <- diff_days
- }
- }
- }
- # Convert to years
- df[[new_col]] <- df[[diff_col]] / 365
- return(df)
- }
- ### Calculate difference in dates
- data_meg_pet_cogn=compute_date_diff(data_meg_pet_cogn, "MCI_date", "MEG_date", "diff_days_meg_mci")
- ### Format variables of interest
- ### Ensure MCI status, apoe, and sex are defined as factors as factors
- data_meg_pet_cogn$mci_status=as.factor(data_meg_pet_cogn$mci_status)
- data_meg_pet_cogn$apoe=as.factor(data_meg_pet_cogn$apoe)
- data_meg_pet_cogn$sex=as.factor(data_meg_pet_cogn$sex)
- data_meg_pet_cogn$sex=factor(data_meg_pet_cogn$sex, levels = c("Male", "Female"))
- ### Z-score biomarker measurements
- data_meg_pet_cogn$age_z=scale(data_meg_pet_cogn$age);
- data_meg_pet_cogn$education_z=scale(data_meg_pet_cogn$education)
- data_meg_pet_cogn$hippocampal_volume_z=scale(data_meg_pet_cogn$hippocampal_volume)
- data_meg_pet_cogn$tau_meta_roi_meg_delta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_delta)
- data_meg_pet_cogn$tau_meta_roi_meg_theta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_theta)
- data_meg_pet_cogn$tau_meta_roi_meg_alpha_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_alpha)
- data_meg_pet_cogn$tau_meta_roi_meg_beta_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_beta)
- data_meg_pet_cogn$tau_meta_roi_meg_gamma1_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_gamma1)
- data_meg_pet_cogn$tau_meta_roi_meg_gamma2_z=scale(data_meg_pet_cogn$tau_meta_roi_meg_gamma2)
- data_meg_pet_cogn$Ab42_40_ratio_z=scale(data_meg_pet_cogn$Ab42_40_ratio)
- data_meg_pet_cogn$ptau_217_z=scale(data_meg_pet_cogn$ptau_217)
- data_meg_pet_cogn$amyloid_index_z=scale(data_meg_pet_cogn$amyloid_index)
- data_meg_pet_cogn$tau_entorhinal_z=scale(data_meg_pet_cogn$tau_entorhinal)
- ### Generate new dataset removing the subjects that do not have plasma data
- no_plasma=c("MTL0139","MTL0224")
- data_meg_pet_cogn3=data_meg_pet_cogn %>% filter(!pscid %in% no_plasma)
- ### Compute additional differences in dates
- data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MEG_date", "Plasma_date", "diff_days_meg_plasma")
- data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "Ab_date", "Tau_date", "diff_days_ab_tau")
- data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MEG_date", "MRI_date", "diff_days_meg_mri")
- data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "Ab_date", "MEG_date", "diff_days_ab_meg")
- data_meg_pet_cogn3=compute_date_diff(data_meg_pet_cogn3, "MCI_date", "Plasma_date", "diff_days_plasma_mci")
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 3. Load extended Ab and tau PET and plasma datasets----
- ### Load full PET dataset
- 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="")
- 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="")
- ### Separate BL and FU
- Ab_data_bl_all_subs <- filter(Ab_data_all_subs, Visit_label_PET == "ses-01")
- Ab_data_fu_all_subs <- filter(Ab_data_all_subs, Visit_label_PET == "ses-02")
- Tau_data_bl_all_subs <- filter(Tau_data_all_subs, Visit_label_PET == "ses-01")
- Tau_data_fu_all_subs <- filter(Tau_data_all_subs, Visit_label_PET == "ses-02")
- ### Get subset of individuals with MEG (n=104)
- Ab_data_bl_NN_subs=filter(Ab_data_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Ab_data_fu_NN_subs=filter(Ab_data_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Tau_data_bl_NN_subs=filter(Tau_data_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Tau_data_fu_NN_subs=filter(Tau_data_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- ### Keep subjects with both Ab and tau FU (N=86)
- Ab_data_bl_both=filter(Ab_data_bl_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
- Ab_data_fu_both=filter(Ab_data_fu_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
- Tau_data_bl_both=filter(Tau_data_bl_NN_subs,PSCID%in%Tau_data_fu_NN_subs$PSCID)
- Tau_data_fu_both=Tau_data_fu_NN_subs
- ### Load demographic information
- 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)
- demographics_ab_243=filter(demographics_387,pscid%in%Ab_data_bl_all_subs$PSCID)
- demographics_tau_240=demographics_387 %>% filter(pscid %in% Tau_data_bl_all_subs$PSCID)
- ### Get subset of individuals with BL Ab and Tau PET
- 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
- 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
- ### Load MCI dates
- 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")
- ### Subset MCI list for individuals with Ab PET (n=255)
- MCI_dates_ab_255=data.frame(pscid=Ab_data_bl_all_subs$PSCID)
- MCI_dates_ab_255$mci_status=as.integer(Ab_data_bl_all_subs$PSCID %in% MCI_dates_all$pscid)
- 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")
- ### Subset MCI list for individuals with Ab PET (n=255)
- MCI_dates_tau_252=data.frame(pscid=Tau_data_bl_all_subs$PSCID)
- MCI_dates_tau_252$mci_status=as.integer(Tau_data_bl_all_subs$PSCID %in% MCI_dates_all$pscid)
- 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")
- MCI_dates_ab_243=filter(MCI_dates_ab_255,pscid%in%demographics_ab_243$pscid);
- MCI_dates_tau_240=filter(MCI_dates_tau_252,pscid%in%demographics_tau_240$pscid);
- ### Load PET scans dates
- PET_dates_all_subs=read.csv('C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/PET/Nov2023/PAD_PET_info_Nov2023.csv',header=T, sep=",")
- PET_dates_all_subs$date_scan=as.Date(as.character(PET_dates_all_subs$date_scan), format = "%Y%m%d")
- ### Get dates for BL and FU Ab and tau PET scans
- Ab_dates_bl_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-01" & Tracer_PET == "NAV")
- Ab_dates_fu_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-02" & Tracer_PET == "NAV")
- Tau_dates_bl_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-01" & Tracer_PET == "TAU")
- Tau_dates_fu_all_subs=filter(PET_dates_all_subs, Visit_label_PET == "ses-02" & Tracer_PET == "TAU")
- ### Get subset of individuals with MEG (used for predictive model analysis)
- Ab_dates_bl_NN_subs=filter(Ab_dates_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Ab_dates_fu_NN_subs=filter(Ab_dates_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Tau_dates_bl_NN_subs=filter(Tau_dates_bl_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- Tau_dates_fu_NN_subs=filter(Tau_dates_fu_all_subs,PSCID%in%data_meg_pet_cogn$pscid)
- ### Keep subjects with both Ab and tau FU (N=86; used for predictive model analysis)
- Ab_dates_bl_both=filter(Ab_dates_bl_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
- Ab_dates_fu_both=filter(Ab_dates_fu_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
- Tau_dates_bl_both=filter(Tau_dates_bl_NN_subs,PSCID%in%Tau_dates_fu_NN_subs$PSCID)
- Tau_dates_fu_both=Tau_dates_fu_NN_subs
- Ab_dates_bl_243=filter(Ab_dates_bl_all_subs,PSCID%in%Ab_data_bl_243$PSCID)
- Tau_dates_bl_240=filter(Tau_dates_bl_all_subs,PSCID%in%Tau_data_bl_240$PSCID)
- ### Create df to run Cox regression models on full Ab sample
- df_ab_243=data.frame(cbind(pscid=Ab_data_bl_243$PSCID, age=Ab_dates_bl_243$age_PET, sex=demographics_ab_243$Sex,
- education=demographics_ab_243$Education, apoe=demographics_ab_243$apoe,
- amyloid_index=amyloid_index_243,PET_date=Ab_dates_bl_243$Date_PET,
- mci_status=MCI_dates_ab_243$mci_status,
- mci_date=MCI_dates_ab_243$mci_date))
- df_ab_243$mci_date=as.Date(as.character(df_ab_243$mci_date), format = "%Y%m%d")
- df_ab_243$amyloid_index=as.double(df_ab_243$amyloid_index)
- 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]
- ### Create df to run Cox regression models on full Ab sample
- df_tau_240=data.frame(cbind(pscid=Tau_data_bl_240$PSCID, age=Tau_dates_bl_240$age_PET, sex=demographics_tau_240$Sex,
- education=demographics_tau_240$Education, apoe=demographics_tau_240$apoe,
- tau_meta_roi=tau_meta_roi_240,PET_date=Tau_dates_bl_240$Date_PET,
- mci_status=MCI_dates_tau_240$mci_status,
- mci_date=MCI_dates_tau_240$mci_date))
- df_tau_240$mci_date=as.Date(as.character(df_tau_240$mci_date), format = "%Y%m%d")
- df_tau_240$tau_meta_roi=as.double(df_tau_240$tau_meta_roi)
- 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]
- ### Calculate entorhinal cortex SUVR
- tau_entorhinal=vector()
- for (i in 1:nrow(Tau_data_bl_240)){
- tau_entorhinal_s=mean(Tau_data_bl_240$ctx.lh.entorhinal[i],Tau_data_bl_240$ctx.rh.entorhinal[i])
- tau_entorhinal=rbind(tau_entorhinal,tau_entorhinal_s)
- }
- tau_entorhinal=data.frame(tau_entorhinal=tau_entorhinal)
- df_tau_240$tau_entorhinal=as.double(tau_entorhinal$tau_entorhinal)
- 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]
- ### Calculate difference in dates between Ab PET and MCI diagnosis
- df_ab_243=compute_date_diff(df_ab_243, "mci_date", "PET_date", "diff_days_pet_mci")
- ### Get MCI date for Cox regression models for full Ab sample
- df_ab_243$diff_days_pet_mci_cox=df_ab_243$diff_days_pet_mci
- 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")
- ### Transform age and education to z scores
- 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]
- 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]
- df_ab_243$mci_status=as.double(df_ab_243$mci_status)+1
- 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
- df_ab_243$mci_status_cox=as.double(df_ab_243$mci_status_cox)
- df_ab_243$mci_status_cox=df_ab_243$mci_status_cox-1
- ### Remove subjects that had the PET after being diagnoses as MCI
- df_ab_226=df_ab_243[df_ab_243$diff_years_pet_mci_cox >= 0, ]
- ### Create df with only the additional subjects
- df_ab_124=filter(df_ab_226,!pscid%in%data_meg_pet_cogn3$pscid)
- ### Calculate difference in dates between Tau PET and MCI diagnosis
- df_tau_240=compute_date_diff(df_tau_240, "mci_date", "PET_date", "diff_days_pet_mci")
- ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
- df_tau_240$diff_days_pet_mci_cox=df_tau_240$diff_days_pet_mci
- 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")
- ### Transform age and education to z scores
- 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]
- 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]
- ### Add MCI vars for Cox
- df_tau_240$mci_status=as.double(df_tau_240$mci_status)+1
- 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
- df_tau_240$mci_status_cox=as.double(df_tau_240$mci_status_cox)
- df_tau_240$mci_status_cox=df_tau_240$mci_status_cox-1
- ### Remove subjects that had the PET after being diagnoses as MCI
- df_tau_224=df_tau_240[df_tau_240$diff_years_pet_mci_cox >= 0, ]
- ### Create df with only the additional subjects
- df_tau_122=filter(df_tau_224,!pscid%in%data_meg_pet_cogn3$pscid)
- ### Load full plasma data spreadsheets
- plasma_ab4240=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Plasma/PREVENT-AD_n387_IPMS_totaltau_ptau_4plex.tsv",header=T, sep="");
- plasma_ptau_217=read.csv("C:/Users/jogar/Documents/PREVENT_AD/Multimodal_data/Plasma/PREVENT-AD_n387_ptau217_UGOT.tsv",header=T, sep="");
- plasma_ab4240$Candidate_Age=plasma_ab4240$Candidate_Age/12; plasma_ptau_217$Candidate_Age=plasma_ptau_217$Candidate_Age/12
- plasma_ab4240_216=plasma_ab4240[!is.na(plasma_ab4240$AB_ratio_MS_UGOT), ]
- plasma_ptau_217_216=filter(plasma_ptau_217,PSCID %in% plasma_ab4240_216$PSCID);
- plasma_ptau_217_216=inner_join(plasma_ptau_217_216, plasma_ab4240_216, by = c("PSCID", "Study_visit_label"))
- demographics_plasma_216=filter(demographics_387,pscid%in%plasma_ptau_217_216$PSCID)
- ### Subset MCI list for individuals with Ab PET (n=255)
- MCI_dates_plasma_216=data.frame(pscid=plasma_ptau_217_216$PSCID)
- MCI_dates_plasma_216$mci_status=as.integer(plasma_ptau_217_216$PSCID %in% MCI_dates_all$pscid)
- 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")
- ### Create df to run Cox regression models on full Ab sample
- df_plasma_216=data.frame(cbind(pscid=plasma_ab4240_216$PSCID, age=plasma_ab4240_216$Candidate_Age, sex=demographics_plasma_216$Sex,
- education=demographics_plasma_216$Education, apoe=demographics_plasma_216$apoe,
- plasma_date=plasma_ab4240_216$Date_taken,
- mci_status=MCI_dates_plasma_216$mci_status,
- mci_date=MCI_dates_plasma_216$mci_date,
- Ab42_40_ratio=plasma_ab4240_216$AB_ratio_MS_UGOT,
- ptau_217=plasma_ptau_217_216$UGOT_ptau217))
- df_plasma_216$mci_date=as.Date(as.character(df_plasma_216$mci_date), format = "%Y%m%d")
- ### Calculate difference in dates between plasma and MCI diagnosis
- df_plasma_216=compute_date_diff(df_plasma_216, "mci_date", "plasma_date", "diff_days_plasma_mci")
- ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
- df_plasma_216$diff_days_plasma_mci_cox=df_plasma_216$diff_days_plasma_mci
- 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")
- ### Transform age and education to z scores
- 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]
- 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]
- 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]
- 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]
- df_plasma_216$mci_status=as.double(df_plasma_216$mci_status)+1
- 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
- df_plasma_216$mci_status_cox=as.double(df_plasma_216$mci_status_cox)
- df_plasma_216$mci_status_cox=df_plasma_216$mci_status_cox-1
- ### Remove subjects that had the PET after being diagnoses as MCI and missing ptau217 from the same visit as the Ab4240 ratio
- df_plasma_211=df_plasma_216[df_plasma_216$diff_years_plasma_mci_cox >= 0, ];
- df_plasma_211=df_plasma_211[!is.na(df_plasma_211$ptau_217), ]
- ### Create df with only the additional subjects
- df_plasma_109=filter(df_plasma_211,!pscid%in%data_meg_pet_cogn3$pscid)
- df_plasma_211_mci <- df_plasma_211[df_plasma_211$mci_status == 2, ]
- df_ab_226_mci <- df_ab_226[df_ab_226$mci_status == 2, ]
- df_tau_224_mci <- df_tau_224[df_tau_224$mci_status == 2, ]
- mean(df_plasma_211_mci$diff_years_plasma_mci_cox); sd(df_plasma_211_mci$diff_years_plasma_mci_cox)
- min(df_plasma_211_mci$diff_years_plasma_mci_cox); max(df_plasma_211_mci$diff_years_plasma_mci_cox);
- mean(df_ab_226_mci$diff_years_pet_mci_cox); sd(df_ab_226_mci$diff_years_pet_mci_cox)
- min(df_ab_226_mci$diff_years_pet_mci_cox); max(df_ab_226_mci$diff_years_pet_mci_cox);
- mean(df_tau_224_mci$diff_years_pet_mci_cox); sd(df_tau_224_mci$diff_years_pet_mci_cox)
- min(df_tau_224_mci$diff_years_pet_mci_cox); max(df_tau_224_mci$diff_years_pet_mci_cox);
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 4. Plot MCI proportion and compare progressors vs non-progressors----
- ### Numbers updated up to July 2024
- mci_chart <- data.frame(mci_status = c("CU", "MCI"), num_sub = c(71, 31), perc_sub = c(69.6, 30.4))
- ### Create a basic bar chart
- pie_chart <- ggplot(mci_chart, aes(x = "", y = num_sub, fill = mci_status)) +
- geom_bar(stat = "identity") + coord_polar(theta = "y") +
- scale_fill_manual(values = c("#ACD8A7","#E0B0FF")) + theme_void()
- ### Print the pie chart
- print(pie_chart)
- ### Plot variability in the time bw biomarker collection and MCI diagnosis
- ggplot(data_meg_pet_cogn3, aes(x = mci_status, y = diff_days_meg_mci, fill = mci_status)) +
- geom_boxplot() + geom_jitter(width = 0.2, color = "black", alpha = 0.7) + # Adds jittered points
- scale_fill_manual(values = c("white", "#E0B0FF")) + # Example colors
- labs(title = "Boxplot with Modern Theme", x = "Group", y = "Value") +
- theme_minimal(base_size = 15) + theme_modern()
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 5. Group comparison between MCI progressors and non-progressors----
- ### Calculate group means and sd for MCI progressors and non-progressors
- aggregate(data_meg_pet_cogn3$tau_entorhinal, list(data_meg_pet_cogn3$mci_status), FUN=mean)
- aggregate(data_meg_pet_cogn3$tau_entorhinal, list(data_meg_pet_cogn3$mci_status), FUN=sd)
- ### Run Wilcox test
- wilcox_age=wilcox.test(data_meg_pet_cogn3$age~data_meg_pet_cogn3$mci_status)
- wilcox_education=wilcox.test(data_meg_pet_cogn3$education~data_meg_pet_cogn3$mci_status)
- wilcox_tau_meta_roi_meg_delta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_delta~data_meg_pet_cogn3$mci_status)
- wilcox_tau_meta_roi_meg_theta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_theta~data_meg_pet_cogn3$mci_status)
- wilcox_tau_meta_roi_meg_alpha=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_alpha~data_meg_pet_cogn3$mci_status)
- wilcox_tau_meta_roi_meg_beta=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_beta~data_meg_pet_cogn3$mci_status)
- wilcox_tau_meta_roi_meg_gamma1=wilcox.test(data_meg_pet_cogn3$tau_meta_roi_meg_gamma1~data_meg_pet_cogn3$mci_status)
- wilcox_hipp=wilcox.test(data_meg_pet_cogn3$hippocampal_volume~data_meg_pet_cogn3$mci_status)
- wilcox_ab4240=wilcox.test(data_meg_pet_cogn3$Ab42_40_ratio~data_meg_pet_cogn3$mci_status)
- wilcox_ptau217=wilcox.test(data_meg_pet_cogn3$ptau_217~data_meg_pet_cogn3$mci_status)
- wilcox_ab=wilcox.test(data_meg_pet_cogn3$amyloid_index~data_meg_pet_cogn3$mci_status)
- wilcox_tau=wilcox.test(data_meg_pet_cogn3$tau_entorhinal~data_meg_pet_cogn3$mci_status)
- ### Check proportion of subjects for categorical variables (sex and APOE) and run chi squared
- tt=table(data_meg_pet_cogn3$mci_status,data_meg_pet_cogn3$sex); tt; chi_sex=chisq.test(tt)
- tt=table(data_meg_pet_cogn3$mci_status,data_meg_pet_cogn3$apoe); tt; chi_apoe=chisq.test(tt)
- wil_chi_stats=c(wilcox_age$statistic, chi_sex$statistic, wilcox_education$statistic, chi_apoe$statistic,
- wilcox_tau_meta_roi_meg_delta$statistic, wilcox_tau_meta_roi_meg_theta$statistic, wilcox_tau_meta_roi_meg_alpha$statistic,
- wilcox_tau_meta_roi_meg_beta$statistic, wilcox_tau_meta_roi_meg_gamma1$statistic,
- wilcox_hipp$statistic, wilcox_ab4240$statistic, wilcox_ptau217$statistic, wilcox_ab$statistic, wilcox_tau$statistic)
- p_vals_wilcox=c(wilcox_age$p.value, chi_sex$p.value, wilcox_education$p.value, chi_apoe$p.value,
- wilcox_tau_meta_roi_meg_delta$p.value, wilcox_tau_meta_roi_meg_theta$p.value, wilcox_tau_meta_roi_meg_alpha$p.value,
- wilcox_tau_meta_roi_meg_beta$p.value, wilcox_tau_meta_roi_meg_gamma1$p.value,
- wilcox_hipp$p.value, wilcox_ab4240$p.value, wilcox_ptau217$p.value, wilcox_ab$p.value, wilcox_tau$p.value)
- p_vals_wilcox_fdr=p.adjust(p_vals_wilcox, method = "fdr")
- sprintf("%.6f", p_vals_wilcox_fdr)
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 6. MCI Cox regression survival analysis (no time interactions)----
- ### 6.1 Prepare data----
- ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
- data_meg_pet_cogn3$diff_days_meg_mci_cox=data_meg_pet_cogn3$diff_days_meg_mci
- 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")
- ### Define MCI list as 0s and 1s to run cox models
- 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
- data_meg_pet_cogn3$mci_status_cox=as.double(data_meg_pet_cogn3$mci_status_cox)
- data_meg_pet_cogn3$mci_status_cox=data_meg_pet_cogn3$mci_status_cox-1
- ### Compute days to cutoff date (July 31, 2024) for non-progressors to run Cox regression models
- data_meg_pet_cogn3$diff_days_plasma_mci_cox=data_meg_pet_cogn3$diff_days_plasma_mci
- 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")
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 6.2 Run Cox regression models----
- ### Generate function that computes Cox regression models, get summary,
- ### compute c-index, check PH assumption, fit survival curves and plot them
- ### Only works for models that do not include a time interaction
- run_cox_model <- function(formula, data, model_name) {
- ### Fit Cox model
- formula <- as.formula(formula)
- model <- coxph(formula, data = data); model_summary <- summary(model)
- ### Check proportional hazard assumption
- if (model_name != "null_model") {
- pha=cox.zph(model)}
- ### Compute C-index
- c_index <- model_summary$concordance
- ### Fit survival curves
- surv_fit <- survfit(model)
- ### Log-rank test & p-value
- lr_test <- model_summary$logtest[1]; p_val <- model_summary$logtest[3]
- ### Plot survival curve
- plot(surv_fit, xlab = "Time (years)", ylab = "Survival Probability", ylim = c(0, 1))
- ### Save model summary to a text file
- summary_filename <- paste0("Cox_model_", model_name, "_summary.txt")
- #capture.output(model_summary, file = summary_filename)
- ### Return results for null model (no PH assumption)
- if (model_name == "null_model") {
- return(list(model = model, summary = model_summary, c_index = c_index,
- log_rank_test = lr_test, p_value = p_val))}
- if (model_name != "null_model") {
- return(list(model = model, summary = model_summary, ph_assumption=pha, c_index = c_index,
- log_rank_test = lr_test, p_value = p_val))}
- }
- ### Run Cox regressions for each model (main models)
- ### Clinical
- 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")
- ### MRI
- 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")
- ### MEG
- 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")
- 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")
- 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")
- 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")
- 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")
- 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")
- 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")
- ### Plasma
- 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")
- ### PET
- 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")
- 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")
- ### All biomarkers
- 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")
- ### Results using full plasma (n=211) Ab (n=226) and Tau PET (n=224) samples
- 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")
- 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")
- 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")
- 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")
- 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")
- 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")
- ### Get LRT statistic and pvalues from the main models
- ### Extract LRT statistic values (vs null model)
- 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,
- results_clin_ab$log_rank_test, results_clin_tau$log_rank_test, results_all$log_rank_test)
- ### Extract p-values and correct for multiple comparisons
- 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,
- results_clin_ab$p_value, results_clin_tau$p_value, results_all$p_value)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- sprintf("%.6f", p_vals_fdr_lrt)
- ### Get LRT statistic and pvalues from the extended models
- ### Extract LRT statistic values (vs null model)
- stats_lrt=c(results_clin_plasma_211$log_rank_test, results_clin_ab_226$log_rank_test, results_clin_tau_224$log_rank_test)
- ### Extract p-values and correct for multiple comparisons
- p_vals_lrt=c(results_clin_plasma_211$p_value, results_clin_ab_226$p_value, results_clin_tau_224$p_value)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- sprintf("%.6f", p_vals_fdr_lrt)
- ### Generate function to compare two models using LRT
- compare_models_lrt <- function(model1, model2) {
- log_like_1 <- logLik(model1)
- log_like_2 <- logLik(model2)
- lr_test <- -2 * (as.numeric(log_like_1) - as.numeric(log_like_2))
- df <- attr(log_like_2, "df") - attr(log_like_1, "df")
- p_value <- pchisq(lr_test, df = df, lower.tail = FALSE)
- return(list(
- lr_test_statistic = lr_test,
- df = df,
- p_value = p_value
- ))
- }
- ### Compare each model against the reference clinical model
- clin_vs_clin_meg_alpha_gamma1 <- compare_models_lrt(results_clin$model, results_clin_meg_alpha_gamma1$model)
- clin_vs_clin_mri <- compare_models_lrt(results_clin$model, results_clin_mri$model)
- clin_vs_clin_plasma <- compare_models_lrt(results_clin$model, results_clin_plasma$model)
- clin_vs_clin_ab <- compare_models_lrt(results_clin$model, results_clin_ab$model)
- clin_vs_clin_tau <- compare_models_lrt(results_clin$model, results_clin_tau$model)
- clin_vs_all <- compare_models_lrt(results_clin$model, results_all$model)
- #
- # ### Extract LRT statistic values (vs clinical model)
- 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,
- clin_vs_clin_ab$lr_test_statistic, clin_vs_clin_tau$lr_test_statistic, clin_vs_all$lr_test_statistic)
- ### Extract p-values and correct for multiple comparisons
- p_vals_lrt=c(clin_vs_clin_meg_alpha_gamma1$p_value, clin_vs_clin_mri$p_value, clin_vs_clin_plasma$p_value,
- clin_vs_clin_ab$p_value, clin_vs_clin_tau$p_value, clin_vs_all$p_value)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- sprintf("%.6f", p_vals_fdr_lrt)
- ### Comparisons of proteinopathy models using the extnded sample
- clin_vs_clin_plasma_211 <- compare_models_lrt(results_clin_211$model, results_clin_plasma_211$model)
- clin_vs_clin_ab_226 <- compare_models_lrt(results_clin_226$model, results_clin_ab_226$model)
- clin_vs_clin_tau_224 <- compare_models_lrt(results_clin_224$model, results_clin_tau_224$model)
- ### Extract LRT statistic values (vs clinical model)
- 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)
- ### Extract p-values and correct for multiple comparisons
- 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)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- sprintf("%.6f", p_vals_fdr_lrt)
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 6.3 Generate Kaplan-Meier survival curves----
- ### SVG figures
- for (i in seq(1,10)) {
- ### Models
- model_options = list(results_clin$model, results_clin_mri$model, results_clin_meg_alpha_gamma1$model,
- results_clin_plasma$model, results_clin_ab$model, results_clin_tau$model,
- results_all$model, results_clin_plasma_211$model,
- results_clin_ab_226$model, results_clin_tau_224$model)
- model_colors = c("gray5", "gray50","#F23F43","#FF7F50", "#1E98DD","#7600BC", "#00BA38",
- "#FF7F50","#1E98DD","#7600BC")
- model_names = c("clin","clin_mri","clin_meg","clin_plasma","clin_ab","clin_tau",
- "all","clin_plasma_211","clin_ab_226","clin_tau_224")
- ### Select model
- model = model_options[[i]]
- model_color = model_colors[i]
- model_name = model_names[i]
- ### Axis settings
- if (i %in% seq(1, 7)) {
- df_plot = data_meg_pet_cogn3
- x_limits = c(0, 7.5)
- y_limits = c(0.6, 1)
- y_breaks = seq(0.6, 1, by = 0.1)
- }
- if (i %in% c(8)) {
- df_plot = df_plasma_211
- x_limits = c(0, 11.2)
- y_limits = c(0.45, 1)
- y_breaks = seq(0.5, 1, by = 0.1)
- }
- if (i %in% c(9)) {
- df_plot = df_ab_226
- x_limits = c(0, 7.5)
- y_limits = c(0.45, 1)
- y_breaks = seq(0.5, 1, by = 0.1)
- }
- if (i %in% c(10)) {
- df_plot = df_tau_224
- x_limits = c(0, 7.5)
- y_limits = c(0.45, 1)
- y_breaks = seq(0.5, 1, by = 0.1)
- }
- ### Generate base plot
- p = ggsurvplot(
- fit = survfit(model),
- data = data_meg_pet_cogn3,
- xlab = "Time (years)",
- ylab = "Survival probability",
- xlim = x_limits,
- ylim = y_limits,
- palette = model_color,
- size = 0.3, # ???? thinner main survival curve
- conf.int = TRUE,
- conf.int.fill = model_color,
- conf.int.alpha = 0.15,
- conf.int.style = "ribbon",
- surv.median.line = "none",
- risk.table = FALSE,
- legend = "none",
- censor.size = 1, # ???? smaller + symbols
- ggtheme = theme_classic(base_family = "Arial") +
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm")
- )
- )
- ### Refine plot (CI boundary lines slightly thinner)
- g = p$plot +
- geom_step(aes(y = upper), color = model_color, linewidth = 0.2, alpha = 1) +
- geom_step(aes(y = lower), color = model_color, linewidth = 0.2, alpha = 1) +
- scale_x_continuous(
- limits = x_limits,
- breaks = seq(0, ceiling(max(x_limits)), by = 1)
- ) +
- scale_y_continuous(
- limits = y_limits,
- breaks = y_breaks
- )
- ### Save as SVG (Illustrator-ready, editable text)
- svg_filename <- file.path(path, paste0("cox_surv_curve_", model_name, "_panel.svg"))
- svglite::svglite(
- file = svg_filename,
- width = 4.6 / 2.54, # cm ??? inches
- height = 4.6 / 2.54
- )
- print(g)
- dev.off()
- ### Display in R
- print(g)
- }
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 6.4 Plot survival curves stratified by risk group----
- ##### Manually change features
- library(survival)
- library(survminer)
- library(svglite)
- library(grid)
- ### Compute risk groups
- risk_scores <- predict(results_all$model, type = "lp")
- risk_group <- cut(
- risk_scores,
- breaks = quantile(risk_scores, probs = c(0, 1/3, 2/3, 1)),
- labels = c("Low", "Medium", "High"),
- include.lowest = TRUE
- )
- data_meg_pet_cogn3$risk_group <- risk_group
- ### Fit survival model
- fit <- survfit(
- Surv(diff_years_meg_mci_cox, mci_status_cox) ~ risk_group,
- data = data_meg_pet_cogn3
- )
- ### Generate survival plot + risk table
- p <- ggsurvplot(
- fit,
- data = data_meg_pet_cogn3,
- xlab = "Time (years)",
- ylab = "Survival probability",
- legend.labs = c("L", "M", "H"),
- palette = c("#49beaa", "#ffbe0b", "#f27a7d"),
- size = 0.4,
- censor.size = 1,
- conf.int = TRUE,
- conf.int.alpha = 0.15,
- conf.int.style = "ribbon",
- risk.table = TRUE,
- risk.table.col = "strata",
- break.time.by = 1,
- xlim = c(0, 7.5),
- tables.theme = theme_classic(base_family = "Arial"),
- ggtheme = theme_classic(base_family = "Arial"),
- legend = "none"
- )
- ### Style MAIN plot
- p$plot <- p$plot +
- scale_x_continuous(limits = c(0, 7.5), breaks = seq(0, 7, by = 1)) +
- scale_y_continuous(limits = c(0, 1), breaks = seq(0, 1, by = 0.2)) +
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm")
- )
- ### Style RISK TABLE (clean)
- # Set font sizes for numbers inside table
- p$table <- p$table +
- theme_classic(base_family = "Arial") +
- theme(
- axis.title = element_blank(),
- axis.text.x = element_text(size = 8),
- axis.text.y = element_text(size = 8), # row labels (Low, Medium, High)
- axis.ticks = element_line(linewidth = 0.4),
- axis.line = element_line(linewidth = 0.4),
- plot.margin = margin(1, 2, 2, 2, "mm"),
- )
- # Remove "Number at risk" title
- g <- ggplotGrob(p$table)
- title_index <- which(g$layout$name == "title")
- if(length(title_index) > 0) g$grobs[[title_index]] <- nullGrob()
- p$table <- g # replace table with cleaned grob
- ### SAVE FILES (SVG ??? Illustrator)
- # Survival panel
- svglite::svglite(
- file = file.path(path, "survival_curve_all.svg"),
- width = 4.6 / 2.54,
- height = 4.6 / 2.54
- )
- print(p$plot)
- dev.off()
- # Risk table panel
- svglite::svglite(
- file = file.path(path, "risk_table_all.svg"),
- width = 5.9 / 2.54,
- height = 4.6 / 2.54
- )
- grid.draw(p$table) # ???? Use grid.draw for grob objects
- dev.off()
- ### Optional: print in R
- print(p$plot)
- grid.draw(p$table)
- # ### Log-rank test
- # survdiff(
- # Surv(diff_years_meg_mci_cox, mci_status_cox) ~ risk_group,
- # data = data_meg_pet_cogn3
- # )
- create_time_table <- function(
- model,
- data,
- time_var,
- event_var,
- time_points = seq(0, 7, by = 1),
- output_name = "time_table",
- path = "."
- ) {
- # Ensure proper format
- data <- as.data.frame(data)
- ### Risk groups
- risk_scores <- predict(model, type = "lp")
- risk_group <- cut(
- risk_scores,
- breaks = quantile(risk_scores, probs = c(0, 1/3, 2/3, 1), na.rm = TRUE),
- labels = c("Low", "Medium", "High"),
- include.lowest = TRUE
- )
- data$risk_group <- factor(risk_group, levels = c("Low", "Medium", "High"))
- ### Survival fit
- surv_formula <- as.formula(
- paste0("Surv(", time_var, ", ", event_var, ") ~ risk_group")
- )
- fit <- survfit(surv_formula, data = data)
- ### Fixed time table
- fit_summary_fixed <- summary(fit, times = time_points)
- time_table_fixed <- data.frame(
- strata = fit_summary_fixed$strata,
- time = fit_summary_fixed$time,
- n_risk = fit_summary_fixed$n.risk,
- n_event = fit_summary_fixed$n.event,
- n_censor = fit_summary_fixed$n.censor
- )
- ### Cumulative values
- time_table_cum <- time_table_fixed %>%
- group_by(strata) %>%
- arrange(time) %>%
- mutate(
- cum_events = cumsum(n_event),
- cum_censor = cumsum(n_censor)
- ) %>%
- ungroup() %>%
- arrange(strata, time)
- ### Save CSV
- write.csv(
- time_table_cum,
- file = file.path(path, paste0(output_name, ".csv")),
- row.names = FALSE
- )
- ### Return table
- return(time_table_cum)
- }
- time_table_clin <- create_time_table(model = results_clin$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin", path = path)
- time_table_clin_meg <- create_time_table(model = results_clin_meg_alpha_gamma1$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_meg", path = path)
- time_table_clin_mri <- create_time_table(model = results_clin_mri$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_mri", path = path)
- time_table_clin_plasma <- create_time_table(model = results_clin_plasma$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_plasma", path = path)
- time_table_clin_ab <- create_time_table(model = results_clin_ab$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_ab", path = path)
- time_table_clin_tau <- create_time_table(model = results_clin_tau$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_tau", path = path)
- time_table_all <- create_time_table(model = results_all$model, data = data_meg_pet_cogn3,
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_all", path = path)
- time_table_clin_plasma_211 <- create_time_table(model = results_clin_plasma_211$model, data = df_plasma_211,
- time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 11, by = 1),
- output_name = "time_table_clin_plasma_211", path = path)
- time_table_clin_ab_226 <- create_time_table(model = results_clin_ab_226$model, data = df_ab_226,
- time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_ab_226", path = path)
- time_table_clin_tau_224 <- create_time_table(model = results_clin_tau_224$model, data = df_tau_224,
- time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", time_points = seq(0, 7, by = 1),
- output_name = "time_table_clin_tau_224", path = path)
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- #### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### Assess correlation between risk scores and biomarkers----
- ### Change labels for APOE
- data_meg_pet_cogn3$apoe <- factor(data_meg_pet_cogn3$apoe, levels = c(0, 1), labels = c("Non-carrier", "Carrier"))
- model_options=list(results_clin$model, results_clin_meg_alpha_gamma1$model, results_clin_mri$model, results_clin_plasma$model,
- results_clin_ab$model, results_clin_tau$model, results_all$model,
- results_clin_plasma_211$model, results_clin_ab_226$model, results_clin_tau_224$model)
- compare_risk_simple <- function(variable_name, variable_type, model_idx,
- model_options, data, y_label, custom_colors, path,integer_y=FALSE) {
- # Compute risk scores
- risk_scores <- predict(model_options[[model_idx]], type="lp")
- data$risk_scores <- risk_scores
- # Continuous variables
- if(variable_type == "continuous") {
- formula <- as.formula(paste("risk_scores ~", variable_name))
- lm_res <- summary(lm(formula, data=data))
- t_val <- lm_res$coefficients[variable_name, "t value"]
- p_val <- lm_res$coefficients[variable_name, "Pr(>|t|)"]
- # Scatterplot with regression line
- p_scatter <- ggplot(data, aes(x=.data[[variable_name]], y=risk_scores)) +
- geom_point(color=custom_colors[1], alpha=0.5, size=1.5, stroke=0.1) +
- geom_smooth(method="lm", se=TRUE, color=custom_colors[1], fill=custom_colors[1], alpha=0.15, linewidth=0.3) +
- labs(x=y_label, y="Relative risk score") +
- theme_classic(base_family="Arial") +
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm")
- )
- if(integer_y) {
- p_scatter <- p_scatter +
- scale_y_continuous(
- breaks = seq(
- floor(min(data[[variable_name]], na.rm = TRUE)),
- ceiling(max(data[[variable_name]], na.rm = TRUE)),
- by = 2
- )
- )
- }
- svglite::svglite(file=file.path(path, paste0("scatter_risk_score_", variable_name, ".svg")),
- width=4.6/2.54, height=4.6/2.54)
- print(p_scatter); dev.off()
- return(list(lm_summary=lm_res, t_val=t_val, p_val=p_val))
- }
- # Categorical variables (sex, apoe)
- if(variable_type == "categorical") {
- formula <- as.formula(paste("risk_scores ~", variable_name))
- t_res <- t.test(formula, data=data)
- # Box + violin plot
- p_box <- ggplot(data, aes(x=.data[[variable_name]], y=risk_scores, fill=.data[[variable_name]])) +
- geom_violin(trim=FALSE, linewidth=0.3) +
- geom_boxplot(width=0.3, outlier.shape=NA, linewidth=0.3) +
- geom_jitter(width=0.15, color="black", alpha=0.25, size=1.5, stroke = 0.1) +
- scale_fill_manual(values=custom_colors) +
- labs(x=y_label, y="Relative risk score") +
- theme_classic(base_family="Arial") +
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm"),
- legend.position="none")
- svglite::svglite(file=file.path(path, paste0("box_risk_score_", variable_name, ".svg")),
- width=4.6/2.54, height=4.6/2.54)
- print(p_box); dev.off()
- return(list(t_test=t_res))
- }
- }
- ### Define parameters for running the models
- variables = c("age_z", "sex","education_z", "apoe", "tau_meta_roi_meg_alpha_z","tau_meta_roi_meg_gamma1_z", "hippocampal_volume_z",
- "Ab42_40_ratio_z","ptau_217_z","amyloid_index_z","tau_entorhinal_z")
- y_labels=c("Age (z-score)", "Sex", "Education (z-score)", "APOE ??4 carrier status",
- "Alpha power (z-score)", "Gamma power (z-score)", "Hipp. volume (z-score)",
- "A??42/40 ratio (z-score)", "p-tau217 (z-score)", "Neocortical A?? (z-score)", "Entorhinal tau (z-score)")
- model=c("clin","clin_meg","clin_mri","clin_plasma","clin_ab","clin_tau","all","clin_plasma_211","clin_ab_226","clin_tau_224")
- custom_colors_con=c("#49beaa","#ffbe0b","#f27a7d")
- custom_colors_cat=c("#c8f0e7","#ffecb3","#fcdfe0","#49beaa","#ffbe0b","#f27a7d")
- model_colors=c("gray5", "#F23F43", "gray50", "#FF7F50", "#1E98DD","#7600BC", "#00BA38", "#FF7F50", "#1E98DD","#7600BC")
- My_Theme = theme(plot.title = element_text(hjust = 0.5, size = 16, family = "TT Arial"), axis.text.x = element_text(family = "TT Arial"),
- axis.text.y = element_text(family = "TT Arial"), axis.title.x = element_text(family = "TT Arial"),
- axis.title.y = element_text(family = "TT Arial"), legend.text=element_text(family = "TT Arial"),
- legend.title =element_text(family = "TT Arial"))
- # For sex (categorical)
- res_sex <- compare_risk_simple(variable_name = "sex", variable_type = "categorical", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Sex",
- custom_colors = c("gray80","gray80"), path = path)
- # For sex (categorical)
- res_apoe <- compare_risk_simple(variable_name = "apoe", variable_type = "categorical", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "APOE e4 status",
- custom_colors = c("gray80","gray80"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "age_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3$apoe, y_label = "Age (z-score)",
- custom_colors = c("gray5"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "education_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Education (z-score)",
- custom_colors = c("gray5"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "tau_meta_roi_meg_alpha_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Alpha power (z-score)",
- custom_colors = c("#F23F43"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "tau_meta_roi_meg_gamma1_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Gamma power (z-score)",
- custom_colors = c("#F23F43"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "hippocampal_volume_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Hipp. volume (z-score)",
- custom_colors = c("gray50"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "Ab42_40_ratio_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "A??42/40 ratio (z-score)",
- custom_colors = c("#FF7F50"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "ptau_217_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "p-tau217 (z-score)",
- custom_colors = c("#FF7F50"), path = path, integer_y = TRUE)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "amyloid_index_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Neocortical A?? (z-score)",
- custom_colors = c("#1E98DD"), path = path)
- # For age_z (continuous)
- res_age <- compare_risk_simple(variable_name = "tau_entorhinal_z", variable_type = "continuous", model_idx = 7,
- model_options = model_options, data = data_meg_pet_cogn3, y_label = "Entorhinal tau (z-score)",
- custom_colors = c("#7600BC"), path = path)
- ### Extract t/t statistic values (risk scores)
- 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,
- 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,
- 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,
- results_risk_group_ab_all$t_val_risk_score,results_risk_group_tau_all$t_val_risk_score)
- ### Correct p-values for multiple comparisons
- 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,
- 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,
- 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,
- results_risk_group_ab_all$p_value_risk_score,results_risk_group_tau_all$p_value_risk_score)
- p_vals_fdr_t_all=p.adjust(p_vals_t_all, method = "fdr")
- sprintf("%.6f", p_vals_fdr_t_all)
- ### 8. MCI Cox regression survival analysis with time interactions----
- ### 8.1 Run Cox regressions including time interactions----
- ### Generate function to run a Cox model including an interaction term
- run_cox_model_time_int <- function(formula, data, model_name) {
- formula <- as.formula(formula)
- model <- coxph(formula, data = data, tt = function(x, time, ...) x * time)
- model_summary <- summary(model)
- c_index <- model_summary$concordance
- lr_test <- model_summary$logtest[1]
- p_val <- model_summary$logtest[3]
- summary_filename <- paste0("Cox_model_", model_name, "_summary.txt")
- #capture.output(model_summary, file = summary_filename)
- return(list(model = model, summary = model_summary, c_index = c_index, log_rank_test = lr_test, p_value = p_val))
- }
- ### Run Cox regressions including time interactions for each biomarker
- ### Age
- results_clin_age_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + tt(age_z)", data = data_meg_pet_cogn3, model_name = "age_tt")
- ### Education
- results_clin_edu_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + tt(education_z)", data = data_meg_pet_cogn3, model_name = "edu_tt")
- ### MEG delta
- results_clin_meg_delta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG theta
- results_clin_meg_theta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG alpha
- results_clin_meg_alpha_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG beta
- results_clin_meg_beta_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG gamma1
- results_clin_meg_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG gamma2
- results_clin_meg_gamma2_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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")
- ### MEG alpha and gamma1
- results_clin_meg_alpha_gamma1_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- 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)",
- data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
- ### MRI hipp vol
- results_clin_mri_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + hippocampal_volume_z + tt(hippocampal_volume_z)", data = data_meg_pet_cogn3, model_name = "hipp_tt")
- ### Plasma Ab42/40 ratio
- results_clin_Ab4240_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + Ab42_40_ratio_z + tt(Ab42_40_ratio_z)", data = data_meg_pet_cogn3, model_name = "Ab42_40_tt")
- ### Plasma p-tau217
- results_clin_ptau_217_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + ptau_217_z + tt(ptau_217_z)", data = data_meg_pet_cogn3, model_name = "ptau_217_tt")
- ### Ab PET
- results_clin_ab_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + amyloid_index_z + tt(amyloid_index_z)", data = data_meg_pet_cogn3, model_name = "amyloid_index_tt")
- ### Tau PET
- results_clin_tau_nl=run_cox_model_time_int(formula = "Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + tau_entorhinal_z + tt(tau_entorhinal_z)", data = data_meg_pet_cogn3, model_name = "tau_entorhinal_tt")
- ### Run Cox regression models with time interaction for the extended samples
- ### Plasma Ab42/40 ratio (n=211)
- results_clin_Ab4240_211_nl=run_cox_model_time_int(formula = "Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + Ab42_40_ratio_z + tt(Ab42_40_ratio_z)", data = df_plasma_211, model_name = "ab4240_211_tt")
- ### Plasma p-tau217 (n=211)
- results_clin_ptau_217_211_nl=run_cox_model_time_int(formula = "Surv(diff_years_plasma_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + ptau_217_z + tt(ptau_217_z)", data = df_plasma_211, model_name = "ptau_217_211_tt")
- ### Ab PET (n=226)
- results_clin_ab_226_nl=run_cox_model_time_int(formula = "Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + amyloid_index_z + tt(amyloid_index_z)", data = df_ab_226, model_name = "ab_226_tt")
- ### Tau PET (n=224)
- results_clin_tau_224_nl=run_cox_model_time_int(formula = "Surv(diff_years_pet_mci_cox, mci_status_cox) ~ age_z + sex +
- education_z + apoe + tau_entorhinal_z + tt(tau_entorhinal_z)", data = df_tau_224, model_name = "tau_224_tt")
- ### Combine MEG nl and proteinopathy
- ### MEG nl and plasma
- 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 +
- 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)",
- data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
- ### MEG nl and Ab PET
- 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 +
- 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)",
- data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
- ### MEG nl and tau PET
- 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 +
- 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)",
- data = data_meg_pet_cogn3, model_name = "alpha_gamma1_tt")
- ### Comparisons of proteinopathy models including the gamma1 time interaction
- clin_plasma_vs_clin_plasma_meg_alpha_gamma1 <- compare_models_lrt(results_clin_plasma$model, results_clin_plasma_meg_alpha_gamma1_nl$model)
- clin_ab_vs_clin_ab_meg_alpha_gamma1 <- compare_models_lrt(results_clin_ab_nl$model, results_clin_ab_meg_alpha_gamma1_nl$model)
- clin_tau_vs_clin_tau_meg_alpha_gamma1 <- compare_models_lrt(results_clin_tau$model, results_clin_tau_meg_alpha_gamma1_nl$model)
- clin_plasma_vs_clin_plasma_meg_alpha_gamma1=anova(results_clin_plasma$model, results_clin_plasma_meg_alpha_gamma1_nl$model, test = "LRT")
- ### Extract p-values and correct for multiple comparisons
- 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)
- 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)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- sprintf("%.6f", p_vals_fdr_lrt)
- ### Get LRT statistic and pvalues from the models including a time interaction
- ### Extract LRT statistic values (vs null model)
- 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,
- 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)
- ### Extract p-values and correct for multiple comparisons
- 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,
- 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)
- p_vals_fdr_lrt=p.adjust(p_vals_lrt, method = "fdr")
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 8.2 Plot interaction effect: Hazard ratio change over time----
- ### Generate function to plot change in Hazard ratio over time from the interaction coefficients
- library(ggplot2)
- library(svglite)
- plot_time_varying_hr_with_ci <- function(cox_model, var_name, time_max = 7, n_points = 100,
- color = "#ffbe0b", path = ".", ylim_fixed = "no") {
- # Extract coefficients
- coef_main <- coef(cox_model)[[var_name]]
- coef_tt <- coef(cox_model)[[paste0("tt(", var_name, ")")]]
- # Variance-covariance
- vcov_matrix <- vcov(cox_model)
- var_main <- vcov_matrix[var_name, var_name]
- var_tt <- vcov_matrix[paste0("tt(", var_name, ")"), paste0("tt(", var_name, ")")]
- cov_main_tt <- vcov_matrix[var_name, paste0("tt(", var_name, ")")]
- # Time sequence
- time_seq <- seq(0, time_max, length.out = n_points)
- # Linear predictor and variance
- lp_time <- coef_main + coef_tt * time_seq
- var_lp_time <- var_main + (time_seq^2) * var_tt + 2 * time_seq * cov_main_tt
- # Confidence intervals
- se_lp_time <- sqrt(var_lp_time)
- lower_lp <- lp_time - 1.96 * se_lp_time
- upper_lp <- lp_time + 1.96 * se_lp_time
- # Dataframe
- df_hr <- data.frame(
- Time = time_seq,
- HR = exp(lp_time),
- HR_lower = exp(lower_lp),
- HR_upper = exp(upper_lp)
- )
- # HR=1 crossing
- time_hr1 <- if (coef_tt != 0) -coef_main / coef_tt else NA
- y_max <- if (ylim_fixed == "yes") 2 else max(df_hr$HR_upper, na.rm = TRUE)
- y_seg_end <- y_max / 3
- # Create ggplot
- p <- ggplot(df_hr, aes(x = Time, y = HR)) +
- geom_line(color = color, linewidth = 0.6) +
- geom_ribbon(aes(ymin = HR_lower, ymax = HR_upper), fill = color, alpha = 0.2) +
- geom_hline(yintercept = 1, linetype = "dashed", color = "gray50", linewidth = 0.4) +
- labs(x = "Years from biomarker", y = "Hazard ratio") +
- theme_classic(base_family = "Arial") + # Use system font
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm")
- )
- # Y-axis breaks
- if (ylim_fixed == "yes") {
- p <- p + scale_y_continuous(limits = c(0, 2), breaks = 0:2)
- } else {
- p <- p + scale_y_continuous(breaks = seq(0, ceiling(max(df_hr$HR_upper)), by = 1))
- }
- # HR=1 annotation
- if (!is.na(time_hr1) && time_hr1 >= 0 && time_hr1 <= time_max) {
- p <- p +
- annotate("segment",
- x = time_hr1, xend = time_hr1,
- y = 1, yend = 1 - (y_seg_end * 0.4),
- linetype = "dashed", color = "gray50", linewidth = 0.4) +
- annotate("text",
- x = time_hr1,
- y = 1 - (y_seg_end * 0.4),
- label = "HR=1",
- size = 2.2, # ??? 8 pt
- vjust = 1.2)
- }
- # Save as SVG (editable text in Illustrator)
- svg_filename <- paste0(path, "/", var_name, "_panel.svg")
- svglite::svglite(file = svg_filename, width = 4.6/2.54, height = 4.6/2.54) # cm ??? inches
- print(p)
- dev.off()
- return(p)
- }
- ## Generate plots for each biomarker
- plot_time_varying_hr_with_ci(results_clin_age_nl$model, "age_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_edu_nl$model, "education_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_delta_nl$model, "tau_meta_roi_meg_delta_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_theta_nl$model, "tau_meta_roi_meg_theta_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_alpha_nl$model, "tau_meta_roi_meg_alpha_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_beta_nl$model, "tau_meta_roi_meg_beta_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_gamma1_nl$model, "tau_meta_roi_meg_gamma1_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_meg_gamma2_nl$model, "tau_meta_roi_meg_gamma2_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_mri_nl$model, "hippocampal_volume_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_Ab4240_nl$model, "Ab42_40_ratio_z", 7, 100, "#ffbe0b", path,"yes")
- plot_time_varying_hr_with_ci(results_clin_ptau_217_nl$model, "ptau_217_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_ab_nl$model, "amyloid_index_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_tau_nl$model, "tau_entorhinal_z", 7, 100, "#ffbe0b", path, "no")
- ### Generate plots for the extended samples for plasma and PET
- plot_time_varying_hr_with_ci(results_clin_Ab4240_211_nl$model, "Ab42_40_ratio_z", 11, 100, "#ffbe0b", path,"no")
- plot_time_varying_hr_with_ci(results_clin_ptau_217_211_nl$model, "ptau_217_z", 11, 100, "#ffbe0b", path,"no")
- plot_time_varying_hr_with_ci(results_clin_ab_226_nl$model, "amyloid_index_z", 7, 100, "#ffbe0b", path, "no")
- plot_time_varying_hr_with_ci(results_clin_tau_224_nl$model, "tau_entorhinal_z", 7, 100, "#ffbe0b", path, "no")
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 8.3 Compute relative risk and plot linear predictor as a function of time----
- ### Function: Compute relative risks + Illustrator-friendly plotting
- compute_relative_risks <- function(data, biomarker, time_var, event_var, covariates = NULL,
- time_points = 1:7, id_var = "pscid", plot = TRUE, model_name = NULL,
- path = ".", include_ci = TRUE, overlay_baseline = FALSE,
- save_csv = FALSE, csv_filename = NULL, scale = "log") {
- # Check scale
- if (!scale %in% c("log", "exp")) stop("scale must be 'log' or 'exp'")
- # Build Cox formula
- all_covariates <- paste(c(covariates, biomarker, paste0("tt(", biomarker, ")")), collapse = " + ")
- cox_formula <- as.formula(paste0("Surv(", time_var, ", ", event_var, ") ~ ", all_covariates))
- # Fit Cox model with time interaction
- cox_model <- coxph(cox_formula, data = data, tt = function(x, time, ...) x * time)
- # Compute linear predictors
- linear_predictor <- predict(cox_model, type = "lp", newdata = data)
- coefs <- coef(cox_model)
- vcov_mat <- vcov(cox_model)
- biom_coef <- coefs[biomarker]
- interaction_name <- paste0("tt(", biomarker, ")")
- interaction_coef <- coefs[interaction_name]
- se_biom <- sqrt(vcov_mat[biomarker, biomarker])
- se_inter <- sqrt(vcov_mat[interaction_name, interaction_name])
- cov_biom_inter <- vcov_mat[biomarker, interaction_name]
- lp_no_main <- linear_predictor - data[[biomarker]] * biom_coef
- # Compute relative risks for each time point
- relative_risks <- lapply(time_points, function(t) {
- biom <- data[[biomarker]]
- lp <- lp_no_main + biom * (biom_coef + interaction_coef * t)
- if (include_ci) {
- se_lp <- sqrt((biom^2) * (se_biom^2 + t^2 * se_inter^2 + 2 * t * cov_biom_inter))
- lower <- lp - 1.96 * se_lp
- upper <- lp + 1.96 * se_lp
- if (scale == "exp") {
- return(data.frame(lp = exp(lp), lower = exp(lower), upper = exp(upper)))
- } else {
- return(data.frame(lp = lp, lower = lower, upper = upper))
- }
- } else {
- return(data.frame(lp = if (scale == "exp") exp(lp) else lp))
- }
- })
- # Combine results
- rr_df <- do.call(rbind, lapply(seq_along(time_points), function(i) {
- df <- relative_risks[[i]]
- df[[id_var]] <- data[[id_var]]
- df$biomarker_value <- data[[biomarker]]
- df$time <- time_points[i]
- return(df)
- }))
- rr_df <- rr_df[, c(id_var, "time", "biomarker_value", "lp", if (include_ci) c("lower", "upper") else NULL)]
- names(rr_df)[names(rr_df) == "lp"] <- "relative_risk"
- # Save CSV if requested
- if (save_csv && !is.null(csv_filename)) {
- write.csv(rr_df, file = file.path(path, csv_filename), row.names = FALSE)
- }
- # Plotting (Illustrator-friendly)
- if (plot && !is.null(model_name)) {
- # Colors
- parula_colors <- c("#352A87", "#3B52A1", "#3F7FBA", "#469BBA", "#58B89E", "#84CA79", "#D9D93A")
- if (length(time_points) != length(parula_colors)) stop("Number of time points must match parula_colors length")
- names(parula_colors) <- as.character(time_points)
- # Labels
- xlab <- switch(biomarker,
- "age_z" = "Age (z-score)",
- "education_z" = "Education (z-score)",
- "tau_meta_roi_meg_delta_z" = "Delta power (z-score)",
- "tau_meta_roi_meg_theta_z" = "Theta power (z-score)",
- "tau_meta_roi_meg_alpha_z" = "Alpha power (z-score)",
- "tau_meta_roi_meg_beta_z" = "Beta power (z-score)",
- "tau_meta_roi_meg_gamma1_z" = "Gamma power (z-score)",
- "tau_meta_roi_meg_gamma2_z" = "Gamma2 power (z-score)",
- "hippocampal_volume_z" = "Hipp. volume (z-score)",
- "Ab42_40_ratio_z" = "A??42/40 ratio (z-score)",
- "ptau_217_z" = "p-tau217 (z-score)",
- "amyloid_index_z" = "Neocortical A?? (z-score)",
- "tau_entorhinal_z" = "Entorhinal tau (z-score)",
- biomarker)
- ylab <- if (scale == "exp") "Relative Risk (HR)" else "Relative risk score"
- # Base plot
- p <- ggplot(rr_df, aes(x = biomarker_value, y = relative_risk, color = factor(time))) +
- geom_point(size = 0.6, alpha = 0.7) +
- geom_smooth(method = "lm", aes(group = time), se = FALSE, linewidth = 0.6) +
- scale_color_manual(values = parula_colors, name = "Time") +
- guides(color = guide_legend(nrow = 1))
- # CI ribbons
- if (include_ci) {
- p <- p + geom_ribbon(aes(ymin = lower, ymax = upper, fill = factor(time)), alpha = 0.15, color = NA) +
- scale_fill_manual(values = parula_colors, guide = "none")
- }
- # Adaptive integer y-axis
- p <- p + scale_y_continuous(
- breaks = function(x) {
- bks <- pretty(x)
- bks_int <- unique(round(bks))
- bks_int[bks_int >= min(x) & bks_int <= max(x)]
- },
- labels = scales::label_number(accuracy = 1)
- )
- # Color scale
- p <- p + scale_color_manual(values = parula_colors, name = "Time")
- # Theme
- p <- p +
- labs(x = xlab, y = ylab) +
- theme_classic(base_family = "Arial") +
- theme(
- axis.title = element_text(size = 8),
- axis.text = element_text(size = 8),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- legend.position = "none",
- legend.title = element_text(size = 8),
- legend.text = element_text(size = 7),
- legend.key.width = unit(0.4, "cm"),
- legend.spacing.x = unit(0.05, "cm"),
- legend.margin = margin(0, 0, 0, 0),
- legend.box.margin = margin(0, 0, 0, 0),
- plot.margin = margin(2, 2, 2, 2, "mm")
- )
- # Save as SVG for Illustrator
- svg_filename <- file.path(path, paste0(model_name, "_panel.svg"))
- svglite::svglite(file = svg_filename, width = 4.6/2.54, height = 4.6/2.54) # cm ??? inches
- print(p)
- dev.off()
- # Also show plot in R
- print(p)
- }
- return(rr_df)
- }
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "age_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_age", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_age.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "education_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_education", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_education.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_delta_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_delta", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_delta.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_theta_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_theta", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_theta.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_alpha_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_alpha", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_alpha.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_beta_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_beta", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_beta.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_gamma1_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_gamma1", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_gamma1.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_meta_roi_meg_gamma2_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_meg_gamma2", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_meg_gamma2.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "hippocampal_volume_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_hipp_vol", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_hipp_vol.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "Ab42_40_ratio_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_Ab4240", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_Ab4240.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "ptau_217_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_ptau217", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_ptau217.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "amyloid_index_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_amyloid_idx", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_amyloid_idx.csv")
- rr_df <- compute_relative_risks(data = data_meg_pet_cogn3, biomarker = "tau_entorhinal_z",
- time_var = "diff_years_meg_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_tau_ent", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_tau_ent.csv")
- rr_df <- compute_relative_risks(data = df_plasma_211, biomarker = "Ab42_40_ratio_z",
- time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_Ab4240_211", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_Ab4240_211.csv")
- rr_df <- compute_relative_risks(data = df_plasma_211, biomarker = "ptau_217_z",
- time_var = "diff_years_plasma_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_ptau217_211", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_ptau217_211.csv")
- rr_df <- compute_relative_risks(data = df_ab_226, biomarker = "amyloid_index_z",
- time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_amyloid_idx_226", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_amyloid_idx_226.csv")
- rr_df <- compute_relative_risks(data = df_tau_224, biomarker = "tau_entorhinal_z",
- time_var = "diff_years_pet_mci_cox", event_var = "mci_status_cox", covariates = c("age", "sex", "education_z", "apoe"),
- time_points = 1:7, id_var = "pscid", model_name = "linear_predictor_tau_ent_224", path = path,
- include_ci = FALSE, scale = "log", save_csv = FALSE, csv_filename = "relative_risks_tau_ent_224.csv")
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 9. Run stepwise Cox regression----
- ### including alpha and gamma1 and amyloid time interactions and hippocampal volume
- step_cox=step(coxph(Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex + education_z + apoe +
- 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) +
- Ab42_40_ratio_z + ptau_217_z + amyloid_index_z + tt(amyloid_index_z) + tau_entorhinal_z, data=data_meg_pet_cogn3))
- ### Selected model
- final_cox <- coxph(Surv(diff_years_meg_mci_cox, mci_status_cox) ~ age_z + sex + hippocampal_volume_z + tau_meta_roi_meg_gamma1_z +
- tt(tau_meta_roi_meg_alpha_z) + amyloid_index_z + tt(amyloid_index_z) + tau_entorhinal_z, data = data_meg_pet_cogn3)
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### Supplementary materials----
- ### 10. AIC-based model comparison----
- ### Set color palette for the diff matrix
- color_scale <- colorRampPalette(c("#71bfff","white", "#fc9c9c"))(100)
- ### Get AIC values
- AIC_1=AIC(results_clin$model); AIC_2=AIC(results_clin_meg_alpha_gamma1$model);
- AIC_3=AIC(results_clin_meg_alpha_nl$model); AIC_4=AIC(results_clin_meg_gamma1_nl$model);
- AIC_5=AIC(results_clin_mri$model); AIC_6=AIC(results_clin_plasma$model);
- AIC_7=AIC(results_clin_ab$model); AIC_8=AIC(results_clin_ab_nl$model);
- AIC_9=AIC(results_clin_tau$model); AIC_10=AIC(results_all$model);
- values_AIC <- c(AIC_1, AIC_2, AIC_3, AIC_4, AIC_5, AIC_6, AIC_7,AIC_8, AIC_9, AIC_10)
- ##### Calculate differences in AIC/BIC values between models
- diff_matrix <- outer(values_AIC, values_AIC, "-"); diag(diff_matrix) <- NA
- 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");
- colnames(diff_matrix)=models; rownames(diff_matrix)=models; print(diff_matrix)
- diff_matrix=diff_matrix*(-1)
- library(corrplot)
- library(svglite)
- plot_aic_matrix <- function(
- model_list,
- model_names,
- output_name = "AIC_matrix",
- path = "."
- ) {
- # Compute AIC values
- values_AIC <- sapply(model_list, AIC)
- # Difference matrix
- diff_matrix <- outer(values_AIC, values_AIC, "-")
- diff_matrix <- diff_matrix * (-1)
- colnames(diff_matrix) <- model_names
- rownames(diff_matrix) <- model_names
- diff_vals <- diff_matrix
- diag(diff_matrix) <- 0
- # Create star matrix (LOWER TRIANGLE ONLY)
- star_matrix <- matrix("", nrow = nrow(diff_vals), ncol = ncol(diff_vals))
- for(i in 1:nrow(diff_vals)) {
- for(j in 1:ncol(diff_vals)) {
- if(i > j) { # ???? only lower triangle
- val <- diff_vals[i, j]
- if(!is.na(val)) {
- if(abs(val) > 10) {
- star_matrix[i, j] <- "**"
- } else if(abs(val) > 2) {
- star_matrix[i, j] <- "*"
- }
- }
- }
- }
- }
- # Color scale
- color_scale <- colorRampPalette(c("#71bfff", "white", "#fc9c9c"))(100)
- # Save SVG (slightly larger ??? better scaling)
- svglite::svglite(
- file = file.path(path, paste0(output_name, ".svg")),
- width = 7 / 2.54,
- height = 7 / 2.54
- )
- # ???? remove ALL margins (outer + inner)
- par(mar = c(0, 0, 0, 0), mai = c(0, 0, 0, 0))
- # Plot matrix
- corrplot(
- corr = diff_matrix,
- method = "color",
- col = color_scale,
- is.corr = FALSE,
- type = "lower",
- diag = FALSE,
- outline = FALSE,
- addCoef.col = NULL,
- tl.cex = 0.6, # ~8 pt labels
- tl.col = "black",
- tl.srt = 45,
- tl.offset = 0.1, # ???? tighter ??? reduces top whitespace
- cl.pos = "n", # no legend
- bg = "white",
- addgrid.col = "black"
- )
- # Add stars (lower triangle only)
- n <- nrow(diff_matrix)
- for(i in 1:n) {
- for(j in 1:n) {
- if(i > j) { # ???? enforce lower triangle
- label <- star_matrix[i, j]
- if(label != "") {
- text(
- x = j,
- y = n - i + 1,
- labels = label,
- cex = 0.6 # ~8 pt
- )
- }
- }
- }
- }
- dev.off()
- return(diff_vals)
- }
- model_list <- list(
- results_clin$model,
- results_clin_meg_alpha_gamma1$model,
- results_clin_meg_alpha_nl$model,
- results_clin_meg_gamma1_nl$model,
- results_clin_mri$model,
- results_clin_plasma$model,
- results_clin_ab$model,
- results_clin_ab_nl$model,
- results_clin_tau$model,
- results_all$model
- )
- model_names <- c(
- "Ref","Ref MEG","Ref MEG A (t)","Ref MEG G (t)",
- "Ref MRI","Ref plasma","Ref A??","Ref A?? (t)",
- "Ref tau","All"
- )
- diff_matrix <- plot_aic_matrix(
- model_list = model_list,
- model_names = model_names,
- output_name = "AIC_difference_models",
- path = path
- )
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 11. Concordance index----
- ### Set plot colors
- # Define model labels and colors
- 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 biomarkers")
- # Create dataframe with custom model labels
- df_c_index <- data.frame(
- Model = factor(models, levels = models), # Set desired order and labels
- 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],
- results_clin_mri$c_index[1], results_clin_plasma$c_index[1], results_clin_ab$c_index[1],
- results_clin_ab_nl$c_index[1], results_clin_tau$c_index[1], results_all$c_index[1]),
- 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],
- results_clin_mri$c_index[2], results_clin_plasma$c_index[2], results_clin_ab$c_index[2],
- results_clin_ab_nl$c_index[2], results_clin_tau$c_index[2], results_all$c_index[2])
- )
- c_index_colors <- c("gray20", "#F23F43","#F23F43","#F23F43", "gray50", "#FF7F50",
- "#1E98DD","#1E98DD", "#7600BC", "#00BA38")
- plot_c_index <- function(
- df_c_index,
- colors,
- output_name = "c_index_models",
- path = "."
- ) {
- # Plot
- p <- ggplot(df_c_index, aes(x = Model, y = C, fill = Model)) +
- geom_bar(
- stat = "identity",
- color = "black",
- linewidth = 0.3 # ???? thin outline
- ) +
- geom_errorbar(
- aes(ymin = C - seC, ymax = C + seC),
- width = 0.15,
- linewidth = 0.3 # ???? thin error bars
- ) +
- scale_fill_manual(values = colors) +
- labs(
- x = NULL,
- y = "Concordance index"
- ) +
- coord_cartesian(ylim = c(0.5, 0.85)) +
- theme_classic(base_family = "Arial") +
- theme(
- legend.position = "none",
- axis.text.x = element_text(
- angle = 70,
- hjust = 1,
- size = 6
- ),
- axis.text.y = element_text(size = 8),
- axis.title.y = element_text(size = 8),
- axis.title.x = element_blank(),
- axis.line = element_line(linewidth = 0.4),
- axis.ticks = element_line(linewidth = 0.4),
- plot.margin = margin(2, 2, 2, 2, "mm") # ???? tight margins
- )
- # Save SVG
- svglite::svglite(
- file = file.path(path, paste0(output_name, ".svg")),
- width = 4.6 / 2.54, # ???? match your other panels
- height = 4.6 / 2.54
- )
- print(p)
- dev.off()
- return(p)
- }
- 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 biomarkers")
- df_c_index <- data.frame(
- Model = factor(models, levels = models),
- 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],
- results_clin_mri$c_index[1],
- results_clin_plasma$c_index[1],
- results_clin_ab$c_index[1],
- results_clin_ab_nl$c_index[1],
- results_clin_tau$c_index[1],
- results_all$c_index[1]
- ),
- 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],
- results_clin_mri$c_index[2],
- results_clin_plasma$c_index[2],
- results_clin_ab$c_index[2],
- results_clin_ab_nl$c_index[2],
- results_clin_tau$c_index[2],
- results_all$c_index[2]
- )
- )
- plot_c_index(
- df_c_index = df_c_index,
- colors = c_index_colors,
- output_name = "c_index_models",
- path = path
- )
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ###
- ### 12. Plot correlations coefficients between model features----
- ### Function to compute p-values for correlations
- cor_pvalues <- function(mat) {
- p_mat <- matrix(NA, ncol = ncol(mat), nrow = nrow(mat))
- colnames(p_mat) <- colnames(mat)
- rownames(p_mat) <- rownames(mat)
- for (i in 1:(ncol(mat) - 1)) {
- for (j in (i + 1):ncol(mat)) {
- test <- cor.test(mat[, i], mat[, j], use = "complete.obs")
- p_mat[i, j] <- test$p.value
- p_mat[j, i] <- test$p.value # Symmetric matrix
- }
- }
- diag(p_mat) <- NA # No p-values on the diagonal
- return(p_mat)
- }
- # Compute correlation matrix
- cor_matrix <- cor(data_meg_pet_cogn3[, c("age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1","hippocampal_volume",
- "Ab42_40_ratio","ptau_217","amyloid_index",
- "tau_entorhinal")], use = "complete.obs")
- # Compute p-value matrix
- p_matrix <- cor_pvalues(data_meg_pet_cogn3[, c("age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1","hippocampal_volume",
- "Ab42_40_ratio","ptau_217","amyloid_index",
- "tau_entorhinal")])
- p_matrix=p_matrix[c(seq(1,7)),seq(1,7)]
- # Labels
- labels <- c("Age", "Education", "Alpha power", "Gamma power", "Hipp. vol.",
- "A??42/40 ratio", "p-tau217", "Necortical A??", "Entorhinal tau")
- rownames(cor_matrix) <- labels
- colnames(cor_matrix) <- labels
- rownames(p_matrix) <- labels
- colnames(p_matrix) <- labels
- # Color scale for correlation matrix
- #color_scale_corr <- colorRampPalette(c("#3A97A3", "white", "#ff84e1"))(100)
- color_scale_corr <- colorRampPalette(c("#71bfff","white", "#fc9c9c"))(100)
- # Color scale for p-values (e.g., significant values darker)
- color_scale_pval <- colorRampPalette(c("white", "red"))(100)
- plot_correlation_matrix <- function(
- data,
- variables,
- labels = NULL,
- output_name = "correlation_matrix",
- path = "."
- ) {
- library(corrplot)
- library(svglite)
- # Subset data
- mat <- data[, variables]
- # Compute correlation matrix
- cor_matrix <- cor(mat, use = "complete.obs")
- # Labels
- if(!is.null(labels)) {
- rownames(cor_matrix) <- labels
- colnames(cor_matrix) <- labels
- }
- # Create star matrix (LOWER TRIANGLE ONLY)
- star_matrix <- matrix("", nrow = nrow(cor_matrix), ncol = ncol(cor_matrix))
- for(i in 1:nrow(cor_matrix)) {
- for(j in 1:ncol(cor_matrix)) {
- if(i > j) {
- val <- cor_matrix[i, j]
- if(!is.na(val)) {
- if(abs(val) > 0.5) {
- star_matrix[i, j] <- "**"
- } else if(abs(val) > 0.3) {
- star_matrix[i, j] <- "*"
- }
- }
- }
- }
- }
- # Color scale
- color_scale <- colorRampPalette(c("#71bfff","white","#fc9c9c"))(100)
- # Save SVG
- svglite::svglite(
- file = file.path(path, paste0(output_name, ".svg")),
- width = 7 / 2.54,
- height = 7 / 2.54
- )
- par(mar = c(0, 0, 0, 0), mai = c(0, 0, 0, 0))
- # Plot matrix (no numbers)
- corrplot(
- cor_matrix,
- method = "color",
- col = color_scale,
- is.corr = TRUE,
- type = "lower",
- diag = FALSE,
- outline = FALSE,
- addCoef.col = NULL,
- tl.cex = 0.6, # ~8 pt labels
- tl.col = "black",
- tl.srt = 45,
- tl.offset = 0.1,
- cl.pos = "b", # ???? color legend at bottom
- cl.cex = 0.6, # ~8 pt legend text
- cl.length = 5, # cleaner ticks
- cl.align.text = "c",
- bg = "white",
- addgrid.col = "black"
- )
- # Add stars (lower triangle only)
- n <- nrow(cor_matrix)
- for(i in 1:n) {
- for(j in 1:n) {
- if(i > j) {
- label <- star_matrix[i, j]
- if(label != "") {
- text(
- x = j,
- y = n - i + 1,
- labels = label,
- cex = 0.6 # ~8 pt
- )
- }
- }
- }
- }
- dev.off()
- return(cor_matrix)
- }
- vars <- c(
- "age","education","tau_meta_roi_meg_alpha","tau_meta_roi_meg_gamma1",
- "hippocampal_volume","Ab42_40_ratio","ptau_217",
- "amyloid_index","tau_entorhinal"
- )
- labels <- c(
- "Age", "Education", "Alpha power", "Gamma power",
- "Hipp. vol.", "A??42/40 ratio", "p-tau217",
- "Neocortical A??", "Entorhinal tau"
- )
- plot_correlation_matrix(
- data = data_meg_pet_cogn3,
- variables = vars,
- labels = labels,
- output_name = "corr_features_stars",
- path = path
- )
Cox_regression_analysis.R at commit 179a41c, no license · at the source
Overview
14 affiliations
- Douglas Research Centre, McGill University, Montreal, Canada
- McConnell Brain Imaging Centre, Montreal Neurological Institute, McGill University, Montreal, Canada
- Department of Biomedical Physiology and Kinesiology, Simon Fraser University, Burnaby, Canada
- Department of Psychiatry and Neurochemistry, Institute of Neuroscience and Physiology, The Sahlgrenska Academy, University of Gothenburg, Gothenburg, Sweden
- Department of Neurodegenerative Disease, UCL Queen Square Institute of Neurology, University College London, London, UK
- Clinical Neurochemistry Laboratory, Sahlgrenska University Hospital, Mölndal, Sweden
- UK Dementia Research Institute at UCL, London, UK
- Hong Kong Center for Neurodegenerative Diseases, Hong Kong, Hong Kong
- UW Department of Medicine, School of Medicine and Public Health, Madison, WI, USA
- UW Department of Pathology and Laboratory of Medicine, School of Medicine and Public Health, Madison, WI, USA
- Centre for Brain Research, Indian Institute of Science, Bangalore, India
- Institute of Neuroscience and Physiology, University of Gothenburg, Mölndal, Sweden
- Centre of Research of University of Montreal Health Centre (CRCHUM), Montreal, Canada
- Department of Neuroscience, University of Montreal, Montreal, Canada
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
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
37 files
- pipelines/
templates/ , Python, 37 linesapply_anat2tpl.py - pipelines/
templates/ , Python, 137 linesapply_pet2anat.py - pipelines/
templates/ , Python, 41 linescompute_metrics_anat.py - pipelines/
templates/ , Python, 54 linescompute_metrics_tpl.py - pipelines/
templates/ , Shell, 20 linesestimate_pet2anat_ants.s h - pipelines/
templates/ , Python, 26 linesestimate_pet2anat_spm.py - pipelines/
templates/ , Python, 33 linespetconvert.py - pipelines/
templates/ , Python, 14 linespickatlas.py - pipelines/
templates/ , Python, 82 linesqa_mosaics_T1w.py - pipelines/
templates/ , Python, 46 linesqa_mosaics_suit.py - pipelines/
templates/ , Python, 62 linesqa_mosaics_tpl.py - pipelines/
templates/ , Python, 23 linesrealign.py - pipelines/
templates/ , Python, 39 linesreorient_anat.py - pipelines/
templates/ , Python, 31 linesreorient_pet.py - pipelines/
templates/ , Python, 89 linessegmentation.py - pipelines/
templates/ , Python, 64 linessuvr_baker.py - setup.py, Python, 41 lines
- templates/
apply_anat2tpl.m , MATLAB, 40 lines - templates/
beluga.sh , Shell, 67 lines - templates/
estimate_pet2anat.m , MATLAB, 45 lines - templates/
guillimin.sh , Shell, 67 lines - templates/
qa_dataSubjects.js , JavaScript, 14 lines - templates/
realign.m , MATLAB, 43 lines - templates/
seg_spm12.m , MATLAB, 59 lines - templates/
seg_suit.m , MATLAB, 70 lines - templates/
set_origin_to_centerOfMa , MATLAB, 57 linesss_anat.m - templates/
set_origin_to_centerOfMa , MATLAB, 57 linesss_pet.m - templates/
suvr_baker.m , MATLAB, 30 lines - vlpp/
__init__.py , Python, 1 line - vlpp/
dashboards.py , Python, 144 lines - vlpp/
operation.py , Python, 198 lines - vlpp/
qamosaic.py , Python, 262 lines - vlpp/
realign.py , Python, 169 lines - vlpp/
registration.py , Python, 81 lines - vlpp/
utils.py , Python, 114 lines - README.md, Text, 68 lines
- license.txt, License, 22 lines
openpreventad.loris.ca
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
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
ggsegverse/ggseg
ccb3e8e98faa0fee4ff1edf6fb1bb3be76d25b4d, 11 September 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
54 files
- R/
adapt_scales.R , R, 85 lines - R/
annotate-brain-polygon.R , R, 113 lines - R/
annotate-brain.R , R, 207 lines - R/
brain-test-plot.R , R, 67 lines - R/
brain_join.R , R, 68 lines - R/
coord-funcs.R , R, 44 lines - R/
coords.R , R, 57 lines - R/
geom-brain-polygon.R , R, 421 lines - R/
geom-brain.R , R, 304 lines - R/
ggseg-package.R , R, 43 lines - R/
ggseg.R , R, 23 lines - R/
layer-brain.R , R, 174 lines - R/
position-brain-polygon.R , R, 395 lines - R/
position-brain.R , R, 670 lines - R/
position-layout.R , R, 111 lines - R/
scale-brain.R , R, 280 lines - R/
sf-availability.R , R, 35 lines - R/
stat-brain.R , R, 293 lines - R/
theme-brain.R , R, 131 lines - README.Rmd, R, 142 lines
- data-raw/
make_hex.R , R, 53 lines - dev/
cran_submit.R , R, 40 lines - tests/
spelling.R , R, 7 lines - tests/
testthat.R , R, 12 lines - tests/
testthat/ , R, 20 lineshelpers.R - tests/
testthat/ , R, 62 linestest-annotate-brain-poly gon.R - tests/
testthat/ , R, 232 linestest-annotate-brain.R - tests/
testthat/ , R, 212 linestest-brain-atlas-plots.R - tests/
testthat/ , R, 46 linestest-brain-test-plot.R - tests/
testthat/ , R, 74 linestest-brain_join.R - tests/
testthat/ , R, 38 linestest-coord-funcs.R - tests/
testthat/ , R, 35 linestest-coords.R - tests/
testthat/ , R, 508 linestest-geom-brain-polygon. R - tests/
testthat/ , R, 326 linestest-geom-brain.R - tests/
testthat/ , R, 75 linestest-ggseg-adapt_scales. R - tests/
testthat/ , R, 5 linestest-ggseg.R - tests/
testthat/ , R, 190 linestest-position-brain-poly gon.R - tests/
testthat/ , R, 452 linestest-position-brain.R - tests/
testthat/ , R, 96 linestest-position-layout.R - tests/
testthat/ , R, 123 linestest-read_freesurfer_sta ts.R - tests/
testthat/ , R, 176 linestest-scale-brain.R - tests/
testthat/ , R, 34 linestest-sf-availability.R - tests/
testthat/ , R, 292 linestest-stat-brain.R - tests/
testthat/ , R, 85 linestest-theme_brain.R - tests/
testthat/ , R, 25 linestest-utils.R - vignettes/
external-data.Rmd , R, 143 lines - vignettes/
freesurfer-files.Rmd , R, 148 lines - vignettes/
geom-sf.Rmd , R, 159 lines - vignettes/
ggseg.Rmd , R, 239 lines - vignettes/
how-geom-brain-works.Rmd , R, 181 lines - vignettes/
positioning-views.Rmd , R, 368 lines - LICENSE, License, 2 lines
- LICENSE.md, License, 21 lines
- README.md, Text, 144 lines
Zenodo 19411014
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
2 files
- Cox_regression_analysis.
R , R, 2,408 lines - README.md, Text, 2 lines
jogaru1818/progression_to_mci
179a41c360792b31a87d6ff8e121c09454c02133, 3 April 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
2 files
- Cox_regression_analysis.
R , R, 2,408 lines, 11 matches - README.md, Text, 2 lines
villeneuvelab/vlpp
88d3cc43742e594237153c9cdd98efc345836287, 11 June 2019Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
37 files
- pipelines/
templates/ , Python, 37 linesapply_anat2tpl.py - pipelines/
templates/ , Python, 137 linesapply_pet2anat.py - pipelines/
templates/ , Python, 41 linescompute_metrics_anat.py - pipelines/
templates/ , Python, 54 linescompute_metrics_tpl.py - pipelines/
templates/ , Shell, 20 linesestimate_pet2anat_ants.s h - pipelines/
templates/ , Python, 26 linesestimate_pet2anat_spm.py - pipelines/
templates/ , Python, 33 linespetconvert.py - pipelines/
templates/ , Python, 14 linespickatlas.py - pipelines/
templates/ , Python, 82 linesqa_mosaics_T1w.py - pipelines/
templates/ , Python, 46 linesqa_mosaics_suit.py - pipelines/
templates/ , Python, 62 linesqa_mosaics_tpl.py - pipelines/
templates/ , Python, 23 linesrealign.py - pipelines/
templates/ , Python, 39 linesreorient_anat.py - pipelines/
templates/ , Python, 31 linesreorient_pet.py - pipelines/
templates/ , Python, 89 linessegmentation.py - pipelines/
templates/ , Python, 64 linessuvr_baker.py - setup.py, Python, 41 lines
- templates/
apply_anat2tpl.m , MATLAB, 40 lines - templates/
beluga.sh , Shell, 67 lines - templates/
estimate_pet2anat.m , MATLAB, 45 lines - templates/
guillimin.sh , Shell, 67 lines - templates/
qa_dataSubjects.js , JavaScript, 14 lines - templates/
realign.m , MATLAB, 43 lines - templates/
seg_spm12.m , MATLAB, 59 lines - templates/
seg_suit.m , MATLAB, 70 lines - templates/
set_origin_to_centerOfMa , MATLAB, 57 linesss_anat.m - templates/
set_origin_to_centerOfMa , MATLAB, 57 linesss_pet.m - templates/
suvr_baker.m , MATLAB, 30 lines - vlpp/
__init__.py , Python, 1 line - vlpp/
dashboards.py , Python, 144 lines - vlpp/
operation.py , Python, 198 lines - vlpp/
qamosaic.py , Python, 262 lines - vlpp/
realign.py , Python, 169 lines - vlpp/
registration.py , Python, 81 lines - vlpp/
utils.py , Python, 114 lines - README.md, Text, 68 lines
- license.txt, License, 22 lines
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://
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://
BibTeX
@article{gallegorudolf20
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/
url = {https://
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/
VL - 12
IS - 33
SP - eaee2305
SN - 2375-2548
PB - American Association for the Advancement of Science
DO - 10.1126/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1126/
"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":
"volume": "12",
"issue": "33",
"page": "eaee2305",
"DOI": "10.1126/
"PMID": "42585316",
"PMCID": "PMC13464481",
"ISSN": "2375-2548",
"publisher": "American Association for the Advancement of Science",
"URL": "https://
"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 neurologyIn 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 mappingIn 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 communicationsIn 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. ClinicalIn 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 advancesIn 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 consciousnessIn 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: HippocampusIn 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 neuroscienceIn 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 communicationsIn 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 biologyIn 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.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 7 repositories of the authors' code, each at its verified commit and with its license, 123 scripts, and 11 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:9b8ce64babc7f5f7…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
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.
