Shared representations in brains and models reveal a two-route cortical organization during scene perception.
The 10 matches
- [1] § Results › Inter-subject shared geometry during scene viewing ↔ 5_notebooks/01_rsa_spatial_alignment.ipynb, lines 921–1016 · score 0.73 · power law, CI, language model alignment, bootstrap, model brain alignment, LOTC hub
- [2] § Results › Model-brain alignment reveals modality-specific shared geometry ↔ 5_notebooks/01_rsa_spatial_alignment.ipynb, lines 921–1016 · score 0.70 · power law, RSA spatial, model brain alignment, LOTC hubs, vision model alignment, overlap
- [3] § Results › Inter-subject shared geometry during scene viewing ↔ 3_alignment/2_rsa_nsd_subject_model_alignment.py, lines 1–64 · score 0.67 · deep neural networks, model RSA, fMRI, model layer, vector, activations
- [4] § Results › Inter-subject shared geometry during scene viewing ↔ 3_alignment/6_rsa_nsd_subject_group_subject_alignment.py, lines 1–56 · score 0.62 · representational dissimilarity matrices, shifted repetition, inter subject alignment, cortical, RDMs, hemisphere
- [5] § Results › Inter-subject shared geometry during scene viewing ↔ 3_alignment/7_rsa_other_subject_subject_alignment.py, lines 1–81 · score 0.59 · representational dissimilarity matrices, brain activity, inter subject RSA, RDMs, scene, alignment
- [6] § Methods › Alignment measure ↔ 3_alignment/7_rsa_other_subject_subject_alignment.py, lines 1–81 · score 0.55 · representational dissimilarity matrix, RSA score, RDM, brain, Alignment
- [7] § Results › Hierarchical correspondence between models and cortex ↔ 5_notebooks/03_rsa_model_hierarchy.ipynb, lines 534–575 · score 0.54 · Cortical surface map, peak alignment depths, normalized peak, vision model alignment, hierarchy, Ventral
- [8] § Methods › Model features extraction ↔ 4_other_alignments/7_untrained_models_alignment.py, lines 1–59 · score 0.54 · models encode, fMRI, training, computational, language, vision
- [9] § Results › A representational network linking ventromedial and lateral hubs ↔ 5_notebooks/03_rsa_model_hierarchy.ipynb, lines 437–525 · score 0.52 · peak alignment depth, LOTC hub, Ventral hub, FST, V4t, VMV1
- [10] § Results › Model-brain alignment reveals modality-specific shared geometry ↔ 2_model_extraction/1_extract_vision_features.py, lines 1–41 · score 0.52 · Vision Transformer, extracted layer, pretrained, vision models, activations, NSD
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,158 lines · 38 KB · MIT · 2 matches
- # %% [markdown]
- # # 1 - RSA Spatial Alignment
- #
- # Generate spatial RSA maps for inter‑subject and subject‑model alignment, including boxplots, cortical surface projections, scatter comparisons, and hub‐based contrasts.
- # %% [markdown]
- # ## 0. Imports & Setup
- # Load required libraries (path handling, data frames, plotting, stats), plus project utilities for power‐law fitting, parcel plotting, figure styling, and alignment processing. Initialize fonts.
- # %%
- # add .. to path for convergence module
- import sys
- from pathlib import Path
- sys.path.append(str(Path.cwd().parent))
- # %%
- from pathlib import Path
- import pandas as pd
- import matplotlib.lines as mlines
- import matplotlib.pyplot as plt
- import matplotlib.ticker as mticker
- import numpy as np
- import seaborn as sns
- from scipy.stats import ttest_rel
- from statsmodels.stats.multitest import multipletests
- # Functions reused in other parts of the project
- from convergence.power_law import fit_powerlaw_zero_with_bootstrap
- from convergence.plotting import plot_faverage_parcelation, add_area_labels
- from convergence.figures import (
- plot_boxplot_rois,
- add_pvalue_bracket,
- plot_cbar,
- plot_comparison,
- setup_matplotlib_fonts,
- )
- from convergence.figures_utils import add_cluster, process_intersubject_rois, proccess_alignment
- setup_matplotlib_fonts()
- # %% [markdown]
- # ## 1. Data Paths & Validation
- # Define paths to derivatives folder, metadata (HCP atlas, model info), RSA p‑value tables, and alignment parquet files. Create output directory and assert that all required inputs exist.
- # %%
- # Required filenames for the figures
- data_folder = Path("../derivatives")
- hcp_filename = data_folder / "metadata" / "hcp.csv"
- models_filename = data_folder / "metadata" / "models-info.csv"
- nsd_folder = data_folder / "nsd"
- pvalues_filename = nsd_folder / "rsa_pvalues_subject_language_vision.parquet"
- intersubject_shift1_joined_filename = nsd_folder / "rsa_subject_subject_alignment_shift_1_joined.parquet"
- intersubject_shift1_separated_filename = nsd_folder / "rsa_subject_subject_alignment_shift_1_separated.parquet"
- model_brain_joined_filename = nsd_folder / "rsa_subject_model_alignment_joined.parquet"
- model_brain_separated_filename = nsd_folder / "rsa_subject_model_alignment_separated.parquet"
- # Output folder for figures
- figure_folder = Path("../figures")
- output = figure_folder / "01_rsa_spatial_alignment"
- output.mkdir(exist_ok=True, parents=True)
- # %% [markdown]
- # ## 2. ROI RSA Boxplots
- # Visualize ROI‑wise RSA distributions with null distributions and statistical annotations.
- # %% [markdown]
- # ### 2.1 Inter‑subject — Top Areas
- # – Select the top 10 anatomical areas by mean alignment.
- # – Overlay group‑level null mean ± std.
- # – Draw per‑ROI boxplots of inter‑subject RSA and annotate paired t‑test p‑value brackets.
- # %%
- # Load the inter-subject alignment data
- df = pd.read_parquet(intersubject_shift1_joined_filename)
- # Process the data to get the top 10 ROIs (aggregate and filter) and select rois of top areas
- df_g = process_intersubject_rois(df, hcp_filename=hcp_filename, top=10)
- order = (
- df_g.sort_values(["area_id", "roi_order"])
- .drop_duplicates("roi")
- .drop_duplicates("name")
- .name.tolist()
- )
- order_roi = df_g.sort_values(["area_id", "roi_order"]).drop_duplicates("roi").roi.tolist()
- hue_order = df_g.sort_values("area_id").drop_duplicates("area_id").area.tolist()
- palette = df_g.sort_values("area_id").drop_duplicates("area_id").area_color.tolist()
- # Sort by order
- df_subject_pvalues = pd.read_parquet(pvalues_filename).query("comparison == 'intersubject'")
- df_subject_pvalues.name = df_subject_pvalues.name.replace("H", "Hipp")
- df_subject_pvalues = df_subject_pvalues.set_index("roi")
- df_subject_pvalues = df_subject_pvalues.loc[order_roi].reset_index()
- # The df_subject_pvalues is sorted in the same order that boxplots.
- # Plot the mean line given by null_mean column of df_subject_pvalues
- null_std = df_subject_pvalues.null_std.values
- null_mean = df_subject_pvalues.null_mean.values
- fig, ax = plt.subplots(1, 1, figsize=(20, 4), dpi=300)
- ax.fill_between(
- np.arange(len(order)), null_mean - null_std, null_mean + null_std, color="maroon", alpha=0.15
- )
- ax.plot(
- np.arange(len(order)),
- df_subject_pvalues.null_mean.values,
- color="maroon",
- linestyle="--",
- linewidth=1,
- )
- plot_boxplot_rois(
- df_g,
- ax,
- order,
- hue_order,
- palette,
- title="",
- legend=True,
- fontsize=10,
- legend_fontsize=13,
- legend_kwargs={"bbox_to_anchor": (1, 1), "title": ""},
- vmax=0.3,
- )
- ax.set_ylabel(r"Inter-subject Alignment (RSA)", fontsize=12)
- add_pvalue_bracket(
- ax=ax,
- x1=0,
- x2=3,
- y=0.25 - 0.011,
- y_text=0.268 - 0.011,
- height=0.008,
- text="Early Visual\nCortex",
- )
- add_pvalue_bracket(
- ax=ax, x1=8, x2=13, y=0.25 - 0.011, y_text=0.268 - 0.011, height=0.008, text="Ventral Hub"
- )
- add_pvalue_bracket(ax=ax, x1=30, x2=35, y=0.045, y_text=0.02, height=-0.008, text="LOTC Hub")
- # Set yaxis (RSA) in decimal format not percentage
- ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%.2f"))
- ax.set_ylim(-0.015, 0.30)
- ax.set_xlim(-0.8, 71.8)
- ax.text(
- 0,
- 0.008,
- r"Group-level null distribution (mean $\pm$ std)",
- color="maroon",
- fontsize=8,
- ha="left",
- )
- # Add p-values
- apa_significance = df_subject_pvalues.apa_star.values
- for i, (p, name) in enumerate(zip(apa_significance, order)):
- if p != "***":
- if p == "n.s.":
- ax.text(i, 0.012, p, fontsize=7, ha="center")
- else:
- ax.text(i, 0.01, p, fontsize=11, ha="center")
- # fig.savefig(
- # output / "01_rsa_intersubject_boxplot_selected_shift_1.pdf",
- # bbox_inches="tight",
- # transparent=True,
- # )
- # %% [markdown]
- # ### 2.2 Inter‑subject — All Areas
- # – Repeat full‑brain boxplot for every ROI.
- # – Adjust strip sizes and fonts for high‑density plotting.
- # – Annotate key area comparisons.
- # %%
- df = pd.read_parquet(intersubject_shift1_joined_filename)
- df_g = process_intersubject_rois(
- df, hcp_filename=hcp_filename, top=1000
- ) # If select more than max rois it will take all
- order = df_g.sort_values(["area_id", "roi_order"]).drop_duplicates("name").name.unique()
- order_roi = df_g.sort_values(["area_id", "roi_order"]).drop_duplicates("roi").roi.unique()
- hue_order = list(df_g.sort_values("area_id").drop_duplicates("area_id").area.tolist())
- palette = list(df_g.sort_values("area_id").drop_duplicates("area_id").area_color.tolist())
- models_info = pd.read_csv(models_filename)
- # Sort by order
- df_subject_pvalues = pd.read_parquet(pvalues_filename).query("comparison == 'intersubject'")
- df_subject_pvalues.name = df_subject_pvalues.name.replace("H", "Hipp")
- df_subject_pvalues = df_subject_pvalues.set_index("roi")
- #
- df_subject_pvalues = df_subject_pvalues.loc[order_roi].reset_index()
- fig, ax = plt.subplots(1, 1, figsize=(18, 4), dpi=300)
- null_mean = df_subject_pvalues.null_mean.values
- null_std = df_subject_pvalues.null_std.values
- ax.fill_between(
- np.arange(len(order)), null_mean - null_std, null_mean + null_std, color="maroon", alpha=0.15
- )
- ax.plot(
- np.arange(len(order)),
- df_subject_pvalues.null_mean.values,
- color="maroon",
- linestyle="--",
- linewidth=0.7,
- )
- plot_boxplot_rois(
- df_g,
- ax,
- order,
- hue_order,
- palette,
- title="",
- legend=True,
- fontsize=6,
- legend_fontsize=9,
- legend_kwargs={"bbox_to_anchor": (1, 1), "title": ""},
- vmax=0.3,
- strip_size=2,
- )
- ax.set_ylabel("Inter-subject Alignment (RSA)")
- ax.text(0, 0.008, "Null distribution\n(mean ± std)", color="maroon", fontsize=8, ha="left")
- # Set yaxis (RSA) in decimal format not percentage
- ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%.2f"))
- ax.set_title("Inter-subject RSA Alignment")
- apa_significance = df_subject_pvalues.apa_star.values
- max_observed_values = df_g.groupby("name").aggregate({"score": "max"}).loc[order].score.values
- for i, (p, name, max_observed) in enumerate(zip(apa_significance, order, max_observed_values)):
- if p != "***":
- if p == "n.s.":
- ax.text(i, max_observed + 0.01, p, fontsize=6, ha="center", rotation=90, va="bottom")
- else:
- ax.text(i, max_observed + 0.005, p, fontsize=6, ha="center", rotation=0, va="center")
- add_pvalue_bracket(
- ax=ax,
- x1=0,
- x2=3,
- y=0.25 - 0.006,
- y_text=0.268 - 0.011,
- height=0.006,
- text="Visual\nCortex",
- fontsize=7,
- )
- add_pvalue_bracket(
- ax=ax,
- x1=8,
- x2=13,
- y=0.25 - 0.006,
- y_text=0.268 - 0.011,
- height=0.006,
- text="Ventral Hub",
- fontsize=7,
- )
- add_pvalue_bracket(
- ax=ax,
- x1=38,
- x2=43,
- y=0.29 - 0.006 - 0.006,
- y_text=0.29 - 0.011 + 0.018 - 0.007,
- height=0.006,
- text="LOTC Hub",
- fontsize=7,
- )
- ax.set_ylim
- # fig.savefig(
- # output / "01_rsa_intersubject_boxplot_all_shift_1.pdf", bbox_inches="tight", transparent=True
- # )
- # %% [markdown]
- # ## Within subject all areas
- # %%
- df = pd.read_parquet(intersubject_shift1_joined_filename)
- df_g = process_intersubject_rois(
- df, hcp_filename=hcp_filename, top=1000, within_subject=True
- ) # If select more than max rois it will take all
- order = df_g.sort_values(["area_id", "roi_order"]).drop_duplicates("name").name.unique()
- order_roi = df_g.sort_values(["area_id", "roi_order"]).drop_duplicates("roi").roi.unique()
- hue_order = list(df_g.sort_values("area_id").drop_duplicates("area_id").area.tolist())
- palette = list(df_g.sort_values("area_id").drop_duplicates("area_id").area_color.tolist())
- models_info = pd.read_csv(models_filename)
- # Sort by order
- df_subject_pvalues = pd.read_parquet(pvalues_filename).query("comparison == 'intersubject'")
- df_subject_pvalues.name = df_subject_pvalues.name.replace("H", "Hipp")
- df_subject_pvalues = df_subject_pvalues.set_index("roi")
- #
- df_subject_pvalues = df_subject_pvalues.loc[order_roi].reset_index()
- fig, ax = plt.subplots(1, 1, figsize=(18, 4), dpi=300)
- null_mean = df_subject_pvalues.null_mean.values
- null_std = df_subject_pvalues.null_std.values
- ax.fill_between(
- np.arange(len(order)), null_mean - null_std, null_mean + null_std, color="maroon", alpha=0.15
- )
- ax.plot(
- np.arange(len(order)),
- df_subject_pvalues.null_mean.values,
- color="maroon",
- linestyle="--",
- linewidth=0.7,
- )
- plot_boxplot_rois(
- df_g,
- ax,
- order,
- hue_order,
- palette,
- title="",
- legend=True,
- fontsize=6,
- legend_fontsize=9,
- legend_kwargs={"bbox_to_anchor": (1, 1), "title": ""},
- vmax=0.55,
- strip_size=2,
- )
- ax.set_ylabel("Inter-subject Alignment (RSA)")
- ax.text(0, 0.008, "Null distribution\n(mean ± std)", color="maroon", fontsize=8, ha="left")
- # Set yaxis (RSA) in decimal format not percentage
- ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%.2f"))
- ax.set_title("Inter-subject RSA Alignment")
- apa_significance = df_subject_pvalues.apa_star.values
- max_observed_values = df_g.groupby("name").aggregate({"score": "max"}).loc[order].score.values
- for i, (p, name, max_observed) in enumerate(zip(apa_significance, order, max_observed_values)):
- if p != "***":
- if p == "n.s.":
- ax.text(i, max_observed + 0.01, p, fontsize=6, ha="center", rotation=90, va="bottom")
- else:
- ax.text(i, max_observed + 0.005, p, fontsize=6, ha="center", rotation=0, va="center")
- add_pvalue_bracket(
- ax=ax,
- x1=0,
- x2=3,
- y=0.25 - 0.006,
- y_text=0.268 - 0.011,
- height=0.006,
- text="Visual\nCortex",
- fontsize=7,
- )
- add_pvalue_bracket(
- ax=ax,
- x1=8,
- x2=13,
- y=0.25 - 0.006,
- y_text=0.268 - 0.011,
- height=0.006,
- text="Ventral Hub",
- fontsize=7,
- )
- add_pvalue_bracket(
- ax=ax,
- x1=38,
- x2=43,
- y=0.29 - 0.006 - 0.006,
- y_text=0.29 - 0.011 + 0.018 - 0.007,
- height=0.006,
- text="LOTC Hub",
- fontsize=7,
- )
- ax.set_ylim(-0.03, 0.53)
- ax.set_title("Within-subject RSA Alignment")
- ax.set_ylabel("Within-subject Alignment (RSA)")
- # fig.savefig(
- # output / "01_rsa_withinsubject_boxplot_all_shift_1.pdf", bbox_inches="tight", transparent=True
- # )
- # %%
- models_info = pd.read_csv(models_filename)
- df_comparison_subjects = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_joined_filename,
- subject_alignment_filename=intersubject_shift1_joined_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=False,
- )
- df = pd.read_parquet(intersubject_shift1_joined_filename)
- df_g_within = process_intersubject_rois(
- df, hcp_filename=hcp_filename, top=1000, within_subject=True
- ) # If select more than max rois it will take all
- df_g_within = df_g_within[["roi", "subject", "score"]].rename(columns={"score": "within_subject_rsa"})
- df_comparison_subjects = df_comparison_subjects.merge(df_g_within, on=["roi", "subject"], how="left")
- df = df_comparison_subjects[["roi", "subject", "intersubject_rsa", "within_subject_rsa", "vision_rsa", "language_rsa"]]
- hcp = pd.read_csv(hcp_filename)[["roi", "name", "area", "area_id", "area_color", "roi_order"]]
- df = df.merge(hcp, on="roi")
- # %%
- # Convert to long format for seaborn with roi, subject, name, area, measure (intersubject_rsa, within_subject_rsa, vision_rsa, language_rsa) and the score
- df_long = pd.melt(
- df,
- id_vars=["roi", "subject", "name", "area", "area_id", "area_color", "roi_order"],
- value_vars=["intersubject_rsa", "within_subject_rsa", "vision_rsa", "language_rsa"],
- var_name="measure",
- value_name="score",
- )
- df_subplot = df_long.query("area == 'Posterior Cingulate' and measure != 'language_rsa'").copy()
- order = df_subplot.query("measure == 'vision_rsa'").groupby("name").score.mean().reset_index().sort_values("score", ascending=False).name.tolist()
- hue_order = ["vision_rsa", "within_subject_rsa", "intersubject_rsa",]
- new_labels = {
- "vision_rsa": "Vision Model - Brain RSA",
- "within_subject_rsa": "Within-subject RSA",
- "intersubject_rsa": "Inter-subject RSA",
- }
- hue_order_new = [new_labels[h] for h in hue_order]
- df_subplot["measure"] = df_subplot["measure"].replace(new_labels)
- fig, (ax, ax2, ax3) = plt.subplots(1, 3, figsize=(4*4, 4))
- sns.boxplot(data=df_subplot, x="name", y="score", hue="measure", hue_order=hue_order_new, ax=ax,
- showfliers=False, order=order,
- )
- # Add an stripplot
- sns.stripplot(data=df_subplot, x="name", y="score", hue="measure", hue_order=hue_order_new,
- dodge=True, size=3, linewidth=0.8, ax=ax, legend=False, order=order,
- )
- # Remove legend title
- ax.legend_.set_title("")
- # Move legend to left center
- ax.legend_.set_bbox_to_anchor((1, 0.8))
- sns.despine(ax=ax)
- ax.set_xlabel("")
- ax.set_ylabel("RSA Score (Pearson's $\\rho$)")
- # Rotate xlabels
- # Set ticklabels for avoid warning
- ax.set_xticks(ax.get_xticks())
- ax.set_xticklabels(ax.get_xticklabels(), rotation=90, ha="center", fontsize=10);
- ax.set_title("Posterior Cingulate Cortex regions")
- palette = df.sort_values("area").drop_duplicates("area").set_index("area").area_color.to_dict()
- hue_order = df.sort_values("area").drop_duplicates("area").area.tolist()
- df_grouped = df.groupby(["roi", "name", "area"]).aggregate({"intersubject_rsa": "mean", "within_subject_rsa": "mean",
- "vision_rsa": "mean", "language_rsa": "mean"}).reset_index()
- for x_column, ax in zip(["intersubject_rsa", "within_subject_rsa"], [ax2, ax3]):
- df_grouped["greater"] = df_grouped["vision_rsa"] > df_grouped[x_column]
- sns.scatterplot(data=df_grouped, x=x_column, y="vision_rsa", hue="area", legend=False, palette=palette, hue_order=hue_order,
- style="greater", markers={True: "X", False: "o"}, ax=ax)
- # Plot x=y line
- max_val = max(df_grouped[x_column].max(), df_grouped["vision_rsa"].max())
- ax.plot([0, max_val], [0, max_val], color=(0.24, 0.24, 0.24), linestyle="--", zorder=-1000, lw=1)
- # Mark text of all regions of the posterior cingulate
- for i, row in df_grouped.query("name in ['DVT', 'POS1', 'ProS']").iterrows():
- ax.text(row[x_column], row["vision_rsa"] + 0.005, row["name"], fontsize=7,ha='center')
- xmin = df_grouped[x_column].min()
- # # Plot the powerlaw fit line
- x_fit, y_fit, y_lower, y_upper, r2, params = fit_powerlaw_zero_with_bootstrap(
- df_grouped[x_column] - xmin, df_grouped["vision_rsa"], n_boot=10000
- )
- print(r2)
- x_fit = x_fit + xmin
- ax.plot(
- x_fit, y_fit, color="maroon", label=f"$f(x)={params[0]:.2f}x^{{{params[1]:.2f}}}$", zorder=-10
- )
- ax.fill_between(x_fit, y_lower, y_upper, color="maroon", alpha=0.1, zorder=-20)
- x = np.linspace(0, 0.2, 100)
- ax.fill_between(x, x, 0.2, color='gray', alpha=0.1, label='y > x', zorder=-1000)
- ax.legend(loc="lower right")
- sns.despine(ax=ax)
- ax.set_ylabel("Vision Model - Brain RSA")
- ax.set_ylim(0, 0.18)
- ax2.set_xlabel("Inter-subject RSA")
- ax2.set_title("Inter-subject vs Vision Model alignment")
- ax3.set_xlabel("Within-subject RSA")
- ax3.set_title("Within-subject vs Vision Model alignment")
- #fig.savefig(figure_folder / "18_revision" / "posterior_cingulate_rsa_alignment.pdf", bbox_inches="tight", transparent=True)
- # %% [markdown]
- # ### 2.3 Subject‑Model — Vision Models
- # – Compute per‑ROI vision model vs. brain RSA.
- # – Overlay group null, plot boxplots, annotate p‑values, and save with/without legend.
- # %%
- # Load models info
- models_info = pd.read_csv(models_filename)
- df_comparison_subjects = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_joined_filename,
- subject_alignment_filename=intersubject_shift1_joined_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=False,
- )
- df_comparison_subjects.name = df_comparison_subjects.name.replace("H", "Hipp")
- df_comparison_subjects = df_comparison_subjects.sort_values(["area_id", "roi_order"]).reset_index(
- drop=True
- )
- order = (
- df_comparison_subjects.sort_values(["area_id", "roi_order"])
- .drop_duplicates("name")
- .name.unique()
- )
- order_roi = (
- df_comparison_subjects.sort_values(["area_id", "roi_order"]).drop_duplicates("roi").roi.unique()
- )
- hue_order = list(
- df_comparison_subjects.sort_values("area_id").drop_duplicates("area_id").area.tolist()
- )
- palette = list(
- df_comparison_subjects.sort_values("area_id").drop_duplicates("area_id").area_color.tolist()
- )
- null_mean = df_comparison_subjects.drop_duplicates("roi").vision_null_mean.values
- null_std = df_comparison_subjects.drop_duplicates("roi").vision_null_std.values
- # Make the plot
- fig, ax = plt.subplots(1, 1, figsize=(18, 4), dpi=300)
- ax.fill_between(
- np.arange(len(order)), null_mean - null_std, null_mean + null_std, color="maroon", alpha=0.15
- )
- ax.plot(np.arange(len(order)), null_mean, color="maroon", linestyle="--", linewidth=0.7)
- plot_boxplot_rois(
- df_comparison_subjects.rename(columns={"vision_rsa": "score"}),
- ax,
- order,
- hue_order,
- palette,
- title="",
- legend=True,
- fontsize=6,
- legend_fontsize=9,
- legend_kwargs={"bbox_to_anchor": (1, 1), "title": ""},
- vmax=0.23,
- strip_size=2,
- )
- ax.set_ylabel("Vision Model - Brain Alignment (RSA)")
- ax.text(
- 0,
- -0.006,
- "Group-level null distribution\n(mean ± std)",
- color="maroon",
- fontsize=8,
- ha="left",
- va="top",
- )
- # Plot the significance of the p-values
- apa_significance = df_comparison_subjects.drop_duplicates("roi").vision_apa_star
- max_observed_values = (
- df_comparison_subjects.groupby("name")
- .aggregate({"vision_rsa": "max"})
- .loc[order]
- .vision_rsa.values
- )
- for i, (p, name, max_observed) in enumerate(zip(apa_significance, order, max_observed_values)):
- if p != "***":
- # print(p, name, max_observed)
- if p == "n.s.":
- ax.text(i, max_observed + 0.01, p, fontsize=6, ha="center", rotation=90, va="bottom")
- else:
- ax.text(i, max_observed + 0.005, p, fontsize=6, ha="center", rotation=0, va="center")
- add_pvalue_bracket(
- ax=ax, x1=0, x2=3, y=0.20, y_text=0.21, height=0.005, text="Visual\nCortex", fontsize=7
- )
- add_pvalue_bracket(
- ax=ax, x1=8, x2=13, y=0.175, y_text=0.185, height=0.005, text="Ventral Hub", fontsize=7
- )
- add_pvalue_bracket(
- ax=ax, x1=38, x2=43, y=0.18, y_text=0.19, height=0.005, text="LOTC Hub", fontsize=7
- )
- ax.set_title("Vision Models - Brain RSA Alignment")
- ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%.2f"))
- ax.set_ylim(-0.043, 0.21)
- # Save with legend
- fig.savefig(output / "02_rsa_vision_boxplot_all.pdf", bbox_inches="tight", transparent=True)
- # Save without legend
- ax.get_legend().remove()
- fig.savefig(
- output / "03_rsa_vision_boxplot_all_no_legend.pdf", bbox_inches="tight", transparent=True
- )
- # %% [markdown]
- #
- # ### 2.4 Subject‑Model — Language Models
- # – Same as 2.3 but for language models, with separate null overlay and significance brackets.
- # %%
- models_info = pd.read_csv(models_filename)
- df_comparison_subjects = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_joined_filename,
- subject_alignment_filename=intersubject_shift1_joined_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=False,
- )
- df_comparison_subjects.name = df_comparison_subjects.name.replace("H", "Hipp")
- df_comparison_subjects = df_comparison_subjects.sort_values(["area_id", "roi_order"]).reset_index(
- drop=True
- )
- order = (
- df_comparison_subjects.sort_values(["area_id", "roi_order"])
- .drop_duplicates("name")
- .name.unique()
- )
- order_roi = (
- df_comparison_subjects.sort_values(["area_id", "roi_order"]).drop_duplicates("roi").roi.unique()
- )
- hue_order = list(
- df_comparison_subjects.sort_values("area_id").drop_duplicates("area_id").area.tolist()
- )
- palette = list(
- df_comparison_subjects.sort_values("area_id").drop_duplicates("area_id").area_color.tolist()
- )
- null_mean = df_comparison_subjects.drop_duplicates("roi").language_null_mean.values
- null_std = df_comparison_subjects.drop_duplicates("roi").language_null_std.values
- fig, ax = plt.subplots(1, 1, figsize=(18, 4), dpi=300)
- ax.fill_between(
- np.arange(len(order)), null_mean - null_std, null_mean + null_std, color="maroon", alpha=0.15
- )
- ax.plot(np.arange(len(order)), null_mean, color="maroon", linestyle="--", linewidth=0.7)
- plot_boxplot_rois(
- df_comparison_subjects.rename(columns={"language_rsa": "score"}),
- ax,
- order,
- hue_order,
- palette,
- title="",
- legend=True,
- fontsize=6,
- legend_fontsize=9,
- legend_kwargs={"bbox_to_anchor": (1, 1), "title": ""},
- vmax=0.23,
- strip_size=2,
- )
- ax.set_ylabel("Language Model - Brain Alignment (RSA)")
- ax.yaxis.set_major_formatter(mticker.FormatStrFormatter("%.2f"))
- apa_significance = df_comparison_subjects.drop_duplicates("roi").language_apa_star
- max_observed_values = (
- df_comparison_subjects.groupby("name")
- .aggregate({"language_rsa": "max"})
- .loc[order]
- .language_rsa.values
- )
- for i, (p, name, max_observed) in enumerate(zip(apa_significance, order, max_observed_values)):
- if p != "***":
- if p == "n.s.":
- ax.text(i, max_observed + 0.01, p, fontsize=6, ha="center", rotation=90, va="bottom")
- else:
- ax.text(i, max_observed + 0.005, p, fontsize=6, ha="center", rotation=0, va="center")
- add_pvalue_bracket(
- ax=ax, x1=0, x2=3, y=0.07, y_text=0.08, height=0.005, text="Visual\nCortex", fontsize=7
- )
- add_pvalue_bracket(
- ax=ax, x1=8, x2=13, y=0.1, y_text=0.11, height=0.005, text="Ventral\nHub", fontsize=7
- )
- add_pvalue_bracket(
- ax=ax, x1=38, x2=43, y=0.22, y_text=0.23, height=0.005, text="LOTC Hub", fontsize=7
- )
- ax.set_ylim(-0.08, 0.23)
- ax.set_title("Language Models - Brain RSA Alignment")
- fig.savefig(
- output / "04_rsa_language_boxplot_all.pdf", bbox_inches="tight", dpi=300, transparent=True
- )
- # Save without legend
- ax.get_legend().remove()
- fig.savefig(
- output / "05_rsa_language_boxplot_all_no_legend.pdf",
- bbox_inches="tight",
- dpi=300,
- transparent=True,
- )
- # %% [markdown]
- # ## 3. Group Average Cortical Maps
- # Project group‑mean RSA values onto the fsaverage surface for three modalities: inter‑subject, vision, and language.
- # %% [markdown]
- # ### 3.1 Surface Plotting
- # For each modality and hemisphere (lh/rh), render lateral, medial, ventral, and caudal views; save PNG/TIFF with and without area labels.
- # %%
- visual_cortex = ["V1", "V2", "V3", "V4"]
- ventral_cluster = ["VMV1", "VMV2", "VMV3", "PHA1", "PHA2", "PHA3"]
- dorsal_cluster = ["MT", "MST", "FST", "V4t", "TPOJ2", "TPOJ3"]
- visual_cortex_color = "gray"
- ventral_cluster_color = "#FE4A00"
- dorsal_cluster_color = "#2ca02c"
- # Load all the files and aggregate the data
- df_comparison_subjects = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_separated_filename,
- subject_alignment_filename=intersubject_shift1_separated_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=True,
- )
- # Load HCP and annotate hubs to add the areas in the map
- hcp_annotated = pd.read_csv(hcp_filename)
- hcp_annotated.loc[hcp_annotated.name.isin(ventral_cluster), "area_id"] = -1
- hcp_annotated.loc[hcp_annotated.name.isin(ventral_cluster), "area_color"] = ventral_cluster_color
- hcp_annotated.loc[hcp_annotated.name.isin(dorsal_cluster), "area_id"] = -2
- hcp_annotated.loc[hcp_annotated.name.isin(dorsal_cluster), "area_color"] = dorsal_cluster_color
- hcp_annotated.loc[hcp_annotated.name.isin(visual_cortex), "area_id"] = -3
- df = df_comparison_subjects.drop(columns=["name", "mne_name"]).merge(
- hcp_annotated[["roi", "name", "mne_name"]], on="roi"
- )
- modalities = {
- "language": {"column": "language_rsa", "pvalue_column": "language_p_value", "vmax": 0.15},
- "vision": {"column": "vision_rsa", "pvalue_column": "vision_p_value", "vmax": 0.15},
- "intersubject": {
- "column": "intersubject_rsa",
- "pvalue_column": "intersubject_p_value",
- "vmax": 0.2,
- },
- }
- faverage_folder = output / "06_faverage_spatial"
- faverage_folder.mkdir(exist_ok=True, parents=True)
- for modality in modalities.keys():
- params = modalities[modality]
- pvalue_column = params["pvalue_column"]
- vmax = params["vmax"]
- print(f"Processing modality: {modality} scale: ({-vmax}, 0, {vmax})")
- column = params["column"]
- significant = df_comparison_subjects.query(f"{pvalue_column} < 0.05").name.tolist()
- df_modality = df.query(f"name in @significant")
- for hemisphere in ["lh", "rh"]:
- brain = plot_faverage_parcelation(
- df_modality,
- cmap="RdBu_r",
- value_column=column,
- normalize=(-vmax, vmax),
- default_value=None,
- default_color="lightgray",
- hemisphere=hemisphere,
- size=(2 * 800, 2 * 600),
- )
- for view in ["lateral", "medial", "ventral", "caudal"]:
- brain.show_view(view)
- brain.save_image(
- faverage_folder / f"06_rsa_{modality}_{hemisphere}_{view}_{vmax:.2f}.png",
- mode="rgba",
- )
- # Save also as tigg
- brain.save_image(
- faverage_folder / f"06_rsa_{modality}_{hemisphere}_{view}_{vmax:.2f}.tiff",
- mode="rgba",
- )
- add_area_labels(brain, hcp_annotated, area_ids=[-1, -2, -3], hemispheres=[hemisphere])
- for view in ["lateral", "medial", "ventral", "caudal"]:
- brain.show_view(view)
- brain.save_image(
- faverage_folder / f"06_rsa_{modality}_{hemisphere}_{view}_{vmax:.2f}_annot.png",
- mode="rgba",
- )
- brain.save_image(
- faverage_folder / f"06_rsa_{modality}_{hemisphere}_{view}_{vmax:.2f}_annot.tiff",
- mode="rgba",
- )
- brain.close()
- # %% [markdown]
- # Inspect generated brain surface maps
- # %%
- hemisphere = "lh" # "lh", "rh"
- views = ["lateral", "medial", "ventral", "caudal"]
- fig, axes = plt.subplots(3, len(views), figsize=(12, 6))
- for j, modality in enumerate(["intersubject", "vision", "language"]):
- for i, view in enumerate(views):
- vmax_file = modalities[modality]["vmax"]
- img_filename = faverage_folder / f"06_rsa_{modality}_{hemisphere}_{view}_{vmax_file:.2f}_annot.png"
- img = plt.imread(img_filename)
- axes[j, i].axis("off")
- axes[j, i].imshow(img)
- axes[j, i].set_title(f"{hemisphere.upper()} {view} {modality}", fontsize=12)
- # %% [markdown]
- # ### 3.2 Colorbars
- # Generate horizontal and vertical colorbars for RSA scales (−vmax…+vmax) to accompany spatial maps.
- # %%
- eps = 0.00000001
- vmax = 0.20
- fig_cbar, ax_cbar = plot_cbar(
- cmap="RdBu_r",
- title=r"Inter-subject alignment (RSA Pearson's $\rho$)",
- vmin=-vmax - eps,
- vmax=vmax + eps,
- locator=0.04,
- horizontal=True,
- rotation=0,
- labelpad=5,
- figsize=(6, 0.25),
- percent=False,
- )
- ax_cbar.set_xlim(0, vmax + eps)
- fig_cbar.savefig(
- faverage_folder / f"06_colorbar_intersubject_{vmax:.2f}_BuRd.pdf",
- bbox_inches="tight",
- dpi=300,
- transparent=True,
- )
- # %%
- vmax = 0.15
- fig_cbar, ax_cbar = plot_cbar(
- cmap="RdBu_r",
- title=r"Models-brain alignment (RSA Pearsons's $\rho$)",
- vmin=-vmax - eps,
- vmax=vmax + eps,
- locator=0.03,
- horizontal=True,
- rotation=0,
- labelpad=5,
- figsize=(6, 0.25),
- percent=False,
- )
- ax_cbar.set_xlim(-0.06, vmax)
- fig_cbar.savefig(
- faverage_folder / f"06_colorbar_model_{vmax:.2f}_BuRd.pdf",
- bbox_inches="tight",
- dpi=300,
- transparent=True,
- )
- fig_cbar, ax_cbar = plot_cbar(
- cmap="RdBu_r",
- title=r"Models-brain alignment (RSA)",
- vmin=-vmax - eps,
- vmax=vmax + eps,
- locator=0.03,
- horizontal=False,
- rotation=90,
- labelpad=5,
- figsize=(0.25, 6),
- percent=False,
- )
- ax_cbar.set_ylim(-0.06, vmax)
- fig_cbar.savefig(
- faverage_folder / f"06_colorbar_model_{vmax:.2f}_BuRd_horizontal.pdf",
- bbox_inches="tight",
- dpi=300,
- transparent=True,
- )
- # Close vertical to improve the notebook readability
- plt.close(fig_cbar)
- # %% [markdown]
- # ## 4. Model–Brain Scatter Comparisons
- # Scatterplot inter‑subject RSA vs. model‑brain RSA (vision & language), fit a power‑law model with bootstrap confidence intervals, label outliers, and save vector graphics for manual annotation.
- # %%
- # Load all the files and aggregate the data
- df_comparison = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_joined_filename,
- subject_alignment_filename=intersubject_shift1_joined_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=True,
- )
- n_boot = 10000
- df_intersubject = pd.read_parquet(intersubject_shift1_joined_filename)
- top_areas = process_intersubject_rois(
- df_intersubject, hcp_filename=hcp_filename, top=10
- ).area.unique()
- palette = (
- df_comparison[["area", "area_color"]]
- .drop_duplicates()
- .set_index("area")
- .to_dict()["area_color"]
- )
- data = df_comparison.query("intersubject_p_value < 0.05").copy()
- data = add_cluster(data)
- data["top_area"] = data["area"].apply(lambda x: x in top_areas)
- fig1, ax1 = plt.subplots(1, 1, figsize=(6, 6)) # Vision models
- fig2, ax2 = plt.subplots(1, 1, figsize=(6, 6)) # Language models
- # Vision models
- plot_comparison(data=data, x="intersubject_rsa", y="vision_rsa", ax=ax1, palette=palette)
- x_fit, y_fit, y_lower, y_upper, r2, params = fit_powerlaw_zero_with_bootstrap(
- data["intersubject_rsa"], data["vision_rsa"], n_boot=n_boot
- )
- print(f"Vision Models - Brain Pseudo-R²: {r2:.3f} N Boostrap samples: {n_boot}")
- print(f"Power-law parameters: y={params[0]:.3f}x^{params[1]:.3f}")
- ax1.plot(
- x_fit, y_fit, color="maroon", label=f"$f(x)={params[0]:.2f}x^{{{params[1]:.2f}}}$", zorder=-10
- )
- ax1.fill_between(x_fit, y_lower, y_upper, color="maroon", alpha=0.1, label="95% CI", zorder=-20)
- ax1.legend(loc="lower right")
- # Language models
- plot_comparison(data=data, x="intersubject_rsa", y="language_rsa", ax=ax2, palette=palette)
- for i, row in data.iterrows():
- if row["vision_rsa"] > 0.04 or row["intersubject_rsa"] > 0.04 or row["language_rsa"] > 0.04:
- ax1.text(
- row["intersubject_rsa"],
- row["vision_rsa"],
- row["name"],
- ha="center",
- )
- ax2.text(row["intersubject_rsa"], row["language_rsa"], row["name"], ha="center")
- # Add labels, legend and some plot adjustments
- ax1.set_xlabel("Inter-subject Alignment (RSA)")
- ax1.set_ylabel("Vision Models - Brain Alignment (RSA)")
- ax2.set_xlabel("Inter-subject Alignment (RSA)")
- ax2.set_ylabel("Language Models - Brain Alignment (RSA)")
- triangle = mlines.Line2D(
- [], [], color="black", marker="^", linestyle="None", label="Early Visual Cortex"
- )
- square = mlines.Line2D([], [], color="black", marker="s", linestyle="None", label="Ventral Hub")
- diamond = mlines.Line2D([], [], color="black", marker="D", linestyle="None", label="LOTC Hub")
- circle = mlines.Line2D([], [], color="black", marker="o", linestyle="None", label="Other Regions")
- legend_handles = [triangle, square, diamond, circle]
- ax2.legend(handles=legend_handles, title="")
- ax1.set_ylim(0, 0.175)
- ax2.set_ylim(-0.05, 0.175)
- ax1.set_xlim(-0.005, 0.225)
- ax2.set_xlim(-0.005, 0.225)
- # Add a major tick every 0.05, a minor tick every 0.025
- for ax in (ax1, ax2):
- ax.xaxis.set_major_locator(mticker.MultipleLocator(0.05))
- ax.xaxis.set_minor_locator(mticker.MultipleLocator(0.025))
- ax.yaxis.set_major_locator(mticker.MultipleLocator(0.05))
- ax.yaxis.set_minor_locator(mticker.MultipleLocator(0.025))
- # Save as svg to manually edit the text labels to avoid overlapping
- fig1.savefig(output / "07_rsa_models_vision_comparison.svg", bbox_inches="tight", transparent=True)
- fig2.savefig(
- output / "07_rsa_models_language_comparison.svg", bbox_inches="tight", transparent=True
- )
- # %% [markdown]
- # ## 5. Hub‑Based Modality Comparison
- # Group alignments by predefined hubs (Visual Cortex, Ventral Hub, Dorsal Hub), plot boxplots comparing modalities (Inter‑subject, Vision, Language), conduct paired t‑tests with Bonferroni correction, and annotate significant differences.
- # %%
- df_comparison_subjects = proccess_alignment(
- models_filename=models_filename,
- models_alignment_filename=model_brain_joined_filename,
- subject_alignment_filename=intersubject_shift1_joined_filename,
- pvalues_filename=pvalues_filename,
- hcp_filename=hcp_filename,
- group_subject=False,
- )
- df_comparison_subjects = add_cluster(df_comparison_subjects)
- df_comparison_subjects_grouped = (
- df_comparison_subjects.groupby(["subject", "cluster"])
- .aggregate({"intersubject_rsa": "mean", "vision_rsa": "mean", "language_rsa": "mean"})
- .reset_index()
- )
- df_comparison_subjects_test = df_comparison_subjects_grouped.query("cluster != 'Other'").copy()
- # Boxplot
- fig, ax = plt.subplots(1, 1, figsize=(3.8, 6))
- order = ["Visual\nCortex", "Ventral\nHub", "Dorsal\nHub"]
- df_comparison_subjects_grouped.cluster = df_comparison_subjects_grouped.cluster.str.replace(
- " ", "\n"
- ).str.replace("Cluster", "Hub")
- hue_names = {
- "intersubject_rsa": "Inter-subject",
- "vision_rsa": "Vision Models",
- "language_rsa": "Language Models",
- }
- # hue_order = ["Inter-subject", "Vision Models", "Language Models"]
- hue_order = ["Vision Models", "Language Models"]
- # hue_order = ["Vision", "Language"]
- df_comparison_subjects_grouped = df_comparison_subjects_grouped.rename(columns=hue_names)
- df_comparison_subjects_grouped = df_comparison_subjects_grouped.melt(
- id_vars=["subject", "cluster"], var_name="modality", value_name="score"
- ).copy()
- df_comparison_subjects_grouped
- from cmap import Colormap
- cm = Colormap("colorbrewer:Set2_3").to_matplotlib()([0.5, 0, 1])
- cm = list(list(c) for c in cm)[:2]
- sns.boxplot(
- data=df_comparison_subjects_grouped,
- x="cluster",
- y="score",
- hue="modality",
- ax=ax,
- order=order,
- hue_order=hue_order,
- palette=cm,
- showfliers=False, # width=0.5,
- )
- # Plot individual points with jitter
- sns.stripplot(
- data=df_comparison_subjects_grouped,
- x="cluster",
- y="score",
- hue="modality",
- ax=ax,
- order=order,
- hue_order=hue_order,
- palette=cm,
- dodge=True,
- alpha=1,
- linewidth=0.5,
- size=4,
- jitter=True,
- edgecolor=(0.24, 0.24, 0.24),
- zorder=100,
- legend=False,
- )
- ax.set_xlabel("")
- ax.set_ylabel(r"Alignment (RSA Pearson's $\rho$)")
- # Remove title from legend
- ax.legend(title="", loc="upper left") # , bbox_to_anchor=(0, 0.3))
- # xticks from each 0.05
- ax.set_yticks(np.arange(-0.04, 0.21, 0.04))
- sns.despine(ax=ax)
- # Define dataframe
- df_comparison_subjects_test
- # Define comparisons explicitly
- comparisons = [
- ("Visual Cortex", "vision_rsa", "language_rsa"),
- ("Ventral Cluster", "vision_rsa", "language_rsa"),
- ("Dorsal Cluster", "vision_rsa", "language_rsa"),
- # ("Dorsal Cluster", "language_rsa", "intersubject_rsa"),
- # ("Visual Cortex", "vision_rsa", "intersubject_rsa"),
- ]
- results = []
- for cluster, cond1, cond2 in comparisons:
- subset = df_comparison_subjects_test.query("cluster == @cluster")
- t_stat, p_value = ttest_rel(subset[cond1], subset[cond2])
- results.append(
- {
- "cluster": cluster,
- "comparison": f"{cond1} vs {cond2}",
- "t_stat": t_stat,
- "p_value": p_value,
- "mean_diff": subset[cond1].mean() - subset[cond2].mean(),
- }
- )
- # Convert to dataframe
- results_df = pd.DataFrame(results)
- # Correct p-values (FDR correction)
- results_df["p_corrected"] = multipletests(results_df["p_value"], method="bonferroni")[
- 1
- ] # .round(5)
- results_df["apa_star"] = results_df["p_corrected"].apply(
- lambda x: "***" if x < 0.001 else ("**" if x < 0.01 else ("*" if x < 0.05 else "n.s."))
- )
- # Show results
- display(results_df)
- ax.set_ylim(-0.040, 0.22)
- width = 0.135
- add_pvalue_bracket(ax=ax, x1=-width, x2=width, y=0.175, height=0.0025, text="***")
- add_pvalue_bracket(ax=ax, x1=1 - width, x2=1 + width, y=0.15, height=0.0025, text="***")
- add_pvalue_bracket(ax=ax, x1=2 - width, x2=2 + width, y=0.19, height=0.0025, text="***")
- ax.set_xticks([0, 1, 2])
- ax.set_xticklabels(["Early Visual\nCortex", "Ventral\nHub", "LOTC\nHub"])
- fig.savefig(
- output / "08_rsa_modality_boxplot_comparison.pdf", bbox_inches="tight", transparent=True
- )
01_rsa_spatial_alignment.ipynb at commit c1ebe1d, under MIT · at the source
Overview
- Department of Cognition, Development and Education Psychology, Faculty of Psychology, University of Barcelona,Barcelona, Spain
- Institute of Neurosciences, University of Barcelona,Barcelona, Spain
- Bellvitge Institute for Biomedical Research,Barcelona, Spain
Abstract
The brain transforms visual inputs into cortical representations that support diverse cognitive and behavioral goals. Characterizing how this information is organized and routed across the human brain is essential for understanding how we process complex visual scenes. Here, we applied representational similarity analysis to 7T fMRI data collected during natural scene viewing. We quantified representational geometry shared across individuals and compared it to hierarchical features from vision and language neural networks across model layers. By integrating these comparisons with representational connectivity between cortical regions, we identified two distinct processing routes: a ventromedial pathway specialized for scene layout and environmental context, and a lateral occipitotemporal pathway selective for animate content. Vision models aligned with shared structure in both routes, whereas language models corresponded primarily with the lateral pathway and showed negative alignment in early visual and ventral cortex. These findings refine classical visual-stream models by revealing a distributed cortical network with separable representational routes for context and animate content during scene perception.
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 10 matches between paragraphs and lines of code.
huggingface.co/collections/pablomm
Availability: 1 check, the latest on 28 September 2026: the link is dead
- 28 September 2026: the link is dead
memory-formation/shared-representations
c1ebe1d4f1fd49e5f4f53b09f8c3d2a6acfb12e5, 20 March 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
47 files
- 1_dataset_preparation/
1_nsd_check_dataset.py , Python, 127 lines - 1_dataset_preparation/
2_nsd_create_indexes.py , Python, 318 lines - 1_dataset_preparation/
3_create_coco_indexes.py , Python, 323 lines - 1_dataset_preparation/
4_nsd_organize_betas.py , Python, 104 lines - 1_dataset_preparation/
5_things_process_dataset , Jupyter, 270 lines.ipynb - 1_dataset_preparation/
6_bold5000_process_datas , Jupyter, 187 lineset.ipynb - 1_dataset_preparation/
bold5000_hcp_atlas/ , Shell, 25 linesextract_atlas_rois.sh - 1_dataset_preparation/
bold5000_hcp_atlas/ , Shell, 159 linesmni_to_functional.sh - 2_model_extraction/
1_extract_vision_feature , Python, 96 lines, 1 matchs.py - 2_model_extraction/
2_caption_dataset_pixtra , Python, 209 linesl.py - 2_model_extraction/
3_extract_language_featu , Python, 116 linesres.py - 2_model_extraction/
4_extract_fmri_kmcca.ipy , Jupyter, 611 linesnb - 3_alignment/
1_rsa_nsd_subject_subjec , Python, 550 linest_alignment.py - 3_alignment/
2_rsa_nsd_subject_model_ , Python, 491 lines, 1 matchalignment.py - 3_alignment/
3_rsa_nsd_subject_subjec , Python, 472 linest_alignment_partitions.p y - 3_alignment/
4_rsa_nsd_subject_subjec , Python, 473 linest_alignment_controled.py - 3_alignment/
5_rsa_nsd_subject_model_ , Python, 449 linesalignment_controled.py - 3_alignment/
6_rsa_nsd_subject_group_ , Python, 570 lines, 1 matchsubject_alignment.py - 3_alignment/
7_rsa_other_subject_subj , Python, 360 lines, 2 matchesect_alignment.py - 3_alignment/
8_rsa_other_subject_mode , Python, 416 linesl_alignment.py - 3_alignment/
9_compute_rsa_pvalues.ip , Jupyter, 264 linesynb - 4_other_alignments/
1_subject_subject_alignm , Python, 298 linesent_other_metrics.py - 4_other_alignments/
2_subject_model_alignmen , Python, 219 linest_other_metrics.py - 4_other_alignments/
3_aligment_language_visi , Python, 159 lineson_features.py - 4_other_alignments/
4_categories_alignment.p , Python, 204 linesy - 4_other_alignments/
5_extract_tokenizer_voca , Python, 320 linesbulary.py - 4_other_alignments/
6_alignment_perceptual_s , Python, 157 linestatistics.py - 4_other_alignments/
7_untrained_models_align , Python, 240 lines, 1 matchment.py - 4_other_alignments/
8_extract_object_boxes.p , Python, 167 linesy - 4_other_alignments/
9_cross_subject_out_of_o , Python, 133 linesrder.py - 4_other_alignments/
legacy/ , Python, 190 lines01_model_brain_alignment _legacy.py - 4_other_alignments/
legacy/ , Python, 335 lines02_inter_subject_alignme nt_legacy.py - 4_other_alignments/
legacy/ , Python, 133 lines03_cross_subject_common_ shufled.py - 4_other_alignments/
legacy/ , Python, 258 lines04_nsd_alignment_partiti ons_modalities.py - 4_other_alignments/
legacy/ , Python, 244 lines05_nsd_cka_cross_subject _permutations_edges.py - 4_other_alignments/
legacy/ , Python, 187 lines06_nsd_cka_cross_subject _permutations.py - 4_other_alignments/
legacy/ , Python, 343 lines07_nsd_cka_model_subject _permutations.py - 4_other_alignments/
legacy/ , Python, 193 lines08_nsd_extract_inter_sub ject_partitions.py - 4_other_alignments/
legacy/ , Python, 198 lines09_nsd_extract_subject_m odel_partitions.py - 4_other_alignments/
legacy/ , Python, 328 lines10_partitions_cross_subj ect_similarities.py - 4_other_alignments/
legacy/ , Python, 366 lines11_partitions_subject_mo dels_similarities.py - 4_other_alignments/
legacy/ , Python, 219 lines12_rsa_other_dataset_int ersubject_simplified.py - 4_other_alignments/
legacy/ , Python, 259 lines13_subject_model_alignme nt_optimized_paritions.p y - 4_other_alignments/
legacy/ , Python, 166 lines14_token_cka.py - 5_notebooks/
01_rsa_spatial_alignment , Jupyter, 1,158 lines, 2 matches.ipynb - 5_notebooks/
02_rsa_hemisphere_compar , Jupyter, 755 linesison.ipynb - 5_notebooks/
03_rsa_model_hierarchy.i , Jupyter, 942 lines, 2 matchespynb - repository limit reached (2,000 files or 30 MB): the rest is at the source (50 files)
Zenodo 19581037
Availability: 1 check, the latest on 28 September 2026: the link answers (HTTP 200)
- 28 September 2026: the link answers (HTTP 200)
47 files
- 1_dataset_preparation/
1_nsd_check_dataset.py , Python, 127 lines - 1_dataset_preparation/
2_nsd_create_indexes.py , Python, 318 lines - 1_dataset_preparation/
3_create_coco_indexes.py , Python, 323 lines - 1_dataset_preparation/
4_nsd_organize_betas.py , Python, 104 lines - 1_dataset_preparation/
5_things_process_dataset , Jupyter, 270 lines.ipynb - 1_dataset_preparation/
6_bold5000_process_datas , Jupyter, 187 lineset.ipynb - 1_dataset_preparation/
bold5000_hcp_atlas/ , Shell, 25 linesextract_atlas_rois.sh - 1_dataset_preparation/
bold5000_hcp_atlas/ , Shell, 159 linesmni_to_functional.sh - 2_model_extraction/
1_extract_vision_feature , Python, 96 liness.py - 2_model_extraction/
2_caption_dataset_pixtra , Python, 209 linesl.py - 2_model_extraction/
3_extract_language_featu , Python, 116 linesres.py - 2_model_extraction/
4_extract_fmri_kmcca.ipy , Jupyter, 611 linesnb - 3_alignment/
1_rsa_nsd_subject_subjec , Python, 550 linest_alignment.py - 3_alignment/
2_rsa_nsd_subject_model_ , Python, 491 linesalignment.py - 3_alignment/
3_rsa_nsd_subject_subjec , Python, 472 linest_alignment_partitions.p y - 3_alignment/
4_rsa_nsd_subject_subjec , Python, 473 linest_alignment_controled.py - 3_alignment/
5_rsa_nsd_subject_model_ , Python, 449 linesalignment_controled.py - 3_alignment/
6_rsa_nsd_subject_group_ , Python, 570 linessubject_alignment.py - 3_alignment/
7_rsa_other_subject_subj , Python, 360 linesect_alignment.py - 3_alignment/
8_rsa_other_subject_mode , Python, 416 linesl_alignment.py - 3_alignment/
9_compute_rsa_pvalues.ip , Jupyter, 264 linesynb - 4_other_alignments/
1_subject_subject_alignm , Python, 298 linesent_other_metrics.py - 4_other_alignments/
2_subject_model_alignmen , Python, 219 linest_other_metrics.py - 4_other_alignments/
3_aligment_language_visi , Python, 159 lineson_features.py - 4_other_alignments/
4_categories_alignment.p , Python, 204 linesy - 4_other_alignments/
5_extract_tokenizer_voca , Python, 320 linesbulary.py - 4_other_alignments/
6_alignment_perceptual_s , Python, 157 linestatistics.py - 4_other_alignments/
7_untrained_models_align , Python, 240 linesment.py - 4_other_alignments/
8_extract_object_boxes.p , Python, 167 linesy - 4_other_alignments/
9_cross_subject_out_of_o , Python, 133 linesrder.py - 4_other_alignments/
legacy/ , Python, 190 lines01_model_brain_alignment _legacy.py - 4_other_alignments/
legacy/ , Python, 335 lines02_inter_subject_alignme nt_legacy.py - 4_other_alignments/
legacy/ , Python, 133 lines03_cross_subject_common_ shufled.py - 4_other_alignments/
legacy/ , Python, 258 lines04_nsd_alignment_partiti ons_modalities.py - 4_other_alignments/
legacy/ , Python, 244 lines05_nsd_cka_cross_subject _permutations_edges.py - 4_other_alignments/
legacy/ , Python, 187 lines06_nsd_cka_cross_subject _permutations.py - 4_other_alignments/
legacy/ , Python, 343 lines07_nsd_cka_model_subject _permutations.py - 4_other_alignments/
legacy/ , Python, 193 lines08_nsd_extract_inter_sub ject_partitions.py - 4_other_alignments/
legacy/ , Python, 198 lines09_nsd_extract_subject_m odel_partitions.py - 4_other_alignments/
legacy/ , Python, 328 lines10_partitions_cross_subj ect_similarities.py - 4_other_alignments/
legacy/ , Python, 366 lines11_partitions_subject_mo dels_similarities.py - 4_other_alignments/
legacy/ , Python, 219 lines12_rsa_other_dataset_int ersubject_simplified.py - 4_other_alignments/
legacy/ , Python, 259 lines13_subject_model_alignme nt_optimized_paritions.p y - 4_other_alignments/
legacy/ , Python, 166 lines14_token_cka.py - 5_notebooks/
01_rsa_spatial_alignment , Jupyter, 1,158 lines.ipynb - 5_notebooks/
02_rsa_hemisphere_compar , Jupyter, 755 linesison.ipynb - 5_notebooks/
03_rsa_model_hierarchy.i , Jupyter, 942 linespynb - repository limit reached (2,000 files or 30 MB): the rest is at the source
Code availability
Code to reproduce analyses is available at github.com/
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:
- 3 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 94 scripts, each with its path and the digest of its content;
- 10 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
- doi:10.18112/
openneuro.ds001499.v1.3. , at OpenNeuro; found in “Data availability”0 - figshare:30753239, at figshare; found in “Data availability”
Data availability
Functional-MRI data and image stimuli were obtained from the Natural Scenes Dataset (accessed via 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, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 2 authors, 3 keywords, 13 MeSH terms, 2 funders, 72 references.
Cite
This paper
Marcos-Manchón, P., & Fuentemilla, L. (2026). Shared representations in brains and models reveal a two-route cortical organization during scene perception. Communications biology, 9(1), 950. https://
BibTeX
@article{marcosmanchon20
author = {Marcos-Manchón, Pablo and Fuentemilla, Lluís},
title = {{Shared representations in brains and models reveal a two-route cortical organization during scene perception}},
journal = {Communications biology},
year = {2026},
month = may,
volume = {9},
number = {1},
pages = {950},
publisher = {Nature Publishing Group},
issn = {2399-3642},
doi = {10.1038/
url = {https://
pmid = {42098492},
pmcid = {PMC13365445}
}
RIS
TY - JOUR
AU - Marcos-Manchón, Pablo
AU - Fuentemilla, Lluís
TI - Shared representations in brains and models reveal a two-route cortical organization during scene perception
T2 - Communications biology
J2 - Commun Biol
PY - 2026
DA - 2026/
VL - 9
IS - 1
SP - 950
SN - 2399-3642
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "Shared representations in brains and models reveal a two-route cortical organization during scene perception",
"container-title": "Communications biology",
"author": [
{
"family": "Marcos-Manchón",
"given": "Pablo"
},
{
"family": "Fuentemilla",
"given": "Lluís"
}
],
"container-title-short":
"volume": "9",
"issue": "1",
"page": "950",
"DOI": "10.1038/
"PMID": "42098492",
"PMCID": "PMC13365445",
"ISSN": "2399-3642",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
7
]
]
}
}
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/s41467-026-76098-y [code]
- A single computational objective can produce specialization of streams in visual cortex.Journal: Nature communicationsIn common: Hugging Face Transformers, h5py, Pillow, 9 other tools, 14 references
- [2] doi:10.1038/s41597-026-07248-6 [code]
- A large-scale fMRI dataset for vision-language semantic association.Journal: Scientific dataIn common: FSL, h5py, Pillow, 7 other tools, 14 references
- [3] doi:10.1162/imag.a.1309 [code]
- Probing the content of semantic representations in body-selective regions.Journal: Imaging neuroscience (Cambridge, Mass.)In common: h5py, Pillow, NiBabel, 8 other tools, 10 references
- [4] doi:10.7554/elife.107933 [code]
- Modality-agnostic decoding of vision and language from fMRI.Journal: eLifeIn common: Hugging Face Transformers, h5py, Pillow, 9 other tools, cognitive, 7 references
- [5] doi:10.1523/jneurosci.0038-26.2026 [code]
- Multidimensional Feature Tuning in Category Selective Areas of Human Visual Cortex.Journal: The Journal of neuroscience : the official journal of the Society for NeuroscienceIn common: h5py, Pillow, NiBabel, 8 other tools, 8 references
- [6] doi:10.1167/jov.26.5.7 [code]
- Representations in vision and language converge in a shared, multidimensional space of perceived similarities.Journal: Journal of visionIn common: h5py, Pillow, NiBabel, 8 other tools, cognitive, 7 references
- [7] doi:10.1162/imag.a.1207 [code]
- Investigating the temporal dynamics and modeling of mid-level feature representations in humans.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Pillow, statsmodels, PyTorch, 6 other tools, cognitive, 8 references
- [8] doi:10.1016/j.isci.2026.117180 [code]
- Developmental changes in similarity between neural representations of mental arithmetic and artificial neural networks.Journal: iScienceIn common: Hugging Face Transformers, h5py, NiBabel, 8 other tools, 5 references
- [9] doi:10.1038/s41593-026-02285-1 [code]
- Fixation duration on natural scenes is explained by memory encoding not processing demand.Journal: Nature neuroscienceIn common: h5py, Pillow, statsmodels, 7 other tools, cognitive, 5 references
- [10] doi:10.1371/journal.pcbi.1014656 [code]
- Contrastive learning to fine-tune feature extraction models for the visual cortex.Journal: PLoS computational biologyIn common: Pillow, NiBabel, PyTorch, 5 other tools, 7 references
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 3 repositories of the authors' code, each at its verified commit and with its license, 94 scripts, and 10 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:bc3524397f1f0f7c…
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.
