Skip to content

Commit b502569

Browse files
authored
Merge pull request #655 from FZJ-INM1-BDA/maint_paper_nbs
Maint paper nbs
2 parents dbf2374 + 58c50c0 commit b502569

17 files changed

Lines changed: 354 additions & 281 deletions

File tree

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ license: Apache-2.0
1515

1616
repository-code: https://github.com/FZJ-INM1-BDA/siibra-python
1717

18-
version: v1.0.1-alpha.2
18+
version: v1.0.1-alpha.4
1919

20-
date-released: 2025-03-12
20+
date-released: 2025-04-10
2121

2222
doi: 10.5281/zenodo.7885728
2323

examples/tutorials/2025-paper-fig3.py

Lines changed: 54 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
"""
2828

2929
# %%
30-
import re
3130
import numpy as np
3231
import pandas as pd
3332
import matplotlib.pyplot as plt
@@ -39,6 +38,7 @@
3938

4039
sns.set_style("dark")
4140

41+
4242
# %%
4343
# Input: Activation map or other feature distribution as image volume in MNI space
4444
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@
7272

7373
# %%
7474
# Split input volume into cluster components
75-
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
75+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
7676
#
7777
# There are many ways to get components out of a feature map. Here we use siibra to
7878
# - draw random points from the distribution encoded by the input volume, then
@@ -105,11 +105,11 @@
105105
)
106106

107107
# %%
108-
# Assign peaks and clusters to cytoarchitectonic regions
109-
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
108+
# Assign clusters to cytoarchitectonic regions
109+
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
110110
#
111111
# To assign the clusters to brain regions, we build feature maps from each cluster
112-
# and assign them to the Julich-Brain probabilistic maps. The assignemint is one-to-many
112+
# and assign them to the Julich-Brain probabilistic maps. The assignment is one-to-many
113113
# since the structures in the image and parcellation are continuous. Assignments
114114
# report correlation, intersection over union, and some other measures which we can use
115115
# to filter and sort them. The result is an assignment table from cluster components
@@ -120,24 +120,8 @@
120120
pmaps = siibra.get_map(
121121
parcellation="julich 3.0.3", space="mni152", maptype="statistical"
122122
)
123-
assignments = []
124-
125-
# assign peaks to regions
126-
peaks = input_volume.find_peaks(mindist=5, sigma_mm=0)
127-
with siibra.QUIET:
128-
df = pmaps.assign(peaks)
129-
df.query(f"`map value` >= {min_map_value}", engine="python", inplace=True)
130-
df["type"] = "peak"
131-
df["id"] = df["input structure"]
132-
assignments.append(df[["type", "id", "region", "map value"]])
133-
134-
view = plotting.plot_glass_brain(
135-
input_volume.fetch(), alpha=1, cmap="RdBu", symmetric_cbar=True
136-
)
137-
view.add_markers(peaks.as_list(), marker_size=30)
138123

139-
# %%
140-
# assign clusters to regions
124+
assignments = []
141125
for cl in clusterlabels:
142126
clustermap = siibra.volumes.from_pointcloud(samples, label=cl, target=input_volume)
143127
plotting.plot_glass_brain(
@@ -150,12 +134,11 @@
150134
with siibra.QUIET:
151135
df = pmaps.assign(clustermap)
152136
df.query(f"correlation >= {min_correlation}", engine="python", inplace=True)
153-
df["type"] = "cluster"
154-
df["id"] = cl
155-
assignments.append(df[["type", "id", "region", "correlation", "map value"]])
137+
df['id'] = cl
138+
assignments.append(df[["id", "region", "correlation", "map value"]])
156139

157140
all_assignments = pd.concat(assignments).sort_values(by="correlation", ascending=False)
158-
all_assignments
141+
all_assignments[:9].round(2)
159142

160143
# %%
161144
# plot the three primary assigned probability maps
@@ -174,64 +157,58 @@
174157
# Find features
175158
# ^^^^^^^^^^^^^
176159
#
177-
# To demonstrate multimodal feature profiling, we only choose the first connected region.
178-
179-
180-
def shortname(region):
181-
return (
182-
re.sub("\s*\(.*\)\s*|\s*Areaa\s*", " ", region.name)
183-
.replace("left", "L")
184-
.replace("right", "R")
185-
.strip()
186-
)
187-
188-
189-
def filter_features(feats, region):
190-
return [f for f in feats if any(r.assign(region) for r in f.anchor.regions)]
160+
# To demonstrate multimodal feature profiling, we choose one of the connected regions.
161+
selected_region = siibra.get_region("julich 3.0.3", "Area hOc1 (V1, 17, CalcS) left")
191162

163+
# %%
164+
# Let us first query receptor density fingerprint for this region
165+
receptor_fingerprints = siibra.features.get(selected_region, "receptor density fingerprint")
166+
fp = receptor_fingerprints[0]
167+
print("\n".join(fp.urls))
168+
fig, axs = plt.subplots(1, 1, figsize=(4, 3.2))
169+
fp.plot(ax=axs, capsize=4)
192170

193-
def plot_receptors(region, ax):
194-
fts = filter_features(siibra.features.get(region, "ReceptorDensityFingerprint"), region)
195-
fts[0].plot(ax=ax)
196171

172+
# %%
173+
# Now, query for gene expressions for the same region
174+
genes = ["gabarapl1", "gabarapl2", "maoa", "tac1"]
175+
gene_expressions = siibra.features.get(selected_region, "gene expressions", gene=genes)
176+
print("\n".join(gene_expressions[0].urls))
177+
fig, axs = plt.subplots(1, 1, figsize=(4, 3.5))
178+
gene_expressions[0].plot(ax=axs)
197179

198-
def plot_celldensities(region, ax):
199-
fts = filter_features(siibra.features.get(region, "LayerwiseCellDensity"), region)
200-
fts[0].plot(ax=ax)
201180

181+
# %%
182+
# We can next obtain cell body density values for this region
183+
cell_densities = siibra.features.get(selected_region, "LayerwiseCellDensity")
184+
print("\n".join(cell_densities[0].urls))
185+
fig, axs = plt.subplots(1, 1, figsize=(4, 2.8))
186+
cell_densities[0].plot(ax=axs, textwrap=30)
187+
# axs.set_title(axs.get_title().replace("in", "\n"))
202188

203-
def plot_gene_expressions(region, ax, genes):
204-
fts = siibra.features.get(region, "GeneExpressions", gene=genes)
205-
fts[0].plot(ax=ax)
189+
# %%
190+
# Lastly, we can obtain the regional profile of streamline count type
191+
# parcellation-based connectivity matrices
192+
connectivity_matrices = siibra.features.get(selected_region, "StreamlineCounts")
193+
conn = connectivity_matrices[0] # select the first set of matrices
194+
print("\n".join(conn.urls))
206195

207196

208-
def plot_connectivity(region, ax):
209-
fts = siibra.features.get(region, "StreamlineCounts")
210-
conndata = fts[0][0].get_profile(region).data
211-
conndata.rename(index={r: shortname(r) for r in conndata.index}, inplace=True)
212-
conndata[:15].plot(kind="bar", ax=ax)
213-
plt.xticks(ha="right")
214-
plt.tight_layout()
215-
plt.grid(True)
216-
plt.title(f"Connectivity for {region.name}")
197+
def shorten_name(region):
198+
# to simplify readability
199+
return (
200+
region.replace("Area ", "")
201+
.replace(" (GapMap)", "")
202+
.replace("left", "L")
203+
.replace("right", "R")
204+
)
217205

218206

219-
# %%
220-
selected_region = siibra.get_region("julich 3.0.3", "Area hOc1 (V1, 17, CalcS) left")
221-
plot_funcs = [
222-
lambda r, a: plot_receptors(r, a),
223-
lambda r, a: plot_celldensities(r, a),
224-
lambda r, a: plot_connectivity(r, a),
225-
lambda r, a: plot_gene_expressions(
226-
r, a, ["gabarapl1", "gabarapl2", "maoa", "tac1"]
227-
),
228-
]
229-
230-
fig, axs = plt.subplots(1, len(plot_funcs), figsize=(3.5 * len(plot_funcs), 4))
231-
for ax, func in zip(axs.ravel(), plot_funcs):
232-
func(r=selected_region, a=ax)
233-
ax.grid(True)
234-
xtl = ax.get_xticklabels()
235-
ax.set_xticklabels(xtl, rotation=55, ha="right")
236-
plt.suptitle("")
237-
plt.tight_layout()
207+
fig, axs = plt.subplots(1, 1, figsize=(4, 4.5))
208+
conn.plot(selected_region, ax=axs, max_rows=15, kind="bar", rot=50, width=0.7)
209+
axs.set_ylabel(
210+
f"Mean of {conn.modality} \u00b1 std \n in {len(conn.elements)} {conn.indexing_attributes[0]}s"
211+
)
212+
axs.xaxis.set_ticklabels([shorten_name(t.get_text()) for t in axs.xaxis.get_majorticklabels()])
213+
plt.grid(True, 'minor')
214+
plt.title(f"Connectivity for {selected_region.name}")

examples/tutorials/2025-paper-fig4.py

Lines changed: 61 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@
2323
# %%
2424
import matplotlib.pyplot as plt
2525
from nilearn import plotting
26-
import pandas as pd
27-
import re
2826
import seaborn as sns
2927
import siibra
3028

@@ -38,6 +36,7 @@
3836

3937
jubrain = siibra.parcellations.JULICH_BRAIN_CYTOARCHITECTONIC_ATLAS_V3_0_3
4038
pmaps = jubrain.get_map(space="mni152", maptype="statistical")
39+
print(jubrain.publications[0]['citation'])
4140

4241
# %%
4342
# Obtain definitions and probabilistic maps in MNI asymmetric space of area IFG 44
@@ -53,73 +52,59 @@
5352
colorbar=False,
5453
annotate=False,
5554
symmetric_cbar=True,
55+
title=r.name,
5656
)
57-
plt.savefig(f"{r.key}.png")
5857

5958
# %%
60-
# For each of the two brain areas, retrieve average densities of a selection of monogenetic
61-
# neurotransmitter receptors, layer-specific distributions of cell bodies, as well as expressions
62-
# of a selection of genes coding for these receptors.
59+
# For each of the two brain areas, query for layer-specific distributions of cell bodies.
60+
fig, axs = plt.subplots(1, len(regions), sharey=True, figsize=(8, 2.7))
61+
for i, region in enumerate(regions):
62+
layerwise_cellbody_densities = siibra.features.get(region, "layerwise cell density")
63+
layerwise_cellbody_densities[0].plot(ax=axs[i], textwrap=25)
64+
print(layerwise_cellbody_densities[0].urls)
65+
axs[i].set_ylim(25, 115)
6366

64-
receptors = ["M1", "M2", "M3", "D1", "5-HT1A", "5-HT2"]
65-
genes = ["CHRM1", "CHRM2", "CHRM3", "HTR1A", "HTR2A", "DRD1"]
66-
modalities = [
67-
("receptor density fingerprint", {}, {"receptors": receptors, "rot": 90}),
68-
("layerwise cell density", {}, {"rot": 0}),
69-
("gene expressions", {"gene": genes}, {"rot": 90}),
70-
]
71-
fig, axs = plt.subplots(
72-
len(modalities) + 1, len(regions), figsize=(4 * len(regions), 11), sharey="row"
73-
)
74-
ymax = [1000, 150, None]
67+
# %%
68+
# Next, retrieve average densities of a selection of monogenetic
69+
# neurotransmitter receptors.
70+
receptors = ["M1", "M2", "M3", "5-HT1A", "5-HT2", "D1"]
71+
fig, axs = plt.subplots(1, len(regions), sharey=True, figsize=(8, 3.5))
72+
for i, region in enumerate(regions):
73+
receptor_fingerprints = siibra.features.get(region, "receptor density fingerprint")
74+
receptor_fingerprints[0].plot(receptors=receptors, ax=axs[i])
75+
print(receptor_fingerprints[0].urls)
76+
axs[i].set_ylim(0, 1000)
77+
axs[i].title.set_size(12)
78+
transmitters = [
79+
siibra.vocabularies.RECEPTOR_SYMBOLS[r]['neurotransmitter']['name']
80+
for r in receptors
81+
]
82+
axs[i].set_xticklabels([f"{r}\n({n})" for r, n in zip(receptors, transmitters)])
7583

84+
# %%
85+
# Now, for further insight, query for expressions of a selection of genes coding
86+
# for these receptors.
87+
genes = ["CHRM1", "CHRM2", "CHRM3", "HTR1A", "HTR2A", "DRD1"]
88+
fig, axs = plt.subplots(1, len(regions), sharey=True, figsize=(7, 3))
7689
for i, region in enumerate(regions):
77-
axs[0, i].imshow(plt.imread(f"{region.key}.png"))
78-
axs[0, i].set_title(f'{region.name.replace("Area ", "")}')
79-
axs[0, i].axis("off")
80-
for j, (modality, kwargs, plotargs) in enumerate(modalities):
81-
fts = siibra.features.get(region, modality, **kwargs)
82-
assert len(fts) > 0
83-
if len(fts) > 1:
84-
print(f"More than one feature found for {modality}, {region.name}")
85-
f = fts[0]
86-
f.plot(ax=axs[j + 1, i], **plotargs)
87-
if modality == "receptor density fingerprint":
88-
# add neurotransmitter names to receptor names in xtick labels
89-
transmitters = [re.sub(r"(^.*\()|(\))", "", n) for n in f.neurotransmitters]
90-
axs[j + 1, i].set_xticklabels(
91-
[f"{r}\n({n})" for r, n in zip(receptors, transmitters)]
92-
)
93-
if ymax[j] is not None:
94-
axs[j + 1, i].set_ylim(0, ymax[j])
95-
if "std" in axs[j + 1, i].yaxis.get_label_text():
96-
axs[j + 1, i].set_ylabel(
97-
axs[j + 1, i].yaxis.get_label_text().replace("std", "std\n")
98-
)
99-
axs[j + 1, i].set_title(f"{fts[0].modality}")
100-
fig.suptitle("")
101-
fig.tight_layout()
90+
gene_expressions = siibra.features.get(region, "gene expressions", gene=genes)
91+
assert len(gene_expressions) == 1
92+
gene_expressions[0].plot(ax=axs[i])
93+
axs[i].title.set_size(11)
94+
print(gene_expressions[0].urls)
10295

10396
# %%
10497
# For each of the two brain areas, collect functional connectivity profiles referring to
10598
# temporal correlation of fMRI timeseries of several hundred subjects from the Human Connectome
10699
# Project. We show the strongest connections per brain area for the average connectivity patterns
107-
fts = siibra.features.get(regions[0], "functional connectivity")
108-
conn = fts[1]
109-
110-
# aggregate connectivity profiles for first region across subjects
111-
D1 = (
112-
pd.concat([c.get_profile(regions[0]).data for c in conn], axis=1)
113-
.agg(["mean", "std"], axis=1)
114-
.sort_values(by="mean", ascending=False)
115-
)
116-
117-
# aggregate connectivity profiles for second region across subjects
118-
D2 = (
119-
pd.concat([c.get_profile(regions[1]).data for c in conn], axis=1)
120-
.agg(["mean", "std"], axis=1)
121-
.sort_values(by="mean", ascending=False)
122-
)
100+
fts = siibra.features.get(jubrain, "functional connectivity")
101+
for cf in fts:
102+
if cf.cohort != "HCP":
103+
continue
104+
if cf.paradigm == "Resting state (EmpCorrFC concatenated)":
105+
conn = cf
106+
break
107+
print(conn.urls)
123108

124109

125110
# plot both average connectivity profiles to target regions
@@ -132,35 +117,32 @@ def shorten_name(n):
132117
)
133118

134119

135-
fig, (a1, a2) = plt.subplots(1, 2, sharey=True, figsize=(3.6 * len(regions), 4.1))
136-
kwargs = {"kind": "bar", "width": 0.85, "logy": True}
137-
D1.iloc[:17]["mean"].plot(
138-
**kwargs, yerr=D1.iloc[:17]["std"], ax=a1, ylabel=shorten_name(regions[0].name)
139-
)
140-
D2.iloc[:17]["mean"].plot(
141-
**kwargs, yerr=D2.iloc[:17]["std"], ax=a2, ylabel=shorten_name(regions[1].name)
142-
)
143-
a1.set_ylabel("temporal correlation")
144-
a1.xaxis.set_ticklabels(
145-
[shorten_name(t.get_text()) for t in a1.xaxis.get_majorticklabels()]
146-
)
147-
a2.xaxis.set_ticklabels(
148-
[shorten_name(t.get_text()) for t in a2.xaxis.get_majorticklabels()]
149-
)
150-
a1.grid(True)
151-
a2.grid(True)
152-
a1.set_title(regions[0].name.replace("Area ", ""))
153-
a2.set_title(regions[1].name.replace("Area ", ""))
120+
plotkwargs = {
121+
"kind": "bar",
122+
"width": 0.85,
123+
"logscale": True,
124+
"xlabel": "",
125+
"ylabel": "temporal correlation",
126+
"rot": 90,
127+
"ylim": (0.3, 1.1),
128+
"capsize": 2,
129+
}
130+
fig, axs = plt.subplots(1, len(regions), sharey=True, figsize=(7, 4.5))
131+
for i, region in enumerate(regions):
132+
plotkwargs["ax"] = axs[i]
133+
conn.plot(region, max_rows=17, **plotkwargs)
134+
axs[i].xaxis.set_ticklabels([shorten_name(t.get_text()) for t in axs[i].xaxis.get_majorticklabels()])
135+
axs[i].set_title(region.name.replace("Area ", ""), fontsize=8)
136+
axs[i].grid(True, 'minor')
154137
plt.suptitle("Functional Connectivity")
155-
plt.tight_layout()
156138

157139
# %%
158140
# For both brain areas, sample representative cortical image patches at 1µm
159141
# resolution that were automatically extracted from scans of BigBrain sections.
160142
# The image patches display clearly the differences in laminar structure of the two regions
161143

162144
selected_sections = [4950, 1345]
163-
fig, axs = plt.subplots(1, len(regions))
145+
fig, axs = plt.subplots(1, len(regions), sharey=True)
164146
for r, sn, ax in zip(regions, selected_sections, axs):
165147
# find 1 micron sections intersecting the region
166148
pmap = pmaps.get_volume(r)
@@ -176,6 +158,4 @@ def shorten_name(n):
176158
ax.axis("off")
177159
ax.set_title(r.name)
178160

179-
plt.tight_layout()
180-
181161
# sphinx_gallery_thumbnail_number = 2

0 commit comments

Comments
 (0)