Skip to content

Commit ce76346

Browse files
committed
Update figures
1 parent 30339b3 commit ce76346

File tree

4 files changed

+184
-45
lines changed

4 files changed

+184
-45
lines changed

scripts/figures/plot_fig2.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import argparse
22
import os
3-
from glob import glob
43

54
import numpy as np
65
import pandas as pd
@@ -100,7 +99,9 @@ def fig_02c(save_path, plot=False, all_versions=False):
10099

101100
# Load the synapse counts for all IHCs from the relevant tables.
102101
def _load_ribbon_synapse_counts():
103-
tables = glob("ihc_counts/*M_LR*.tsv")
102+
ihc_version = "ihc_counts_v4c"
103+
synapse_dir = f"/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/predictions/synapses/{ihc_version}"
104+
tables = [entry.path for entry in os.scandir(synapse_dir) if "ihc_count_M_LR" in entry.name]
104105
syn_counts = []
105106
for tab in tables:
106107
x = pd.read_csv(tab, sep="\t")
@@ -141,9 +142,9 @@ def fig_02d_01(save_path, plot=False, all_versions=False, plot_average_ribbon_sy
141142
# Labels and formatting
142143
ax[0].set_xticklabels(["SGN"], fontsize=main_label_size)
143144

144-
ylim0 = 9500
145+
ylim0 = 8500
145146
ylim1 = 12500
146-
y_ticks = [i for i in range(10000, 12000 + 1, 1000)]
147+
y_ticks = [i for i in range(9000, 12000 + 1, 1000)]
147148

148149
ax[0].set_ylabel("Count per cochlea", fontsize=main_label_size)
149150
ax[0].set_yticks(y_ticks)
@@ -157,12 +158,12 @@ def fig_02d_01(save_path, plot=False, all_versions=False, plot_average_ribbon_sy
157158
ax[0].set_xlim(xmin, xmax)
158159
lower_y, upper_y = literature_reference_values("SGN")
159160
ax[0].hlines([lower_y, upper_y], xmin, xmax)
160-
ax[0].text(1, upper_y + 100, "literature", color="C0", fontsize=main_tick_size, ha="center")
161+
ax[0].text(1.1, (lower_y + upper_y) // 2, "literature", color="C0", fontsize=main_tick_size, ha="left")
161162
ax[0].fill_between([xmin, xmax], lower_y, upper_y, color="C0", alpha=0.05, interpolate=True)
162163

163-
ylim0 = 500
164-
ylim1 = 950
165-
y_ticks = [i for i in range(500, 900 + 1, 100)]
164+
ylim0 = 600
165+
ylim1 = 800
166+
y_ticks = [i for i in range(600, 800 + 1, 100)]
166167

167168
ax[1].set_xticklabels(["IHC"], fontsize=main_label_size)
168169
ax[1].set_yticks(y_ticks)
@@ -178,28 +179,28 @@ def fig_02d_01(save_path, plot=False, all_versions=False, plot_average_ribbon_sy
178179
lower_y, upper_y = literature_reference_values("IHC")
179180
ax[1].set_xlim(xmin, xmax)
180181
ax[1].hlines([lower_y, upper_y], xmin, xmax)
181-
ax[1].text(1, upper_y + 20, "literature", color="C0", fontsize=main_tick_size, ha="center")
182+
ax[1].text(1.1, (lower_y + upper_y) // 2, "literature", color="C0", fontsize=main_tick_size, ha="left")
182183
ax[1].fill_between([xmin, xmax], lower_y, upper_y, color="C0", alpha=0.05, interpolate=True)
183184

184185
if plot_average_ribbon_synapses:
185186
ribbon_synapse_counts = _load_ribbon_synapse_counts()
186-
# ylim0 = 4.9
187-
# ylim1 = 25.1
187+
ylim0 = -1
188+
ylim1 = 41
188189
y_ticks = [0, 10, 20, 30, 40, 50]
189190

190191
ax[2].boxplot(ribbon_synapse_counts)
191192
ax[2].set_xticklabels(["Ribbon Syn. per IHC"], fontsize=main_label_size)
192193
ax[2].set_yticks(y_ticks)
193194
ax[2].set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
194-
# ax[2].set_ylim(ylim0, ylim1)
195+
ax[2].set_ylim(ylim0, ylim1)
195196

196197
# set range of literature values
197198
xmin = 0.5
198199
xmax = 1.5
199200
lower_y, upper_y = literature_reference_values("synapse")
200201
ax[2].set_xlim(xmin, xmax)
201202
ax[2].hlines([lower_y, upper_y], xmin, xmax)
202-
ax[2].text(1, upper_y + 2, "literature", color="C0", fontsize=main_tick_size, ha="center")
203+
ax[2].text(1.1, (lower_y + upper_y) // 2, "literature", color="C0", fontsize=main_tick_size, ha="left")
203204
ax[2].fill_between([xmin, xmax], lower_y, upper_y, color="C0", alpha=0.05, interpolate=True)
204205

205206
plt.tight_layout()

scripts/figures/plot_fig4.py

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,9 @@ def get_chreef_data():
7171
chreef_data[cochlea] = values
7272

7373
with open(cache_path, "wb") as f:
74-
chreef_data = pickle.dump(chreef_data, f)
75-
return chreef_data
74+
pickle.dump(chreef_data, f)
75+
with open(cache_path, "rb") as f:
76+
return pickle.load(f)
7677

7778

7879
def group_lr(names_lr, values):
@@ -143,7 +144,7 @@ def fig_04c(chreef_data, save_path, plot=False, plot_by_side=False):
143144
plt.xlim(xmin, xmax)
144145
lower_y, upper_y = literature_reference_values("SGN")
145146
plt.hlines([lower_y, upper_y], xmin, xmax)
146-
plt.text(1, lower_y - 400, "literature", color="C0", fontsize=main_tick_size, ha="center")
147+
plt.text(1.5, lower_y - 400, "literature", color="C0", fontsize=main_tick_size, ha="center")
147148
plt.fill_between([xmin, xmax], lower_y, upper_y, color="C0", alpha=0.05, interpolate=True)
148149

149150
sgn_values = [11153, 11398, 10333, 11820]
@@ -154,12 +155,12 @@ def fig_04c(chreef_data, save_path, plot=False, plot_by_side=False):
154155
lower_y = sgn_value - 1.96 * sgn_std
155156

156157
plt.hlines([lower_y, upper_y], xmin, xmax, colors=["C1" for _ in range(2)])
157-
plt.text(1, upper_y + 100, "healthy cochleae (95% confidence interval)",
158+
plt.text(1.5, upper_y + 100, "healthy cochleae (95% confidence interval)",
158159
color="C1", fontsize=main_tick_size, ha="center")
159160
plt.fill_between([xmin, xmax], lower_y, upper_y, color="C1", alpha=0.05, interpolate=True)
160161

161-
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)
162162
plt.tight_layout()
163+
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)
163164

164165
if plot:
165166
plt.show()
@@ -179,12 +180,10 @@ def fig_04d(chreef_data, save_path, plot=False, plot_by_side=False, intensity=Fa
179180
intensities = vals["median"].values
180181
values.append(intensities.mean())
181182
else:
182-
# The marker labels don't make sense yet, they are in
183-
# 0: unlabeled
183+
# marker labels
184+
# 0: unlabeled - no median intensity in object_measures table
184185
# 1: positive
185186
# 2: negative
186-
# but they should all be either positive or negative.
187-
# Or am I missing something?
188187
marker_labels = vals["marker_labels"].values
189188
n_pos = (marker_labels == 1).sum()
190189
n_neg = (marker_labels == 2).sum()
@@ -222,8 +221,8 @@ def fig_04d(chreef_data, save_path, plot=False, plot_by_side=False, intensity=Fa
222221
if not intensity:
223222
plt.ylim(0.5, 1.05)
224223

225-
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)
226224
plt.tight_layout()
225+
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)
227226

228227
if plot:
229228
plt.show()
@@ -255,30 +254,76 @@ def fig_04e(chreef_data, save_path, plot, intensity=False):
255254
band_to_x = {band: i for i, band in enumerate(bin_labels)}
256255
result["x_pos"] = result["octave_band"].map(band_to_x)
257256

258-
fig, axes = plt.subplots(1, 2, figsize=(8, 4), sharex=True, sharey=True)
257+
fig, ax = plt.subplots(figsize=(8, 4))
258+
259+
sub_tick_label_size = 8
260+
tick_label_size = 12
261+
label_size = 12
262+
legend_size = 8
263+
if intensity:
264+
band_label_offset_y = 0.09
265+
else:
266+
band_label_offset_y = 0.07
267+
ax.set_ylim(0.45, 1.05)
268+
269+
# Offsets within each octave band
270+
offset_map = {"L": -0.15, "R": 0.15}
271+
sublabels = {"L": "L", "R": "R"}
272+
273+
# Assign a color to each cochlea (ignoring side)
274+
cochleas = sorted({name_lr[:-1] for name_lr in result["cochlea"].unique()})
275+
colors = plt.cm.tab10.colors # pick a colormap
276+
color_map = {cochlea: colors[i % len(colors)] for i, cochlea in enumerate(cochleas)}
277+
278+
# Track which cochlea names we have already added to the legend
279+
legend_added = set()
280+
281+
all_x_positions = []
282+
all_x_labels = []
283+
259284
for name_lr, grp in result.groupby("cochlea"):
260285
name, side = name_lr[:-1], name_lr[-1]
261-
ax, marker = (axes[0], "o") if side == "L" else (axes[1], "x")
262-
ax.scatter(grp["x_pos"], grp["value"], label=name, s=60, alpha=0.8, marker=marker)
263-
264-
for ax in axes:
265-
ax.set_xticks(range(len(bin_labels)))
266-
ax.set_xticklabels(bin_labels)
267-
ax.set_xlabel("Octave band (kHz)")
286+
x_positions = grp["x_pos"] + offset_map[side]
287+
ax.scatter(
288+
x_positions,
289+
grp["value"],
290+
label=name if name not in legend_added else None,
291+
s=60,
292+
alpha=0.8,
293+
marker="o" if side == "L" else "x",
294+
color=color_map[name]
295+
)
296+
if name not in legend_added:
297+
legend_added.add(name)
298+
299+
# Store for sublabel ticks
300+
all_x_positions.extend(x_positions)
301+
all_x_labels.extend([sublabels[side]] * len(x_positions))
302+
303+
# Create combined tick positions & labels
304+
main_ticks = range(len(bin_labels))
305+
# add a final tick for label '>64k'
306+
ax.set_xticks([pos + offset_map["L"] for pos in main_ticks[:-1]] +
307+
[pos + offset_map["R"] for pos in main_ticks[:-1]] +
308+
[pos for pos in main_ticks[-1:]])
309+
ax.set_xticklabels(["L"] * len(main_ticks[:-1]) + ["R"] * len(main_ticks[:-1]) + [""], fontsize=sub_tick_label_size)
310+
311+
# Add main octave band labels above sublabels
312+
for i, label in enumerate(bin_labels):
313+
ax.text(i, ax.get_ylim()[0] - band_label_offset_y*(ax.get_ylim()[1]-ax.get_ylim()[0]),
314+
label, ha='center', va='top', fontsize=tick_label_size, fontweight='bold')
315+
316+
ax.set_xlabel("Octave band (kHz)", fontsize=label_size)
317+
ax.xaxis.set_label_coords(.5, -.16)
268318

269319
if intensity:
270-
axes[0].set_ylabel("Marker Intensity")
271-
for ax, side in zip(axes, ("Left", "Right")):
272-
ax.set_title(f"Intensity per octave band ({side})")
320+
ax.set_ylabel("Marker Intensity", fontsize=label_size)
321+
ax.set_title("Intensity per octave band (Left/Right)")
273322
else:
274-
axes[0].set_ylabel("Transduction Efficiency")
275-
axes[0].set_ylim(0.5, 1.05)
276-
for ax, side in zip(axes, ("Left", "Right")):
277-
ax.set_title(f"Transduction efficiency per octave band ({side})")
278-
279-
# FIXME make this uniform across the plots!
280-
axes[0].legend(title="Cochlea")
281-
axes[1].legend(title="Cochlea")
323+
ax.set_ylabel("Transduction Efficiency", fontsize=label_size)
324+
ax.set_title("Transduction efficiency per octave band (Left/Right)")
325+
326+
ax.legend(title="Cochlea", fontsize=legend_size)
282327
plt.tight_layout()
283328

284329
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)

scripts/figures/plot_fig5.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
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+
from util import literature_reference_values
9+
10+
png_dpi = 300
11+
12+
13+
def _load_ribbon_synapse_counts():
14+
ihc_version = "ihc_counts_v4b"
15+
synapse_dir = f"/mnt/vast-nhr/projects/nim00007/data/moser/cochlea-lightsheet/predictions/synapses/{ihc_version}"
16+
tables = [entry.path for entry in os.scandir(synapse_dir) if "ihc_count_M_AMD" in entry.name]
17+
syn_counts = []
18+
for tab in tables:
19+
x = pd.read_csv(tab, sep="\t")
20+
syn_counts.extend(x["synapse_count"].values.tolist())
21+
return syn_counts
22+
23+
24+
def fig_05c(save_path, plot=False):
25+
"""Bar plot showing the distribution of synapse markers per IHC segmentation average over OTOF cochlea.
26+
"""
27+
28+
main_label_size = 20
29+
main_tick_size = 12
30+
htext_size = 10
31+
32+
ribbon_synapse_counts = _load_ribbon_synapse_counts()
33+
34+
fig, ax = plt.subplots(figsize=(8, 4))
35+
36+
ylim0 = -1
37+
ylim1 = 24
38+
y_ticks = [i for i in range(0, 25, 5)]
39+
40+
ax.set_ylabel("Ribbon Syn. per IHC", fontsize=main_label_size)
41+
ax.set_yticks(y_ticks)
42+
ax.set_yticklabels(y_ticks, rotation=0, fontsize=main_tick_size)
43+
ax.set_ylim(ylim0, ylim1)
44+
45+
ax.boxplot(ribbon_synapse_counts)
46+
ax.set_xticklabels(["MOTOF1L"], fontsize=main_label_size)
47+
48+
# set range of literature values
49+
xmin = 0.5
50+
xmax = 1.5
51+
lower_y, upper_y = literature_reference_values("synapse")
52+
ax.set_xlim(xmin, xmax)
53+
ax.hlines([lower_y, upper_y], xmin, xmax)
54+
ax.text(1.25, upper_y + 0.01*ax.get_ylim()[1]-ax.get_ylim()[0], "literature",
55+
color="C0", fontsize=htext_size, ha="center")
56+
ax.fill_between([xmin, xmax], lower_y, upper_y, color="C0", alpha=0.05, interpolate=True)
57+
58+
ihc_values = [14.1, 12.7, 13.8, 13.4] # MLR226L, MLR226R, MLR227L, MLR227R
59+
60+
ihc_value = np.mean(ihc_values)
61+
ihc_std = np.std(ihc_values)
62+
63+
upper_y = ihc_value + 1.96 * ihc_std
64+
lower_y = ihc_value - 1.96 * ihc_std
65+
66+
plt.hlines([lower_y, upper_y], xmin, xmax, colors=["C1" for _ in range(2)])
67+
plt.text(1.25, upper_y + 0.01*ax.get_ylim()[1]-ax.get_ylim()[0], "healthy cochleae (95% confidence interval)",
68+
color="C1", fontsize=htext_size, ha="center")
69+
plt.fill_between([xmin, xmax], lower_y, upper_y, color="C1", alpha=0.05, interpolate=True)
70+
71+
plt.tight_layout()
72+
plt.savefig(save_path, bbox_inches="tight", pad_inches=0.1, dpi=png_dpi)
73+
74+
if plot:
75+
plt.show()
76+
else:
77+
plt.close()
78+
79+
80+
def main():
81+
parser = argparse.ArgumentParser(description="Generate plots for Fig 5 of the cochlea paper.")
82+
parser.add_argument("--figure_dir", "-f", type=str, help="Output directory for plots.", default="./panels/fig5")
83+
parser.add_argument("--plot", action="store_true")
84+
args = parser.parse_args()
85+
86+
os.makedirs(args.figure_dir, exist_ok=True)
87+
88+
# Panel C: Monitoring of the Syn / IHC loss
89+
fig_05c(save_path=os.path.join(args.figure_dir, "fig_05c"), plot=args.plot)
90+
91+
92+
if __name__ == "__main__":
93+
main()

scripts/figures/util.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,11 @@ def sliding_runlength_sum(run_length, values, width):
6363
# TODO determine these from Aleyna's table!
6464
def literature_reference_values(structure):
6565
if structure == "SGN":
66-
lower_bound, upper_bound = 10000, 12000
66+
lower_bound, upper_bound = 8728, 10325
6767
elif structure == "IHC":
68-
lower_bound, upper_bound = 780, 850
68+
lower_bound, upper_bound = 656, 681
6969
elif structure == "synapse":
70-
lower_bound, upper_bound = 10, 25
70+
lower_bound, upper_bound = 9.1, 20.7
7171
else:
7272
raise ValueError
7373
return lower_bound, upper_bound

0 commit comments

Comments
 (0)