Dynamic Functional Synchronization Profiles in Autism Differ by Spatial Scale and Along Hierarchical Cortical Gradients.
The 7 matches
- [1] § Methods and Materials › Turbulence Analysis ↔ code/turbulence.m, lines 69–167 · score 0.89 · analytic signal, Euclidean distance, Hilbert transform, kernel function, Kuramoto local, summed
- [2] § Methods and Materials › Statistical Analysis ↔ code/Turbulence_postproc_analysis.Rmd, lines 634–692 · score 0.73 · information cascade flow, linear models, information transfer, sex, Cohen, ADOS
- [3] § Methods and Materials › Turbulence Analysis ↔ code/Turbulence_postproc_analysis.Rmd, lines 634–692 · score 0.67 · standard deviation, information cascade flow, information transfer, vector, linear, row
- [4] § Results › Variability of Parcel-Level Functional Synchronization Maps Onto S-A and Functional Gradients in Autism ↔ code/gradient_computations.ipynb, lines 115–193 · score 0.58 · Spearman rho, turbulence maps, functional gradient, fitted, ranks, Cohen
- [5] § Methods and Materials › Turbulence Analysis ↔ code/turbulence.m, lines 69–167 · score 0.55 · Kuramoto local, information transfer, vector, row, dimensional, computation
- [6] § Results › Variability of Parcel-Level Functional Synchronization Maps Onto S-A and Functional Gradients in Autism ↔ code/gradient_computations.ipynb, lines 115–193 · score 0.55 · turbulence maps, 0.07 mm, Spearman, permutation, spin, gradients
- [7] § Results › Weaker, Less Spatially Correlated, and Rapidly Decaying Functional Synchronization Over Space in Autism ↔ code/Turbulence_postproc_analysis.Rmd, lines 341–442 · score 0.51 · ADOS percentage scores, information transfer, intermediate, KLOP, parcels
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
R Markdown · 1,459 lines · 57 KB · no license · 3 matches
- ---
- title: "Turbulence_final_revision"
- output: html_document
- date: "2026-03-21"
- ---
- Post-Processing analysis of Turbulence Project on Abide data.
- ```{r, message=FALSE, warning=FALSE}
- ### Loading libraries
- library("easypackages")
- libraries("scales","reticulate", "ppcor","gganimate", "patchwork","ggseg","ggsegSchaefer","car","gvlma","performance","effectsize", "grid","gridExtra","MatchIt","gt","lme4","lsr", "effsize", "ggsignif","tidyverse","here","sva","R.matlab","car","ggplot2", "tidyverse","ggeasy","ggpackets","emmeans", "plotly", "fmsb", "ggradar", "ggseg3d", "minpack.lm", "psych")
- ### Handling paths
- root = Sys.getenv("ROOT_PATH")
- #root = "/Users/bened/Library/CloudStorage/OneDrive-FondazioneIstitutoItalianoTecnologia/Desktop/University/PhD_IIT/turbulence/Turbulence_Autisms/Turbulence_Project/turbulence_final_folder" # For local processing
- figures_directory = paste0(root,"/figures")
- ### Loading functions and data
- #Geom_scatterbox for plotting
- geom_scatterbox <- ggpacket() +
- geom_jitter(size = 1.5, width = 0.25) +
- geom_boxplot(fill = NA, colour = "#000000", outlier.shape = NA, size = 1)
- #Loading in the phenotypical and qualitative subject and scanner data
- filenames = list.files(file.path(root, "data","postproc"))
- motionTable = read.csv(file.path(root, "data", "pheno", "qc_preproc_abideI_abideII.csv"))
- qcLombardo = read.csv(file.path(root, "data", "pheno", "qc_preproc_mvlombardo.csv"))
- mri_data = read.csv2((file.path(root, "data", "pheno", "mriparams_summary_abide.csv")))
- pheno = read.csv(file.path(root, "data", "pheno","pheno_abide_I_abide_II.csv"))
- #### Data Filtering
- #Filtering data for FD and Quality-checks
- #Merging dataframes by subid, site and dataset
- mergedPheno = merge.data.frame(motionTable,pheno, by = c("dataset", "site","subid"))
- mergedPheno = merge.data.frame(mergedPheno, qcLombardo, c("dataset", "site","subid"))
- mergedPheno = merge.data.frame(mergedPheno, mri_data, c("dataset", "site"))
- #Extracting all the IDs for the subjects that pass the quality check and have FD < 0.5
- filteredPheno = mergedPheno %>%
- dplyr::filter(pass == "YES") %>%
- dplyr::filter(mean_fd < 0.5)
- #Check visually for distributions of diagnosis over sites
- table(filteredPheno$site, filteredPheno$diagnosis)
- # merge LEUVEN, NYU, and UM labels, to even distributions in these sites
- filteredPheno$site_orig = filteredPheno$site
- filteredPheno$site[is.element(filteredPheno$site, c("NYU_1","NYU_2"))] = "NYU"
- filteredPheno$site[is.element(filteredPheno$site, c("LEUVEN_1","LEUVEN_2"))] = "LEUVEN"
- filteredPheno$site[is.element(filteredPheno$site, c("UM_1","UM_2"))] = "UM"
- filteredPheno$site[is.element(filteredPheno$site, c("UCLA_1","UCLA_2"))] = "UCLA"
- filteredPheno$site = factor(filteredPheno$site)
- #Check distributions again
- table(filteredPheno$site, filteredPheno$diagnosis)
- ```
- ```{r warning=FALSE}
- # remove UM, since it has an odds ratio of almost 9:1
- mask = filteredPheno$site != "UM"
- filteredPheno = filteredPheno %>% dplyr::filter(mask)
- filteredPheno$site = factor(filteredPheno$site)
- filteredPheno$mean_fd = as.numeric(filteredPheno$mean_fd)
- #Check, if site and diagnosis are equally distributed across the sites with a chi-square test
- chisq.test(filteredPheno$site, filteredPheno$diagnosis)
- ```
- p-value > 0.05, so we can go on.
- ```{r}
- # Checking distributions of framewise displacement.
- t.test(mean_fd ~ as.factor(diagnosis), data = filteredPheno)
- ```
- Significant (p < 0.001). Autistic subjects move more in the scanner.
- ```{r}
- #Plotting the association of FD and diagnosis
- t_meanfd = t.test(data = filteredPheno, mean_fd ~ diagnosis)
- p_meanfd = t_meanfd$p.value
- tab = table(filteredPheno$diagnosis)
- d_meanfd = round(t_meanfd$statistic / sqrt((as.double(tab[1])*as.double(tab[2]))/(as.double(tab[1])+as.double(tab[2]))),3)
- mean_fd_dx_plot_before = ggplot(data = filteredPheno, aes(x = diagnosis, y = mean_fd, colour = diagnosis)) +
- geom_scatterbox() +
- geom_signif(comparisons = list(c("Autism", "Control")),
- map_signif_level = TRUE,
- test = "t.test",
- textsize = 8,
- color = "black",
- size = 1.3) +
- ylab("Mean Framewise Displacement (FD) mm") +
- xlab("Diagnosis") + guides(colour = "none") +
- ylim(NA, max(filteredPheno$mean_fd) + 0.1) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- plot.title = element_blank()
- ) +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- ggtitle("Mean Framewise displacement of ASD vs. Control")
- mean_fd_dx_plot_before
- ```
- Addressing the increased movement of autism vs TD via matching the subjects from both groups with a similarity of +- 0.05mm.
- ```{r}
- #factorising the diagnosis and sex variables
- filteredPheno$sex = as.factor(filteredPheno$sex)
- filteredPheno$diagnosis = as.factor(filteredPheno$diagnosis)
- #matching the two groups on FD
- m_1 = matchit(formula = diagnosis ~ mean_fd + sex + age_years,
- data = filteredPheno,
- method = "nearest",
- caliper = 0.05)
- summary(m_1)
- plot(m_1,
- type = "density",
- which.xs = ~mean_fd,
- interactive = FALSE)
- ```
- ```{r}
- # Successful matching; extracting the data
- filteredPheno = match_data(m_1)
- t.test(data= filteredPheno, mean_fd ~ diagnosis)
- t_meanfd = t.test(data = filteredPheno, mean_fd ~ diagnosis)
- p_meanfd = t_meanfd$p.value
- d_meanfd = round(t_meanfd$statistic / sqrt((as.double(tab[1])*as.double(tab[2]))/(as.double(tab[1])+as.double(tab[2]))),3)
- mean_fd_dx_plot_after = ggplot(data = filteredPheno, aes(x = diagnosis, y = mean_fd, colour = diagnosis)) +
- geom_scatterbox() +
- geom_signif(comparisons = list(c("Autism", "Control")),
- map_signif_level = TRUE,
- test = "t.test",
- textsize = 8,
- color = "black",
- size = 1.3) +
- ylab("Mean Framewise Displacement (FD) mm") +
- xlab("Diagnosis") + guides(colour = "none") +
- ylim(NA, max(filteredPheno$mean_fd) + 0.1) +
- theme_minimal() +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- ggtitle("Mean Framewise displacement of Autism vs. Control")
- mean_fd_dx_plot_after
- ```
- ```{r}
- ### Loading turbulence data
- #Variable pre-allocation
- flattenedData = c()
- turbData = list()
- allIDs = list()
- out_data = list()
- counter = 0
- names_turb = names(turbData)
- names_allIDs = as.character(allIDs$subid)
- ### Exclusion of Subjects based on fallible Hilbert-transormation
- #For 5 of the subjects, the time-series extraction did not work. The phases matrix is empty. Exclude these from the analysis.
- outliers_subid = c(51468, 28793, 50726, 28939, 51193)
- for (sub in outliers_subid){
- currID = as.character(sub)
- currName = paste0(currID,"_turbulence_measures.mat")
- matData = readMat(file.path(root, "data","postproc", currName))
- matData = matData[["output"]]
- matData = data.frame(matData)
- matData = matData$X1.1
- out_data[[as.character(sub)]] = matData
- }
- #Looping over the subjects of the phenotypical data to read in Turbulence measures. This step is necessary since the subids in the phenotypical are not matching with all the subids of which fMRI data was available.
- for (subject in 1:length(filteredPheno$subid)){
- #Extract the current subject-ID
- currID = filteredPheno$subid[subject]
- currName = paste0(currID,"_turbulence_measures.mat")
- #Skip outliers
- if (currID %in% outliers_subid){
- next}
- #If that ID is in the list of subjects on which the turbulence analysis was applied, go on, otherwise, exit
- if (currName %in% filenames){
- #Reading the Turbulence values of current subject
- matData = readMat(file.path(root, "data","postproc", currName))
- matData = matData[["output"]]
- matData = data.frame(matData)
- matData = matData$X1.1
- #Deleting the first entry of the Information.Cascade flow (because they are all NAs, due to formal, theoretical reasons)
- matData[[6]] = matData[[6]][-1]
- #For the information transfer, we still have to subtract the values calculated by the MATLAB script
- #from a constant value k (k=2). The reason for that is, to keep the rational consistent with the other measures:
- #higher values mean the information travels across longer distances (originally in the MATLAB script
- #higher values meant steeper slope which in turn means information travels across smaller distances)
- #For all that, see methods of Cruzat et al., 2022)
- matData[[4]] = 2 - matData[[4]]
- if (length(matData) == 11){
- #Compute the mean across time of the KLOP for each parcel
- matData$mean_KLOP = apply(matData$LocalKuramoto, c(1,2), FUN = mean)
- names(matData)[c(9,12)] = names(matData)[c(12,9)]
- matData[c(9, 12)] = matData[c(12, 9)]
- }
- #Delete these two, because not needed
- matData$Phases = NULL
- matData$LocalKuramoto = NULL
- #Some of the subjects have fallible node turbulence (only extracted for 1000 nodes). Leave them out.
- if (dim(matData$Turbulence.Node)[1] == 1054){
- counter = counter + 1
- allIDs[[length(allIDs)+1]] = currID #Save that ID in a list
- #Saving the entry in a new list
- turbData[[as.character(currID)]] = matData #List-Variable with all the Turbulence measures for all subjects
- }
- }
- }
- ### Performing batch correction to control for differences in scanning sites
- # Extracting number of parcels and lambda values from turbulence data
- nParcels = dim(matData$Turbulence.Node)[1]
- nLambda = dim(matData$Turbulence.Node)[2]
- dim_node = nParcels*nLambda
- dim_RSN = dim(matData$Turbulence.RSN)[1] * nLambda
- dim_output = length(matData)
- #Create data.frame for subject IDs
- allIDs = unlist(allIDs)
- allIDs = data.frame(allIDs)
- colnames(allIDs) = "subid"
- #Merge the analyzed subjects with the phenotypical data
- filteredPheno = merge.data.frame(filteredPheno,allIDs, by = "subid")
- #Order it in the same way as my Turbulence matrix
- filteredPheno = filteredPheno[match(allIDs$subid, filteredPheno$subid),]
- #Factorise the covariates site, sex and diagnosis and numerise mean_fd and age
- filteredPheno = filteredPheno %>% mutate(
- sex = as.factor(sex),
- site = as.factor(site),
- diagnosis = as.factor(diagnosis)
- )
- ### Run Batch Correction
- #Pre-allocation
- flattenedData = c()
- my_lengths = list()
- #For each subject, flatten out the vector to prepare for the batch correction
- for (sub in 1:length(filteredPheno$subid)){
- currVec = c()
- for (m in 1:dim_output){
- currVec = c(currVec, as.vector(unlist(turbData[[sub]][m])))
- }
- my_lengths[sub] = length(currVec)
- flattenedData = cbind(flattenedData, currVec)
- }
- #Vector-length of flattened participant data
- length_flattened_data = dim(flattenedData)[1]
- #Remove Lambdas from flattened data for the batch correction. Add the lambda data later.
- lambda_flattened = flattenedData[(length_flattened_data-nLambda+1):length_flattened_data,][,1]
- flattenedData = flattenedData[1:(length_flattened_data-nLambda),]
- #Batch variable
- batch = filteredPheno$site
- #Running the batch correction with the ComBat function
- model = model.matrix(~ mean_fd + sex + age_years + diagnosis + diagnosis:age_years, data = filteredPheno)
- correctedData = ComBat(dat=flattenedData, mod=model, batch=batch, par.prior=TRUE)
- #Testing if Batch correction worked with PCA
- #Extracting first PC from uncorrected data
- pca_uncorrected = prcomp(t(flattenedData), center = TRUE, scale. = TRUE)
- first_PC_uncorrected = pca_uncorrected$x[,1]
- #Run Anova for uncorrected PCA
- anova_model = aov(first_PC_uncorrected ~ batch)
- summary(anova_model)
- ```
- highly significant (p < 0.001)
- ```{r}
- #Extracting first PC from corrected data
- pca_corrected = prcomp(t(correctedData), center = TRUE, scale. = TRUE)
- first_PC_corrected = pca_corrected$x[,1]
- #Run Anova for corrected PCA
- anova_model = aov(first_PC_corrected ~ batch)
- summary(anova_model)
- ```
- Batch correction worked (p = 0.395)
- Plotting the results of the Batch correction
- ```{r}
- #Plotting results of the PCA before Batch correction
- PC_data = data.frame(batch, first_PC_corrected, first_PC_uncorrected)
- PC_beforeBatchCorrection = ggplot(PC_data, aes(x = batch, y = first_PC_uncorrected, color = batch)) +
- geom_scatterbox() +
- ylab("PC1") +
- xlab("Site") +
- guides(colour = "none") +
- easy_rotate_x_labels(angle = 45, side = "right") +
- ggtitle("Before batch correction") +
- theme(
- axis.title = element_text(size = 24), # axis labels
- axis.text = element_text(size = 18), # x and y axis tick labels
- plot.title = element_text(size = 24, hjust = 0.5) # title centered and larger
- )
- PC_beforeBatchCorrection
- ##ggsave(paste0(figures_directory,"/supplementary/PC_before_batch.pdf"), PC_beforeBatchCorrection, width = 16, height = 8)
- ```
- ```{r}
- #And after the batch correction
- PC_afterBatchCorrection = ggplot(PC_data, aes(x = batch, y = first_PC_corrected, color = batch)) +
- geom_scatterbox() +
- ylab("PC1") +
- xlab("Site") +
- guides(colour = "none") +
- easy_rotate_x_labels(angle = 45, side = "right") +
- ggtitle("After batch correction") +
- theme(
- axis.title = element_text(size = 24), # axis labels
- axis.text = element_text(size = 18), # x and y axis tick labels
- plot.title = element_text(size = 24, hjust = 0.5) # title centered and larger
- )
- PC_afterBatchCorrection
- ##ggsave(paste0(figures_directory,"/supplementary/PC_after_batch.pdf"), PC_afterBatchCorrection, width = 16, height = 8)
- ```
- ```{r}
- ### Reshaping
- # Reshaping the batch-corrected data into the different measures
- # Pre-allocation
- correctedTurbData = list()
- turbDataTest = list() #list to test if the dimensions line up with the initial data
- intermediate_num = (dim_node+nLambda+dim_RSN)
- # Loop over all subjects to save the batch-corrected data
- for (k in 1:length(filteredPheno$subid)){
- Turbulence = correctedData[1:nLambda,k]
- TurbulenceTest = flattenedData[1:nLambda,k]
- TurbulenceNode = matrix(correctedData[(nLambda+1):(dim_node+nLambda),k],nrow = nParcels, ncol = nLambda)
- TurbulenceNodeTest = matrix(flattenedData[(nLambda+1):(dim_node+nLambda),k],nrow = nParcels, ncol = nLambda)
- TurbulenceRSN = matrix(correctedData[(dim_node+nLambda+1):(dim_node+nLambda+dim_RSN),k], nrow = dim_RSN/nLambda, ncol = nLambda)
- TurbulenceRSNTest = matrix(flattenedData[(dim_node+nLambda+1):(dim_node+nLambda+dim_RSN),k], nrow = dim_RSN/nLambda, ncol = nLambda)
- InformationTransfer = correctedData[(intermediate_num+1):(intermediate_num+nLambda),k]
- InformationTransferTest = flattenedData[(intermediate_num+1):(intermediate_num+nLambda),k]
- InformationCascade = correctedData[intermediate_num+nLambda+1,k]
- InformationCascadeTest = flattenedData[intermediate_num+nLambda+1,k]
- InformationCascadeflow = correctedData[(intermediate_num+nLambda+2):(intermediate_num+(2*(nLambda))),k]
- InformationCascadeflowTest = flattenedData[(intermediate_num+nLambda+2):(intermediate_num+(2*(nLambda))),k]
- GlobalKuramoto = correctedData[intermediate_num+(2*(nLambda)+1),k]
- GlobalKuramotoTest = flattenedData[intermediate_num+(2*(nLambda)+1),k]
- Metastability = correctedData[intermediate_num+(2*(nLambda)+2),k]
- MetastabilityTest = flattenedData[intermediate_num+(2*(nLambda)+2),k]
- mean_KLOP = matrix(correctedData[(intermediate_num+(2*(nLambda)+3)):(dim(correctedData)[1]),k], ncol = nParcels, nrow = nLambda)
- mean_KLOPTest = matrix(flattenedData[(intermediate_num+(2*(nLambda)+3)):(dim(correctedData)[1]),k], ncol = nParcels, nrow = nLambda)
- Lambda = lambda_flattened
- LambdaTest = lambda_flattened
- subjectData = list(Turbulence,TurbulenceNode,TurbulenceRSN,InformationTransfer,InformationCascade,InformationCascadeflow,GlobalKuramoto,Metastability,mean_KLOP, Lambda)
- subjectDataTest = list(TurbulenceTest,TurbulenceNodeTest,TurbulenceRSNTest,InformationTransferTest,InformationCascadeTest,InformationCascadeflowTest,GlobalKuramotoTest,MetastabilityTest, mean_KLOPTest,LambdaTest)
- currentID = filteredPheno$subid[k]
- correctedTurbData[[as.character(currentID)]] = subjectData
- turbDataTest[[as.character(currentID)]] = subjectDataTest
- }
- #Extracting each measures
- amplTurb = t(as.data.frame(lapply(correctedTurbData, function(row) row[[1]])))
- nodeTurb = lapply(correctedTurbData, function(row) row[[2]])
- rsnTurb = lapply(correctedTurbData, function(row) row[[3]])
- transfer = t(as.data.frame(lapply(correctedTurbData, function(row) row[[4]])))
- cascade = as.numeric(unlist(lapply(correctedTurbData, function(row) row[[5]])))
- cascadeflow = t(as.data.frame(lapply(correctedTurbData, function(row) row[[6]])))
- gloKur = as.numeric(unlist(lapply(correctedTurbData, function(row) row[[7]])))
- meta = as.numeric(unlist(lapply(correctedTurbData, function(row) row[[8]])))
- mean_KLOP = t(as.data.frame(lapply(correctedTurbData, function(row) apply(row[[9]],1, FUN = mean))))
- nodeSync = lapply(correctedTurbData, function(row) t(row[[9]]))
- subid = as.factor(names(correctedTurbData))
- #Defining global datameasures
- global_data = data.frame(
- global_kuramoto = gloKur,
- metastability = meta,
- cascade = cascade,
- filteredPheno
- )
- ### Transforming ADOS values into percentage scores ###
- # Adding ADOS columns to the data frame
- global_data = global_data %>%
- mutate(
- ados2_socaff_css = NaN,
- ados2_rrb_css = NaN,
- ados2_total_css = NaN,
- ados_comb = NaN,
- ados_version = NaN
- )
- #Loop over subjects to compute the ADOS percentage score
- for (i in 1:nrow(global_data)){
- if (!is.na(global_data$ados2_total[i]) & !is.na(global_data$ados_module[i])){
- if (global_data$ados_module[i] == 4){
- denominator = 29
- }
- else{
- denominator = 28}
- global_data$ados_comb[i] = (global_data$ados2_total[i]/denominator)*100
- global_data$ados_version[i] = "ados"
- }
- else if(!is.na(global_data$ados_total[i]) & !is.na(global_data$ados_module[i])){
- denominator = 32
- global_data$ados_comb[i] = (global_data$ados_total[i]/denominator)*100
- global_data$ados_version[i] = "ados"}
- }
- # Filtering for subjects with existing total score
- raw_ados = global_data %>% dplyr::filter(!is.na(ados_comb))
- raw_ados$ados_version = as.factor(raw_ados$ados_version)
- ```
- Demographic data
- ```{r}
- ASD_data = filteredPheno %>% filter(diagnosis == "Autism")
- TD_data = filteredPheno %>% filter(diagnosis == "Control")
- #Ranges
- demo_summary = filteredPheno %>%
- filter(diagnosis %in% c("Autism", "Control")) %>%
- group_by(diagnosis) %>%
- summarise(
- age_min = min(age_years, na.rm = TRUE),
- age_max = max(age_years, na.rm = TRUE),
- age_mean = mean(age_years, na.rm = TRUE),
- age_sd = sd(age_years, na.rm = TRUE),
- FIQ_min = min(fiq, na.rm = TRUE),
- FIQ_max = max(fiq, na.rm = TRUE),
- FIQ_mean = mean(fiq, na.rm = TRUE),
- FIQ_sd = sd(fiq, na.rm = TRUE),
- VIQ_min = min(viq, na.rm = TRUE),
- VIQ_max = max(viq, na.rm = TRUE),
- VIQ_mean = mean(viq, na.rm = TRUE),
- VIQ_sd = sd(viq, na.rm = TRUE),
- PIQ_min = min(piq, na.rm = TRUE),
- PIQ_max = max(piq, na.rm = TRUE),
- PIQ_mean = mean(piq, na.rm = TRUE),
- PIQ_sd = sd(piq, na.rm = TRUE)
- )
- ### --------- age --------- ###
- #Histograms
- #ASD
- asd_age_hist = ggplot(filteredPheno[filteredPheno$diagnosis == "Autism",], aes(x = age_years)) +
- geom_histogram(alpha = 0.8, position = "identity", bins = 70, fill = "#ff7f0e", color = "black") +
- ylim(c(0,50))+
- xlim(c(0,65)) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank()
- )
- #TD
- td_age_hist = ggplot(filteredPheno[filteredPheno$diagnosis == "Control",], aes(x = age_years)) +
- geom_histogram(alpha = 0.8, position = "identity", bins = 70, fill = "#1f77b4", color = "black") +
- ylim(c(0,50))+
- xlim(c(0,65)) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank()
- )
- #Both
- comb_age_hist = ggplot(filteredPheno, aes(x = age_years, fill = diagnosis)) +
- geom_histogram(alpha = 0.5, position = "identity", bins = 70, color = "black") +
- scale_fill_manual(values = c("Autism" = "#ff7f0e", "Control" = "#1f77b4")) +
- ylim(c(0,50))+
- xlim(c(0,65)) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank(),
- legend.position = "none"
- )
- ### --- Printing and Saving -- ###
- print(td_age_hist)
- print(asd_age_hist)
- print(comb_age_hist)
- #ggsave(paste0(figures_directory, "/supplementary/age/td_age_hist.jpg"), width = 10, height = 6, td_age_hist, dpi = 300)
- #ggsave(paste0(figures_directory, "/supplementary/age/asd_age_hist.jpg"),width = 10, height = 6 ,asd_age_hist, dpi = 300)
- #ggsave(paste0(figures_directory, "/supplementary/age/comb_age_hist.jpg"),width = 10, height = 6, comb_age_hist, dpi = 300)
- ```
- Overview of the Turbulence measures in the data list (with dimensions in brackets):
- 1: Amplitude Turbulence [10,1], 2: Parcel-level Turbulence [1054,10], 3: RSN Turbulence [8,10]
- 4: Information Transfer [10,1], 5: Information Cascade [1], 6: Information Cascadeflow [9,1]
- 7: GolbalKuramoto [1], 8: Metastability [1], 9: Node_Synchronization, 10: Lambda values [1,10]
- Lambda values range from high to low, so small to large distances (1=0.28, 2=0.25, ... ,10 = 0.01)
- 1.0: Statistical analysis of whole-brain summary statistics: Global Synchronization, Metastability
- ```{r, warning=FALSE, message=FALSE}
- #Variable handling and pre-allocation
- counter = 0
- global_measures = c("global_kuramoto", "metastability")
- axes_labels = c("Global Synchronization", "Metastability")
- global_statistics = list()
- #Loop over all global measures
- for (curr_measure in global_measures){
- counter = counter+1
- ### TESTS
- # 1) Diagnosis as IV
- # ANOVAs
- diag_mod = lm(formula = global_data[[curr_measure]] ~ mean_fd + sex + age_years + diagnosis + diagnosis:age_years, data = global_data) #create model
- diag_ANOVA = anova(diag_mod)
- #Compute Cohens D:
- #Group sizes
- n1 = sum(global_data$diagnosis == "Control")
- n2 = sum(global_data$diagnosis == "Autism")
- diag_f = diag_ANOVA["diagnosis", "F value"]
- diag_d = round(sqrt(diag_f * (n1 + n2) / (n1 * n2)), 3)
- diag_eta = c(round(effectsize(diag_ANOVA)$Eta2_partial,3),NA)
- # 2) ADOS as IV
- #ANOVAs
- ados_mod = lm(formula = raw_ados[[curr_measure]] ~ mean_fd + sex + age_years + ados_comb + ados_comb:age_years,
- data = raw_ados)
- ados_ANOVA = anova(ados_mod)
- ados_f = ados_ANOVA["ados_comb", "F value"]
- ados_d = round(sqrt(ados_f * (n1 + n2) / (n1 * n2)), 3)
- # Correlations of ADOS with measures
- # Compute correlation test
- curr_corr = cor.test(raw_ados[[curr_measure]], raw_ados$ados_comb)
- # Correlations of FD with measures differences between ASD and TD
- curr_ASD = global_data %>% filter(diagnosis == "Autism")
- curr_TD = global_data %>% filter(diagnosis == "Control")
- ASD_corr = cor.test(curr_ASD[[curr_measure]], curr_ASD$mean_fd)
- TD_corr = cor.test(curr_TD[[curr_measure]], curr_TD$mean_fd)
- curr_paired_r = paired.r(ASD_corr$estimate, TD_corr$estimate, NULL, nrow(curr_ASD), nrow(curr_TD))
- curr_paired_p = curr_paired_r$p
- curr_paired_z = curr_paired_r$z
- curr_paired_stats = data.frame(
- ASD_corr$estimate, TD_corr$estimate, curr_paired_z, curr_paired_p)
- #Merging output
- global_statistics[[curr_measure]] = list(data.frame(diag_ANOVA),
- paste("d value diagnosis",diag_d),
- data.frame(ados_ANOVA),
- paste("d value ados",ados_d),
- data.frame(curr_corr$estimate,
- curr_corr$p.value,
- curr_corr$statistic),
- curr_paired_stats)
- ### PLOTS ###
- # Boxplot of measure dx diagnosis
- curr_boxplot = ggplot(data = global_data, aes(x = diagnosis, y = global_data[[curr_measure]], colour = diagnosis)) +
- geom_scatterbox() +
- ylab(axes_labels[counter]) +
- xlab("Diagnosis") +
- guides(colour = "none") +
- scale_x_discrete(labels = c("Autism" = "Autism", "Control" = "TD"))+
- theme_minimal() +
- ylim(NA, max(global_data[[curr_measure]])+0.1) +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank(),
- )
- #Correlation of the measure dx ados
- curr_corr_plot = ggplot(raw_ados, aes(x = ados_comb, y = .data[[curr_measure]])) +
- geom_jitter(size = 5) +
- geom_smooth(method = "lm", linewidth = 3) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank(),
- )
- curr_corr_plot = curr_corr_plot +
- theme(plot.margin = margin(0, 15, 0, 0))
- # Printing and saving
- print(curr_boxplot)
- print(curr_corr_plot)
- ##ggsave(paste0(figures_directory,"/Figure_3/",curr_measure,".jpg"), curr_boxplot, width = 8, height = 6)
- #ggsave(paste0(figures_directory,"/supplementary/S3/",curr_measure,"_ADOS_corr.jpg"), curr_corr_plot, width = 10, height = 6, dpi = 300)
- ##ggsave(paste0(figures_directory,"/Figure_7/",curr_measure,".jpg"), curr_boxplot, width = 8, height = 6)
- }
- #Change the list names
- for (c in names(global_statistics)){
- names(global_statistics[[c]]) = c("ANOVA_diagnosis", "diagnosis_d", "ANOVA_ados", "ados_d", "correlation_ados")}
- ```
- 2.0 Statistical analysis of local measures: Scale-specific Synchronization, Amplitude Turbulence, Information Cascadeflow, Information Transfer
- ```{r, warning=FALSE, message=FALSE}
- #Variable handling and pre-allocation
- local_statistics = list()
- turbulence_ados_stats = list()
- corr_meanFD_summary = list()
- local_measures = c("turbulence", "transfer", "cascadeflow", "scale_synchronization")
- local_vectors = list(
- "turbulence" = unlist(lapply(amplTurb, function(row) row[1])),
- "transfer" = unlist(lapply(transfer, function(row) row[1])),
- "cascadeflow" = unlist(lapply(cascadeflow, function(row) row[1])),
- "scale_synchronization" = unlist(lapply(mean_KLOP, function(row) row[1]))
- )
- plot_labels = c("Amplitude turbulence", "Information transfer", "Information cascade flow", "Scale-specific synchronization")
- counter = 0
- curr_measure = "turbulence"
- for (curr_measure in local_measures){
- counter = counter +1
- if (curr_measure == "cascadeflow"){
- nLambda = 9
- Lambda = lambda_flattened[-1]
- } else{
- nLambda = 10
- Lambda = lambda_flattened
- }
- #Data initialization
- curr_data = data.frame(
- subid = as.factor(rep(subid, times = nLambda)),
- lambda = as.numeric(rep(Lambda, each = length(global_data$subid))),
- measure = local_vectors[[curr_measure]],
- diagnosis = factor(rep(global_data$diagnosis, times = nLambda)),
- sex = factor(rep(global_data$sex, times = nLambda)),
- mean_fd = rep(global_data$mean_fd, times = nLambda),
- age_years = rep(global_data$age_years, times = nLambda),
- ados = rep(global_data$ados_comb, times = nLambda))
- #1) Diagnosis as IV
- #ANOVA
- diag_mod = lmerTest::lmer(formula = measure ~ mean_fd + sex + age_years + lambda + diagnosis + diagnosis:lambda + diagnosis:age_years + (1+lambda|subid), data = curr_data) #Model initialization
- diag_ANOVA = anova(diag_mod, type = 1) #Step-wise Anova
- diag_eta = round(effectsize(diag_ANOVA)$Eta2_partial,3)
- diag_emm_curr = emmeans(diag_mod, ~ diagnosis | lambda, at = list(lambda = sort(unique(curr_data$lambda)))) #Posthoc tests for each Lambda level
- diag_stat = summary(pairs(diag_emm_curr))
- curr_lambda = diag_stat$lambda
- diag_p = diag_stat$p.value
- diag_d = summary(eff_size(diag_emm_curr, sigma = sigma(diag_mod), edf = df.residual(diag_mod))) #Cohens d in linear models is d = mean_1 - mean2 / sigma, where sigma is residual standard deviation
- diag_d = diag_d$effect.size
- diag_means = summary(diag_emm_curr)$emmean
- means_ASD = diag_means[seq(1,length(diag_p)*2, by = 2)]
- means_TD = diag_means[seq(2,length(diag_p)*2, by = 2)]
- diag_stat_summary = data.frame(
- means_ASD = means_ASD,
- means_TD = means_TD,
- means_diff = means_ASD - means_TD,
- p = p.adjust(diag_p, method = "BH"),
- d = diag_d,
- lambda = curr_lambda
- )
- #ANOVA results saving
- diag_ANOVA_results = cbind(data.frame(diag_ANOVA),
- eta = diag_eta)
- #2) ADOS as IV
- #Filtering data to only autisms with existing ados percentage score
- curr_ados_data = curr_data %>% dplyr::filter(!is.na(ados))
- ados_mod = lmerTest::lmer(formula = measure ~ mean_fd + sex + age_years + lambda + ados + ados:lambda + ados:age_years + (1+lambda|subid), data = curr_ados_data) #Model initialization
- ados_ANOVA = anova(ados_mod, type = 1) #Step-wise Anova
- ados_trends = emtrends(ados_mod, ~ lambda, var = "ados",
- at = list(lambda = sort(unique(curr_data$lambda))))
- ados_stats = summary(ados_trends, infer = TRUE)
- # Correlations of measures with ados
- curr_ados_data = curr_data %>%
- group_by(subid, age_years, diagnosis, ados) %>%
- summarise(overall_mean = mean(measure))
- curr_ados_data = curr_ados_data %>% filter(!is.na(ados))
- curr_ados_corr = cor.test(curr_ados_data$ados, curr_ados_data$overall_mean)
- # Saving in data frame
- local_statistics[[curr_measure]] = list(
- diag_stat_summary,
- diag_ANOVA_results,
- data.frame(ados_ANOVA),
- ados_stats,
- data.frame(ados_corr_p = curr_ados_corr$p.value,
- ados_corr_r = curr_ados_corr$estimate))
- # Investigating age effects
- curr_age_data = curr_data %>% group_by(subid, age_years, diagnosis) %>% summarise(overall_mean = mean(measure))
- curr_age_data_ASD = curr_age_data %>% filter(diagnosis == "Autism")
- curr_age_data_TD = curr_age_data %>% filter(diagnosis == "Control")
- ASD_cor_test = cor.test(curr_age_data_ASD$age_years, curr_age_data_ASD$overall_mean)
- TD_cor_test = cor.test(curr_age_data_TD$age_years, curr_age_data_TD$overall_mean)
- a = paired.r(ASD_cor_test$estimate,TD_cor_test$estimate, NULL, 504,505)
- #Investigating effects of meanFD on metrics
- curr_meanFD_data = curr_data %>%
- group_by(subid, mean_fd, diagnosis) %>%
- summarise(overall_mean = mean(measure))
- #Correlation meanFD ~ autism
- asd_data = curr_meanFD_data %>%
- filter(diagnosis == "Autism")
- corr_autism_meanFD = cor.test(asd_data$overall_mean, asd_data$mean_fd)
- #Correlation meanFD ~ TD
- control_data = curr_meanFD_data %>%
- filter(diagnosis == "Control")
- corr_td_meanFD = cor.test(control_data$overall_mean, control_data$mean_fd)
- #Fisher z-test
- curr_paired_r = paired.r(corr_autism_meanFD$estimate, corr_td_meanFD$estimate, NULL,
- nrow(asd_data), nrow(control_data))
- curr_paired_p = curr_paired_r$p
- curr_paired_z = curr_paired_r$z
- curr_paired_stats = data.frame(
- corr_autism_meanFD$estimate, corr_td_meanFD$estimate, curr_paired_z, curr_paired_p)
- #Saving
- corr_meanFD_summary[[curr_measure]] = curr_paired_stats
- ### PLOTS
- #Inverting Lambda-levels
- curr_data$lambda = as.factor(curr_data$lambda) #Only for the plots!
- #curr_data$lambda = factor(curr_data$lambda, levels = rev(levels(curr_data$lambda)))
- #Measure dx meanFD
- curr_meanFD_plot = ggplot(data = curr_meanFD_data, aes(x = mean_fd, y = overall_mean, colour = diagnosis, group = diagnosis)) +
- geom_jitter() +
- geom_smooth(method = "lm") +
- ylab(plot_labels[counter]) +
- xlab("mean framewise displacement") +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- theme_minimal() +
- theme(
- axis.title = element_text(size = 30),
- axis.text = element_text(size = 30),
- legend.text = element_blank(),
- legend.title = element_blank(),
- legend.position = "none")
- #Boxplot of measure and Lambda by diagnosis
- curr_dx_Lambda_by_diagnosis_boxplot = ggplot(data = curr_data,
- aes(x = lambda,
- y = measure,
- color = diagnosis)) +
- geom_jitter(position = position_jitterdodge(jitter.width = 0.5, dodge.width = 1), alpha = 0.5, size = 1.5) +
- geom_boxplot(aes(group = interaction(lambda, diagnosis)), colour = "black",fill = NA,position=position_dodge(width=1),outlier.shape = NA, size = 1.5) +
- ylab(plot_labels[counter]) +
- xlab("Lambda (in 1/mm)") +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- theme_minimal() +
- theme(
- axis.title = element_text(size = 30),
- axis.text = element_text(size = 30),
- legend.text = element_blank(),
- legend.title = element_blank(),
- legend.position = "none")
- #Measure dx age
- curr_age_plot = ggplot(data = curr_age_data, aes(x = age_years, y = overall_mean, colour = diagnosis, group = diagnosis)) +
- geom_jitter(size = 4) +
- geom_smooth(method = "lm", linewidth = 4) +
- scale_color_manual(values = c("Control" = "#1f77b4", "Autism" = "#ff7f0e")) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank(),
- legend.position = "none")
- #Measure dx ADOS
- curr_ados_plot = ggplot(data = curr_ados_data, aes(x = ados, y = overall_mean)) +
- geom_jitter(size = 5) +
- geom_smooth(method = "lm", linewidth = 3) +
- theme_minimal() +
- theme(
- axis.text = element_blank(),
- axis.title = element_blank(),
- legend.position = "none")
- # Correlation interaction for Turbulence
- if (curr_measure == "turbulence"){
- lambdas_to_plot = c(0.01,0.28)
- for (curr_lambda in lambdas_to_plot){
- curr_lam_data = curr_data %>% filter(lambda == curr_lambda)
- curr_ados_lambda_plot = ggplot(data = curr_lam_data, aes(x = ados, y = measure)) +
- geom_jitter(size = 5) +
- geom_smooth(method = "lm", linewidth = 3) +
- theme_minimal() +
- theme(
- axis.text = element_blank(),
- axis.title = element_blank(),
- legend.position = "none")
- #Correlation Tests
- # Correlations of measures with ados
- curr_ados_corr = cor.test(curr_lam_data$ados, curr_lam_data$measure)
- turbulence_ados_stats[[paste0(curr_lambda)]] = data.frame(
- ados_corr_p = curr_ados_corr$p.value,
- ados_corr_r = curr_ados_corr$estimate)
- print(curr_ados_lambda_plot)
- ##ggsave(paste0(figures_directory,"/supplementary/S3/",curr_measure,"_",curr_lambda,"_ados_corr.jpg"), curr_ados_lambda_plot, height = 6, width = 10, dpi = 300)
- }
- }
- #Printing and saving
- print(curr_dx_Lambda_by_diagnosis_boxplot)
- print(curr_age_plot)
- #print(curr_ados_plot)
- #print(curr_meanFD_plot)
- ##ggsave(paste0(figures_directory,"/Figure_6/box_",curr_measure,".pdf"), curr_dx_Lambda_by_diagnosis_boxplot, height = 12, width = 16)
- ##ggsave(paste0(figures_directory,"/Figure_6/diff_",curr_measure,".pdf"), curr_diff_plot, height = 12, width = 16)
- ##ggsave(paste0(figures_directory,"/supplementary/S3/",curr_measure,"_ados_corr.jpg"), curr_ados_plot, height = 6, width = 10, dpi = 300)
- #ggsave(paste0(figures_directory,"/supplementary/S5/",curr_measure,"_age_corr.jpg"), curr_age_plot, height = 6, width = 10, dpi = 300)
- }
- ```
- 3. Network-wise analysis
- In this part of the analysis, we want to investigate if there are Turbulence patterns of ASD vs. TD in different RSNs.
- To do so, we leverage the node-turbulence and RSN turbulence matrices.
- 3.1. Node-wise RSN analysis. Starting from individual node turbulence matrices (nNodes*nLambda). Computing average for ASD and TD and calculate the difference.
- The highest 10% values of differences are extracted for each lambda. These 100 nodes are then associated to the RSNs.
- ```{R, message=FALSE, warning=FALSE}
- ### Loading and initializing data
- #Mapping from RSN number to label:
- rsns = c("VIS", "SOM", "DAN", "VAN", "LIM", "CON", "DMN", "SUB")
- rsnStats = list()
- rsn_labels = unlist(readMat(file.path(root, "code/labels.mat"))) #Loading RSN-labels of parcellation
- schaef_regions = read.table(file.path(root, "code/Schaefer2018_1000Parcels_7Networks_order.txt")) #Load Schaefer region names
- tian_regions = read.table(file.path(root, "code/Tian_Subcortex_S4_3T_label.txt")) #Load Tian region names
- Lambda = c(0.28,0.25,0.22,0.19,0.16,0.13,0.10,0.07,0.04,0.01)
- #Pre-allocation
- parcel_indices = list()
- parcel_ind_TD_higher = list()
- nodeTurb_residuals = array(NA,dim = c(nParcels, nLambda, nrow(filteredPheno)))
- # Testing if parcel level turbulence is associated to FD differently between ASD and TD
- Autism_data = nodeTurb[which(filteredPheno$diagnosis == "Autism")]
- TD_data = nodeTurb[which(filteredPheno$diagnosis == "Control")]
- my_ps = data.frame()
- my_zs = data.frame()
- for (curr_parcel in 1:nParcels){
- for (curr_lambda in 1:nLambda){
- ### -------- Correlations of parcel-level turbulence with FD -------- ###
- #Correlation of parcel turbulence with FD for ASD
- currASD_data = drop_na(data.frame(
- brain = sapply(Autism_data, function(x) {x[curr_parcel,curr_lambda]}),
- fd = filteredPheno[filteredPheno$diagnosis == "Autism",]$mean_fd))
- curr_cor_ASD = cor.test(currASD_data$brain,currASD_data$fd)
- #Correlation of parcel turbulence with FD for TD
- currTD_data = drop_na(data.frame(
- brain = sapply(TD_data, function(x) {x[curr_parcel,curr_lambda]}),
- fd = filteredPheno[filteredPheno$diagnosis == "Control",]$mean_fd))
- curr_cor_TD = cor.test(currTD_data$brain,currTD_data$fd)
- # Ztest of difference
- curr_paired = paired.r(curr_cor_ASD$estimate, curr_cor_TD$estimate, NULL, nrow(currASD_data), nrow(currTD_data))
- my_ps[curr_parcel,curr_lambda] = curr_paired$p
- my_zs[curr_parcel,curr_lambda] = curr_paired$z
- ### ------ computation of parcel-level turbulence residuals after covariate regression ------ ###
- curr_Parcel_Turb_data = drop_na(data.frame(
- parcel_turb = sapply(nodeTurb, function(x) {x[curr_parcel,curr_lambda]}),
- fd = filteredPheno$mean_fd,
- sex = as.factor(filteredPheno$sex),
- age = filteredPheno$age_years))
- my_model = lm(formula = parcel_turb ~ fd + sex + age, data = curr_Parcel_Turb_data)
- curr_residuals = resid(my_model)
- nodeTurb_residuals[curr_parcel, curr_lambda,] = curr_residuals
- }
- }
- #Compute FDR
- my_ps_fdr = data.frame(matrix(
- p.adjust(flatten(my_ps), method = "BH"),
- nrow = nParcels,
- ncol = length(lambda_flattened)
- ))
- #write_csv(my_zs, paste0(root,"/code/z_values_meanFD_parcelTurb.csv"))
- #write_csv(my_ps_fdr, paste0(root,"/code/p_values_meanFD_parcelTurb.csv"))
- ### No significant differences in correlations between groups
- #Set data to node Turbulence residuals
- nSubjects = dim(nodeTurb_residuals)[3]
- nodeTurb_residuals = lapply(
- 1:nSubjects,
- function(s) nodeTurb_residuals[ , , s]
- )
- names(nodeTurb_residuals) = names(nodeTurb)
- #Extracting masks of ASD and control
- ASD_labels = which(filteredPheno$diagnosis == "Autism")
- TD_labels = which(filteredPheno$diagnosis == "Control")
- ASD_node = nodeTurb_residuals[ASD_labels]
- TD_node = nodeTurb_residuals[TD_labels]
- parcel_labels = c(unlist(schaef_regions$V2), tian_regions$V1)
- #Computing means and effect sizes
- ASD_mean = Reduce("+", ASD_node) / length(ASD_labels)
- TD_mean = Reduce("+", TD_node) / length(TD_labels)
- ASD_3d = simplify2array(ASD_node)
- TD_3d = simplify2array(TD_node)
- n_asd = length(ASD_node)
- n_td = length(TD_node)
- #Standard deviation
- pooled_sd = sqrt((
- apply(ASD_3d, c(1,2), var)*(n_asd-1) +
- apply(TD_3d, c(1,2), var)*(n_td-1)
- )/(n_asd+n_td-2))
- #Save Cohen's D
- turb_CohensD = (TD_mean-ASD_mean)/pooled_sd
- ### Network-wise analysis and radar plots
- #Absolute node-diff
- node_diff = abs(TD_mean-ASD_mean)
- # Number of parcels to be investigated
- n_Top_Parcels = 200
- #Extracting top 10% of absolute node-difference for each lamba
- ind = apply(node_diff, 2, function(col) {
- top = sort(col, decreasing = TRUE)[1:n_Top_Parcels]
- col %in% top})
- #Save in list
- parcel_indices = ind
- rsn_ind = apply(ind, 2, function(col) rsn_labels[col]) #Assign to each of these parcels its RSN according to the Schaefer parcellation
- counts = apply(rsn_ind, 2, table) #Compute for each Lambda its counts
- #Add missing values in the counts with 0:
- list_names = sapply(counts, names)
- for (i in 1:length(counts)){
- for (ii in 1:length(rsns)){
- if (!(as.character(ii) %in% list_names[[i]])){
- counts[[i]][as.character(ii)] = 0
- }
- }
- to_order = counts[[i]]
- counts[[i]] = to_order[order(as.numeric(names(to_order)))]
- }
- #Inverting the list to save in a data-frame.
- #Also, scale each count with the maximum number of parcels of that RSN. Result is a percentage score for each RSN
- n_parcels_RSN = as.numeric(table(rsn_labels))
- counts_new = matrix(nrow = length(Lambda), ncol = length(rsns))
- for (i in 1:length(counts)){
- counts_new[i,] = as.numeric(counts[[i]])#/n_parcels_RSN
- }
- #counts_new = counts_new*(1/max(counts_new))
- counts = data.frame(Lambda, counts_new)
- colnames(counts) = c("Lambda",rsns)
- rownames(counts) = Lambda
- ##### Directed analysis, i.e., checking which networks are higher in ASD vs. TD
- #Compute directed node-difference
- node_direct = TD_mean-ASD_mean
- #Checking for each lambda, how many of the values are higher
- #preallocation
- ind_TD_higher = ind
- rsn_diff = 0
- for (lam in 1:10){
- count = 0
- curr_vec = node_direct[,lam]
- for (parcel in 1:length(curr_vec)){
- ind_TD_higher[parcel,lam] = FALSE
- if (curr_vec[parcel] > 0 && ind[parcel,lam]){
- count = count+1
- ind_TD_higher[parcel,lam] = TRUE
- }
- }
- rsn_diff[lam] = count
- }
- parcel_ind_TD_higher = ind_TD_higher
- ### Plotting
- color_vec = colorRampPalette(c("#1f77b4", "black", "#ff7f0e"))(10)
- #1) Combined radarplot
- combined_radar = ggradar(counts,
- axis.label.size = 5,
- legend.position = "right",
- group.point.size = 4,
- group.colours = (color_vec),
- group.line.width = 1.2,
- values.radar = c(0,max(counts)/2,max(counts)),
- base.size = 10,
- label.gridline.min = FALSE,
- grid.mid = max(counts)/2,
- grid.max = max(counts),
- legend.text.size = 10,
- legend.title = "Lambda",
- background.circle.colour = "white",
- background.circle.transparency = 0.2) +
- theme(
- #axis.title = element_text(size = 18), # axis labels
- #axis.text = element_text(size = 18), # x and y axis tick labels
- #plot.title = element_text(size = 18, hjust = 0.5), # title centered and larger
- legend.text = element_blank(), # legend label font size
- legend.title = element_blank(),
- legend.position = "none")
- #2) Barplot which shows the distribution of contribution of autism and TD as a function of lambda
- # Adjust rsn_diff so the reference point is 50
- dist_data = data.frame(
- Lambda = rep(Lambda,2),
- values = c((n_Top_Parcels - rsn_diff),rsn_diff),
- cohort = rep(c("TD>Autism", "Autism>TD"), each = nLambda)
- )
- distribution_plot = ggplot(dist_data, aes(x = Lambda, y = values, fill = cohort)) +
- geom_bar(stat = "identity") +
- scale_fill_manual(values = c("TD>Autism" = "#00CED1", "Autism>TD" = "#A0522D")) +
- scale_x_reverse() +
- labs(
- x = "Lambda (in 1/mm)",
- y = "#parcels TD > ASD (in %)",
- fill = "Distribution"
- ) +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_blank(),
- axis.ticks.x = element_blank(),
- axis.ticks.y = element_blank(),
- plot.title = element_blank(),
- legend.position = "none",
- panel.background = element_rect(fill = "white", color = NA),
- panel.grid.major.y = element_line(color = "grey75", size = 0.5),
- panel.grid.minor.y = element_line(color = "grey75", size = 0.25),
- panel.grid.minor.x = element_blank(),
- panel.grid.major.x = element_blank()
- )
- #Printing and saving
- print(distribution_plot)
- print(combined_radar)
- #ggsave(paste0(figures_directory,"/Figure_4/RSN_distribution_",n_Top_Parcels, "parcels.jpg"), distribution_plot, height = 12, width = 16, dpi = 300)
- #ggsave(paste0(figures_directory,"/Figure_4/RSN_radar_",n_Top_Parcels, "parcels.jpg"), combined_radar, height = 6, width = 6,dpi = 300)
- ```
- 3.2 Association of 100 Nodes with biggest differences to Sydnor gradients
- ```{R}
- #First, we need to compute the rankings of the 1054 parcels. We do this with a python script. The input to the python script are the parcel-level effect sizes of both, the synchronization strength, as well as the turbulence. Here we save these into the directory in which the code of the python script operates.
- #Turbulence data
- currAutism_data = nodeTurb_residuals[which(filteredPheno$diagnosis == "Autism")]
- currAutism_data = data.frame(Reduce("+", currAutism_data) / length(currAutism_data))
- currTD_data = nodeTurb_residuals[which(filteredPheno$diagnosis == "Control")]
- currTD_data = data.frame(Reduce("+", currTD_data) / length(currTD_data))
- curr_d = turb_CohensD
- #Save csv files
- write_csv(data.frame(currAutism_data), paste0(root,"/data/gradient_analysis/", paste0("parcel_turb","_autism_1054", ".csv")))
- write_csv(data.frame(currTD_data), paste0(root,"/data/gradient_analysis/", paste0("parcel_turb","_TD_1054", ".csv")))
- write_csv(data.frame(curr_d), paste0(root,"/data/gradient_analysis/", paste0("parcel_turb","_CohensD_1054", ".csv")))
- #Run the python script with the reticulate option
- use_virtualenv("~/pyenvs/reticulate_arm", required = TRUE)
- use_python("/Users/bened/pyenvs/reticulate_arm/bin/python", required = TRUE)
- py_run_file(paste0(root, "/code/", "gradient_computations.py"))
- ```
- Association of parcel-level turbulence levels with S-A and functional gradients
- ```{R}
- #Loading in data
- weighted_sydnor = read.csv(paste0(root, "/data/gradient_analysis/gradients_1054.csv"))
- weighted_sydnor = weighted_sydnor[1:1000,c(1:11)]#only cortical regions
- names(weighted_sydnor) = c("Anatomical T1w/T2w", "Functional Gradient", "Cortical Expansion", "Allometric Scaling",
- "Aerobic Glycolysis", "CBF", "Gene Expression", "NeuroSynth", "Externopyramidization", "Cortical Thickness",
- "Sensorimotor-Association Gradient")
- plotnames = c("Anatomical_T1wT2w", "Functional_Gradient", "Cortical_Expansion", "Allometric_Scaling",
- "Aerobic_Glycolysis", "CBF", "Gene_Expression", "NeuroSynth", "Externopyramidization", "Cortical_Thickness",
- "Sensorimotor_Association_Gradient")
- #Directions of the gradient for each measure: True from high to low, False from Low to high
- gradient_directions = data.frame(
- gradient = names(weighted_sydnor),
- direction = c(FALSE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, TRUE, FALSE, TRUE, TRUE)
- )
- #Since we are doing the comparison of the parcel-level turbulence with the S-A axes in the Schaefer1000 parcellation, we have to compute the parcels with the biggest differences again on that parcellation
- rsn_labels = rsn_labels[1:1000] #Change labels to only the first 1000
- parcel_labels = c(unlist(schaef_regions$V2))
- node_cortical = lapply(nodeTurb_residuals, function(row) row[1:1000,])
- ASD_node = node_cortical[ASD_labels]
- TD_node = node_cortical[TD_labels]
- #Computing means and effect sizes
- ASD_mean = Reduce("+", ASD_node) / length(ASD_labels)
- TD_mean = Reduce("+", TD_node) / length(TD_labels)
- ASD_3d = simplify2array(ASD_node)
- TD_3d = simplify2array(TD_node)
- n_asd = length(ASD_node)
- n_td = length(TD_node)
- pooled_sd = sqrt((
- apply(ASD_3d, c(1,2), var)*(n_asd-1) +
- apply(TD_3d, c(1,2), var)*(n_td-1)
- )/(n_asd+n_td-2))
- #Save Cohen's D
- turb_CohensD = (TD_mean-ASD_mean)/pooled_sd
- ### Network-wise analysis and radar plots
- #Absolute node-diff
- node_diff = abs(TD_mean-ASD_mean)
- #Extracting top 10% of absolute node-difference for each lamba
- ind = apply(node_diff, 2, function(col) {
- top = sort(col, decreasing = TRUE)[1:100]
- col %in% top})
- parcel_indices = ind
- ### Directed analysis
- #Compute directed node-difference
- node_direct = TD_mean-ASD_mean
- #Checking for each lambda, how many of the values are higher
- #preallocation
- ind_TD_higher = ind
- rsn_diff = 0
- for (lam in 1:10){
- count = 0
- curr_vec = node_direct[,lam]
- for (parcel in 1:length(curr_vec)){
- ind_TD_higher[parcel,lam] = FALSE
- if (curr_vec[parcel] > 0 && ind[parcel,lam]){
- count = count+1
- ind_TD_higher[parcel,lam] = TRUE
- }
- }
- rsn_diff[lam] = count
- }
- parcel_ind_TD_higher = ind_TD_higher
- #### Assign to each of the 100 highest nodes a label, if TD is higher -> True
- my_parcels = matrix(sapply(1:ncol(ind), function(j) {
- parcel_ind_TD_higher[parcel_indices[, j], j]
- }), nrow = 100, ncol = 10)
- gradient_mod_comp = list()
- ### Statistical analysis. Looping through the (selected) gradients to statistically test which model fits best
- for (metric in c(2,11)){
- #Pre-allocation
- curr_csv = weighted_sydnor
- data_sensory_assoc = data.frame()
- #Loading data for that specific gradient
- for (i in 1:10){
- curr_data = data.frame(
- score = curr_csv[[metric]][parcel_indices[,i]],
- cohens_d = turb_CohensD[parcel_indices[,i],i],
- lambda = as.factor(rep(lambda_flattened[i],100)),
- parcel = c(1:1000)[parcel_indices[,i]])
- curr_data$group = as.vector(my_parcels[,i])
- data_sensory_assoc = rbind(data_sensory_assoc, curr_data)
- }
- #Grouping by lambda
- data_sensory_assoc = data_sensory_assoc %>%
- group_by(lambda)
- data_sensory_assoc$lambda = as.numeric(as.character(data_sensory_assoc$lambda))
- # Model Fitting
- # 1) Logistic models (modelling the sigmoidal shape)
- # Selection/Computation of initial values for upper, lower asymptode, slope and inflection point
- asymp_up_start = max(data_sensory_assoc$score) # upper asymptode
- asymp_low_start = min(data_sensory_assoc$score) # lower asymptode
- x0_start = mean(range(data_sensory_assoc$lambda)) # inflection point
- k_start = 1 # slope of inflection. negative for decreasing shape
- # Fit 4-parameter logistic model
- logistic_model_4 = nlsLM(
- formula = score ~ asymp_low + (asymp_up - asymp_low) / (1 + exp(k * (lambda - x0))),
- data = data_sensory_assoc,
- start = list(
- asymp_up = asymp_up_start,
- asymp_low = asymp_low_start,
- k = k_start,
- x0 = x0_start
- ),
- trace = TRUE
- )
- print(summary(logistic_model_4))
- #Fitted values for logistic models
- data_sensory_assoc$fitted_log4 = predict(logistic_model_4)
- # 2) Linear Model
- linear_model = lm(score ~ lambda, data = data_sensory_assoc)
- data_sensory_assoc$fitted_linmod = predict(linear_model)
- # 3) Polynomial model
- poly_model = lm(score ~ stats::poly(lambda, 3, raw = TRUE), data = data_sensory_assoc)
- data_sensory_assoc$fitted_poly = predict(poly_model)
- #Comparing the models
- #RSS: raw Model fit., AIC: balance between fit and complexity, BIC: penalizes model complexity more
- c_log_4 = c(sum(residuals(logistic_model_4)^2), AIC(logistic_model_4), BIC(logistic_model_4))
- c_lin = c(sum(residuals(linear_model)^2), AIC(linear_model), BIC(linear_model))
- c_poly = c(sum(residuals(poly_model)^2), AIC(poly_model), BIC(poly_model))
- model_comp = data.frame(
- linear = c_lin,
- logistic_4 = c_log_4,
- polynomial = c_poly
- )
- rownames(model_comp) = c("RSS", "AIC", "BIC")
- gradient_mod_comp[["measures"]] = rownames(model_comp)
- gradient_mod_comp[[names(weighted_sydnor)[metric]]] = model_comp
- gradient_mod_comp[[paste0(names(weighted_sydnor)[metric],"_", "models")]] = list(linear_model,poly_model,logistic_model_4)
- ### Plotting
- # 1 is Control, 0 is TD
- data_sensory_assoc$group = factor(data_sensory_assoc$group,
- levels = c(0, 1),
- labels = c("Autism", "Control"))
- #Compute median for each lambda for plot
- my_medians = data_sensory_assoc %>%
- group_by(lambda) %>%
- summarise(median_score = median(score))
- my_medians$lambda = as.numeric(my_medians$lambda)
- data_sensory_assoc$lambda = as.factor(data_sensory_assoc$lambda)
- data_sensory_assoc$lambda = factor(data_sensory_assoc$lambda, levels = rev(levels(data_sensory_assoc$lambda)))
- #Direction of gradient for that specific metric
- if (gradient_directions$direction[metric]){
- colors = c("goldenrod1","white","#6f1282" )
- } else{
- colors = c("#6f1282","white", "goldenrod1")}
- #Plotting the association of node-turb with the gradient scores for each lambda
- curr_plot = ggplot(aes(x = lambda, y = score), data = data_sensory_assoc) +
- geom_jitter(aes(fill = score, size = cohens_d),
- shape = 21, stroke = 2, colour = "black", width = 0.1, show.legend = c(fill = TRUE, size = FALSE)) +
- labs(size = "Cohen's d") +
- scale_fill_gradientn(colors = colors, guide = guide_colorbar(barwidth = 1.5, barheight = 10)) +
- scale_size_continuous(range = c(-4,10)) +
- geom_boxplot(aes(x = lambda, y = score), size = 1.5, colour = "black", fill = NA,
- position = position_dodge(width = 1), outlier.shape = NA) +
- ylab(names(weighted_sydnor)[metric]) +
- xlab("Lambda (in 1/mm)") +
- theme_minimal() +
- theme(
- axis.title = element_blank(),
- axis.text = element_text(size = 30),
- plot.title = element_blank(),
- axis.title.x = element_blank(),
- axis.text.x = element_blank(),
- axis.ticks.x = element_blank(),
- legend.title = element_blank(),
- legend.text = element_text(size = 30)
- ) +
- geom_line(data = data_sensory_assoc, aes(x = lambda, y = fitted_log4, group = 1), color = "black", linetype = "solid", linewidth = 4)
- print(curr_plot)
- #ggsave(paste0(figures_directory,"/Figure_5/",names(weighted_sydnor)[metric],"_S_SA.jpg"), curr_plot, width = 13.4, height = 8.4, dpi = 300)
- }
- ```
- 3.3 Printing the association of Node-Turbulence to Sydnor gradients
- ```{R}
- #Loading data. The computation of the effect sizes of the 400 parcels has been done with the above python script and is only imported here. The matrices have the size (400x10), where each row corresponds to a parcel and each column to a Lambda value. The matrices are filled with Cohen's d effect sizes (TD-autism)
- SA_data_path = paste0(root,"/data/gradient_analysis/")
- #Loading the converted parcel-level data (for Schaefer400).
- parcel_names = c("parcel_turb_autism_s400", "parcel_turb_TD_s400", "parcel_turb_cohensD_s400")
- parcel_data = list()
- for (currName in parcel_names){
- parcel_data[[currName]] = data.frame(read.csv(paste0(SA_data_path,currName,".csv")))
- }
- # Load schafer atlas
- data("schaefer17_400")
- df = schaefer17_400$data
- my_labs = c(schaefer17_400_3d$ggseg_3d[[1]]$label[-1], schaefer17_400_3d$ggseg_3d[[2]]$label[-1])
- sens_assoc = read_csv(paste0(root,"/code/Sensorimotor_Association_Axis_AverageRanks.csv"))
- brainmaps_gradients = read_csv(paste0(root,"/code","/brainmaps_schaefer.csv"))
- #Preallocation
- curr_lambda_plots = list()
- for (currLam in 1:10){
- #plot init
- curr_data = data.frame(
- dat = parcel_data$parcel_turb_cohensD_s400[,currLam])
- df_gradients = cbind(curr_data, brainmaps_gradients, sens_assoc)
- # Step 1: Filter atlas to labels in my_labs
- df_filtered = df %>%
- dplyr::filter(label %in% my_labs)
- # Step 2: Match each label with its corresponding value
- df_ordered = df_filtered %>%
- left_join(df_gradients, by = "label")
- # Step 3: rearrange the atlas
- schaefer17_400$data = df_ordered
- colors = c("goldenrod1","white","#6f1282" )
- #Adjust color scale
- # Map to quantiles for smoother gradient
- probs = seq(0, 1, length.out = 100)
- quant_vals = quantile(df_ordered$dat, probs, na.rm = TRUE)
- # curr_plot
- curr_plot = ggseg(
- atlas = schaefer17_400,
- mapping = aes(fill = dat),
- color = "black",
- size = 0.3
- ) +
- scale_fill_gradientn(
- colors = colors,
- values = scales::rescale(quant_vals, from = range(quant_vals)),
- limits = range(quant_vals),
- oob = scales::squish
- ) +
- theme_void() +
- theme(
- legend.title = element_blank(),
- legend.text = element_blank(),
- legend.key.height = unit(0.4, "cm"),
- legend.key.width = unit(0.4, "cm"),
- legend.position = "none"
- )
- #Add to list
- curr_lambda_plots[[currLam]] = curr_plot
- }
- # S–A gradient
- SA_plot = ggseg(
- atlas = schaefer17_400,
- mapping = aes(fill = finalrank.wholebrain),
- color = "black",
- size = 0.3) +
- scale_fill_gradientn(colors = colors, values = c(0, 0.5, 1)) +
- theme_void() +
- theme(legend.position = "none")
- #Functional gradient
- FUNC_plot = ggseg(
- atlas = schaefer17_400,
- mapping = aes(fill = G1.fMRI),
- color = "black",
- size = 0.3) +
- scale_fill_gradientn(colors = colors, values = c(0, 0.5, 1)) +
- theme_void() +
- theme(legend.position = "none")
- #Plotting
- plot_selected_lambdas = curr_lambda_plots[[1]]/curr_lambda_plots[[4]]/curr_lambda_plots[[7]]/curr_lambda_plots[[10]]/SA_plot/FUNC_plot
- print(plot_selected_lambdas)
- #ggsave(paste0(figures_directory,"/Figure_5/parcel_turb_S400.jpg"), plot_selected_lambdas, width = 16, height = 16, dpi = 300)
- #Heatmaps of correlation values for SA and func gradient with parcel-level turbulence
- #load rho values and p-values
- corr_results = read.csv(paste0(root, "/data/gradient_analysis/corr_results.csv"))
- #DF for plotting
- df_SA_heatmap = data.frame(
- Variable = rep(factor(Lambda), 2),
- rho = round(c(corr_results$rho_SA, corr_results$rho_func),2),
- neglog10p = c(-log10(corr_results$p_SA), -log10(corr_results$p_func)),
- x = rep(c(0,0.6), each = 10)
- )
- #Create the colormap
- SA_heatmaps = ggplot(df_SA_heatmap, aes(x = x, y = Variable, fill = neglog10p)) +
- geom_tile(color = "white", width = 0.6) +
- geom_text(aes(label = sprintf("%.2f", rho)), size = 10) +
- scale_fill_distiller(palette = "OrRd", direction = 1) +
- scale_x_discrete(expand = c(0, 0.1)) +
- labs(
- x = "",
- y = "",
- fill = "-log10(p)"
- ) +
- theme_minimal() +
- theme(
- axis.text.x = element_blank(),
- axis.ticks.x = element_blank(),
- axis.text.y = element_blank(),
- axis.ticks.y = element_blank(),
- panel.grid = element_blank(),
- legend.position = "none",
- plot.margin = margin(5, 5, 5, 5)
- ) +
- coord_fixed(ratio = 0.25)
- print(SA_heatmaps)
- ##ggsave(paste0(figures_directory,"/Figure_5/SA_heatmaps.jpg"), SA_heatmaps, dpi = 300)
- ```
Turbulence_postproc_analysis.Rmd at commit 15de1d4, no license · at the source
Overview
- Laboratory for Autism and Neurodevelopmental Disorders, Center for Neuroscience and Cognitive Systems, Istituto Italiano di Tecnologia, Rovereto, Italy
- Center for Mind/Brain Sciences, University of Trento, Rovereto, Italy
- Center for Brain and Cognition, Computational Neuroscience Group, Faculty of Medicine and Life Sciences, Universitat Pompeu Fabra, Barcelona, Spain
- Institució Catalana de la Recerca i Estudis Avançats (ICREA), Barcelona, Spain
- Centre for Eudaimonia and Human Flourishing, Linacre College, University of Oxford, Oxford, United Kingdom
- International Centre for Flourishing, Universities of Oxford (United Kingdom), Aarhus (Denmark), and Pompeu Fabra (Spain)
- Department of Clinical Medicine, Aarhus University, Aarhus, Denmark
- Department of Psychiatry, University of Oxford, Oxford, United Kingdom
- Department of Psychiatry, Radboudumc, Nijmegen, the Netherlands
- Donders Institute for Brain, Cognition and Behavior, Radboud University, Nijmegen, the Netherlands
Abstract
Background: Prevailing theories propose that autism is characterized by local cortical overconnectivity and long-range underconnectivity, but empirical evidence remains mixed.
Methods: Here, we applied the turbulence dynamics framework to the ABIDE (Autism Brain Imaging Data Exchange) dataset (N = 1009) to examine how functional synchronization profiles dynamically change over time and across the cortex over different spatial scales.
Results: Autistic individuals showed increased short-range and reduced long-range functional synchronization variability over time, as well as reduced synchronization strength across all spatial scales. Synchronization also decayed more rapidly with distance and exerted weaker influence across scales in autism. These distance-specific alterations suggest that local hyperconnectivity may be associated with turbulent synchronization dynamics that fail to propagate coherently across the cortex, resulting in an overly rigid brain organization at longer distances. Mapping these effects onto the sensorimotor–association
Conclusions: Together, we found evidence of disturbances in functional synchronization dynamics at different spatial scales and along hierarchical brain gradients in autistic individuals. These results consolidate ideas about dynamic functional connectomic organization in autism and situate these alterations along hierarchical brain gradients that are closely linked to neurodevelopmental processes.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 7 matches between paragraphs and lines of code.
gitlab.iit.it/bmp006-public/land_iit/turbulence_abide
15de1d443f70190d86a707b7ff8543f64b2f26b3, 21 March 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
16 files
- code/
.ipynb_checkpoints/ , Jupyter, 194 linesgradient_computations-ch eckpoint.ipynb - code/
.ipynb_checkpoints/ , Python, 300 linesgradient_computations-ch eckpoint.py - code/
.ipynb_checkpoints/ , Python, 291 linesgradient_computations_tr y-checkpoint.py - code/
Turbulence_postproc_anal , R, 1,459 lines, 3 matchesysis.Rmd - code/
demean.m , MATLAB, 23 lines - code/
gradient_computations.ip , Jupyter, 194 lines, 2 matchesynb - code/
gradient_computations.py , Python, 203 lines - code/
parcellate.m , MATLAB, 37 lines - code/
pre_processing/ , Python, 96 linesdvars_se.py - code/
pre_processing/ , Python, 81 linesfd.py - code/
pre_processing/ , Shell, 25 linespreprocessing_pipeline_e xample.sh - code/
pre_processing/ , Python, 576 linesspeedyppX.py - code/
pre_processing/ , Shell, 66 linesspeedyppX_example.sh - code/
turbulence.m , MATLAB, 309 lines, 2 matches - code/
turbulence_master.sh , Shell, 26 lines - README.md, Text, 107 lines
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 15 scripts, each with its path and the digest of its content;
- 7 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.
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 2, 28 September 2026
- Authors: added Benedikt P Fuhr (0009-0005-5879-9198); Michael V Lombardo (0000-0001-6780-8619); removed Benedikt P Fuhr; Michael V Lombardo
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 7 authors, 6 keywords, 1 funder, 62 references.
Cite
This paper
Fuhr, B. P., Perl, Y. S., Severino, I., Kringelbach, M. L., Deco, G., Ruhé, H. G., & Lombardo, M. V. (2026). Dynamic Functional Synchronization Profiles in Autism Differ by Spatial Scale and Along Hierarchical Cortical Gradients. Biological psychiatry global open science, 6(5), 100787. https://
BibTeX
@article{fuhr2026dynamic
author = {Fuhr, Benedikt P and Perl, Yonatan Sanz and Severino, Ines and Kringelbach, Morten L and Deco, Gustavo and Ruhé, Henricus G and Lombardo, Michael V},
title = {{Dynamic Functional Synchronization Profiles in Autism Differ by Spatial Scale and Along Hierarchical Cortical Gradients}},
journal = {Biological psychiatry global open science},
year = {2026},
month = jul,
volume = {6},
number = {5},
pages = {100787},
publisher = {Elsevier},
issn = {2667-1743},
doi = {10.1016/
url = {https://
pmid = {42602560},
pmcid = {PMC13475223}
}
RIS
TY - JOUR
AU - Fuhr, Benedikt P
AU - Perl, Yonatan Sanz
AU - Severino, Ines
AU - Kringelbach, Morten L
AU - Deco, Gustavo
AU - Ruhé, Henricus G
AU - Lombardo, Michael V
TI - Dynamic Functional Synchronization Profiles in Autism Differ by Spatial Scale and Along Hierarchical Cortical Gradients
T2 - Biological psychiatry global open science
J2 - Biol Psychiatry Glob Open Sci
PY - 2026
DA - 2026/
VL - 6
IS - 5
SP - 100787
SN - 2667-1743
PB - Elsevier
DO - 10.1016/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1016/
"type": "article-journal",
"title": "Dynamic Functional Synchronization Profiles in Autism Differ by Spatial Scale and Along Hierarchical Cortical Gradients",
"container-title": "Biological psychiatry global open science",
"author": [
{
"family": "Fuhr",
"given": "Benedikt P"
},
{
"family": "Perl",
"given": "Yonatan Sanz"
},
{
"family": "Severino",
"given": "Ines"
},
{
"family": "Kringelbach",
"given": "Morten L"
},
{
"family": "Deco",
"given": "Gustavo"
},
{
"family": "Ruhé",
"given": "Henricus G"
},
{
"family": "Lombardo",
"given": "Michael V"
}
],
"container-title-short":
"volume": "6",
"issue": "5",
"page": "100787",
"DOI": "10.1016/
"PMID": "42602560",
"PMCID": "PMC13475223",
"ISSN": "2667-1743",
"publisher": "Elsevier",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
8
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1038/s41593-026-02287-z [code]
- Autism subtypes identified using cross-species functional connectivity analyses.Journal: Nature neuroscienceIn common: AFNI, FSL, NiBabel, 2 other tools, autism, fMRI, 12 references, author Michael V Lombardo
- [2] doi:10.1038/s41467-026-76011-7 [code]
- Human cortex organizes dynamic co-fluctuations along the sensorimotor-association
axis. Journal: Nature communicationsIn common: BrainSpace, AFNI, FSL, 7 other tools, systems, 6 references - [3] doi:10.1038/s42003-026-10276-y [code]
- The cellular correlates and adolescent reorganisation of cortical myelination networks in the common marmoset.Journal: Communications biologyIn common: BrainSpace, AFNI, Optimization Toolbox, 8 other tools, 4 references
- [4] doi:10.1038/s41531-026-01354-3 [code]
- Neuromodulation-induced normalization of cortical metastable dynamics signatures in Parkinson's disease.Journal: NPJ Parkinson's diseaseIn common: BrainSpace, Signal Processing Toolbox, NiBabel, 5 other tools, 8 references
- [5] doi:10.1038/s41467-026-73668-y [code]
- Convergent and divergent brain-cognition development in early adolescence.Journal: Nature communicationsIn common: AFNI, FSL, Signal Processing Toolbox, 7 other tools, fMRI, 6 references
- [6] doi:10.1038/s41467-026-71270-w [code]
- Spatiotemporal dynamics of the human cortical functional hierarchy across the lifespan.Journal: Nature communicationsIn common: BrainSpace, FSL, NiBabel, 5 other tools, fMRI, 6 references
- [7] doi:10.64898/2026.03.12.710517 [code]
- Cortical excitability inversely modulates fMRI connectivity via low-frequency neuronal couplingJournal: bioRxiv (preprint)In common: AFNI, Optimization Toolbox, FSL, 6 other tools, fMRI, systems, 3 references
- [8] doi:10.1038/s41586-026-10631-3 [code]
- A prognostic human brain network for diffuse midline glioma.Journal: NatureIn common: BrainSpace, Optimization Toolbox, FSL, 7 other tools, 3 references
- [9] doi:10.1038/s41593-026-02205-3 [code]
- Competitive interactions shape mammalian brain network dynamics and computation.Journal: Nature neuroscienceIn common: FSL, Signal Processing Toolbox, NiBabel, 5 other tools, 5 references
- [10] doi:10.1038/s41467-026-75959-w [code]
- Charting higher-order models of brain function beyond pairwise interactions.Journal: Nature communicationsIn common: BrainSpace, NiBabel, Statistics and Machine Learning Toolbox, 4 other tools, 7 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
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: 1 repository of the authors' code, each at its verified commit and with its license, 15 scripts, and 7 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:19e4cf5c2470008e…
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
[.
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.
