OSCR

Multimodal reference brain atlas of adult <i>Danionella cerebrum</i>

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Methods › Sexual dimorphism analysis ↔ Fig5.ipynb, lines 289–333 · score 0.68 · female biased, male female, volume change, dimorphic, Figure 5
  2. [2] § Methods › Brain area overrepresentation analysis ↔ Fig4.ipynb, lines 890–929 · score 0.52 · Phipson Smyth, enrichment, permutation, Figure 4

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 · 581 lines · 18 KB · CC-BY-NC-SA-4.0 · 1 match

  1. # %% [markdown]
  2. # ## Notebook to generate the panels for the Fig. 5 of Kadobianskyi et al., 2026
  3. # %% [markdown]
  4. # ### Registration and analysis of the morphological differences in male and female Danionella cerebrum
  5. # %% [markdown]
  6. # Load libraries, ants numpy matplotlib. Additional requirements: pandas, seaborn
  7. # %%
  8. %env ITK_GLOBAL_DEFAULT_NUMBER_OF_THREADS=16
  9. import ants
  10. import numpy as np
  11. from matplotlib import pyplot as plt
  12. import sys, os
  13. from ants.core import ants_image_io as iio
  14. # %% [markdown]
  15. # Load the template
  16. # %%
  17. template_mixed = ants.image_read('./template_2p_mixed.nii')
  18. template_mixed
  19. # %%
  20. plt.figure(figsize=(12, 12), dpi=100)
  21. plt.imshow(template_mixed.max(2), cmap='gray')
  22. plt.show()
  23. # %% [markdown]
  24. # Load the jacobian determinants of the warp maps from each individual male and female fish to the mixed template. These were generated from the warp maps in the following way:
  25. #
  26. # ```
  27. # male_warped = ants.image_read('.../fish01.regd.nii.gz') # and 10 other warp maps for male fish
  28. # female_warped = ants.image_read('.../fish11.regd.nii.gz') # and 9 other warp maps for female fish
  29. #
  30. # logj_female = ants.create_jacobian_determinant_image(domain_image=template_mixed, do_log=True, tx='.../deformations/fish01.warptotemp.nii.gz')
  31. # logj_male = ants.create_jacobian_determinant_image(domain_image=template_mixed, do_log=True, tx='.../deformations/fish11.warptotemp.nii.gz')
  32. # ```
  33. #
  34. # and saved into the lists of warp maps onto the disk
  35. #
  36. # ```
  37. # logjs_male_np = [logj.numpy() for logj in logjs_male]
  38. # logjs_female_np = [logj.numpy() for logj in logjs_female]
  39. # np.savez_compressed('./logjs.npz', female=logjs_female_np, male=logjs_male_np)
  40. # %%
  41. logjs = np.load('sdm//logjs.npz')
  42. # %%
  43. logjs_female_np = logjs['female']
  44. logjs_male_np = logjs['male']
  45. # %% [markdown]
  46. # Load the segmentation with masks and remove the eyes
  47. # %%
  48. masks = ants.image_read('./segmentation.nii.gz')
  49. masks_np = masks.numpy()
  50. masks_np[masks_np == 204] = 0
  51. # %%
  52. z = 95
  53. plt.figure(figsize=(12, 12), dpi=100)
  54. plt.imshow(masks_np[:,:,z], cmap='Reds', alpha=.5)
  55. plt.imshow(logjs_male_np[1][:,:,z], cmap='Greys', alpha=.5)
  56. plt.show()
  57. # %% [markdown]
  58. # Import the label definitions
  59. # %%
  60. from collections import defaultdict
  61. label_names = {}
  62. with open('./dc_label_descriptions.txt', 'r') as file:
  63. lines = file.readlines()
  64. for line in lines:
  65. if line.startswith('#') or line.strip() == '':
  66. continue
  67. parts = line.strip().split()
  68. index = int(parts[0])
  69. name = ' '.join(parts[7:]).replace('"', '')
  70. label_names[index] = name
  71. # %% [markdown]
  72. # Prepare the test code
  73. # %%
  74. import numpy as np
  75. import pandas as pd
  76. from scipy.stats import t as tdist
  77. from statsmodels.stats.multitest import multipletests
  78. def prepare_label_index(masks_np, include_ids=None, exclude_ids=None):
  79. lab = np.asarray(masks_np, dtype=np.int64).ravel()
  80. if include_ids is None:
  81. label_ids = np.unique(lab)
  82. label_ids = label_ids[label_ids != 0]
  83. else:
  84. label_ids = np.array(sorted(i for i in include_ids if i != 0), dtype=np.int64)
  85. if exclude_ids is not None:
  86. exclude_ids = set(int(x) for x in exclude_ids)
  87. label_ids = np.array([i for i in label_ids if i not in exclude_ids], dtype=np.int64)
  88. if label_ids.size == 0:
  89. raise ValueError("No labels left after filtering.")
  90. lut = np.full(int(lab.max()) + 1, -1, dtype=np.int64)
  91. lut[label_ids] = np.arange(label_ids.size, dtype=np.int64)
  92. roi_index = lut[lab]
  93. keep_mask = roi_index >= 0
  94. roi_index = roi_index[keep_mask]
  95. voxel_counts = np.bincount(roi_index, minlength=label_ids.size)
  96. return roi_index, keep_mask, label_ids, voxel_counts
  97. def compute_roi_means(volumes, keep_mask, roi_index, n_rois):
  98. X = np.zeros((len(volumes), n_rois), dtype=np.float64)
  99. for i, vol in enumerate(volumes):
  100. values = np.asarray(vol).ravel()[keep_mask]
  101. sums = np.bincount(roi_index, weights=values, minlength=n_rois)
  102. counts = np.bincount(roi_index, minlength=n_rois)
  103. X[i] = sums / np.maximum(counts, 1)
  104. return X
  105. # convert log differences to linear differences
  106. def compute_mean_roi_volumes(volumes, keep_mask, roi_index, n_rois):
  107. out = np.zeros(n_rois, dtype=np.float64)
  108. for vol in volumes:
  109. jac = np.exp(np.asarray(vol).ravel()[keep_mask])
  110. sums = np.bincount(roi_index, weights=jac, minlength=n_rois)
  111. out += sums
  112. return out / max(len(volumes), 1)
  113. # Main test function for the Welch t-test
  114. def roi_stats_logJ(
  115. logjs_male_np,
  116. logjs_female_np,
  117. masks_np,
  118. label_info=None,
  119. include_ids=None,
  120. exclude_ids=None,
  121. min_voxels=50, # min voxel number per mask to consider for the test
  122. ):
  123. roi_index_all, keep_mask, label_ids_all, voxel_counts_all = prepare_label_index(
  124. masks_np,
  125. include_ids=include_ids,
  126. exclude_ids=exclude_ids,
  127. )
  128. n_rois_all = label_ids_all.size
  129. Xm_all = compute_roi_means(logjs_male_np, keep_mask, roi_index_all, n_rois_all)
  130. Xf_all = compute_roi_means(logjs_female_np, keep_mask, roi_index_all, n_rois_all)
  131. keep_roi = voxel_counts_all >= int(min_voxels)
  132. label_ids = label_ids_all[keep_roi]
  133. voxel_counts = voxel_counts_all[keep_roi]
  134. Xm = Xm_all[:, keep_roi]
  135. Xf = Xf_all[:, keep_roi]
  136. nm = Xm.shape[0]
  137. nf = Xf.shape[0]
  138. mean_m = Xm.mean(axis=0)
  139. mean_f = Xf.mean(axis=0)
  140. var_m = Xm.var(axis=0, ddof=1)
  141. var_f = Xf.var(axis=0, ddof=1)
  142. delta_logJ = mean_m - mean_f
  143. pooled_var = ((nm - 1) * var_m + (nf - 1) * var_f) / max(nm + nf - 2, 1)
  144. pooled_sd = np.sqrt(np.maximum(pooled_var, 1e-12))
  145. # Effect sizes: Cohen's d / Hedge's g
  146. cohens_d = delta_logJ / pooled_sd
  147. J = 1.0 - 3.0 / (4 * (nm + nf) - 9)
  148. hedges_g = J * cohens_d
  149. vol_m_all = compute_mean_roi_volumes(logjs_male_np, keep_mask, roi_index_all, n_rois_all)
  150. vol_f_all = compute_mean_roi_volumes(logjs_female_np, keep_mask, roi_index_all, n_rois_all)
  151. vol_m = vol_m_all[keep_roi]
  152. vol_f = vol_f_all[keep_roi]
  153. # linear volume difference
  154. delta_pct = 100.0 * (vol_m - vol_f) / (vol_f + 1e-12)
  155. se = np.sqrt(np.maximum(var_m / nm + var_f / nf, 1e-18))
  156. t_stat = delta_logJ / se
  157. df_num = (var_m / nm + var_f / nf) ** 2
  158. df_den = (
  159. (var_m ** 2) / (nm ** 2 * max(nm - 1, 1))
  160. + (var_f ** 2) / (nf ** 2 * max(nf - 1, 1))
  161. )
  162. dof = np.maximum(df_num / np.maximum(df_den, 1e-18), 1.0)
  163. # p-value
  164. p_value = 2.0 * tdist.sf(np.abs(t_stat), dof)
  165. # correct for multiple tests
  166. reject_null, p_value_adj, _, _ = multipletests(p_value, method="fdr_bh")
  167. df = pd.DataFrame({
  168. "label_id": label_ids.astype(int),
  169. "n_vox": voxel_counts.astype(int),
  170. "mean_male": mean_m,
  171. "mean_female": mean_f,
  172. "delta_logJ": delta_logJ,
  173. "vol_m": vol_m,
  174. "vol_f": vol_f,
  175. "delta_pct": delta_pct,
  176. "sd_male": np.sqrt(var_m),
  177. "sd_female": np.sqrt(var_f),
  178. "cohens_d": cohens_d,
  179. "hedges_g": hedges_g,
  180. "p_value": p_value,
  181. "p_value_adj": p_value_adj,
  182. "reject_null": reject_null,
  183. })
  184. if label_info is not None:
  185. df["label_name"] = pd.Series(label_info).reindex(df["label_id"]).values
  186. return df.sort_values("p_value_adj").reset_index(drop=True)
  187. def largest_volume_changes(df, top_n=20, only_sig=False, q=0.05):
  188. d = add_delta_pct_if_missing(df)
  189. if only_sig and "p_value_adj" in d.columns:
  190. d = d[d["p_value_adj"] < q]
  191. d = d.sort_values("delta_pct", key=lambda s: np.abs(s), ascending=False)
  192. cols = [
  193. c for c in [
  194. "label_id", "label_name", "n_vox",
  195. "vol_m", "vol_f",
  196. "mean_male", "mean_female", "delta_logJ", "delta_pct",
  197. "cohens_d", "hedges_g",
  198. "p_value", "p_value_adj", "reject_null", "direction"
  199. ]
  200. if c in d.columns
  201. ]
  202. return d[cols].head(top_n)
  203. # %% [markdown]
  204. # Now run the test
  205. # %%
  206. df_jac = roi_stats_logJ(
  207. logjs_male_np=logjs_male_np,
  208. logjs_female_np=logjs_female_np,
  209. masks_np=masks_np, # segmentation mask
  210. label_info=label_names, # label names / IDs
  211. exclude_ids=None, # no labels excluded
  212. min_voxels=0 # no small labels excluded
  213. )
  214. # Can be loaded from sdm/welch_SDM_table.csv
  215. # %%
  216. df_jac.head()
  217. # %% [markdown]
  218. # Build a plot of top 10 male- and top 10 female-biased brain regions by relative volume change (linear)
  219. # %%
  220. import numpy as np
  221. import pandas as pd
  222. import seaborn as sns
  223. import matplotlib.pyplot as plt
  224. def plot_regions_by_percent_change(
  225. df, top_n=20, title="Dimorphic regions by volume change",
  226. out_pdf="regions_percent_change.pdf",
  227. p_col="p_value_adj",
  228. size_cap_log10=6.0, size_range=(50, 300),
  229. palette={"female > male": "#5E81AC", "male > female": "#D08770"},
  230. show_size_legend=True,
  231. # squish the plot around the middle -20/+20% to preserve space
  232. squish=True, gap=20.0, center_scale=0.35,
  233. ):
  234. """
  235. Plot dimorphic regions along a single vertical axis.
  236. - Selects top_n/2 regions with largest negative delta_pct (female > male)
  237. and top_n/2 with largest positive delta_pct (male > female),
  238. then plots them together on one axis.
  239. - x-axis is delta_pct (male − female), squished around 0.
  240. - Dot size indicates significance (-log10 p).
  241. """
  242. d = df.copy()
  243. # Split into male-biased (delta>0) and female-biased (delta<0)
  244. d_pos = d[d["delta_pct"] > 0].copy() # male > female
  245. d_neg = d[d["delta_pct"] < 0].copy() # female > male
  246. # Top N labels per side (male/female)
  247. n_each = max(1, top_n // 2)
  248. pos_top = d_pos.sort_values("delta_pct", ascending=False).head(n_each)
  249. neg_top = d_neg.sort_values("delta_pct", ascending=True).head(n_each)
  250. # Combine and sort by signed delta so female-biased appear at top
  251. d_plot = pd.concat([neg_top, pos_top], axis=0)
  252. d_plot = d_plot.sort_values("delta_pct", ascending=True).copy()
  253. # Direction labels for coloring
  254. d_plot["Direction"] = np.where(
  255. d_plot["delta_pct"] > 0, "male > female", "female > male"
  256. )
  257. # Region names for y-axis
  258. d_plot["Region"] = d_plot.get("label_name", d_plot.get("label_id"))
  259. d_plot["Region"] = d_plot["Region"].fillna(d_plot.get("label_id")).astype(str)
  260. d_plot["Region"] = pd.Categorical(
  261. d_plot["Region"], categories=d_plot["Region"], ordered=True
  262. )
  263. # Significance → size mapping
  264. q = d_plot.get(p_col, d_plot.get("p_value", pd.Series(np.ones(len(d_plot))))).astype(float)
  265. q = q.fillna(1.0).clip(lower=1e-300, upper=1.0)
  266. sig = np.minimum(-np.log10(q), size_cap_log10) / size_cap_log10
  267. s_min, s_max = size_range
  268. sizes = s_min + sig * (s_max - s_min)
  269. # transform x axis (squish)
  270. x = d_plot["delta_pct"].values.astype(float)
  271. def ticks_from_data(x_vals, gap=15.0):
  272. lo, hi = np.min(x_vals), np.max(x_vals)
  273. step = 20.0
  274. base = np.arange(np.floor(lo/step)*step, np.ceil(hi/step)*step + 0.1, step)
  275. for v in (-gap, 0.0, +gap):
  276. if v < lo or v > hi:
  277. continue
  278. if not np.any(np.isclose(base, v)):
  279. base = np.sort(np.r_[base, v])
  280. return base
  281. if squish and np.any(x < -gap) and np.any(x > gap) and center_scale < 1.0:
  282. b = center_scale * gap # new half-width for center
  283. shift = gap - b
  284. def f(v):
  285. v = np.asarray(v, float).copy()
  286. mid = (np.abs(v) <= gap)
  287. left = (v < -gap)
  288. right = (v > +gap)
  289. v[mid] = (b/gap) * v[mid] # compress center region
  290. v[left] = v[left] + shift # pull left block rightward
  291. v[right] = v[right] - shift # pull right block leftward
  292. return v
  293. x_plot = f(x)
  294. tick_vals = ticks_from_data(x, gap=gap)
  295. tick_pos = f(tick_vals)
  296. zero_x = f(np.array([0.0]))[0]
  297. else:
  298. def f(v): return np.asarray(v, float)
  299. x_plot = x
  300. tick_vals = ticks_from_data(x, gap=gap)
  301. tick_pos = f(tick_vals)
  302. zero_x = 0.0
  303. d_plot["_x_plot"] = x_plot
  304. # plot
  305. sns.set(style="white", rc={"axes.spines.right": False, "axes.spines.top": False})
  306. fig, ax = plt.subplots(figsize=(4, 7))
  307. h = sns.scatterplot(
  308. data=d_plot,
  309. x="_x_plot",
  310. y="Region",
  311. hue="Direction",
  312. palette=palette,
  313. edgecolor="white",
  314. linewidth=0.5,
  315. alpha=0.9,
  316. ax=ax,
  317. legend=False,
  318. )
  319. h.collections[-1].set_sizes(sizes)
  320. ax.axvline(zero_x, color="lightgray", linestyle="-", linewidth=1)
  321. # ticks show true values; positions are transformed
  322. ax.set_xticks(tick_pos)
  323. ax.set_xticklabels([f"{v:.0f}" for v in tick_vals])
  324. # small padding
  325. lo = x_plot.min()
  326. hi = x_plot.max()
  327. ax.set_xlim(lo - 0.05 * (hi - lo + 1e-9),
  328. hi + 0.05 * (hi - lo + 1e-9))
  329. ax.set_xlabel("Relative volume change (%), male − female", fontsize=14)
  330. ax.set_ylabel("Region")
  331. ax.set_title(title, fontsize=16)
  332. # size legend for p values
  333. if show_size_legend:
  334. example_p = np.array([0.05, 0.01, 0.001])
  335. ex_sig = np.minimum(-np.log10(example_p), size_cap_log10) / size_cap_log10
  336. ex_sizes = s_min + ex_sig * (s_max - s_min)
  337. handles = [
  338. plt.scatter([], [], s=s, c="gray", alpha=0.9, edgecolors="none")
  339. for s in ex_sizes
  340. ]
  341. labels = [f"p = {pv:g}" for pv in example_p]
  342. leg = ax.legend(
  343. handles, labels,
  344. title="significance",
  345. loc="upper right",
  346. frameon=False,
  347. labelspacing=0.6,
  348. )
  349. ax.add_artist(leg)
  350. for sp in ax.spines.values():
  351. sp.set_visible(False)
  352. plt.savefig(out_pdf, dpi=300, bbox_inches="tight")
  353. plt.show()
  354. # %%
  355. plot_regions_by_percent_change(
  356. df_jac,
  357. top_n=20, # 10 female>male + 10 male>female
  358. title="Dimorphic regions by volume change",
  359. out_pdf='sdm_ttest.pdf'
  360. )
  361. # %% [markdown]
  362. # Now calculate voxel-wise Cohen's d for the last panel
  363. # %%
  364. import numpy as np
  365. import matplotlib.pyplot as plt
  366. # 1) voxelwise Cohen's d from lists of logJ volumes in the template space
  367. def voxelwise_cohens_d_from_logJ(logjs_male_np, logjs_female_np, eps=1e-12):
  368. M = np.stack(logjs_male_np, axis=0).astype(np.float32) # (nm, X, Y, Z)
  369. F = np.stack(logjs_female_np, axis=0).astype(np.float32) # (nf, X, Y, Z)
  370. nm, nf = M.shape[0], F.shape[0]
  371. mean_m, mean_f = M.mean(0), F.mean(0)
  372. var_m = M.var(0, ddof=1)
  373. var_f = F.var(0, ddof=1)
  374. sp2 = ((nm-1)*var_m + (nf-1)*var_f) / max(1, (nm + nf - 2))
  375. sp = np.sqrt(np.maximum(sp2, eps))
  376. d = (mean_m - mean_f) / sp
  377. return d
  378. # %%
  379. d_vox = voxelwise_cohens_d_from_logJ(logjs_male_np, logjs_female_np)
  380. template_np = template_mixed.numpy()
  381. # %% [markdown]
  382. # Prepare the plot function
  383. # %%
  384. import numpy as np
  385. import matplotlib.pyplot as plt
  386. from matplotlib.colors import LinearSegmentedColormap, Normalize
  387. from mpl_toolkits.axes_grid1 import make_axes_locatable
  388. def hex2rgb(h):
  389. h = h.lstrip("#")
  390. return np.array([int(h[i:i+2], 16) for i in (0, 2, 4)], dtype=float) / 255.0
  391. def project_mip(vol3d, axis=2):
  392. return np.max(np.asarray(vol3d, float), axis=axis)
  393. def show_axial_split_male_female_vertical(
  394. d_vox,
  395. brain_mask=None,
  396. pos_color="#D08770",
  397. neg_color="#5E81AC",
  398. clip_pct=99.0,
  399. crop_to_mask=True,
  400. pad=2,
  401. figsize=(8.0, 8.0),
  402. title_pos="Male > female (|Cohen's d|)",
  403. title_neg="Female > male (|Cohen's d|)",
  404. out_pdf=None,
  405. ):
  406. d = np.asarray(d_vox, float)
  407. if brain_mask is not None:
  408. d = np.where(brain_mask, d, 0.0)
  409. dpos = np.maximum(d, 0.0)
  410. dneg = np.maximum(-d, 0.0)
  411. P = project_mip(dpos, axis=2)
  412. N = project_mip(dneg, axis=2)
  413. if crop_to_mask and brain_mask is not None:
  414. m2d = np.any(brain_mask, axis=2)
  415. ys, xs = np.where(m2d)
  416. if xs.size and ys.size:
  417. x0 = max(xs.min() - pad, 0)
  418. x1 = min(xs.max() + pad, P.shape[1] - 1)
  419. y0 = max(ys.min() - pad, 0)
  420. y1 = min(ys.max() + pad, P.shape[0] - 1)
  421. P = P[y0:y1+1, x0:x1+1]
  422. N = N[y0:y1+1, x0:x1+1]
  423. eps = 1e-12
  424. pmax = np.percentile(P[P > 0], clip_pct) if np.any(P > 0) else 1.0
  425. nmax = np.percentile(N[N > 0], clip_pct) if np.any(N > 0) else 1.0
  426. Pn = np.clip(P / (pmax + eps), 0, 1)
  427. Nn = np.clip(N / (nmax + eps), 0, 1)
  428. pos_cmap = LinearSegmentedColormap.from_list("pos", [(1, 1, 1), hex2rgb(pos_color)])
  429. neg_cmap = LinearSegmentedColormap.from_list("neg", [(1, 1, 1), hex2rgb(neg_color)])
  430. fig, axes = plt.subplots(2, 1, figsize=figsize, dpi=120, constrained_layout=True)
  431. ax_neg = axes[0]
  432. ax_neg.imshow(Nn, origin="lower", cmap=neg_cmap)
  433. ax_neg.set_title(title_neg, fontsize=12)
  434. ax_neg.axis("off")
  435. ax_pos = axes[1]
  436. ax_pos.imshow(Pn, origin="lower", cmap=pos_cmap)
  437. ax_pos.set_title(title_pos, fontsize=12)
  438. ax_pos.axis("off")
  439. for ax, vmax, label, cmap in [
  440. (ax_neg, nmax, r"Max |Cohen's d| along Z (female > male)", neg_cmap),
  441. (ax_pos, pmax, r"Max |Cohen's d| along Z (male > female)", pos_cmap),
  442. ]:
  443. divider = make_axes_locatable(ax)
  444. cax = divider.append_axes("right", size="3%", pad=0.05)
  445. mappable = plt.cm.ScalarMappable(norm=Normalize(0, vmax), cmap=cmap)
  446. cb = fig.colorbar(mappable, cax=cax, orientation="vertical")
  447. cb.set_label(label, rotation=90, labelpad=6)
  448. cb.outline.set_visible(False)
  449. cb.ax.tick_params(length=3, width=0)
  450. cb.ax.set_facecolor("none")
  451. if out_pdf is not None:
  452. plt.savefig(out_pdf, dpi=300, bbox_inches="tight")
  453. plt.show()
  454. # %%
  455. show_axial_split_male_female_vertical(
  456. d_vox,
  457. brain_mask=masks_np != 0,
  458. pos_color="#D08770",
  459. neg_color="#5E81AC",
  460. clip_pct=99.0,
  461. crop_to_mask=True,
  462. pad=2,
  463. out_pdf="cohensd_volume.pdf",
  464. )
  465. # %%

Fig5.ipynb at commit c855337, under CC-BY-NC-SA-4.0 · at the source

Overview

Authors: Mykola Kadobianskyi1, Jörg Henninger1, Daniil Markov1, Antonia Groneberg1, Johannes Veith1, Marc Renz1, Kutay Deniz Atabay2, Peter W. Reddien2, Leonard Maler3, Benjamin Judkewitz1
  1. Charité Universitätsmedizin, Berlin, Germany
  2. Massachusetts Institute of Technology, Cambridge, MA, USA
  3. University of Ottawa, Ottawa, Canada
Dates: published online 11 March 2026
Type: Preprint
License: CC BY-NC-ND
Identifiers: DOI 10.64898/2026.03.09.710483 · OpenAlex W7135014889
Open access: green, a free copy (OpenAlex)
Status: code verified
Methods: Connectivity, Spectral & time-frequency, Statistics, Machine learning
Keywords: Brain atlas, Danionella, calcium imaging, in situ hybridization
Topic: Retinal Development and Disorders (Molecular Biology, Biochemistry, Genetics and Molecular Biology), according to OpenAlex
Funding: European Research Council (101043615)
Citations: cited by 1 paper (Europe PMC); 94 references in the paper

Abstract

The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.

Repository

Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.

gin.g-node.org/danionella/kadobianskyi_et_al_2026

License: CC-BY-NC-SA-4.0
State: the link answers, verified on 30 September 2026
Evidence: files inventoried
Commit: c855337bd33084103fa3410d6f3c1a313fec388b, 16 July 2026
Languages: Jupyter (2)
Size: 27 files, 2 scripts
Software Heritage: not archived
Found in: “Data availability”
Holds: README, license file, 2 notebooks
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: ANTs (2 files), Matplotlib (2 files), NumPy (2 files), pandas (2 files), SciPy (2 files), statsmodels (2 files), seaborn (1 file)
Availability: 1 check, the latest on 30 September 2026: the link answers
  • 30 September 2026: the link answers
4 files

The paper's code and data availability statement is in the Data section.

Tracing map

Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.

What the map holds:

  • 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
  • 2 scripts, each with its path and the digest of its content;
  • 2 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

Code and data availability statement

The paper has a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:

Read it in the paper: doi.org/10.64898/2026.03.09.710483.

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, 30 September 2026: the first record

Recorded: type, journal, dates, 10 authors, 4 keywords, 1 funder, 94 references.

Cite

This paper

Kadobianskyi, M., Henninger, J., Markov, D., Groneberg, A., Veith, J., Renz, M., Atabay, K. D., Reddien, P. W., Maler, L., & Judkewitz, B. (2026). Multimodal reference brain atlas of adult <i>Danionella cerebrum</i>. bioRxiv (preprint). https://doi.org/10.64898/2026.03.09.710483

BibTeX

@article{kadobianskyi2026multimodal,
author = {Kadobianskyi, Mykola and Henninger, Jörg and Markov, Daniil and Groneberg, Antonia and Veith, Johannes and Renz, Marc and Atabay, Kutay Deniz and Reddien, Peter W. and Maler, Leonard and Judkewitz, Benjamin},
title = {{Multimodal reference brain atlas of adult <i>Danionella cerebrum</i>}},
journal = {bioRxiv (preprint)},
year = {2026},
month = mar,
publisher = {bioRxiv},
issn = {2692-8205},
doi = {10.64898/2026.03.09.710483},
url = {https://doi.org/10.64898/2026.03.09.710483}
}

RIS

TY - JOUR
AU - Kadobianskyi, Mykola
AU - Henninger, Jörg
AU - Markov, Daniil
AU - Groneberg, Antonia
AU - Veith, Johannes
AU - Renz, Marc
AU - Atabay, Kutay Deniz
AU - Reddien, Peter W.
AU - Maler, Leonard
AU - Judkewitz, Benjamin
TI - Multimodal reference brain atlas of adult <i>Danionella cerebrum</i>
T2 - bioRxiv (preprint)
J2 - bioRxiv
PY - 2026
DA - 2026/03/11
SN - 2692-8205
PB - bioRxiv
DO - 10.64898/2026.03.09.710483
UR - https://doi.org/10.64898/2026.03.09.710483
ER -

CSL-JSON

{
"id": "10.64898/2026.03.09.710483",
"type": "article",
"title": "Multimodal reference brain atlas of adult <i>Danionella cerebrum</i>",
"container-title": "bioRxiv (preprint)",
"author": [
{
"family": "Kadobianskyi",
"given": "Mykola"
},
{
"family": "Henninger",
"given": "Jörg"
},
{
"family": "Markov",
"given": "Daniil"
},
{
"family": "Groneberg",
"given": "Antonia"
},
{
"family": "Veith",
"given": "Johannes"
},
{
"family": "Renz",
"given": "Marc"
},
{
"family": "Atabay",
"given": "Kutay Deniz"
},
{
"family": "Reddien",
"given": "Peter W."
},
{
"family": "Maler",
"given": "Leonard"
},
{
"family": "Judkewitz",
"given": "Benjamin"
}
],
"container-title-short": "bioRxiv",
"DOI": "10.64898/2026.03.09.710483",
"ISSN": "2692-8205",
"publisher": "bioRxiv",
"URL": "https://doi.org/10.64898/2026.03.09.710483",
"issued": {
"date-parts": [
[
2026,
3,
11
]
]
}
}

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.64898/2026.03.04.709502 [code]
Distributed hierarchical filters partition the Danionella vocal repertoire
Journal: bioRxiv (preprint)
In common: ANTs, SciPy, Matplotlib, 1 other tool, 3 references, 2 authors
[2] doi:10.1038/s41467-026-69633-4 [code]
Visuomotor decision-making through multifeature convergence in the larval zebrafish hindbrain.
Journal: Nature communications
In common: seaborn, pandas, SciPy, 2 other tools, 4 references
[3] doi:10.1038/s41592-026-03179-7 [code]
Voltage imaging of neurons distributed across entire brains of larval zebrafish.
Journal: Nature methods
In common: SciPy, Matplotlib, NumPy, optical imaging (calcium, voltage, 2-photon), histology / microscopy, 3 references
[4] doi:10.1126/sciadv.aec6922 [code]
A sensorimotor instability drives a locomotor transition during fish development.
Journal: Science advances
In common: 5 references
[5] doi:10.1016/j.isci.2026.116206 [code]
Gut distension evokes rapid neural dynamics in vagal and hindbrain populations of larval zebrafish.
Journal: iScience
In common: statsmodels, seaborn, pandas, 3 other tools, optical imaging (calcium, voltage, 2-photon), 2 references
[6] doi:10.3390/cancers18162636 [code]
Exploring the Impact of T2-Weighted MRI Fat Saturation on Radiomics Stability for Brain Radionecrosis Prediction After Skull-Base Proton Therapy: A Pilot Study.
Journal: Cancers
In common: ANTs, statsmodels, seaborn, 4 other tools, 1 reference
[7] doi:10.1162/netn.a.547 [code]
An evaluation of the efficacy of single-echo and multi-echo fMRI denoising strategies.
Journal: Network neuroscience (Cambridge, Mass.)
In common: ANTs, statsmodels, seaborn, 4 other tools, 1 reference
[8] doi:10.1038/s41593-026-02376-z [code]
A framework for comparative analysis of human and mouse cortical neuron dendrites in corresponding brain regions.
Journal: Nature neuroscience
In common: statsmodels, seaborn, pandas, 3 other tools, 2 references
[9] doi:10.1002/alz.71649 [code]
Postmortem brain MRI reveals differential associations of subcortical and limbic volumes with cortical thinning and neurodegenerative pathologies.
Journal: Alzheimer's & dementia : the journal of the Alzheimer's Association
In common: ANTs, statsmodels, seaborn, 4 other tools, histology / microscopy
[10] doi:10.1162/imag.a.1362 [code]
Human fMRI at 11.7T: Assessing feasibility, stability, and reliability on the Iseult scanner.
Journal: Imaging neuroscience (Cambridge, Mass.)
In common: ANTs, statsmodels, seaborn, 4 other tools, methods / tools

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.

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.