A hierarchical framework for cortical and subcortical gray-matter parcellation across rodents, primates, and humans.
The 22 matches
- [1] § Results › Per-region homology confidence ↔ projects/common_cross_species_atlas/CHA_Validation_Homology_Confidence.ipynb, lines 22–53 · score 0.96 · TEM_Amygdala, FRO_Prefrontal, CIN_Posterior, homology confidence, TEM_Inferior, INS_Posterior
- [2] § Methods › Validation of the common atlas across scales and species › Containment validation using finer-scale species-specific atlases ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 582–717 · score 0.88 · best matching target, finer atlas region, raw expected, target parcels, fractional overlap, volume fraction
- [3] § Results › Per-region homology confidence ↔ projects/common_cross_species_atlas/CHA_Validation_Homology_Confidence.ipynb, lines 56–89 · score 0.88 · functional analog, nomenclature directness, CIN_Posterior, homology confidence, INS_Posterior, OLF_Piriform
- [4] § Results › Per-region homology confidence ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 465–479 · score 0.87 · THL_Thalamus, FRO_Prefrontal, CIN_Posterior, INS_Posterior, TEM_Superior, dorsal
- [5] § Methods › Validation of the common atlas across scales and species › Cross-species connectivity validation ↔ projects/common_cross_species_atlas/CHA_Validation_Homology_Confidence.ipynb, lines 22–53 · score 0.85 · adequate marmoset tracer, INS_Posterior, OLF_Piriform, INS_Anterior, OLF_Anterior, cortical regions
- [6] § Methods › Validation of the common atlas across scales and species › Cross-species connectivity validation ↔ projects/common_cross_species_atlas/CHA_Validation_Tracer.ipynb, lines 202–208 · score 0.80 · systematic injection coverage, INS_Posterior, OLF_Piriform, INS_Anterior, OLF_Anterior, tracer
- [7] § Methods › Validation of the common atlas across scales and species › Per-region homology confidence assessment › Geometric consistency ↔ projects/common_cross_species_atlas/CHA_Validation_Homology_Confidence.ipynb, lines 92–159 · score 0.80 · direction vector, resultant length, mass, centroid, species atlas, CHA region
- [8] § Methods › Validation of the common atlas across scales and species › Validation of human parcellation using neuroparc atlases ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 345–452 · score 0.77 · Wilcoxon signed rank, region Median Dice, hypothesis, margins, shifting, framing
- [9] § Methods › Validation of the common atlas across scales and species › Per-region homology confidence assessment › Cross-species connectivity correspondence ↔ projects/common_cross_species_atlas/CHA_Validation_Tracer.ipynb, lines 202–208 · score 0.77 · INS_Posterior, OLF_Piriform, injection coverage, INS_Anterior, OLF_Anterior, tracer
- [10] § Methods › Validation of the common atlas across scales and species › Per-region homology confidence assessment › Nomenclature directness ↔ projects/common_cross_species_atlas/CHA_Validation_Homology_Confidence.ipynb, lines 56–89 · score 0.74 · functional analog, FRO_Precentral, TEM_Inferior, nomenclature, hippocampus, directness
- [11] § Results › Cross-species connectivity validation ↔ projects/common_cross_species_atlas/CHA_Validation_Tracer.ipynb, lines 369–492 · score 0.73 · marmoset coverage quartile, Rank percentile, projection densities, log10, Q1, Q4
- [12] § Methods › Validation of the common atlas across scales and species › Cross-species connectivity validation ↔ projects/common_cross_species_atlas/CHA_Validation_Tracer.ipynb, lines 99–160 · score 0.72 · Paxinos area, FLNe matrix, Paxinos space, CHA region, matching, mbm
- [13] § Methods › Validation of the common atlas across scales and species › Per-region homology confidence assessment › Cross-species connectivity correspondence ↔ projects/cross_species_connectomics/fig5a.py, lines 1–40 · score 0.71 · INS_Posterior, OLF_Piriform, INS_Anterior, OLF_Anterior, cross species, connectivity
- [14] § Results › Validation of the common human atlas using cross-atlas dice similarity ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 345–452 · score 0.71 · Wilcoxon signed rank, inter atlas agreement, Median Dice, sum, CHA
- [15] § Methods › Construction of brain parcellations ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 465–479 · score 0.70 · perirhinal, auditory, entorhinal, postcentral, claustrum, subthalamus
- [16] § Results › Cross-species parcellation hierarchy ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 25–76 · score 0.67 · basal ganglia, BG, occipital, cingulate, THL, frontal
- [17] § Methods › Validation of the common atlas across scales and species › Containment validation using finer-scale species-specific atlases ↔ projects/common_cross_species_atlas/CHA_Integrate.py, lines 177–189 · score 0.66 · raw expected, best matching, volume fraction, parcels, atlas
- [18] § Methods › Validation of the common atlas across scales and species › Containment validation using finer-scale species-specific atlases ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 538–578 · score 0.63 · Duke CIVM, CHA rat, WHS, baseline, MBM, ABA
- [19] § Methods › Validation of the common atlas across scales and species › Cross-species connectivity validation ↔ projects/common_cross_species_atlas/CHA_Validation_Tracer.ipynb, lines 369–492 · score 0.61 · marmoset signal, rank percentiles, quartiles, transformation, validation, agreement
- [20] § Results › Validation of the common human atlas using cross-atlas dice similarity ↔ projects/common_cross_species_atlas/CHA_quants.ipynb, lines 183–273 · score 0.61 · Dice matrix, median Dice, Neuroparc atlases, angle, scatterplot, inter
- [21] § Results › Cross-species parcellation hierarchy ↔ projects/cross_species_connectomics/cross_species_nets.py, lines 184–216 · score 0.53 · subcortical gray matter, segmentation limitations, root, species
- [22] § Results › Overview of the common atlas framework ↔ projects/common_cross_species_atlas/CHA_Integrate.py, lines 1–56 · score 0.52 · marmoset retrograde tracer, common hierarchical atlas, assignment, template, validated, maps
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,056 lines · 36 KB · MIT · 8 matches
- # %%
- import os
- import nibabel as nb
- import numpy as np
- import pandas as pd
- import matplotlib.pyplot as plt
- from itertools import combinations
- import re
- import seaborn as sns
- from adjustText import adjust_text
- import matplotlib.colors as mcolors
- os.chdir('projects\\common_cross_species_atlas') #<-- set working directory here
- print("Current working directory:", os.getcwd())
- # %%
- # %%
- # %% [markdown]
- # # Figure 4
- # %%
- _dir="data\\roistats\\"
- species=['mouse', 'marmoset', 'rhesus', 'human']
- metric='volume (mm^3)'
- exclude_rois=['OLF_Claustrum', 'BG_Substantia_Nigra', 'THL_Subthalamus', 'CBL', 'BST']
- dfs=[]
- for sp in species:
- df1=pd.read_table(_dir+'L2_332_moused_'+sp+'_roistats.txt', index_col=0).T[[metric]]
- df1.drop(exclude_rois, axis=0, inplace=True)
- #df2=pd.read_table(_dir+sp+'_cran_stats.txt', index_col=0).T[[metric]]
- df1[sp]=df1[metric]
- print(df1[metric].sum())
- df1.drop(metric, axis=1, inplace=True)
- dfs.append(df1)
- df_cons=dfs[0].join(dfs[1]).join(dfs[2]).join(dfs[3])
- df=df_cons
- volume_data=df
- # Group definitions
- region_groups = {
- 'Frontal': ["FRO_Precentral", "FRO_Premotor", "FRO_Prefrontal"],
- 'Parietal': ["PAR"],
- 'Temporal': ["TEM_Superior", "TEM_Inferior", "TEM_Medial", "TEM_Hippocampus", "TEM_Amygdala"],
- 'Occipital': ["OCC_Lateral", "OCC_Medial"],
- 'Insula': ["INS_Anterior", "INS_Posterior"],
- 'Olfactory': ["OLF_Anterior", "OLF_Piriform"],
- 'Cingulate': ["CIN_Anterior", "CIN_Posterior"],
- 'Basal Ganglia': ["BG_CaudoPutamen", "BG_Accumbens", "BG_Pallidum"],
- 'Thalamus': ["THL_Thalamus", "THL_Hypothalamus"]
- }
- # OPTIONAL split
- # Surface/Deep sets (for ordering within each group)
- surface_regions = {
- "FRO_Precentral","FRO_Premotor","FRO_Prefrontal","PAR",
- "TEM_Superior","TEM_Inferior","TEM_Medial",
- "OCC_Lateral","OCC_Medial","INS_Anterior","INS_Posterior",
- "CIN_Anterior","CIN_Posterior"
- }
- deep_regions = {
- "TEM_Hippocampus","TEM_Amygdala","OLF_Anterior","OLF_Piriform",
- "BG_CaudoPutamen","BG_Accumbens","BG_Pallidum",
- "THL_Thalamus","THL_Hypothalamus"
- }
- # %%
- # =========================
- # CONFIG
- # =========================
- species_order = ['mouse', 'marmoset', 'rhesus', 'human']
- colors = {'mouse':'#1f77b4','marmoset':'#ff7f0e','rhesus':'#2ca02c','human':'#d62728'}
- markers = {'mouse':'o','marmoset':'s','rhesus':'D','human':'^'}
- normalize = True
- ref_species = 'human'
- # =========================
- # PREP
- # =========================
- all_defined = [r for gl in region_groups.values() for r in gl]
- regions_available = [r for r in df.index if r in set(all_defined)]
- df_use = df.loc[regions_available].copy().astype(float)
- if normalize:
- df_use = df_use.div(df_use.sum(axis=0), axis=1)
- df_use = df_use[species_order]
- # Order regions: by group; inside group surface→deep; then sort by ref species desc
- ordered_regions, group_slices = [], []
- cursor = 0
- for gname, glist in region_groups.items():
- avail = [r for r in glist if r in df_use.index]
- if not avail:
- continue
- g_surface = [r for r in avail if r in surface_regions]
- g_deep = [r for r in avail if r in deep_regions]
- g_order = g_surface + g_deep if (g_surface or g_deep) else avail
- ref_vals = df_use.loc[g_order, ref_species]
- g_sorted = ref_vals.sort_values(ascending=False).index.tolist()
- start, end = cursor, cursor + len(g_sorted) - 1
- ordered_regions.extend(g_sorted)
- group_slices.append((gname, start, end))
- cursor = end + 1
- df_plot = df_use.loc[ordered_regions, :]
- # =========================
- # PLOT
- # =========================
- N = len(ordered_regions)
- fig_w = 3.0
- fig_h = min(6, 0.25 * N) # <= requested sizing
- fig, ax = plt.subplots(figsize=(fig_w, fig_h))
- y = np.arange(N) # 0..N-1 (top row is 0; we invert later so it appears at top)
- # Higher-contrast alternating shading by group (no labels)
- for i, (_, start, end) in enumerate(group_slices):
- if i % 2 == 0:
- ax.axhspan(start - 0.5, end + 0.5, color='k', alpha=0.14)
- # Dumbbell stems (min→max per region)
- mins = df_plot.min(axis=1).values
- maxs = df_plot.max(axis=1).values
- ax.hlines(y, mins, maxs, color='0.70', linewidth=2, zorder=1)
- # Species markers
- handles = []
- for sp in species_order:
- h = ax.plot(df_plot[sp].values, y, linestyle='None',
- marker=markers.get(sp,'o'), markersize=4.5,
- color=colors.get(sp), label=sp, zorder=2)[0]
- handles.append(h)
- # Y axis
- ax.set_yticks(y)
- ax.set_yticklabels(ordered_regions, fontsize=8)
- ax.invert_yaxis()
- # X axis, grid, labels
- ax.grid(axis='x', alpha=0.3, linewidth=0.6)
- ax.tick_params(axis='x', labelsize=8)
- ax.set_xlabel('Volume fraction \n(per-species total)' if normalize else 'Volume (mm³)')
- #ax.set_title(title, fontsize=10.5, pad=6)
- # Figure-level legend above the axes (kept clear of first row)
- fig.legend(handles, species_order, loc='upper center', ncol=len(species_order),
- frameon=False, bbox_to_anchor=(0.5, 0.995), borderaxespad=0.0)
- # Tight layout: leave top room for legend
- plt.tight_layout(rect=(0, 0, 1, 0.90))
- # %%
- df_plot
- # %%
- # %%
- # %%
- # %% [markdown]
- # # Figure 5a-b
- # %% [markdown]
- # #### Flexible string search to identify regions across Neuroparc atlases
- # atlas source: https://github.com/neurodata/neuroparc
- # %%
- # Set input path to your directory of Dice matrices\
- extract_dir = "data\\dice_validation\\"
- dice_files = [f for f in os.listdir(extract_dir) if f.endswith(".csv")]
- final_annotated_df = []
- boxplot_data = []
- for file in dice_files:
- region_name = file.replace(".csv", "").replace("DICE_ROI_", "")
- df = pd.read_csv(os.path.join(extract_dir, file), index_col=0)
- # Exclude low-performing atlases
- low_avg_atlases=df.columns[df.median()<.1]
- df = df.drop(index=low_avg_atlases, columns=low_avg_atlases, errors='ignore')
- if df.shape[0] < 3:
- continue
- common_labels = [idx for idx in df.index if 'Common' in idx]
- if not common_labels:
- continue
- common_label = common_labels[0]
- try:
- common_vs_others = df.loc[common_label].drop(labels=[common_label]).dropna()
- non_common_df = df.drop(index=common_label, columns=common_label)
- inter_comparisons = non_common_df.where(~np.eye(non_common_df.shape[0], dtype=bool)).stack().values
- if len(common_vs_others) < 1 or len(inter_comparisons) < 1:
- continue
- for val in common_vs_others:
- boxplot_data.append({'Region': region_name, 'Type': 'Common vs. Others', 'Dice': val})
- for val in inter_comparisons:
- boxplot_data.append({'Region': region_name, 'Type': 'Others vs. Others', 'Dice': val})
- final_annotated_df.append({
- "Region": f"{region_name} (n={len(inter_comparisons)})",
- "Median Dice (Common vs. Others)": common_vs_others.median(),
- "Delta Median (Common vs. Others - Others vs. Others)": common_vs_others.median() - np.median(inter_comparisons)
- })
- except Exception:
- continue
- labeled_df = pd.DataFrame(final_annotated_df)
- plt.figure(figsize=(5, 4))
- ax = sns.scatterplot(
- data=labeled_df,
- x="Delta Median (Common vs. Others - Others vs. Others)",
- y="Median Dice (Common vs. Others)",
- color="darkred",
- s=30,
- edgecolor="black"
- )
- plt.axvline(0, color='gray', linestyle='--')
- plt.xlabel("ΔMedian Dice\n(Common vs. Others - Others vs. Others)", fontsize=12)
- plt.ylabel("Median Dice\n(Common vs. Others)", fontsize=12)
- texts = []
- # Annotate region names with (n)
- for _, row in labeled_df.iterrows():
- texts.append(ax.text(
- row["Delta Median (Common vs. Others - Others vs. Others)"],
- row["Median Dice (Common vs. Others)"] + 0.0,
- row["Region"],
- fontsize=10,
- ha='left'
- ))
- adjust_text(texts, arrowprops=dict(
- arrowstyle='->',
- color='red',
- linewidth=1,
- linestyle='dotted',
- connectionstyle='angle3,angleA=90,angleB=0',
- alpha=0
- ))
- plt.ylim([.3, .92])
- plt.xlim([-.05, .32])
- plt.grid(True, axis='y', linestyle='--', linewidth=1, color='gray')
- plt.tight_layout()
- # %%
- # %%
- # %%
- boxplot_df=pd.DataFrame(boxplot_data)
- region_order = (
- boxplot_df[boxplot_df['Type'] == 'Common vs. Others']
- .groupby('Region')['Dice']
- .median()
- .sort_values(ascending=False)
- .index.tolist()
- )
- plt.figure(figsize=(5.75, 4))
- ax = sns.violinplot(
- data=boxplot_df,
- x='Region', y='Dice', hue='Type',
- order=region_order,
- palette='Set2', split=True, cut=0,
- inner=None
- )
- # Get the original colors from palette
- palette = sns.color_palette('Set2')
- color_map = {
- 'Common vs. Others': palette[0],
- 'Others vs. Others': palette[1]
- }
- # Darken each color for marker
- def darken_color(color, factor=0.5):
- return tuple([max(0, c * factor) for c in color])
- # Add bold median markers with darker hues
- for i, region in enumerate(region_order):
- for typ, offset in zip(['Common vs. Others', 'Others vs. Others'], [-0.05, 0.05]):
- subset = boxplot_df[(boxplot_df['Region'] == region) & (boxplot_df['Type'] == typ)]['Dice'].dropna()
- median_val = subset.median()
- marker_color = darken_color(color_map[typ])
- ax.plot(
- i + offset,
- median_val,
- marker='o',
- markersize=5,
- color=marker_color,
- zorder=5
- )
- plt.xticks(rotation=45, ha='right')
- plt.xlabel("",fontsize=12)
- plt.ylabel("Dice Coefficient",fontsize=12)
- plt.grid(axis='y', linestyle='--', linewidth=1, color='gray')
- plt.legend(loc='best', fontsize=10)
- plt.tight_layout()
- # %%
- # %%
- boxplot_df
- # %%
- # %% [markdown]
- # #### dice stat tests for revision
- # %%
- """
- Statistical tests for Fig 5a Dice comparisons.
- Hypothesis: CHA performs at least as well as inter-atlas agreement
- (non-inferiority), not necessarily better.
- """
- import pandas as pd
- import numpy as np
- from scipy import stats
- # Columns: Region, Type ("Common vs. Others" or "Others vs. Others"), Dice
- print(f"Total entries: {len(boxplot_df)}")
- print(f"Regions: {boxplot_df['Region'].nunique()}")
- print(f"Types: {boxplot_df['Type'].unique()}")
- # --- Per-region summary ---
- regions = sorted(boxplot_df["Region"].unique())
- summary = []
- for r in regions:
- common = boxplot_df[(boxplot_df["Region"] == r) &
- (boxplot_df["Type"] == "Common vs. Others")]["Dice"]
- others = boxplot_df[(boxplot_df["Region"] == r) &
- (boxplot_df["Type"] == "Others vs. Others")]["Dice"]
- if len(common) == 0 or len(others) == 0:
- continue
- delta_median = common.median() - others.median()
- delta_mean = common.mean() - others.mean()
- summary.append({
- "Region": r,
- "n_common": len(common),
- "n_others": len(others),
- "median_common": common.median(),
- "median_others": others.median(),
- "delta_median": delta_median,
- "mean_common": common.mean(),
- "mean_others": others.mean(),
- "delta_mean": delta_mean,
- })
- df_sum = pd.DataFrame(summary)
- print(f"\n{'='*70}")
- print("PER-REGION SUMMARY")
- print(f"{'='*70}")
- print(df_sum.to_string(index=False))
- # How many regions have delta >= 0?
- n_positive = (df_sum["delta_median"] >= 0).sum()
- n_total = len(df_sum)
- print(f"\nRegions where Common >= Others (median): {n_positive}/{n_total}")
- # ============================================================
- # TEST 1: Wilcoxon signed-rank on per-region median Dice
- # H0: median(Common) < median(Others) (CHA is worse)
- # H1: median(Common) >= median(Others) (CHA is non-inferior)
- # One-sided test: reject H0 if p < 0.05
- # ============================================================
- print(f"\n{'='*70}")
- print("TEST 1: Wilcoxon signed-rank on per-region median Dice")
- print(f"{'='*70}")
- deltas = df_sum["delta_median"].values
- # One-sided: is delta significantly >= 0?
- stat_wilcox, p_two_wilcox = stats.wilcoxon(deltas, alternative="two-sided")
- _, p_greater_wilcox = stats.wilcoxon(deltas, alternative="greater")
- print(f" n regions: {len(deltas)}")
- print(f" Median of deltas: {np.median(deltas):.4f}")
- print(f" Mean of deltas: {np.mean(deltas):.4f}")
- print(f" Two-sided p: {p_two_wilcox:.4e}")
- print(f" One-sided (greater) p: {p_greater_wilcox:.4e}")
- if p_greater_wilcox < 0.05:
- print(f" → CHA performs significantly at least as well as inter-atlas agreement")
- else:
- print(f" → Cannot reject that CHA performs worse (but see effect direction)")
- # ============================================================
- # TEST 2: Non-inferiority with margin
- # H0: median(Common) - median(Others) < -margin (CHA is meaningfully worse)
- # H1: median(Common) - median(Others) >= -margin (non-inferior)
- # Test: Wilcoxon on (deltas + margin), one-sided greater
- # ============================================================
- print(f"\n{'='*70}")
- print("TEST 2: Non-inferiority test")
- print(f"{'='*70}")
- MARGINS = [0.05, 0.10]
- for margin in MARGINS:
- shifted = deltas + margin # shift by margin
- _, p_ni = stats.wilcoxon(shifted, alternative="greater")
- print(f"\n Margin = {margin}:")
- print(f" H0: Common - Others < -{margin} (CHA is worse by > {margin})")
- print(f" One-sided p: {p_ni:.4e}")
- if p_ni < 0.05:
- print(f" → Non-inferiority established at margin {margin}")
- else:
- print(f" → Cannot establish non-inferiority at margin {margin}")
- # %%
- # %%
- # %%
- # %% [markdown]
- # # Figure 5C
- # %%
- chf_rois_primate=['FRO_Precentral', 'FRO_Premotor', 'FRO_Prefrontal', 'PAR_Postcentral', 'PAR_Superior', 'PAR_Inferior', 'PAR_Precuneus',
- 'TEM_Superior', 'TEM_Inferior', 'TEM_Medial', 'TEM_Hippocampus', 'TEM_Amygdala', 'OCC_Lateral', 'OCC_Medial', 'INS_Anterior',
- 'INS_Posterior', 'OLF_Anterior', 'OLF_Piriform', 'CIN_Anterior', 'CIN_Posterior', 'BG_Caudate', 'BG_Putamen',
- 'BG_Accumbens', 'BG_Pallidum', 'THL_Thalamus', 'THL_Hypothalamus']
- chf_rois_mouse=['FRO_Precentral', 'FRO_Premotor', 'FRO_Prefrontal', 'PAR',
- 'TEM_Superior', 'TEM_Inferior', 'TEM_Medial', 'TEM_Hippocampus', 'TEM_Amygdala', 'OCC_Lateral', 'OCC_Medial', 'INS_Anterior',
- 'INS_Posterior', 'OLF_Anterior', 'OLF_Piriform', 'CIN_Anterior', 'CIN_Posterior', 'BG_CaudoPutamen',
- 'BG_Accumbens', 'BG_Pallidum', 'THL_Thalamus', 'THL_Hypothalamus']
- duke_rois_rat=['ACA__Anterior_Cingulate_Area_left','AI__Agranular_Insular_Area_left','AUD__Auditory_Areas_left','ECT__Ectorhinal_Area_left','GU__Gustatory_Areas_left','ILA__Infralimbic_Area_left','MO__Somatomotor_Areas_left','ORB__Orbital_Area_left','PERI__Perirhinal_Area_left','PL__Prelimbic_Area_left','PTLp__Posterior_Parietal_Association_Areas_left','RSP__Retrosplenial_Area_left','SS__Somatosensory_Areas_left','TEa__Temporal_Association_Areas_left','VIS__Visual_Areas_left','VISC__Visceral_Area_left','Isocortex__Isocortex_Uncharted_left','AA__Amygdalar_Area_left','COA__Cortical_Amygdalar_Area_left','PIR__Piriform_Area_left','TT__Tenia_Tecta_left','OLF__Olfactory_Areas_Uncharted_left','BLA__Basolateral_Amygdala_left','CLA__Claustrum_left','ENT__Entorhinal_Area_left','PAR__Parasubiculum_left','POST__Postsubiculum_left','PRE__Presubiculum_left','HPF__Hippocampal_Formation_Uncharted_left','ZI__Zona_Incerta_Uncharted_left','FF__Fields_of_Forel_left','PH__Posterior_Hypothalamic_Nucleus_left','POA__Preoptic_Areas_Uncharted_left','STN__Subthalamic_Nucleus_left','HY__Hypothalamus_Uncharted_left','LSX__Lateral_Septal_Complex_left','PALd__Pallidum_Dorsal_Region_left','STR__Striatum_left','ACB__Nucleus_Accumbens_left','BST__Bed_Nuclei_of_Stria_Terminalis_left','PALv__Pallidum_Ventral_Region_left','VPM__Ventral_Posteromedial_Nucleus_of_the_Thalamus_left','VPL__Ventral_Posterolateral_Nucleus_of_the_Thalamus_left','LD__Lateral_Dorsal_Nucleus_of_the_Thalamus_Uncharted_left','LDVL__Lateral_Dorsal_Nucleus_of_the_Thalamus_Ventrolateral_Part_left','LP__Lateral_Posterior_Nucleus_of_the_Thalamus_left','ATN__Anterior_Group_of_the_Dorsal_Thalamus_left','MG__Medial_Geniculate_Complex_left','LGd__Dorsal_Part_of_the_Lateral_Geniculate_Complex_left','RT__Reticular_Nucleus_of_the_Thalamus_left','PrG__Pregeniculate_Nucleus_left','MD__Mediodorsal_Thalamic_Nucleus_left','TH__Thalamus_Uncharted_left','FRP__Frontal_Pole_Cerebral_Cortex_left']
- # %%
- # --- Load atlas and CHF images ---
- atlas_img = nb.load('data/atlas/mouse/ABA_major_regions.nii.gz').get_fdata()
- chf_img = nb.load('data/atlas/mouse/cha_mouse.nii.gz').get_fdata()
- # --- Load label maps ---
- atlas_label_df = pd.read_table('data/atlas/mouse/ABA_major_regions.txt', index_col=0, header=None, delim_whitespace=True)
- chf_label_df = pd.read_table('data/atlas/mouse/cha_mouse.txt', index_col=0, header=None, delim_whitespace=True)
- # --- Get valid region labels (exclude 0/background) ---
- atlas_labels = np.unique(atlas_img)
- atlas_labels = atlas_labels[atlas_labels > 0]
- chf_labels = np.unique(chf_img)
- chf_labels = chf_labels[chf_labels > 0]
- # --- Dice similarity function ---
- def dice(mask1, mask2):
- intersection = np.logical_and(mask1, mask2).sum()
- total = mask1.sum() + mask2.sum()
- return 2 * intersection / total if total > 0 else np.nan
- # --- Compute Dice matrix ---
- dice_matrix = np.zeros((len(atlas_labels), len(chf_labels)))
- for i, atlas_val in enumerate(atlas_labels):
- mask1 = atlas_img == atlas_val
- for j, chf_val in enumerate(chf_labels):
- mask2 = chf_img == chf_val
- dice_matrix[i, j] = dice(mask1, mask2)
- # --- Use region names for row/column labels ---
- atlas_names = [atlas_label_df.loc[int(val)][1] for val in atlas_labels]
- chf_names = [chf_label_df.loc[int(val)][1] for val in chf_labels]
- dice_df = pd.DataFrame(dice_matrix, index=[a[:-1] for a in atlas_names], columns=chf_names)
- # %%
- fig=plt.figure(figsize=(6, 6))
- sns.heatmap(dice_df[chf_rois_mouse],
- annot=False, cmap="Reds", fmt=".2f", linewidths=0.5,
- cbar_kws={'label': 'Dice Coefficient',
- 'shrink': 0.3,
- 'aspect': 25,
- 'ticks': [0.0, 0.25, 0.5, 0.75, 1.0],
- 'orientation': 'vertical'},
- square=True)
- plt.grid(alpha=.5)
- plt.tight_layout()
- plt.show()
- # %% [markdown]
- # # Figure 5d
- # ## Containment validation
- # %%
- import numpy as np
- import pandas as pd
- import nibabel as nb
- import matplotlib.pyplot as plt
- from scipy.ndimage import affine_transform
- # =============================================================================
- # Configuration per species
- # =============================================================================
- SPECIES_CONFIG = {
- 'mouse': {
- 'atlas_file': 'ABA.nii.gz',
- 'cha_file': 'cha_mouse.nii.gz',
- 'atlas_name': 'ABA',
- 'baseline_coarser_file': None,
- },
- 'rat': {
- 'atlas_file': 'whs_rat_fit.nii.gz',
- 'cha_file': 'cha_rat.nii.gz',
- 'atlas_name': 'WHS',
- 'baseline_coarser_file': 'duke_civm_fit_combined.nii.gz', # for WHS-in-Duke baseline, an independent evaluation
- },
- 'marmoset': {
- 'atlas_file': 'MBM_vH_subcortical.nii.gz',
- 'cha_file': 'cha_marmoset.nii.gz',
- 'atlas_name': 'MBM',
- 'baseline_coarser_file': None,
- },
- 'rhesus': {
- 'atlas_file': 'civm_rhesus_label.nii.gz',
- 'cha_file': 'cha_rhesus.nii.gz',
- 'atlas_name': 'CIVM',
- 'baseline_coarser_file': None,
- },
- }
- # %%
- # =============================================================================
- # Helper functions
- # =============================================================================
- def read_labels(txt_path):
- """Read label file: '1 LabelName' per line. Returns DataFrame with index=ID, col 1=name."""
- return pd.read_table(txt_path, index_col=0, header=None, sep=r'\s+')
- def get_roi_names(txt_path):
- """Return list of ROI names from a label txt file."""
- labels = read_labels(txt_path)
- return list(labels[1].values)
- def get_percent_overlap(atlas, target, roi_indices):
- """Compute fractional overlap of each atlas ROI with each target parcel.
- For each atlas ROI i and target parcel j:
- overlap[i,j] = (# voxels where atlas==roi_i AND target==j) / (# voxels where atlas==roi_i)
- """
- target_ids = np.unique(target)
- n_atlas = len(roi_indices)
- n_target = len(target_ids)
- percent_overlap = np.zeros((n_atlas, n_target))
- for i, roi_id in enumerate(roi_indices):
- if i % 100 == 0:
- print(f' Processing ROI {i}/{n_atlas}')
- atlas_mask = (atlas == roi_id)
- atlas_count = np.sum(atlas_mask)
- if atlas_count == 0:
- continue
- for j, tid in enumerate(target_ids):
- percent_overlap[i, j] = np.sum(atlas_mask & (target == tid)) / atlas_count
- return percent_overlap, target_ids
- def build_overlap_df(atlas_labels, target_labels_txt, percent_overlap, target_ids):
- """Build a DataFrame from overlap matrix with proper column names."""
- target_labels = read_labels(target_labels_txt)
- # Map target IDs to names; ID=0 maps to '0' (background)
- col_names = []
- for tid in target_ids:
- tid_int = int(tid)
- if tid_int == 0:
- col_names.append('0')
- elif tid_int in target_labels.index:
- col_names.append(target_labels.loc[tid_int, 1])
- else:
- col_names.append(f'unknown_{tid_int}')
- popd = pd.DataFrame(percent_overlap, index=atlas_labels[1].values, columns=col_names)
- popd['SUM'] = popd.sum(axis=1)
- return popd
- def compute_containment(popd, target_rois):
- """Extract max containment per finer-atlas region across target ROIs."""
- valid = popd[target_rois]
- valid = valid[valid.max(axis=1) > 0]
- max_containment = valid.max(axis=1)
- best_match_idx = valid.values.argmax(axis=1)
- best_match = [target_rois[i] for i in best_match_idx]
- return max_containment, best_match
- def compute_target_volume_fractions(target_nii_data, target_labels_txt, target_rois):
- """Compute volume fraction of each target ROI relative to total labeled volume."""
- target_labels = read_labels(target_labels_txt)
- # Build name -> list of IDs mapping
- name_to_ids = {}
- for idx, row in target_labels.iterrows():
- name = row[1]
- if name in target_rois:
- if name not in name_to_ids:
- name_to_ids[name] = []
- name_to_ids[name].append(idx)
- # Count voxels per target ROI
- total_labeled = 0
- roi_volumes = {}
- for name in target_rois:
- if name in name_to_ids:
- count = sum(np.sum(target_nii_data == rid) for rid in name_to_ids[name])
- else:
- count = 0
- roi_volumes[name] = count
- total_labeled += count
- # Convert to fractions
- vol_fracs = {name: count / total_labeled if total_labeled > 0 else 0
- for name, count in roi_volumes.items()}
- return vol_fracs
- def adjust_containment(popd, target_rois, target_vol_fracs):
- """Apply specificity correction: adjusted = (raw - expected) / (1 - expected).
- expected = volume fraction of best-matching target parcel.
- Corrects for inflation due to large target parcels.
- """
- valid = popd[target_rois]
- valid = valid[valid.max(axis=1) > 0]
- raw_max = valid.max(axis=1)
- best_idx = valid.values.argmax(axis=1)
- expected = np.array([target_vol_fracs[target_rois[i]] for i in best_idx])
- adjusted = (raw_max.values - expected) / (1 - expected)
- adjusted = np.clip(adjusted, 0, 1) # floor at 0
- return pd.Series(adjusted, index=raw_max.index)
- def spatial_perturbation(atlas_data, target_data, atlas_labels, target_labels_txt, rot=0):
- """Apply rotation perturbation to target and recompute containment."""
- theta = np.deg2rad(rot)
- affine_matrix = np.array([
- [np.cos(theta), -np.sin(theta), 0],
- [np.sin(theta), np.cos(theta), 0],
- [0, 0, 1]
- ])
- transformed = affine_transform(target_data, affine_matrix, offset=[0, 0, 0], order=0, mode='nearest')
- percent_overlap, target_ids = get_percent_overlap(atlas_data, transformed, atlas_labels.index.values)
- popd = build_overlap_df(atlas_labels, target_labels_txt, percent_overlap, target_ids)
- return popd
- # %%
- def run_containment_analysis(species, use_adjusted=False, plot=True):
- """Run full containment analysis for a species.
- Parameters
- ----------
- species : str
- One of 'mouse', 'rat', 'marmoset', 'rhesus'
- use_adjusted : bool
- If True, apply specificity correction for parcel size
- plot : bool
- If True, generate ECDF plot
- """
- cfg = SPECIES_CONFIG[species]
- atlas_dir = f'data/atlas/{species}/'
- # --- Read atlas and CHA files ---
- atlas_file = cfg['atlas_file']
- cha_file = cfg['cha_file']
- atlas_name = cfg['atlas_name']
- atlas_labels_txt = atlas_dir + atlas_file.replace('.nii.gz', '.txt')
- cha_labels_txt = atlas_dir + cha_file.replace('.nii.gz', '.txt')
- atlas_labels = read_labels(atlas_labels_txt)
- cha_rois = get_roi_names(cha_labels_txt)
- print(f'\n{"="*60}')
- print(f'Species: {species}')
- print(f'Atlas: {atlas_name} ({len(atlas_labels)} regions)')
- print(f'CHA: {len(cha_rois)} regions')
- print(f'{"="*60}')
- # --- Load volumes (full, unfiltered — matches old code) ---
- vols_file = atlas_dir + atlas_file.replace('.nii.gz', '_roistats.txt')
- vols_df = pd.read_table(vols_file, index_col=0).loc['volume (mm^3)']
- vols = vols_df.values
- # --- Load or compute CHA containment ---
- cha_overlap_file = (atlas_dir + atlas_file.replace('.nii.gz', '') +
- '_overlaps_' + cha_file.replace('.nii.gz', '') + '.csv')
- try:
- popd_cha = pd.read_csv(cha_overlap_file, index_col=0)
- print(f'Loaded CHA containment from {cha_overlap_file}')
- except FileNotFoundError:
- print(f'Computing CHA containment...')
- at = nb.load(atlas_dir + atlas_file)
- chf = nb.load(atlas_dir + cha_file)
- atlas_data = at.get_fdata()
- cha_data = chf.get_fdata()
- overlap, target_ids = get_percent_overlap(atlas_data, cha_data, atlas_labels.index.values)
- popd_cha = build_overlap_df(atlas_labels, cha_labels_txt, overlap, target_ids)
- popd_cha.to_csv(cha_overlap_file)
- # --- Compute containment (replicating old code logic) ---
- # Filter to regions with any nonzero CHA overlap, get max per region
- containment_values_cha = popd_cha[cha_rois][popd_cha[cha_rois].max(axis=1) > 0].max(axis=1)
- containment_scores_cha = np.array(containment_values_cha)
- # Apply specificity correction if requested
- if use_adjusted:
- chf = nb.load(atlas_dir + cha_file)
- cha_data = chf.get_fdata()
- cha_vol_fracs = compute_target_volume_fractions(cha_data, cha_labels_txt, cha_rois)
- # Get best-matching CHA region for each atlas region
- valid_rows = popd_cha[cha_rois][popd_cha[cha_rois].max(axis=1) > 0]
- best_idx = valid_rows.values.argmax(axis=1)
- expected = np.array([cha_vol_fracs[cha_rois[i]] for i in best_idx])
- containment_scores_cha = np.clip(
- (containment_scores_cha - expected) / (1 - expected), 0, 1)
- # Volumes: use full array, index with sorted_idx (old code logic)
- volumes_cha = np.array(vols)
- sorted_idx_cha = np.argsort(containment_scores_cha)
- sorted_scores_cha = containment_scores_cha[sorted_idx_cha]
- sorted_volumes_cha = volumes_cha[sorted_idx_cha]
- total_volume_cha = np.sum(sorted_volumes_cha)
- ecdf_cha = np.cumsum(sorted_volumes_cha) / total_volume_cha
- # --- Print summary ---
- def print_summary(scores, volumes_sorted, total_vol, label):
- n = len(scores)
- s = np.array(scores)
- print(f'\n {label} ({n} regions):')
- print(f' >= 0.95: {np.sum(s >= 0.95):3d} ({100*np.sum(s >= 0.95)/n:.0f}%)')
- print(f' >= 0.80: {np.sum(s >= 0.80):3d} ({100*np.sum(s >= 0.80)/n:.0f}%)')
- print(f' >= 0.50: {np.sum(s >= 0.50):3d} ({100*np.sum(s >= 0.50)/n:.0f}%)')
- print(f' Mean: {np.mean(s):.3f} Median: {np.median(s):.3f}')
- for threshold in [0.50, 0.80, 0.95]:
- vol_frac = np.sum(volumes_sorted[s >= threshold]) / total_vol
- print(f' {100*vol_frac:.0f}% vol >= {threshold}')
- print_summary(sorted_scores_cha, sorted_volumes_cha, total_volume_cha,
- f'{atlas_name} in CHA')
- # --- Baseline comparison (if available) ---
- sorted_scores_bl = None
- sorted_volumes_bl = None
- ecdf_bl = None
- baseline_name = None
- if cfg['baseline_coarser_file'] is not None:
- baseline_file = cfg['baseline_coarser_file']
- baseline_labels_txt = atlas_dir + baseline_file.replace('.nii.gz', '.txt')
- baseline_rois = get_roi_names(baseline_labels_txt)
- baseline_name = baseline_file.replace('.nii.gz', '').replace('_combined', '')
- baseline_overlap_file = (atlas_dir + atlas_file.replace('.nii.gz', '') +
- '_overlaps_' + baseline_file.replace('.nii.gz', '') + '.csv')
- try:
- popd_bl = pd.read_csv(baseline_overlap_file, index_col=0)
- print(f'Loaded baseline containment from {baseline_overlap_file}')
- except FileNotFoundError:
- print(f'Computing baseline containment...')
- at = nb.load(atlas_dir + atlas_file)
- bl = nb.load(atlas_dir + baseline_file)
- atlas_data = at.get_fdata()
- bl_data = bl.get_fdata()
- overlap, target_ids = get_percent_overlap(atlas_data, bl_data, atlas_labels.index.values)
- popd_bl = build_overlap_df(atlas_labels, baseline_labels_txt, overlap, target_ids)
- popd_bl.to_csv(baseline_overlap_file)
- # Containment scores for baseline
- containment_values_bl = popd_bl[baseline_rois][popd_bl[baseline_rois].max(axis=1) > 0].max(axis=1)
- containment_scores_bl = np.array(containment_values_bl)
- if use_adjusted:
- bl = nb.load(atlas_dir + baseline_file)
- bl_data = bl.get_fdata()
- bl_vol_fracs = compute_target_volume_fractions(bl_data, baseline_labels_txt, baseline_rois)
- valid_rows_bl = popd_bl[baseline_rois][popd_bl[baseline_rois].max(axis=1) > 0]
- best_idx_bl = valid_rows_bl.values.argmax(axis=1)
- expected_bl = np.array([bl_vol_fracs[baseline_rois[i]] for i in best_idx_bl])
- containment_scores_bl = np.clip(
- (containment_scores_bl - expected_bl) / (1 - expected_bl), 0, 1)
- volumes_bl = np.array(vols)
- sorted_idx_bl = np.argsort(containment_scores_bl)
- sorted_scores_bl = containment_scores_bl[sorted_idx_bl]
- sorted_volumes_bl = volumes_bl[sorted_idx_bl]
- total_volume_bl = np.sum(sorted_volumes_bl)
- ecdf_bl = np.cumsum(sorted_volumes_bl) / total_volume_bl
- print_summary(sorted_scores_bl, sorted_volumes_bl, total_volume_bl,
- f'{atlas_name} in {baseline_name}')
- # --- Plot ECDF ---
- if plot:
- fig, ax = plt.subplots(figsize=(2.5, 2.5))
- # CHA containment ECDF
- ax.plot(sorted_scores_cha, ecdf_cha, marker='.', markersize=2,
- linewidth=1, color='seagreen', label=f'{atlas_name} in CHA')
- # Baseline ECDF (if available)
- if sorted_scores_bl is not None:
- ax.plot(sorted_scores_bl, ecdf_bl, marker='.', markersize=2,
- linewidth=1, color='gray', alpha=0.6,
- label=f'{atlas_name} in {baseline_name}')
- ax.legend(fontsize=6)
- # Threshold annotations
- for threshold in [0.8, 0.95]:
- prop = np.sum(sorted_volumes_cha[sorted_scores_cha >= threshold]) / total_volume_cha
- ax.axvline(threshold, color='gray', linestyle='--', alpha=0.5)
- ax.text(threshold - 0.075, 0.4, f"{prop:.0%} vol ≥ {threshold}",
- rotation=90, va='bottom', fontsize=7)
- adj_label = ' (adjusted)' if use_adjusted else ''
- ax.set_xlabel(f"Max containment per {atlas_name} region", fontsize=8)
- ax.set_ylabel(f"Cumulative fraction of\ntotal {atlas_name} volume", fontsize=8)
- ax.set_title(f"ECDF: Containment of {atlas_name} regions\n{species}{adj_label}", fontsize=8)
- ax.set_xlim([-0.025, 1.05])
- ax.tick_params(labelsize=7)
- ax.grid(True, linestyle='--', axis='y', alpha=1)
- plt.tight_layout()
- plt.savefig(f'{atlas_dir}containment_ecdf_{species}.svg')
- plt.show()
- return sorted_scores_cha, sorted_scores_bl
- # %%
- for species in ['mouse', 'rat', 'marmoset', 'rhesus']:
- run_containment_analysis(species, use_adjusted=True, plot=True)
- # %%
- # %%
- # %%
- # %%
- from scipy.ndimage import affine_transform
- def get_percent_overlap(atlas, tract, roi_indices):
- CHF_ROIS=chf_labels_num
- percent_overlap=np.zeros((len(roi_indices), len(CHF_ROIS)))
- print(len(roi_indices))
- for roi_id in range(len(percent_overlap)):
- if roi_id%100==0:
- print(roi_id)
- for id_ in range(len(percent_overlap[roi_id])):
- percent_overlap[roi_id][id_] = np.sum(atlas[tract==id_]==roi_indices[roi_id]) /np.sum(atlas==roi_indices[roi_id])
- return percent_overlap
- #spatial perturbation
- def transform_small(atlas_,
- chf_nii_,
- atlas_labels_,
- chf_labels_,
- rot=0):
- theta = np.deg2rad(rot)
- affine_matrix = np.array([
- [np.cos(theta), -np.sin(theta), 0],
- [np.sin(theta), np.cos(theta), 0],
- [0, 0, 1]
- ])
- offset = [0, 0, 0] # voxel shift
- transformed = affine_transform(chf_nii_, affine_matrix, offset=offset, order=0, mode='nearest')
- percent_overlap=get_percent_overlap(atlas_, transformed, atlas_labels_.index.values)
- ls_=list(chf_labels_[1].values)
- ls_.insert(0, '0')
- popd_=pd.DataFrame(percent_overlap, index=atlas_labels_[1].values, columns=ls_)
- popd_['SUM']=popd_.sum(axis=1)
- return popd_
- # %%
- # %%
- # %% [markdown]
- # #### Rat preprocessing
- # %% [markdown]
- # #### apply spatial perturbation
- # %% [markdown]
- # ### Set species
- # %%
- species='mouse'
- # %%
- atlas_dir = f'data/atlas/{species}/'
- atlas_file=SPECIES_CONFIG[species]['atlas_file']
- atlas_name=SPECIES_CONFIG[species]['atlas_name']
- cha_file=SPECIES_CONFIG[species]['cha_file']
- baseline_overlap_file = (atlas_dir + atlas_file.replace('.nii.gz', '') +
- '_overlaps_' + cha_file.replace('.nii.gz', ''))
- # %% [markdown]
- # #### read spatial perturbation outputs
- # %%
- angles=[0,1,2,3,4,5]
- containment_values=[]
- if species == 'mouse':
- chf_rois=chf_rois_mouse
- if species == 'rhesus' or species == 'marmoset':
- chf_rois=chf_rois_primate
- if species == 'rat':
- print('containment rotational perturbation only performed for mouse, marmoset and rhesus which do not have an independent baseline containment criteria similar to WHS in DUke for Rat atlas!')
- for ang in angles:
- popd_=pd.read_csv(baseline_overlap_file+'_rot'+str(ang)+'.csv', index_col=0)
- containment_values.append(popd_[chf_rois][popd_[chf_rois].max(axis=1)>0].max(axis=1))
- # %%
- # Flatten into long-form DataFrame
- data = pd.DataFrame({
- 'Containment': [val for s in containment_values for val in s],
- 'Rotation': [angle for angle, s in zip(angles, containment_values) for _ in s]
- })
- # Compute summary statistics
- mean_values = [s.mean() for s in containment_values]
- median_values = [s.median() for s in containment_values]
- #plt.figure(figsize=(1.35, 1.55))
- plt.figure(figsize=(5,5))
- ax = sns.violinplot(
- data=data,
- x='Rotation',
- y='Containment',
- palette='Set2',
- cut=0,
- inner='box', linewidth=0.1
- )
- # Overlay the curve
- plt.plot(angles, median_values, '--', color='black', linewidth=1, label='Median Containment', alpha=.5)
- region_counts = [len(s) for s in containment_values]
- for i, count in enumerate(region_counts):
- ax.text(i-.3, 0.05, f'n={count}', ha='center', va='bottom', fontsize=11, rotation=90)
- ax.set_title("Containment Distribution \nAcross Rotations: "+atlas_name, fontsize=11)
- ax.set_xlabel("Rotation Angle (°)", fontsize=11)
- ax.set_ylabel("")
- plt.xticks(fontsize=11)
- plt.yticks(fontsize=11)
- ax.spines['top'].set_visible(False)
- ax.spines['right'].set_visible(False)
- plt.tight_layout()
- #plt.show()
- # %%
- # %%
- # %%
CHA_quants.ipynb at commit 056d459, under MIT · at the source
Overview
- Department of Neurological Surgery, University of Pittsburgh, Pittsburgh, PA USA
- Department of Bioengineering, University of Pittsburgh, Pittsburgh, PA USA
- Duke Center for In Vivo Microscopy, Departments of Radiology and Biomedical Engineering, Duke University, Durham, NC USA
- Department of Neurobiology, University of Pittsburgh, Pittsburgh, PA USA
Abstract
Translational neuroscience requires consistent anatomical frameworks to compare brain organization across species despite differences in size and specialization. Existing atlases are species-specific, limiting cross-species analyses. Here we developed a hierarchical common atlas delineating homologous cortical and subcortical gray matter regions across mouse, rat, marmoset, rhesus macaque, and human, built upon population-averaged minimal deformation templates and uniform tissue segmentation. We validated the atlas using four independent approaches: cross-atlas Dice similarity against established human parcellations, cross-scale containment against species-specific atlases, cross-species geometric consistency of regional positioning, and comparison of independent mouse and marmoset tracer connectivity. A per-region homology confidence index quantifies the strength of each regional assignment. Cross-species tracer comparison revealed a structured gradient of correspondence, with sensorimotor connections showing strong conservation and association connections showing progressive divergence. This freely available atlas provides a unified coordinate system for comparative neuroscience, enabling quantitative evaluation of where cross-species correspondence holds and where species-specific divergence emerges.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repositories
Its files are read in the Code ↔ Paper reader above, with 22 matches between paragraphs and lines of code.
Zenodo 17653308
Availability: 1 check, the latest on 27 September 2026: the link answers (HTTP 200)
- 27 September 2026: the link answers (HTTP 200)
19 files
- projects/
common_cross_species_atl , Python, 297 linesas/ CHA_Integrate.py - projects/
common_cross_species_atl , Jupyter, 317 linesas/ CHA_Validation_Homology_ Confidence.ipynb - projects/
common_cross_species_atl , Jupyter, 505 linesas/ CHA_Validation_Tracer.ip ynb - projects/
common_cross_species_atl , Jupyter, 1,056 linesas/ CHA_quants.ipynb - projects/
cross_species_connectomi , Python, 94 linescs/ cross_modal_validation.p y - projects/
cross_species_connectomi , Python, 216 linescs/ cross_species_nets.py - projects/
cross_species_connectomi , Python, 63 linescs/ dir_mat.py - projects/
cross_species_connectomi , Shell, 905 linescs/ dsi_aba_mouse.sh - projects/
cross_species_connectomi , Shell, 762 linescs/ dsi_aba_mouse_perm.sh - projects/
cross_species_connectomi , Python, 148 linescs/ fig3a.py - projects/
cross_species_connectomi , Python, 116 linescs/ fig3b-4a.py - projects/
cross_species_connectomi , Python, 73 linescs/ fig4c.py - projects/
cross_species_connectomi , Python, 184 linescs/ fig5a.py - projects/
cross_species_connectomi , Python, 98 linescs/ figs_aba_scale.py - projects/
cross_species_connectomi , Python, 89 linescs/ stat_utils.py - projects/
cross_species_connectomi , Python, 218 linescs/ vis_utils.py - projects/
cross_species_connectomi , Python, 57 linescs/ voxel_count_stats.py - LICENSE, License, 21 lines
- README.md, Text, 75 lines
sivaven/omniconnectmodel
056d459a3c13d0be365f5f52ac1306c429e7941c, 17 June 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
19 files
- projects/
common_cross_species_atl , Python, 297 lines, 2 matchesas/ CHA_Integrate.py - projects/
common_cross_species_atl , Jupyter, 317 lines, 5 matchesas/ CHA_Validation_Homology_ Confidence.ipynb - projects/
common_cross_species_atl , Jupyter, 505 lines, 5 matchesas/ CHA_Validation_Tracer.ip ynb - projects/
common_cross_species_atl , Jupyter, 1,056 lines, 8 matchesas/ CHA_quants.ipynb - projects/
cross_species_connectomi , Python, 94 linescs/ cross_modal_validation.p y - projects/
cross_species_connectomi , Python, 216 lines, 1 matchcs/ cross_species_nets.py - projects/
cross_species_connectomi , Python, 63 linescs/ dir_mat.py - projects/
cross_species_connectomi , Shell, 905 linescs/ dsi_aba_mouse.sh - projects/
cross_species_connectomi , Shell, 762 linescs/ dsi_aba_mouse_perm.sh - projects/
cross_species_connectomi , Python, 148 linescs/ fig3a.py - projects/
cross_species_connectomi , Python, 116 linescs/ fig3b-4a.py - projects/
cross_species_connectomi , Python, 73 linescs/ fig4c.py - projects/
cross_species_connectomi , Python, 184 lines, 1 matchcs/ fig5a.py - projects/
cross_species_connectomi , Python, 98 linescs/ figs_aba_scale.py - projects/
cross_species_connectomi , Python, 89 linescs/ stat_utils.py - projects/
cross_species_connectomi , Python, 218 linescs/ vis_utils.py - projects/
cross_species_connectomi , Python, 57 linescs/ voxel_count_stats.py - LICENSE, License, 21 lines
- README.md, Text, 75 lines
Code availability
All analysis and figure-generation code supporting the findings of this study, including a standalone module (CHA_integrate.py) implementing atlas and connectome integration, are archived at Zenodo (10.5281/
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:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 34 scripts, each with its path and the digest of its content;
- 22 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data Availability Statement
The Common Hierarchical Atlas (CHA) generated in this study is provided as Supplementary Data with this paper. The source datasets analyzed in this study are publicly available: the ICBM 2009a human template (ref. 9); the Brain/
All analysis and figure-generation code supporting the findings of this study, including a standalone module (CHA_integrate.py) implementing atlas and connectome integration, are archived at Zenodo (10.5281/
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, 11 authors, 2 keywords, 12 MeSH terms, 4 funders, 58 references.
Cite
This paper
Venkadesh, S., Tian, Y., Linn, W.-J., Barrios-Martinez, J., Mansour, H., Cook, J., Schaeffer, D. J., Szczupak, D., Silva, A. C., Johnson, G. A., & Yeh, F.-C. (2026). A hierarchical framework for cortical and subcortical gray-matter parcellation across rodents, primates, and humans. Nature communications, 17(1), 8964. https://
BibTeX
@article{venkadesh2026hi
author = {Venkadesh, Siva and Tian, Yuhe and Linn, Wen-Jieh and Barrios-Martinez, Jessica and Mansour, Harrison and Cook, James and Schaeffer, David J and Szczupak, Diego and Silva, Afonso C and Johnson, G Allan and Yeh, Fang-Cheng},
title = {{A hierarchical framework for cortical and subcortical gray-matter parcellation across rodents, primates, and humans}},
journal = {Nature communications},
year = {2026},
month = jul,
volume = {17},
number = {1},
pages = {8964},
publisher = {Nature Publishing Group},
issn = {2041-1723},
doi = {10.1038/
url = {https://
pmid = {42637761},
pmcid = {PMC13503721}
}
RIS
TY - JOUR
AU - Venkadesh, Siva
AU - Tian, Yuhe
AU - Linn, Wen-Jieh
AU - Barrios-Martinez, Jessica
AU - Mansour, Harrison
AU - Cook, James
AU - Schaeffer, David J
AU - Szczupak, Diego
AU - Silva, Afonso C
AU - Johnson, G Allan
AU - Yeh, Fang-Cheng
TI - A hierarchical framework for cortical and subcortical gray-matter parcellation across rodents, primates, and humans
T2 - Nature communications
J2 - Nat Commun
PY - 2026
DA - 2026/
VL - 17
IS - 1
SP - 8964
SN - 2041-1723
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "A hierarchical framework for cortical and subcortical gray-matter parcellation across rodents, primates, and humans",
"container-title": "Nature communications",
"author": [
{
"family": "Venkadesh",
"given": "Siva"
},
{
"family": "Tian",
"given": "Yuhe"
},
{
"family": "Linn",
"given": "Wen-Jieh"
},
{
"family": "Barrios-Martinez",
"given": "Jessica"
},
{
"family": "Mansour",
"given": "Harrison"
},
{
"family": "Cook",
"given": "James"
},
{
"family": "Schaeffer",
"given": "David J"
},
{
"family": "Szczupak",
"given": "Diego"
},
{
"family": "Silva",
"given": "Afonso C"
},
{
"family": "Johnson",
"given": "G Allan"
},
{
"family": "Yeh",
"given": "Fang-Cheng"
}
],
"container-title-short":
"volume": "17",
"issue": "1",
"page": "8964",
"DOI": "10.1038/
"PMID": "42637761",
"PMCID": "PMC13503721",
"ISSN": "2041-1723",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
7,
23
]
]
}
}
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/s41592-026-03159-x [code]
- Siibra: a software tool suite for realizing a Multilevel Human Brain Atlas from complex data resources.Journal: Nature methodsIn common: NiBabel, seaborn, pandas, 3 other tools, methods / tools, 4 references
- [2] 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: NiBabel, statsmodels, seaborn, 4 other tools, non-human primate, 3 references
- [3] 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: NiBabel, statsmodels, pandas, 1 other tool, 2 references, author Fang-Cheng Yeh
- [4] doi:10.1016/j.ebiom.2026.106259 [code]
- Translating brain anatomy and disease from mouse to human in latent gene expression space.Journal: EBioMedicineIn common: NiBabel, seaborn, pandas, 3 other tools, mouse, 3 references
- [5] doi:10.1038/s41593-026-02205-3 [code]
- Competitive interactions shape mammalian brain network dynamics and computation.Journal: Nature neuroscienceIn common: NetworkX, NiBabel, seaborn, 4 other tools, non-human primate, mouse, 1 reference
- [6] doi:10.21203/rs.3.rs-9326213/v1 [code]
- Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brainJournal: Research Square (preprint)In common: NiBabel, seaborn, pandas, 3 other tools, 3 references
- [7] doi:10.64898/2026.03.09.710558 [code]
- Multi-task fMRI outperforms resting-state fMRI for revealing task-invariant organization of the human brainJournal: bioRxiv (preprint)In common: NiBabel, seaborn, pandas, 3 other tools, 3 references
- [8] doi:10.1002/hbm.70483 [code]
- Untamed: Unconstrained Tensor Decomposition and Graph Node Embedding for Cortical Parcellation.Journal: Human brain mappingIn common: NiBabel, statsmodels, SciPy, 2 other tools, methods / tools, 3 references
- [9] doi:10.1371/journal.pone.0346575 [code]
- Statistically valid explainable black-box machine learning: applications in sex classification across species using brain imaging.Journal: PloS oneIn common: NiBabel, seaborn, pandas, 3 other tools, non-human primate, methods / tools, 2 references
- [10] doi:10.1038/s41467-026-75959-w [code]
- Charting higher-order models of brain function beyond pairwise interactions.Journal: Nature communicationsIn common: NetworkX, NiBabel, statsmodels, 5 other tools, 1 reference
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
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: 2 repositories of the authors' code, each at its verified commit and with its license, 34 scripts, and 22 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:f597e2120f33b9ef…
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.
