Skip to content

Commit 2594690

Browse files
committed
Initial plots for publishing
1 parent c091e54 commit 2594690

File tree

6 files changed

+351
-0
lines changed

6 files changed

+351
-0
lines changed

scripts/figures/fig_02c.png

102 KB
Loading

scripts/figures/fig_02d_01.png

109 KB
Loading

scripts/figures/fig_02d_02.png

147 KB
Loading

scripts/figures/fig_04c.png

128 KB
Loading

scripts/figures/fig_06a.png

116 KB
Loading

scripts/figures/plot_figures.py

Lines changed: 351 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,351 @@
1+
import argparse
2+
import os
3+
4+
import numpy as np
5+
import pandas as pd
6+
import matplotlib.pyplot as plt
7+
8+
png_dpi = 300
9+
10+
11+
def fig_02c(save_path, plot=False):
12+
"""Scatter plot showing the precision, recall, and F1-score of SGN (distance U-Net, manual),
13+
IHC (distance U-Net, manual), and synapse segmentation (distance U-Net).
14+
"""
15+
settings = ['SGN', 'IHC', 'Synapse']
16+
settings = ['U-Net', 'manual', 'U-Net', 'manual', 'U-Net']
17+
18+
precision = [0.887, 0.95, 0.824, 0.958, 0.931]
19+
recall = [0.88, 0.849, 0.8866, 0.956, 0.905]
20+
f1score = [0.884, 0.9, 0.845, 0.957, 0.918]
21+
descr_y = 0.72
22+
23+
# Convert setting labels to numerical x positions
24+
x = np.array([0.8, 1.2, 1.8, 2.2, 3])
25+
offset = 0.08 # horizontal shift for scatter separation
26+
27+
# Plot
28+
plt.figure(figsize=(8, 5))
29+
30+
main_label_size = 20
31+
sub_label_size = 16
32+
main_tick_size = 12
33+
legendsize = 16
34+
35+
plt.scatter(x - offset, precision, label='Precision', marker="o", s=80)
36+
plt.scatter(x, recall, label='Recall', marker="^", s=80)
37+
plt.scatter(x + offset, f1score, label='F1-score', marker="*", s=80)
38+
39+
plt.text(1, descr_y, "SGN", fontsize=main_label_size, horizontalalignment="center")
40+
plt.text(2, descr_y, "IHC", fontsize=main_label_size, horizontalalignment="center")
41+
plt.text(3, descr_y, "Synapse", fontsize=main_label_size, horizontalalignment="center")
42+
43+
# Labels and formatting
44+
plt.xticks(x, settings, fontsize=sub_label_size)
45+
plt.yticks(fontsize=main_tick_size)
46+
plt.ylabel('Value', fontsize=main_label_size)
47+
plt.ylim(0.76, 1)
48+
plt.legend(loc="upper center", bbox_to_anchor=(0.5, 1.11),
49+
ncol=3, fancybox=True, shadow=False, framealpha=0.8, fontsize=legendsize)
50+
plt.grid(axis='y', linestyle='--', alpha=0.5)
51+
52+
plt.tight_layout()
53+
plt.savefig(save_path, bbox_inches='tight', pad_inches=0.1, dpi=png_dpi)
54+
55+
if plot:
56+
plt.show()
57+
else:
58+
plt.close()
59+
60+
61+
def fig_02d_01(save_path, plot=False):
62+
"""Box plot showing the counts for SGN and IHC per (mouse) cochlea in comparison to literature values.
63+
"""
64+
main_tick_size = 12
65+
main_label_size = 16
66+
67+
rows = 1
68+
columns = 2
69+
70+
fig, axes = plt.subplots(rows, columns, figsize=(columns*4, rows*4))
71+
ax = axes.flatten()
72+
73+
sgn_values = [11153, 11398, 10333, 11820]
74+
ihc_values = [836, 808, 796, 901]
75+
76+
ax[0].boxplot(sgn_values)
77+
ax[1].boxplot(ihc_values)
78+
79+
# Labels and formatting
80+
ax[0].set_xticklabels(["SGN"], fontsize=main_label_size)
81+
82+
ylim0 = 9500
83+
ylim1 = 12500
84+
y_ticks = [i for i in range(ylim0, ylim1 + 1, 500)]
85+
86+
ax[0].set_ylabel('Count per cochlea', fontsize=main_label_size)
87+
ax[0].set_yticks(y_ticks)
88+
ax[0].set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
89+
ax[0].set_ylim(ylim0, ylim1)
90+
91+
# set range of literature values
92+
xmin = 0.5
93+
xmax = 1.5
94+
ax[0].set_xlim(xmin, xmax)
95+
upper_y = 12000
96+
lower_y = 10000
97+
ax[0].hlines([lower_y, upper_y], xmin, xmax)
98+
ax[0].text(1, upper_y + 100, "literature reference (WIP)", color='C0', fontsize=main_tick_size, ha="center")
99+
ax[0].fill_between([xmin, xmax], lower_y, upper_y, color='C0', alpha=0.05, interpolate=True)
100+
101+
ylim0 = 750
102+
ylim1 = 950
103+
y_ticks = [i for i in range(ylim0, ylim1 + 1, 50)]
104+
105+
ax[1].set_xticklabels(["IHC"], fontsize=main_label_size)
106+
107+
ax[1].set_ylabel('Count per cochlea', fontsize=main_label_size)
108+
ax[1].set_yticks(y_ticks)
109+
ax[1].set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
110+
ax[1].set_ylim(ylim0, ylim1)
111+
112+
# set range of literature values
113+
xmin = 0.5
114+
xmax = 1.5
115+
ax[1].set_xlim(xmin, xmax)
116+
upper_y = 850
117+
lower_y = 780
118+
ax[1].hlines([lower_y, upper_y], xmin, xmax)
119+
ax[1].text(1, lower_y - 10, "literature reference (WIP)", color='C0', fontsize=main_tick_size, ha="center")
120+
ax[1].fill_between([xmin, xmax], lower_y, upper_y, color='C0', alpha=0.05, interpolate=True)
121+
122+
plt.tight_layout()
123+
plt.savefig(save_path, dpi=png_dpi)
124+
125+
if plot:
126+
plt.show()
127+
else:
128+
plt.close()
129+
130+
131+
def fig_02d_02(save_path, plot=False):
132+
"""Bar plot showing the distribution of synapse markers per IHC segmentation average over multiple clochleae.
133+
"""
134+
cochleae = ['M_LR_000226_L', 'M_LR_000226_R', 'M_LR_000227_L', 'M_LR_000227_R']
135+
ihc_version = "ihc_counts_v3"
136+
synapse_dir = f"/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/predictions/synapses/{ihc_version}"
137+
138+
max_dist = 3
139+
bins = 10
140+
cap = 30
141+
plot_density = False
142+
143+
results = []
144+
for cochlea in cochleae:
145+
synapse_file = os.path.join(synapse_dir, f"ihc_count_{cochlea}.tsv")
146+
ihc_table = pd.read_csv(synapse_file, sep="\t")
147+
syn_per_ihc = list(ihc_table["synapse_count"])
148+
results.append(syn_per_ihc)
149+
150+
results = [np.clip(r, 0, cap) for r in results]
151+
152+
# Define bins (shared for all datasets)
153+
bins = np.linspace(0, 30, 11) # 29 bins
154+
155+
# Compute histogram (relative) for each dataset
156+
histograms = []
157+
for data in results:
158+
counts, _ = np.histogram(data, bins=bins, density=plot_density)
159+
histograms.append(counts)
160+
161+
histograms = np.array(histograms)
162+
163+
# Compute mean and std for each bin across datasets
164+
mean_counts = histograms.mean(axis=0)
165+
std_counts = histograms.std(axis=0)
166+
167+
# Get bin centers for plotting
168+
bin_centers = 0.5 * (bins[1:] + bins[:-1])
169+
170+
# Plot
171+
plt.figure(figsize=(8, 5))
172+
plt.bar(bin_centers, mean_counts, width=(bins[1] - bins[0]), yerr=std_counts, alpha=0.7, capsize=4,
173+
label='Mean ± Std Dev', edgecolor="black")
174+
175+
main_label_size = 20
176+
main_tick_size = 16
177+
legendsize = 16
178+
179+
# Labels and formatting
180+
x_ticks = [i for i in range(0, cap + 1, 5)]
181+
if plot_density:
182+
y_ticks = [i * 0.02 for i in range(0, 10, 2)]
183+
else:
184+
y_ticks = [i for i in range(0, 300, 50)]
185+
186+
plt.xticks(x_ticks, fontsize=main_tick_size)
187+
plt.yticks(y_ticks, fontsize=main_tick_size)
188+
plt.ylabel('Proportion of IHCs [%]', fontsize=main_label_size)
189+
plt.xlabel(f"Ribbon Synapses per IHC @ {max_dist} µm", fontsize=main_label_size)
190+
191+
plt.title('Average Synapses per IHC for a Dataset of 4 Cochleae')
192+
193+
plt.grid(axis='y', linestyle='--', alpha=0.5)
194+
plt.legend(fontsize=legendsize)
195+
plt.tight_layout()
196+
plt.savefig(save_path, bbox_inches='tight', pad_inches=0.1, dpi=png_dpi)
197+
198+
if plot:
199+
plt.show()
200+
else:
201+
plt.close()
202+
203+
204+
def fig_04c(save_path, plot=False):
205+
"""Box plot showing the SGN counts of ChReef treated cochleae compared to untreated ones.
206+
"""
207+
# cochlea = ["M_LR_000144_L", "M_LR_000145_L", "M_LR_000151_R"]
208+
alias = ['c01', 'c02', 'c03']
209+
210+
sgns = [7796, 6119, 9225]
211+
212+
x = np.arange(len(alias))
213+
214+
# Plot
215+
plt.figure(figsize=(8, 5))
216+
217+
main_label_size = 20
218+
sub_label_size = 16
219+
main_tick_size = 12
220+
legendsize = 16
221+
222+
plt.scatter(x, sgns, label='SGN count', marker="o", s=80)
223+
224+
# Labels and formatting
225+
plt.xticks(x, alias, fontsize=sub_label_size)
226+
plt.xlabel('Cochleae', fontsize=main_label_size)
227+
plt.yticks(fontsize=main_tick_size)
228+
plt.ylabel('SGN count per cochlea', fontsize=main_label_size)
229+
plt.ylim(4000, 13800)
230+
plt.legend(loc="best", fontsize=sub_label_size)
231+
plt.legend(loc="upper center", bbox_to_anchor=(0.5, 1.11),
232+
ncol=3, fancybox=True, shadow=False, framealpha=0.8, fontsize=legendsize)
233+
234+
# set range of literature values
235+
xmin = -0.5
236+
xmax = 2.5
237+
plt.xlim(xmin, xmax)
238+
upper_y = 12000
239+
lower_y = 10000
240+
plt.hlines([lower_y, upper_y], xmin, xmax)
241+
plt.text(1, lower_y - 400, "literature reference (WIP)", color='C0', fontsize=main_tick_size, ha="center")
242+
plt.fill_between([xmin, xmax], lower_y, upper_y, color='C0', alpha=0.05, interpolate=True)
243+
244+
sgn_values = [11153, 11398, 10333, 11820]
245+
sgn_value = np.mean(sgn_values)
246+
sgn_std = np.std(sgn_values)
247+
248+
upper_y = sgn_value + 1.96 * sgn_std
249+
lower_y = sgn_value - 1.96 * sgn_std
250+
251+
plt.hlines([lower_y, upper_y], xmin, xmax, colors=['C1' for _ in range(2)])
252+
plt.text(1, upper_y + 100, "untreated cochleae (95% confidence interval)",
253+
color='C1', fontsize=main_tick_size, ha="center")
254+
plt.fill_between([xmin, xmax], lower_y, upper_y, color='C1', alpha=0.05, interpolate=True)
255+
256+
plt.savefig(save_path, bbox_inches='tight', pad_inches=0.1, dpi=png_dpi)
257+
plt.tight_layout()
258+
259+
if plot:
260+
plt.show()
261+
else:
262+
plt.close()
263+
264+
265+
def fig_06a(save_path, plot=False):
266+
"""Box plot showing the counts for SGN and IHC per gerbil cochlea in comparison to literature values.
267+
"""
268+
main_tick_size = 12
269+
main_label_size = 16
270+
271+
rows = 1
272+
columns = 2
273+
274+
fig, axes = plt.subplots(rows, columns, figsize=(columns*4, rows*4))
275+
ax = axes.flatten()
276+
277+
sgn_values = [20050, 21995]
278+
ihc_values = [1100]
279+
280+
ax[0].boxplot(sgn_values)
281+
ax[1].boxplot(ihc_values)
282+
283+
# Labels and formatting
284+
ax[0].set_xticklabels(["SGN"], fontsize=main_label_size)
285+
286+
ylim0 = 12000
287+
ylim1 = 22500
288+
y_ticks = [i for i in range(ylim0, ylim1 + 1, 2000)]
289+
290+
ax[0].set_ylabel('Count per cochlea', fontsize=main_label_size)
291+
ax[0].set_yticks(y_ticks)
292+
ax[0].set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
293+
ax[0].set_ylim(ylim0, ylim1)
294+
295+
# set range of literature values
296+
xmin = 0.5
297+
xmax = 1.5
298+
ax[0].set_xlim(xmin, xmax)
299+
upper_y = 15000
300+
lower_y = 13000
301+
ax[0].hlines([lower_y, upper_y], xmin, xmax)
302+
ax[0].text(1, upper_y + 100, "literature reference (WIP)", color='C0', fontsize=main_tick_size, ha="center")
303+
ax[0].fill_between([xmin, xmax], lower_y, upper_y, color='C0', alpha=0.05, interpolate=True)
304+
305+
ylim0 = 800
306+
ylim1 = 1400
307+
y_ticks = [i for i in range(ylim0, ylim1 + 1, 100)]
308+
309+
ax[1].set_xticklabels(["IHC"], fontsize=main_label_size)
310+
311+
ax[1].set_ylabel('Count per cochlea', fontsize=main_label_size)
312+
ax[1].set_yticks(y_ticks)
313+
ax[1].set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
314+
ax[1].set_ylim(ylim0, ylim1)
315+
316+
# set range of literature values
317+
xmin = 0.5
318+
xmax = 1.5
319+
ax[1].set_xlim(xmin, xmax)
320+
upper_y = 1200
321+
lower_y = 1000
322+
ax[1].hlines([lower_y, upper_y], xmin, xmax)
323+
ax[1].text(1, upper_y + 10, "literature reference (WIP)", color='C0', fontsize=main_tick_size, ha="center")
324+
ax[1].fill_between([xmin, xmax], lower_y, upper_y, color='C0', alpha=0.05, interpolate=True)
325+
326+
plt.tight_layout()
327+
plt.savefig(save_path, dpi=png_dpi)
328+
if plot:
329+
plt.show()
330+
else:
331+
plt.close()
332+
333+
334+
def main():
335+
parser = argparse.ArgumentParser(
336+
description="Generate plots for lightsheet cochlea paper.")
337+
338+
parser.add_argument('figure_dir', type=str, help="Output directory for plots.")
339+
340+
args = parser.parse_args()
341+
plot = False
342+
343+
fig_02c(save_path=os.path.join(args.figure_dir, "fig_02c"), plot=plot)
344+
fig_02d_01(save_path=os.path.join(args.figure_dir, "fig_02d_01"), plot=plot)
345+
fig_02d_02(save_path=os.path.join(args.figure_dir, "fig_02d_02"), plot=plot)
346+
fig_04c(save_path=os.path.join(args.figure_dir, "fig_04c"), plot=plot)
347+
fig_06a(save_path=os.path.join(args.figure_dir, "fig_06a"), plot=plot)
348+
349+
350+
if __name__ == "__main__":
351+
main()

0 commit comments

Comments
 (0)