Cortical and white matter myelination proceed in concert during early infancy.
The 11 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
- [1] § Methods › Bundle identification with pyBabyAFQ ↔ pyafq_pipeline/pyafq_pipeline_dHCP.ipynb, the whole file · a weak match · score 0.74 · babyAFQ, waypoints, template, AWS, MRtrix, space
- [2] § Methods › Assessing myelin-sensitive imaging metrics across tissues ↔ mrtrix_pipeline/mrtrix_pipeline_dHCP.ipynb, lines 345–389 · score 0.73 · draw EM, FreeSurfer, definition, boundaries, MRtrix, segmentations
- [3] § Methods › Tractography ↔ mrtrix_pipeline/mrtrix_pipeline_dHCP.ipynb, lines 442–504 · score 0.65 · IFOD1, Seeds, angle, dHCP, MRtrix, CSD
- [4] § Methods › Statistical analyses ↔ Figure4.ipynb, lines 217–240 · score 0.61 · motor scores, birth age, ANOVA, scan age, sex, model
- [5] § Results › Coupled myelin development of bundles and their cortical targets ↔ Figure3.ipynb, lines 409–543 · score 0.59 · FcMa, FcMi, regression, hues, IFOF, MLF
- [6] § Results › Reduced T1w/T2w coupling in preterm infants ↔ Figure5.ipynb, lines 154–192 · score 0.55 · FcMa, FcMi, IFOF, MLF, AF, UNC
- [7] § Methods › Tractography ↔ pyafq_pipeline/pyafq_pipeline_dHCP.ipynb, the whole file · a weak match · score 0.55 · tracking, MRtrix, CSD, tractography, segmentation, streamlines
- [8] § Results › Coupled myelin content of bundles and their cortical targets ↔ Figure3.ipynb, lines 409–543 · score 0.54 · FcMa, FcMi, regression, IFOF, MLF, AF
- [9] § Methods › Bundle identification with pyBabyAFQ ↔ EndpointsToCortex.ipynb, lines 632–719 · score 0.53 · Python, ROIs, frontal, cingulate, occipital, inferior
- [10] § Results › Coupled myelin content of bundles and their cortical targets ↔ Figure2.ipynb, lines 21–103 · score 0.51 · FcMa, FcMi, IFOF, MLF, AF, UNC
- [11] § Methods › Statistical analyses ↔ Figure3.ipynb, lines 213–309 · score 0.50 · birth age, scan age, variables, predicting, model, linear
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
Jupyter notebook · 1,128 lines · 39 KB · no license · 3 matches
- # %% [markdown]
- # ## Set up environment
- # %%
- # Set up environment
- import warnings;
- warnings.filterwarnings('ignore');
- from IPython.core.interactiveshell import InteractiveShell;
- InteractiveShell.ast_node_interactivity = "all";
- import pandas as pd
- import os
- import numpy as np
- import seaborn as sns
- import matplotlib.pyplot as plt
- import scipy
- from scipy import stats
- from matplotlib.lines import Line2D;
- from sklearn import preprocessing
- import statsmodels.formula.api as smf
- from utils import get_data
- # %%
- #plotting parameters, these will be the same for all plots
- bundles = ['AFL', 'AFR', 'ATRL', 'ATRR', 'CCL', 'CCR', 'CSL', 'CSR', 'FcMi', 'FcMa', 'IFOFL', 'IFOFR', 'ILFL', 'ILFR',
- 'MLFL', 'MLFR', 'ORL', 'ORR', 'SLFL', 'SLFR', 'UNCL', 'UNCR', 'VOFL', 'VOFR', 'pAFL', 'pAFR']
- rightBundles = ['AFR', 'ATRR', 'CCR', 'CSR', 'FcMi', 'FcMa', 'IFOFR', 'ILFR', 'MLFR', 'ORR', 'pAFR', 'SLFR', 'UNCR', 'VOFR']
- leftBundles = ['AFL', 'ATRL', 'CCL', 'CSL', 'FcMi', 'FcMa', 'IFOFL', 'ILFL', 'MLFL', 'ORL', 'pAFL', 'SLFL', 'UNCL', 'VOFL']
- tractPosHorz = {'AF': (0,0), 'ATR': (0, 1), 'CC': (0, 2), 'CS': (0, 3),
- 'IFOF':(1,0), 'ILF':(1,1), 'MLF':(1,2), 'OR': (1,3),
- 'SLF': (2, 0), 'UNC':(2,1), 'VOF': (2, 2), 'pAF': (2,3),
- 'FcMi': (3, 1), 'FcMa': (3, 2)};
- tracts=['AF', 'ATR', 'CC', 'CS', 'IFOF', 'ILF', 'MLF', 'OR', 'SLF', 'UNC', 'VOF', 'pAF', 'FcMi', 'FcMa']
- colors=['cyan', 'blue', 'green', 'orange', 'purple', 'brown', 'olive', 'coral', 'fuchsia', 'yellow', 'indigo', 'violet', 'salmon', 'red']
- color_list_all=sns.color_palette("tab20")+sns.color_palette("tab20b")
- color_list_complete=color_list_all
- color_list_chosen=color_list_all[18:20]
- color_list_chosen.extend(color_list_all[0:2])
- color_list_chosen.extend(color_list_all[4:6])
- color_list_chosen.extend(color_list_all[2:4])
- color_list_chosen.extend(color_list_all[7:8])
- color_list_chosen.extend(color_list_all[6:7])
- color_list_chosen.extend(color_list_all[8:10])
- color_list_chosen.extend(color_list_all[10:12])
- color_list_chosen.extend(color_list_all[24:25])
- color_list_chosen.extend(color_list_all[27:28])
- color_list_chosen.extend(color_list_all[34:35])
- color_list_chosen.extend(color_list_all[35:36])
- color_list_chosen.extend(color_list_all[12:14])
- color_list_chosen.extend(color_list_all[16:18])
- color_list_chosen.extend(color_list_all[22:23])
- color_list_chosen.extend(color_list_all[23:24])
- color_list_chosen.extend(color_list_all[37:38])
- color_list_chosen.extend(color_list_all[39:40])
- sns.palplot(color_list_all)
- sns.palplot(color_list_chosen)
- color_list_all=color_list_chosen
- color_order=[19, 18, 1, 0, 5, 4, 3, 2, 7, 6, 9, 8, 11, 10, 15, 14, 21, 20, 25, 24, 13, 12, 17, 16, 23, 22]
- color_list_nohemis=sns.color_palette("tab20")+sns.color_palette("tab20b")
- color_list_complete_nohemis=color_list_nohemis
- color_list_chosen_nohemis=color_list_nohemis[18:19]
- color_list_chosen_nohemis.extend(color_list_nohemis[0:1])
- color_list_chosen_nohemis.extend(color_list_nohemis[4:5])
- color_list_chosen_nohemis.extend(color_list_nohemis[2:3])
- color_list_chosen_nohemis.extend(color_list_nohemis[8:9])
- color_list_chosen_nohemis.extend(color_list_nohemis[10:11])
- color_list_chosen_nohemis.extend(color_list_nohemis[24:25])
- color_list_chosen_nohemis.extend(color_list_nohemis[34:35])
- color_list_chosen_nohemis.extend(color_list_nohemis[12:13])
- color_list_chosen_nohemis.extend(color_list_nohemis[16:17])
- color_list_chosen_nohemis.extend(color_list_nohemis[22:23])
- color_list_chosen_nohemis.extend(color_list_nohemis[37:38])
- color_list_chosen_nohemis.extend(color_list_nohemis[7:8])
- color_list_chosen_nohemis.extend(color_list_nohemis[6:7])
- sns.palplot(color_list_chosen_nohemis)
- color_order2=[19, 18, 5, 4, 9, 8, 11, 10, 15, 14, 21, 20, 25, 24, 13, 12, 17, 16, 23, 22, 7, 6]
- color_list_3=sns.color_palette("tab20")+sns.color_palette("tab20b")
- color_list_complete3=color_list_3
- color_list_chosen3=color_list_3[18:19]
- color_list_chosen3.extend(color_list_3[0:1])
- color_list_chosen3.extend(color_list_3[4:5])
- color_list_chosen3.extend(color_list_3[2:3])
- color_list_chosen3.extend(color_list_3[8:9])
- color_list_chosen3.extend(color_list_3[10:11])
- color_list_chosen3.extend(color_list_3[24:25])
- color_list_chosen3.extend(color_list_3[34:35])
- color_list_chosen3.extend(color_list_3[12:13])
- color_list_chosen3.extend(color_list_3[16:17])
- color_list_chosen3.extend(color_list_3[22:23])
- color_list_chosen3.extend(color_list_3[37:38])
- color_list_chosen3.extend(color_list_3[7:8])
- color_list_chosen3.extend(color_list_3[6:7])
- sns.palplot(color_list_chosen3)
- color_order3=[19, 1, 5, 3, 9, 11, 25, 35, 13, 17, 23, 38, 8, 7]
- # %%
- get_data()
- # %%
- slope_analyses_mean=pd.read_csv('./inputData/GMandWMT1wT2wSubjectsAge.csv', index_col=None)
- SlopeDataframe=pd.read_csv('./inputData/R1Slope.csv', index_col=None)
- # %% [markdown]
- # ## T1w/T2w
- # %%
- #Compute slope for scan age for T1w/T2w of white matter
- tractCount=slope_analyses_mean['tractID'].unique()
- coeff=[] #Creating arrays
- se = []
- coeffType=[]
- tractTrack1=[]
- intercept=[]
- r2 = []
- adjustR2 = []
- aic = []
- pvals=[]
- for tract in tractCount:
- dfForStats=slope_analyses_mean[(slope_analyses_mean['tractID']==tract)]
- dfForStats.reset_index(level=0, inplace=True)
- scanAgeArray=np.array(dfForStats['scan_age'])
- scanAgeArray=scanAgeArray.reshape(1,-1)
- scanAgeNorm=preprocessing.normalize(scanAgeArray,axis=1)
- scanAgeNorm=scanAgeNorm.reshape(-1)
- dfForStats["scanAgeNorm"]=scanAgeNorm
- birthAgeArray=np.array(dfForStats['birth_age'])
- birthAgeArray=birthAgeArray.reshape(1,-1)
- birthAgeNorm=preprocessing.normalize(birthAgeArray,axis=1)
- birthAgeNorm=birthAgeNorm.reshape(-1)
- dfForStats["birthAgeNorm"]=birthAgeNorm
- t1wt2wArray=np.array(dfForStats['t1wt2w'])
- t1wt2wArray=t1wt2wArray.reshape(1,-1)
- t1wt2wNorm=preprocessing.normalize(t1wt2wArray,axis=1)
- t1wt2wNorm=t1wt2wNorm.reshape(-1)
- dfForStats["t1wt2wNorm"]=t1wt2wNorm
- df = dfForStats
- md = smf.ols(formula="t1wt2w~ 1 + scan_age", data=df) #Predicting dti_mdNorm through the other variables
- mdf = md.fit() # Actually commanding the linear analysis, it could be any other analysis method
- results=mdf.summary()
- # Note that tables is a list. The table at index 1 is the "core" table. Additionally, read_html puts dfs in a list, so we want index 0
- results_as_html = results.tables[1].as_html()
- LM_results=pd.read_html(results_as_html, header=0, index_col=0)[0]
- results_as_html_R2 = results.tables[0].as_html()
- LM_results_R2=pd.read_html(results_as_html_R2, index_col=0)[0]
- results_R2s=LM_results_R2[3]
- #LM_results.head()
- #LM_results_R2.head()
- #results_R2s.head()
- SE=LM_results['std err']
- inter=mdf.params['Intercept']
- intercept.append(inter)
- #SE.head()
- se.append(SE["scan_age"])
- coeff.append(mdf.params['scan_age'])
- coeff_type=3
- tractTrack1.append(tract)
- #nodeTrack1.append(node)
- coeffType.append(coeff_type) #Add another line below
- r2.append(results_R2s['Dep. Variable:'])
- adjustR2.append(results_R2s['Model:'])
- aic.append(results_R2s['No. Observations:'])
- pvals.append(results_R2s['Date:'])
- print(mdf.summary())
- df=pd.DataFrame(tractTrack1)
- df.columns=['tractID']
- df.insert(1,"coeffType",coeffType)
- df.insert(2,"coeff",coeff)
- df.insert(3,"se",se)
- df.insert(4,"intercept",intercept)
- df.insert(5,"r2",r2)
- df.insert(6,"adjustR2",adjustR2)
- df.insert(7,"aic",aic)
- df.insert(8,"pvals",pvals)
- os.makedirs('./outputData',exist_ok=True)
- df.to_csv('./outputData/LM_result_scanAge_noNorm_WM_AverageSubj.csv')
- df.head()
- df.min(axis=0, numeric_only=True)
- df.max(axis=0, numeric_only=True)
- df.mean(axis=0, numeric_only=True)
- dfScanAge=df
- df.head()
- statsPerBundle=df.groupby('tractID').mean()
- statsPerBundle.head()
- statsPerBundle.to_csv('./outputData/LM_result_scanAge_noNorm_meanPerBundle_WM_AverageSubj.csv')
- # %%
- #Compute slope for scan age for T1w/T2w gray matter
- tractCount=slope_analyses_mean['tractID'].unique()
- coeff=[] #Creating arrays
- se = []
- coeffType=[]
- tractTrack1=[]
- intercept=[]
- r2 = []
- adjustR2 = []
- aic = []
- pvals=[]
- for tract in tractCount:
- dfForStats=slope_analyses_mean[(slope_analyses_mean['tractID']==tract)]
- dfForStats.reset_index(level=0, inplace=True)
- scanAgeArray=np.array(dfForStats['scan_age'])
- scanAgeArray=scanAgeArray.reshape(1,-1)
- scanAgeNorm=preprocessing.normalize(scanAgeArray,axis=1)
- scanAgeNorm=scanAgeNorm.reshape(-1)
- dfForStats["scanAgeNorm"]=scanAgeNorm
- birthAgeArray=np.array(dfForStats['birth_age'])
- birthAgeArray=birthAgeArray.reshape(1,-1)
- birthAgeNorm=preprocessing.normalize(birthAgeArray,axis=1)
- birthAgeNorm=birthAgeNorm.reshape(-1)
- dfForStats["birthAgeNorm"]=birthAgeNorm
- t1wt2wArray=np.array(dfForStats['WeAvGMT1wT2w'])
- t1wt2wArray=t1wt2wArray.reshape(1,-1)
- t1wt2wNorm=preprocessing.normalize(t1wt2wArray,axis=1)
- t1wt2wNorm=t1wt2wNorm.reshape(-1)
- dfForStats["t1wt2wNorm"]=t1wt2wNorm
- #dfForStats.head()
- #dfForStats.to_csv('./'+tract+'_dataCleanedForLME.csv')
- df = dfForStats
- md = smf.ols(formula="WeAvGMT1wT2w~ 1 + scan_age", data=df) #Predicting dti_mdNorm through the other variables
- mdf = md.fit() # Actually commanding the linear analysis, it could be any other analysis method
- results=mdf.summary()
- # Note that tables is a list. The table at index 1 is the "core" table. Additionally, read_html puts dfs in a list, so we want index 0
- results_as_html = results.tables[1].as_html()
- LM_results=pd.read_html(results_as_html, header=0, index_col=0)[0]
- results_as_html_R2 = results.tables[0].as_html()
- LM_results_R2=pd.read_html(results_as_html_R2, index_col=0)[0]
- results_R2s=LM_results_R2[3]
- #LM_results.head()
- #LM_results_R2.head()
- #results_R2s.head()
- SE=LM_results['std err']
- inter=mdf.params['Intercept']
- intercept.append(inter)
- #SE.head()
- se.append(SE["scan_age"])
- coeff.append(mdf.params['scan_age'])
- coeff_type=3
- tractTrack1.append(tract)
- #nodeTrack1.append(node)
- coeffType.append(coeff_type) #Add another line below
- r2.append(results_R2s['Dep. Variable:'])
- adjustR2.append(results_R2s['Model:'])
- aic.append(results_R2s['No. Observations:'])
- pvals.append(results_R2s['Date:'])
- print(mdf.summary())
- df=pd.DataFrame(tractTrack1)
- df.columns=['tractID']
- df.insert(1,"coeffType",coeffType)
- df.insert(2,"coeff_GM",coeff)
- df.insert(3,"se",se)
- df.insert(4,"intercept",intercept)
- df.insert(5,"r2",r2)
- df.insert(6,"adjustR2",adjustR2)
- df.insert(7,"aic",aic)
- df.insert(8,"pvals",pvals)
- df.to_csv('./outputData/LM_result_scanAge_noNorm_GM_AverageSubj.csv')
- df.head()
- df.min(axis=0, numeric_only=True)
- df.max(axis=0, numeric_only=True)
- df.mean(axis=0, numeric_only=True)
- dfScanAge=df
- df.head()
- statsPerBundle=df.groupby('tractID').mean()
- statsPerBundle.head()
- statsPerBundle.to_csv('./outputData/LM_result_scanAge_noNorm_meanPerBundle_GM_AverageSubj.csv')
- # %%
- #Merge statistic dataframes of gray and white matter
- SlopeWM = pd.read_csv('./outputData/LM_result_scanAge_noNorm_WM_AverageSubj.csv', index_col=0)
- SlopeGM = pd.read_csv('./outputData/LM_result_scanAge_noNorm_GM_AverageSubj.csv', index_col=0)
- SlopeBoth = pd.merge(SlopeWM, SlopeGM[['tractID', 'coeff_GM', 'r2', 'aic']], on='tractID')
- SlopeBoth=SlopeBoth.set_index(['tractID']).reindex(['AFL', 'AFR', 'ATRL', 'ATRR', 'CCL', 'CCR', 'CSL', 'CSR', 'FcMi', 'FcMa', 'IFOFL', 'IFOFR', 'ILFL', 'ILFR', 'MLFL', 'MLFR', 'ORL', 'ORR', 'SLFL', 'SLFR', 'UNCL', 'UNCR', 'VOFL', 'VOFR', 'pAFL', 'pAFR']).reset_index()
- SlopeBoth
- SlopeBoth.to_csv('./outputData/SlopeT1wT2w.csv')
- # %%
- # Paired t-test between white matter and gray matter coefficients
- t_stat, p_val = stats.ttest_rel(SlopeBoth['coeff'], SlopeBoth['coeff_GM'])
- print(f"CoeffWM vs. CoeffGM t = {t_stat:.4f}, p = {p_val:.4f}")
- # Print mean values
- mean_wm = SlopeBoth['coeff'].mean()
- mean_gm = SlopeBoth['coeff_GM'].mean()
- print(f"Mean Coeff WM: {mean_wm:.4f}")
- print(f"Mean Coeff GM: {mean_gm:.4f}")
- # Calculate difference metrics
- SlopeBoth['diff_r2'] = SlopeBoth['r2_x'] - SlopeBoth['r2_y']
- SlopeBoth['diff_aic'] = SlopeBoth['aic_x'] - SlopeBoth['aic_y']
- # Print mean differences
- mean_diff_r2 = SlopeBoth['diff_r2'].mean()
- mean_diff_aic = SlopeBoth['diff_aic'].mean()
- print(f"Mean Δr²: {mean_diff_r2:.4f}")
- print(f"Mean ΔAIC: {mean_diff_aic:.4f}")
- # %%
- sns.set_style('white');
- fig1=sns.lmplot(
- data=SlopeBoth, x='coeff_GM', y="coeff", hue="tractID", height=10, scatter_kws={"s": 400}, fit_reg=False, legend=False, palette=color_list_chosen
- )
- fig1=sns.regplot(data=SlopeBoth, x='coeff_GM', y="coeff", scatter=False, ax=fig1.axes[0, 0], line_kws={"color": "darkgrey"})
- res=scipy.stats.pearsonr(SlopeBoth['coeff_GM'], SlopeBoth['coeff'])
- print(res)
- res.confidence_interval()
- #fig1.legend(title='Tract', fontsize='15', title_fontsize='20', bbox_to_anchor=(0.7, 0.25, 0.5, 0.5), loc='right', borderaxespad=0, frameon=FcMilse)
- plt.xlabel("T1w/T2w Slope in GM", fontsize=50)
- plt.ylabel("T1w/T2w Slope in WM", fontsize=50)
- plt.xticks([0.025, 0.035, 0.045], fontsize=45)
- plt.yticks(fontsize=45)
- plt.savefig('./figures/T1wT2w_SlopeGMvsSlopeWM.png', dpi=600, bbox_inches = "tight")
- # %%
- # Define bundles
- b=['AFL', 'AFR', 'ATRL', 'ATRR', 'CCL', 'CCR', 'CSL', 'CSR', 'FcMi', 'FcMa', 'IFOFL', 'IFOFR', 'ILFL', 'ILFR', 'MLFL', 'MLFR', 'ORL', 'ORR', 'SLFL', 'SLFR', 'UNCL', 'UNCR', 'VOFL', 'VOFR', 'pAFL', 'pAFR']
- results = []
- for bundle in b:
- bundle_df = slope_analyses_mean[slope_analyses_mean['tractID'] == bundle]
- if len(bundle_df) >= 2:
- # White matter
- r_wm, p_wm = stats.pearsonr(bundle_df['scan_age'], bundle_df['t1wt2w'])
- # Gray matter
- r_gm, p_gm = stats.pearsonr(bundle_df['scan_age'], bundle_df['WeAvGMT1wT2w'])
- results.append({
- 'Tract': bundle,
- 'r_WM': r_wm,
- 'p_WM': p_wm,
- 'r2_WM': r_wm**2,
- 'r_GM': r_gm,
- 'p_GM': p_gm,
- 'r2_GM': r_gm**2
- })
- # Convert to DataFrame
- df_tract_corrs = pd.DataFrame(results)
- # View or export
- df_tract_corrs
- df_tract_corrs.to_csv('./outputData/tract_r2_pvalues_wm_gm_bothemis.csv', index=False)
- r2_min_wm = df_tract_corrs['r2_WM'].min()
- r2_min_gm = df_tract_corrs['r2_GM'].min()
- r_min_wm = df_tract_corrs['r_WM'].min()
- r_min_gm = df_tract_corrs['r_GM'].min()
- p_max_wm = df_tract_corrs['p_WM'].max()
- p_max_gm = df_tract_corrs['p_GM'].max()
- print(f"Min r² WM: {r2_min_wm}")
- print(f"Min r² GM: {r2_min_gm}")
- print(f"Min r WM: {r_min_wm}")
- print(f"Min r GM: {r_min_gm}")
- print(f"Max p WM: {p_max_wm}")
- print(f"Max p GM: {p_max_gm}")
- # %%
- # --- PREPARE DATA -------------------------------------------------------------
- df = slope_analyses_mean.copy()
- # Extract Hemisphere from tract names (e.g. "AF_L" → "L")
- df["Hemisphere"] = df["tractID"].str.extract(r'(L|R)$')
- # Extract base bundle name (e.g. "AF_L" → "AF")
- df["BundleBase"] = df["tractID"].str.replace(r'(L|R)$', '', regex=True)
- # --- DEFINE LAYOUT AND STYLE --------------------------------------------------
- tractPosHorz = {
- 'AF': (0,0), 'ATR': (0,1), 'CC': (0,2), 'CS': (0,3),
- 'IFOF':(1,0), 'ILF':(1,1), 'MLF':(1,2), 'OR':(1,3),
- 'SLF':(2,0), 'UNC':(2,1), 'VOF':(2,2), 'pAF':(2,3),
- 'FcMi':(3,1), 'FcMa':(3,2)
- }
- tracts = ['AF', 'ATR', 'CC', 'CS', 'IFOF', 'ILF', 'MLF',
- 'OR', 'SLF', 'UNC', 'VOF', 'pAF', 'FcMi', 'FcMa']
- colors = ['cyan', 'blue', 'green', 'orange', 'purple', 'brown', 'olive',
- 'coral', 'fuchsia', 'yellow', 'indigo', 'violet', 'salmon', 'red']
- sns.set(font_scale=2)
- sns.set_style("white")
- fig, axes = plt.subplots(4, 4, figsize=(14,17), frameon=False)
- fig.subplots_adjust(wspace=0.1, hspace=0.9)
- import matplotlib.colors as mcolors
- def lighten_color(color, amount=0.5):
- """
- Lightens the given color by blending it with white.
- amount=0 → white, amount=1 → original color.
- """
- try:
- c = mcolors.cnames[color]
- except KeyError:
- c = color
- rgb = mcolors.to_rgb(c)
- return tuple(1 - (1 - x) * amount for x in rgb)
- # --- MAIN LOOP ---------------------------------------------------------------
- for ct, (bundle, color) in enumerate(zip(tracts, colors)):
- if bundle not in tractPosHorz:
- continue
- ax = axes[tractPosHorz[bundle]]
- # Filter data for the base bundle (both hemispheres)
- SlopeTract = df.query("BundleBase == @bundle")
- # Aggregate per subject, hemisphere, etc.
- SlopeTractGM = SlopeTract.groupby(
- ['subjectID', 'scan_age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['WeAvGMT1wT2w'].mean()
- SlopeTractWM = SlopeTract.groupby(
- ['subjectID', 'scan_age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['t1wt2w'].mean()
- # Make a lighter version of the current tract color for the right hemisphere
- light_color = lighten_color(color, amount=0.5)
- # Plot white matter (same hue, lighter for R)
- sns.scatterplot(
- data=SlopeTractWM, x='scan_age', y='t1wt2w', hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L': color, 'R': light_color}, ax=ax,
- alpha=0.5, legend=False
- )
- # Regression lines per hemisphere
- for hemi, line_color in zip(['L', 'R'], [color, light_color]):
- sns.regplot(
- data=SlopeTractWM[SlopeTractWM['Hemisphere'] == hemi],
- x='scan_age', y='t1wt2w', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # Plot gray matter (with slightly lighter colors)
- sns.scatterplot(
- data=SlopeTractGM, x='scan_age', y='WeAvGMT1wT2w', hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L': 'darkgray', 'R': 'lightgray'}, ax=ax,
- alpha=0.5, legend=False
- )
- for hemi, line_color in zip(['L', 'R'], ['darkgray', 'lightgray']):
- sns.regplot(
- data=SlopeTractGM[SlopeTractGM['Hemisphere'] == hemi],
- x='scan_age', y='WeAvGMT1wT2w', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # --- Titles & labels
- ax.set_title(bundle, pad=5, fontsize=25)
- ax.set_xlabel('Scan Age', labelpad=15, fontsize=20)
- if ct in [0, 4, 8, 12]: # first column
- _=ax.set_ylabel("T1w/T2w",fontsize=20);
- _=ax.set_xticks([30, 34, 38, 42]);
- #_=ax.set_xticklabels(fontsize=20);
- #_=ax.set_yticks([0.4, 0.45, 0.5]);
- #_=ax.set_yticklabels(fontsize=20)
- _=ax.spines['right'].set_visible(False);
- _=ax.spines['top'].set_visible(False);
- _=line1=Line2D([],[],color=color_list_all[ct],linestyle='dashed');
- _=line2=Line2D([],[],color=color_list_all[ct],linestyle='-');
- else:
- _=ax.set_ylabel('T1w/T2w', fontsize=14);
- _=ax.set_xticks([30, 34, 38, 42]);
- #_=ax.set_xticklabels(fontsize=20);
- #_=ax.set_yticks([0.4, 0.45, 0.5]);
- _=ax.yaxis.set_visible(False);
- _=ax.spines['right'].set_visible(False);
- _=ax.spines['left'].set_visible(False);
- _=ax.spines['top'].set_visible(False);
- # Clean frame
- ax.spines['right'].set_visible(False)
- ax.spines['top'].set_visible(False)
- # Turn off unused axes
- axes[3,3].axis("off")
- axes[3,0].axis("off")
- # --- SAVE & SHOW -------------------------------------------------------------
- fig.savefig('./figures/scanage_per_bundle_bothhemis.png', dpi=600)
- plt.show()
- # %%
- # --- PREPARE DATA -------------------------------------------------------------
- df = slope_analyses_mean.copy()
- # 1) Extract Hemisphere (only if tract ends with L or R)
- df["Hemisphere"] = df["tractID"].str.extract(r'(L|R)$')
- # 2) Extract BundleBase safely
- df["BundleBase"] = df["tractID"] # start with full name
- mask = df["tractID"].str.endswith(("L", "R")) # true hemisphere bundles
- df.loc[mask, "BundleBase"] = df.loc[mask, "tractID"].str.replace(r"(L|R)$", "", regex=True)
- # For FcMi & FcMa → both-hemisphere bundles → assign pseudo-hemi
- df.loc[df["BundleBase"].isin(["FcMi", "FcMa"]), "Hemisphere"] = "B"
- # --- DEFINE LAYOUT AND STYLE --------------------------------------------------
- tractPosHorz = {
- 'AF': (0,0), 'ATR': (0,1), 'CC': (0,2), 'CS': (0,3),
- 'IFOF': (1,0), 'ILF': (1,1), 'MLF': (1,2), 'OR': (1,3),
- 'SLF': (2,0), 'UNC': (2,1), 'VOF': (2,2), 'pAF': (2,3),
- 'FcMi': (3,1), 'FcMa': (3,2)
- }
- tracts = ['AF', 'ATR', 'CC', 'CS', 'IFOF', 'ILF', 'MLF',
- 'OR', 'SLF', 'UNC', 'VOF', 'pAF', 'FcMi', 'FcMa']
- colors = ['cyan', 'blue', 'green', 'orange', 'purple', 'brown', 'olive',
- 'coral', 'fuchsia', 'yellow', 'indigo', 'violet', 'salmon', 'red']
- sns.set(font_scale=2)
- sns.set_style("white")
- fig, axes = plt.subplots(4, 4, figsize=(14,17), frameon=False)
- fig.subplots_adjust(wspace=0.1, hspace=0.9)
- import matplotlib.colors as mcolors
- def lighten_color(color, amount=0.5):
- try:
- c = mcolors.cnames[color]
- except KeyError:
- c = color
- rgb = mcolors.to_rgb(c)
- return tuple(1 - (1 - x) * amount for x in rgb)
- # --- MAIN LOOP ---------------------------------------------------------------
- for ct, (bundle, color) in enumerate(zip(tracts, colors)):
- if bundle not in tractPosHorz:
- continue
- ax = axes[tractPosHorz[bundle]]
- # Filter data for the base bundle (both hemispheres)
- SlopeTract = df.query("BundleBase == @bundle")
- # Aggregate per subject & hemisphere
- SlopeTractGM = SlopeTract.groupby(
- ['subjectID', 'scan_age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['WeAvGMT1wT2w'].mean()
- SlopeTractWM = SlopeTract.groupby(
- ['subjectID', 'scan_age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['t1wt2w'].mean()
- # Special case: FcMi & FcMa (ONLY ONE "hemisphere")
- if bundle in ["FcMi", "FcMa"]:
- sns.scatterplot(
- data=SlopeTractWM, x='scan_age', y='t1wt2w',
- color=color, s=60, alpha=0.5, ax=ax
- )
- sns.regplot(
- data=SlopeTractWM,
- x='scan_age', y='t1wt2w', scatter=False, ax=ax,
- line_kws={'color': color, 'alpha': 0.9}
- )
- sns.scatterplot(
- data=SlopeTractGM, x='scan_age', y='WeAvGMT1wT2w',
- color='gray', s=60, alpha=0.5, ax=ax
- )
- sns.regplot(
- data=SlopeTractGM,
- x='scan_age', y='WeAvGMT1wT2w', scatter=False, ax=ax,
- line_kws={'color': 'gray', 'alpha': 0.9}
- )
- else:
- # Normal L/R hemisphere bundles --------------------------------------
- light_color = lighten_color(color, amount=0.5)
- # WM scatter
- sns.scatterplot(
- data=SlopeTractWM, x='scan_age', y='t1wt2w',
- hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L': color, 'R': light_color}, ax=ax,
- alpha=0.5, legend=False
- )
- # WM regression lines
- for hemi, line_color in zip(['L', 'R'], [color, light_color]):
- sns.regplot(
- data=SlopeTractWM[SlopeTractWM['Hemisphere'] == hemi],
- x='scan_age', y='t1wt2w', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # GM scatter
- sns.scatterplot(
- data=SlopeTractGM, x='scan_age', y='WeAvGMT1wT2w',
- hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L': 'darkgray', 'R': 'lightgray'}, ax=ax,
- alpha=0.5, legend=False
- )
- # GM regression
- for hemi, line_color in zip(['L', 'R'], ['darkgray', 'lightgray']):
- sns.regplot(
- data=SlopeTractGM[SlopeTractGM['Hemisphere'] == hemi],
- x='scan_age', y='WeAvGMT1wT2w', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # --- Titles & labels -----------------------------------------------------
- ax.set_title(bundle, pad=5, fontsize=25)
- ax.set_xlabel('Scan Age', labelpad=15, fontsize=20)
- if ct in [0, 4, 8, 12]: # first column
- ax.set_ylabel("T1w/T2w", fontsize=20)
- ax.set_xticks([30, 34, 38, 42])
- ax.spines['right'].set_visible(False)
- ax.spines['top'].set_visible(False)
- else:
- ax.set_ylabel('T1w/T2w', fontsize=14)
- ax.set_xticks([30, 34, 38, 42])
- ax.yaxis.set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['left'].set_visible(False)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['top'].set_visible(False)
- # Turn off unused axes
- axes[3,3].axis("off")
- axes[3,0].axis("off")
- # --- SAVE & SHOW -------------------------------------------------------------
- fig.savefig('./figures/scanage_per_bundle_bothhemis.png', dpi=600)
- plt.show()
- # %% [markdown]
- # ## R1
- # %%
- #Compute slope for scan age for R1 of white matter
- tractCount=SlopeDataframe['tractID'].unique()
- coeff=[] #Creating arrays
- se = []
- coeffType=[]
- tractTrack1=[]
- intercept=[]
- r2 = []
- adjustR2 = []
- aic = []
- pvals=[]
- for tract in tractCount:
- dfForStats=SlopeDataframe[(SlopeDataframe['tractID']==tract)]
- dfForStats.reset_index(level=0, inplace=True)
- scanAgeArray=np.array(dfForStats['age'])
- scanAgeArray=scanAgeArray.reshape(1,-1)
- scanAgeNorm=preprocessing.normalize(scanAgeArray,axis=1)
- scanAgeNorm=scanAgeNorm.reshape(-1)
- dfForStats["scanAgeNorm"]=scanAgeNorm
- R1Array=np.array(dfForStats['R1'])
- R1Array=R1Array.reshape(1,-1)
- R1Norm=preprocessing.normalize(R1Array,axis=1)
- R1Norm=R1Norm.reshape(-1)
- dfForStats["R1Norm"]=R1Norm
- df = dfForStats
- md = smf.ols(formula="R1~ 1 + age", data=df) #Predicting dti_mdNorm through the other variables
- mdf = md.fit() # Actually commanding the linear analysis, it could be any other analysis method
- results=mdf.summary()
- # Note that tables is a list. The table at index 1 is the "core" table. Additionally, read_html puts dfs in a list, so we want index 0
- results_as_html = results.tables[1].as_html()
- LM_results=pd.read_html(results_as_html, header=0, index_col=0)[0]
- results_as_html_R2 = results.tables[0].as_html()
- LM_results_R2=pd.read_html(results_as_html_R2, index_col=0)[0]
- results_R2s=LM_results_R2[3]
- #LM_results.head()
- #LM_results_R2.head()
- #results_R2s.head()
- SE=LM_results['std err']
- inter=mdf.params['Intercept']
- intercept.append(inter)
- #SE.head()
- se.append(SE["age"])
- coeff.append(mdf.params['age'])
- coeff_type=3
- tractTrack1.append(tract)
- #nodeTrack1.append(node)
- coeffType.append(coeff_type) #Add another line below
- r2.append(results_R2s['Dep. Variable:'])
- adjustR2.append(results_R2s['Model:'])
- aic.append(results_R2s['No. Observations:'])
- pvals.append(results_R2s['Date:'])
- print(mdf.summary())
- df=pd.DataFrame(tractTrack1)
- df.columns=['tractID']
- df.insert(1,"coeffType",coeffType)
- df.insert(2,"coeff",coeff)
- df.insert(3,"se",se)
- df.insert(4,"intercept",intercept)
- df.insert(5,"r2",r2)
- df.insert(6,"adjustR2",adjustR2)
- df.insert(7,"aic",aic)
- df.insert(8,"pvals",pvals)
- os.makedirs('./outputData',exist_ok=True)
- df.to_csv('./outputData/LM_result_scanAge_noNorm_WM_AverageSubj_R1.csv')
- df.head()
- df.min(axis=0, numeric_only=True)
- df.max(axis=0, numeric_only=True)
- df.mean(axis=0, numeric_only=True)
- dfScanAge=df
- df.head()
- statsPerBundle=df.groupby('tractID').mean()
- statsPerBundle.head()
- statsPerBundle.to_csv('./outputData/LM_result_scanAge_noNorm_meanPerBundle_WM_AverageSubj_R1.csv')
- # %%
- #Compute slope for scan age for R1 of gray matter
- tractCount=SlopeDataframe['tractID'].unique()
- coeff=[] #Creating arrays
- se = []
- coeffType=[]
- tractTrack1=[]
- intercept=[]
- r2 = []
- adjustR2 = []
- aic = []
- pvals=[]
- for tract in tractCount:
- dfForStats=SlopeDataframe[(SlopeDataframe['tractID']==tract)]
- dfForStats.reset_index(level=0, inplace=True)
- scanAgeArray=np.array(dfForStats['age'])
- scanAgeArray=scanAgeArray.reshape(1,-1)
- scanAgeNorm=preprocessing.normalize(scanAgeArray,axis=1)
- scanAgeNorm=scanAgeNorm.reshape(-1)
- dfForStats["scanAgeNorm"]=scanAgeNorm
- R1Array=np.array(dfForStats['GM_R1'])
- R1Array=R1Array.reshape(1,-1)
- R1Norm=preprocessing.normalize(R1Array,axis=1)
- R1Norm=R1Norm.reshape(-1)
- dfForStats["R1Norm"]=R1Norm
- df = dfForStats
- md = smf.ols(formula="GM_R1~ 1 + age", data=df) #Predicting dti_mdNorm through the other variables
- mdf = md.fit() # Actually commanding the linear analysis, it could be any other analysis method
- results=mdf.summary()
- # Note that tables is a list. The table at index 1 is the "core" table. Additionally, read_html puts dfs in a list, so we want index 0
- results_as_html = results.tables[1].as_html()
- LM_results=pd.read_html(results_as_html, header=0, index_col=0)[0]
- results_as_html_R2 = results.tables[0].as_html()
- LM_results_R2=pd.read_html(results_as_html_R2, index_col=0)[0]
- results_R2s=LM_results_R2[3]
- #LM_results.head()
- #LM_results_R2.head()
- #results_R2s.head()
- SE=LM_results['std err']
- inter=mdf.params['Intercept']
- intercept.append(inter)
- #SE.head()
- se.append(SE["age"])
- coeff.append(mdf.params['age'])
- coeff_type=3
- tractTrack1.append(tract)
- #nodeTrack1.append(node)
- coeffType.append(coeff_type) #Add another line below
- r2.append(results_R2s['Dep. Variable:'])
- adjustR2.append(results_R2s['Model:'])
- aic.append(results_R2s['No. Observations:'])
- pvals.append(results_R2s['Date:'])
- print(mdf.summary())
- df=pd.DataFrame(tractTrack1)
- df.columns=['tractID']
- df.insert(1,"coeffType",coeffType)
- df.insert(2,"coeff_GM",coeff)
- df.insert(3,"se",se)
- df.insert(4,"intercept",intercept)
- df.insert(5,"r2",r2)
- df.insert(6,"adjustR2",adjustR2)
- df.insert(7,"aic",aic)
- df.insert(8,"pvals",pvals)
- os.makedirs('./outputData',exist_ok=True)
- df.to_csv('./outputData/LM_result_scanAge_noNorm_GM_AverageSubj_R1.csv')
- df.head()
- df.min(axis=0, numeric_only=True)
- df.max(axis=0, numeric_only=True)
- df.mean(axis=0, numeric_only=True)
- dfScanAge=df
- df.head()
- statsPerBundle=df.groupby('tractID').mean()
- statsPerBundle.head()
- statsPerBundle.to_csv('./outputData/LM_result_scanAge_noNorm_meanPerBundle_GM_AverageSubj_R1.csv')
- # %%
- SlopeWM_R1 = pd.read_csv('./outputData/LM_result_scanAge_noNorm_WM_AverageSubj_R1.csv', index_col=0)
- SlopeGM_R1 = pd.read_csv('./outputData/LM_result_scanAge_noNorm_GM_AverageSubj_R1.csv', index_col=0)
- SlopeBothR1 = pd.merge(SlopeWM_R1, SlopeGM_R1[['tractID', 'coeff_GM', 'r2', 'aic']], on='tractID')
- SlopeBothR1
- SlopeBothR1=SlopeBothR1.set_index(['tractID']).reindex(['AFL', 'AFR', 'ATRL', 'ATRR', 'CCL', 'CCR', 'CSL', 'CSR', 'FcMi', 'FcMa', 'IFOFL', 'IFOFR', 'ILFL', 'ILFR', 'MLFL', 'MLFR', 'ORL', 'ORR', 'SLFL', 'SLFR', 'UNCL', 'UNCR', 'VOFL', 'VOFR', 'pAFL', 'pAFR']).reset_index()
- SlopeBothR1
- # %%
- # Paired t-test between white matter and gray matter coefficients
- t_stat, p_val = stats.ttest_rel(SlopeBothR1['coeff'], SlopeBothR1['coeff_GM'])
- print(f"CoeffWM vs. CoeffGM t = {t_stat:.4f}, p = {p_val:.4f}")
- # Print mean values
- mean_wm = SlopeBothR1['coeff'].mean()
- mean_gm = SlopeBothR1['coeff_GM'].mean()
- print(f"Mean Coeff WM: {mean_wm:.4f}")
- print(f"Mean Coeff GM: {mean_gm:.4f}")
- # Calculate difference metrics y=GM, x=WM
- SlopeBothR1['diff_r2'] = SlopeBothR1['r2_x'] - SlopeBothR1['r2_y']
- SlopeBothR1['diff_aic'] = SlopeBothR1['aic_x'] - SlopeBothR1['aic_y']
- # Print mean differences
- mean_diff_r2 = SlopeBothR1['diff_r2'].mean()
- mean_diff_aic = SlopeBothR1['diff_aic'].mean()
- print(f"Mean Δr²: {mean_diff_r2:.4f}")
- print(f"Mean ΔAIC: {mean_diff_aic:.4f}")
- # %%
- sns.set_style('white');
- fig1=sns.lmplot(
- data=SlopeBothR1, x='coeff_GM', y="coeff", hue="tractID", height=10, scatter_kws={"s": 400}, fit_reg=False, legend=False, palette=color_list_chosen
- )
- fig1=sns.regplot(data=SlopeBothR1, x='coeff_GM', y="coeff", scatter=False, ax=fig1.axes[0, 0], line_kws={"color": "darkgrey"})
- res=scipy.stats.pearsonr(SlopeBothR1['coeff_GM'], SlopeBothR1['coeff'])
- print(res)
- res.confidence_interval()
- fig1.legend(title='Tract', fontsize='15', title_fontsize='20', bbox_to_anchor=(0.7, 0.25, 0.5, 0.5), loc='right', borderaxespad=0, frameon=False)
- plt.xlabel("R1 Slope in GM [s$^{-1}$/w]", fontsize=50)
- plt.ylabel("R1 Slope in WM [s$^{-1}$/w]", fontsize=50)
- plt.xticks([0.001, 0.003, 0.005], fontsize=45)
- plt.yticks(fontsize=45)
- plt.savefig('./figures/R1Slope.png', dpi=600, bbox_inches='tight', pad_inches=0.1)
- # %%
- import os
- import numpy as np
- import pandas as pd
- import seaborn as sns
- import matplotlib.pyplot as plt
- from matplotlib.lines import Line2D
- import matplotlib.colors as mcolors
- # --- PREPARE DATA -------------------------------------------------------------
- df = SlopeDataframe.copy()
- # Extract Hemisphere from tract names (e.g. "AF_L" → "L"); midline bundles will become NaN here
- df["Hemisphere"] = df["tractID"].str.extract(r'(L|R)$')
- # Extract base bundle name (e.g. "AF_L" → "AF")
- df["BundleBase"] = df["tractID"].str.replace(r'(L|R)$', '', regex=True)
- # --- DEFINE LAYOUT AND STYLE --------------------------------------------------
- tractPosHorz = {
- 'AF': (0,0), 'ATR': (0,1), 'CC': (0,2), 'CS': (0,3),
- 'IFOF':(1,0), 'ILF':(1,1), 'MLF':(1,2), 'OR':(1,3),
- 'SLF':(2,0), 'UNC':(2,1), 'VOF':(2,2), 'pAF':(2,3),
- 'FcMi':(3,1), 'FcMa':(3,2)
- }
- tracts = ['AF', 'ATR', 'CC', 'CS', 'IFOF', 'ILF', 'MLF',
- 'OR', 'SLF', 'UNC', 'VOF', 'pAF', 'FcMi', 'FcMa']
- colors = ['cyan', 'blue', 'green', 'orange', 'purple', 'brown', 'olive',
- 'coral', 'fuchsia', 'yellow', 'indigo', 'violet', 'salmon', 'red']
- sns.set(font_scale=2)
- sns.set_style("white")
- fig, axes = plt.subplots(4, 4, figsize=(14,17), frameon=False)
- fig.subplots_adjust(wspace=0.1, hspace=0.9)
- def lighten_color(color, amount=0.5):
- """
- Lightens the given color by blending it with white.
- amount=0 → white, amount=1 → original color.
- """
- try:
- c = mcolors.cnames[color]
- except KeyError:
- c = color
- rgb = mcolors.to_rgb(c)
- return tuple(1 - (1 - x) * amount for x in rgb)
- # --- MAIN LOOP ---------------------------------------------------------------
- midline_bundles = ["FcMi", "FcMa"] # bundles without L/R hemispheres
- for ct, (bundle, color) in enumerate(zip(tracts, colors)):
- # skip if not in layout mapping
- if bundle not in tractPosHorz:
- continue
- ax = axes[tractPosHorz[bundle]]
- # Filter data for this bundle base (works for both midline and hemispheric bundles)
- SlopeTract = df.query("BundleBase == @bundle")
- # If there's no data at all for this bundle, skip plotting (avoid empty panels)
- if SlopeTract.shape[0] == 0:
- ax.text(0.5, 0.5, "No data", ha='center', va='center', transform=ax.transAxes, fontsize=14)
- ax.set_title(bundle, pad=5, fontsize=20)
- ax.set_xticks([])
- ax.set_yticks([])
- continue
- # --- Different grouping for midline vs. hemispheric bundles ----------------
- if bundle in midline_bundles:
- # Group WITHOUT 'Hemisphere' because it's NaN for midline bundles
- SlopeTractWM = (
- SlopeTract
- .groupby(['subjectID', 'age', 'BundleBase'], as_index=False, sort=False)['R1']
- .mean()
- )
- SlopeTractGM = (
- SlopeTract
- .groupby(['subjectID', 'age', 'BundleBase'], as_index=False, sort=False)['GM_R1']
- .mean()
- )
- # Mark as midline for plotting convenience
- SlopeTractWM["Hemisphere"] = "M"
- SlopeTractGM["Hemisphere"] = "M"
- # Plot WM (single color)
- sns.scatterplot(
- data=SlopeTractWM, x='age', y='R1',
- color=color, s=50, alpha=0.5, ax=ax
- )
- sns.regplot(
- data=SlopeTractWM, x='age', y='R1',
- scatter=False, ax=ax,
- line_kws={'color': color, 'alpha': 0.8}
- )
- # Plot GM (single gray color)
- sns.scatterplot(
- data=SlopeTractGM, x='age', y='GM_R1',
- color='gray', s=50, alpha=0.5, ax=ax
- )
- sns.regplot(
- data=SlopeTractGM, x='age', y='GM_R1',
- scatter=False, ax=ax,
- line_kws={'color': 'gray', 'alpha': 0.8}
- )
- else:
- # Hemispheric bundles: group including Hemisphere field
- # Note: groupby will ignore NaN Hemispheres—this is fine because hemispheric bundles should have L/R
- SlopeTractWM = SlopeTract.groupby(
- ['subjectID', 'age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['R1'].mean()
- SlopeTractGM = SlopeTract.groupby(
- ['subjectID', 'age', 'BundleBase', 'Hemisphere'],
- as_index=False, sort=False
- )['GM_R1'].mean()
- light_color = lighten_color(color, amount=0.5)
- # White matter scatter (L/R)
- sns.scatterplot(
- data=SlopeTractWM, x='age', y='R1', hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L': color, 'R': light_color}, ax=ax,
- alpha=0.5, legend=False
- )
- # Regression lines for L and R (if any data present)
- for hemi, line_color in zip(['L','R'], [color, light_color]):
- hemi_df = SlopeTractWM[SlopeTractWM['Hemisphere'] == hemi]
- if hemi_df.shape[0] > 1: # need at least 2 points for a regression line
- sns.regplot(
- data=hemi_df, x='age', y='R1', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # Gray matter scatter & lines
- sns.scatterplot(
- data=SlopeTractGM, x='age', y='GM_R1', hue='Hemisphere', style='Hemisphere',
- markers={'L': 'o', 'R': 'o'}, s=50,
- palette={'L':'darkgray', 'R':'lightgray'}, ax=ax,
- alpha=0.5, legend=False
- )
- for hemi, line_color in zip(['L','R'], ['darkgray','lightgray']):
- hemi_gm = SlopeTractGM[SlopeTractGM['Hemisphere'] == hemi]
- if hemi_gm.shape[0] > 1:
- sns.regplot(
- data=hemi_gm, x='age', y='GM_R1', scatter=False, ax=ax,
- line_kws={'color': line_color, 'alpha': 0.8}
- )
- # --- TITLES AND LABELS -----------------------------------------------------
- ax.set_title(bundle, pad=5, fontsize=25)
- ax.set_xlabel('Scan Age', labelpad=15, fontsize=20)
- if ct in [0, 4, 8, 12]: # first column
- ax.set_ylabel("R1 [s$^{-1}$]", fontsize=20)
- ax.set_xticks([40, 42, 44, 46])
- ax.set_xticklabels([40, 42, 44, 46], fontsize=20)
- ax.set_yticks([0.4, 0.45, 0.5])
- ax.set_yticklabels([0.4, 0.45, 0.5], fontsize=20)
- ax.spines['right'].set_visible(False)
- ax.spines['top'].set_visible(False)
- else:
- ax.set_ylabel('R1 [s$^{-1}$]', fontsize=14)
- ax.set_xticks([40, 42, 44, 46])
- ax.set_xticklabels([40, 42, 44, 46], fontsize=20)
- ax.set_yticks([0.4, 0.45, 0.5])
- ax.yaxis.set_visible(False)
- ax.spines['right'].set_visible(False)
- ax.spines['left'].set_visible(False)
- ax.spines['top'].set_visible(False)
- # Clean frame (ensure spines hidden where desired)
- ax.spines['right'].set_visible(False)
- ax.spines['top'].set_visible(False)
- # Turn off unused axes
- axes[3,3].axis("off")
- axes[3,0].axis("off")
- # --- SAVE & SHOW -------------------------------------------------------------
- os.makedirs('./figures', exist_ok=True)
- plt.savefig('./figures/R1ScanAge_Hemispheres.png', dpi=600, bbox_inches='tight')
- plt.show()
Figure3.ipynb at commit 57e9055, no license · at the source
Overview
- Department of Psychology, Philipps-Universität Marburg, Marburg, Germany
- Center for Mind, Brain and Behavior - CMBB, Universities of Marburg, Gießen, Darmstadt, Germany
- Lise Meitner Research Group Neuroplasticity in Development and Learning, Max Planck Institute for Human Development, Berlin, Germany
- Department of Psychology, University of Washington, Seattle, WA USA
- eScience Institute, University of Washington, Seattle, WA USA
- Department of Psychology, Stanford University, Stanford, CA USA
- Institute of Science and Technology for Brain-Inspired Intelligence, Fudan University, Shanghai, China
- Wu Tsai Neurosciences Institute, Stanford University, Stanford, CA USA
Abstract
The infant brain undergoes rapid myelination that is critical for healthy brain function. This development has been characterized for gray and white matter independently, but the link between gray and white matter myelination remains unexplored. To close this knowledge gap, we evaluated two complementary myelin-sensitive imaging metrics: Large-scale (N = 273) T1w/
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 11 matches between paragraphs and lines of code.
EduNeuroLab/WMGMMyelinInfants
57e9055dc133eca1fdbe06690a8fd9d233fb5e5c, 31 March 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
10 files
- EndpointsToCortex.ipynb, Jupyter, 1,022 lines, 1 match
- Figure2.ipynb, Jupyter, 354 lines, 1 match
- Figure3.ipynb, Jupyter, 1,128 lines, 3 matches
- Figure4.ipynb, Jupyter, 280 lines, 1 match
- Figure5.ipynb, Jupyter, 499 lines, 1 match
- SupplementaryFigures.ipy
nb , Jupyter, 1,220 lines - mrtrix_pipeline/
mrtrix_pipeline_dHCP.ipy , Jupyter, 505 lines, 2 matchesnb - pyafq_pipeline/
pyafq_pipeline_dHCP.ipyn , Jupyter, 125 lines, 2 matchesb - utils.py, Python, 26 lines
- README.md, Text, 2 lines
Code availability
All data were analyzed using open-source software, including MRtrix110,128 and pyBabyAFQ, which we shared as a component of pyAFQ (https://
Reproduced under the paper's license (CC BY), from the paper cited above.
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;
- 9 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
Datasets cited
- figshare:30667400, at figshare; found in “Data availability”
Data Availability Statement
All data required to generate the main figures are provided as a Source Data file with this paper and are also made available in GitHub (https://
All data were analyzed using open-source software, including MRtrix110,128 and pyBabyAFQ, which we shared as a component of pyAFQ (https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 10 authors, 3 keywords, 11 MeSH terms, 3 funders, 122 references.
Cite
This paper
Zika, S., Chang, K., Orhon, A., Kruper, J., Tyagi, C., Yan, X., Tung, S., Grill-Spector, K., Rokem, A., & Grotheer, M. (2026). Cortical and white matter myelination proceed in concert during early infancy. Nature communications, 17(1), 5353. https://
BibTeX
@article{zika2026cortica
author = {Zika, Stephanie and Chang, Kelly and Orhon, Altan and Kruper, John and Tyagi, Christina and Yan, Xiaoqian and Tung, Sarah and Grill-Spector, Kalanit and Rokem, Ariel and Grotheer, Mareike},
title = {{Cortical and white matter myelination proceed in concert during early infancy}},
journal = {Nature communications},
year = {2026},
month = jun,
volume = {17},
number = {1},
pages = {5353},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42310317},
pmcid = {PMC13276173}
}
RIS
TY - JOUR
AU - Zika, Stephanie
AU - Chang, Kelly
AU - Orhon, Altan
AU - Kruper, John
AU - Tyagi, Christina
AU - Yan, Xiaoqian
AU - Tung, Sarah
AU - Grill-Spector, Kalanit
AU - Rokem, Ariel
AU - Grotheer, Mareike
TI - Cortical and white matter myelination proceed in concert during early infancy
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 5353
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Cortical and white matter myelination proceed in concert during early infancy",
"container-title": "Nature communications",
"author": [
{
"family": "Zika",
"given": "Stephanie"
},
{
"family": "Chang",
"given": "Kelly"
},
{
"family": "Orhon",
"given": "Altan"
},
{
"family": "Kruper",
"given": "John"
},
{
"family": "Tyagi",
"given": "Christina"
},
{
"family": "Yan",
"given": "Xiaoqian"
},
{
"family": "Tung",
"given": "Sarah"
},
{
"family": "Grill-Spector",
"given": "Kalanit"
},
{
"family": "Rokem",
"given": "Ariel"
},
{
"family": "Grotheer",
"given": "Mareike"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "5353",
"DOI": "10.1038/
"PMID": "42310317",
"PMCID": "PMC13276173",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
17
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1126/sciadv.aec2348 [code]
- Congenital blindness reduces myelination in human visual cortex.Journal: Science advancesIn common: FreeSurfer, NiBabel, pandas, 1 other tool, 19 references
- [2] doi:10.1162/imag.a.1325 [code]
- Decoding everyday levels of musical training from subcortical white-matter architecture.Journal: Imaging neuroscience (Cambridge, Mass.)In common: MRtrix3, FreeSurfer, statsmodels, 5 other tools, 11 references
- [3] doi:10.1162/imag.a.1341 [code]
- Massively parallelized brain tractography using compute clusters, supercomputers, and graphics processing units.Journal: Imaging neuroscience (Cambridge, Mass.)In common: DIPY, NiBabel, pandas, 3 other tools, 5 references, 2 authors
- [4] doi:10.1093/cercor/bhag132 [code]
- Spatiotemporal white-matter development across early childhood.Journal: Cerebral cortex (New York, N.Y. : 1991)In common: DIPY, MRtrix3, scikit-image, 5 other tools, developmental, 9 references
- [5] doi:10.1038/s41467-026-73072-6 [code]
- Mapping the spatiotemporal continuum of structural connectivity development across the human connectome in youth.Journal: Nature communicationsIn common: MRtrix3, FreeSurfer, NiBabel, 3 other tools, developmental, 12 references
- [6] doi:10.1038/s41597-026-06869-1 [code]
- Individual Brain Charting: fifth release of high-resolution fMRI data for cognitive mapping.Journal: Scientific dataIn common: DIPY, MRtrix3, Nipype, 9 other tools, author Kalanit Grill-Spector
- [7] doi:10.1016/j.dcn.2026.101775 [code]
- Neonatal brain-age models in full- and preterm infants.Journal: Developmental cognitive neuroscienceIn common: NiBabel, statsmodels, seaborn, 5 other tools, developmental, 9 references
- [8] doi:10.1038/s41598-026-51531-w [code]
- Multimodal age-dependent diffusion-MRI analysis of the neocortex in a rat model of cortical dysplasia.Journal: Scientific reportsIn common: DIPY, MRtrix3, scikit-image, 6 other tools, 6 references
- [9] doi:10.1002/hbm.70562 [code]
- Distinct Physiological Mechanisms Drive Grey Matter Plasticity in Complex Versus Simple Sequence Learning.Journal: Human brain mappingIn common: MRtrix3, NiBabel, statsmodels, 6 other tools, 6 references
- [10] 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: Nipype, FreeSurfer, NiBabel, 7 other tools, 5 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, 9 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:48277cc20c91d0cd…
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.
