BoHuangLab commited on
Commit
2b3e69d
·
verified ·
1 Parent(s): 0167225

Diagram: ViT label, larger in-box type, AUC/AAC anchored in their regions

Browse files
Files changed (1) hide show
  1. app.py +25 -16
app.py CHANGED
@@ -92,7 +92,7 @@ def _schematic_curve():
92
  """A reentrant titration curve used only for the diagram's thumbnails."""
93
  x = pipeline.intensity_ladder(pipeline.NUM_IMAGES).numpy()
94
  lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
95
- p = np.clip(1.55 * np.exp(-((lx - 0.52) ** 2) / (2 * 0.17 ** 2)) - 0.16, 0, 1)
96
  calls = (np.random.default_rng(0).random(p.size) < p).astype(int)
97
  curve = metrics.moving_average(calls, pipeline.NUM_IMAGES // pipeline.WINDOW_DIVISOR)
98
  return x, curve
@@ -111,8 +111,8 @@ def plot_explainer():
111
  stage = fig.add_axes([0, 0, 1, 1]); stage.set_axis_off()
112
  stage.set_xlim(0, 1); stage.set_ylim(0, 1)
113
 
114
- xs = [0.098, 0.368, 0.632, 0.902]
115
- box_y, box_h, box_w = 0.30, 0.50, 0.155
116
 
117
  def node(cx, title, caption):
118
  stage.add_patch(FancyBboxPatch(
@@ -120,17 +120,17 @@ def plot_explainer():
120
  boxstyle="round,pad=0.012,rounding_size=0.02",
121
  linewidth=1.1, edgecolor=MUTED, facecolor="white", zorder=2))
122
  stage.text(cx, box_y + box_h + 0.06, title, ha="center", va="bottom",
123
- fontsize=11, color=INK, fontweight="bold", zorder=4)
124
  if caption:
125
  stage.text(cx, box_y - 0.05, caption, ha="center", va="top",
126
- fontsize=8.2, color=MUTED, linespacing=1.5, zorder=4)
127
 
128
  def arrow(x0, x1, label):
129
  stage.annotate("", xy=(x1, box_y + box_h / 2), xytext=(x0, box_y + box_h / 2),
130
  arrowprops=dict(arrowstyle="-|>", lw=1.6, color=INK,
131
  shrinkA=0, shrinkB=0), zorder=3)
132
  stage.text((x0 + x1) / 2, box_y + box_h / 2 + 0.045, label, ha="center",
133
- va="bottom", fontsize=8.5, color=CURVE, fontweight="bold",
134
  linespacing=1.35, zorder=4)
135
 
136
  node(xs[0], "IDP sequence\n+ expression level", f"{pipeline.SEQUENCE_LENGTH} aa, scanned over\n{pipeline.NUM_IMAGES} concentrations")
@@ -139,14 +139,14 @@ def plot_explainer():
139
  node(xs[3], "AUC, AAC", None)
140
 
141
  arrow(xs[0] + box_w / 2, xs[1] - box_w / 2, "CELL-FM")
142
- arrow(xs[1] + box_w / 2, xs[2] - box_w / 2, "ViT classification\nmodel")
143
  arrow(xs[2] + box_w / 2, xs[3] - box_w / 2, "integrate")
144
 
145
  # the last node's caption replaces the bullets that used to sit under the title
146
  stage.text(xs[3], box_y - 0.05, "AUC — range spent condensed", ha="center", va="top",
147
- fontsize=8.2, color=CURVE, fontweight="bold", zorder=4)
148
  stage.text(xs[3], box_y - 0.16, "AAC — condensation lost to\nreentrant dissolution",
149
- ha="center", va="top", fontsize=8.2, color=GAP, fontweight="bold",
150
  linespacing=1.5, zorder=4)
151
 
152
  def panel(cx, pad_x=0.020, pad_y=0.055):
@@ -164,11 +164,11 @@ def plot_explainer():
164
 
165
  # 1. inputs: the sequence, and the concentration ladder it is scanned over
166
  a = panel(xs[0]); a.set_axis_off(); a.set_xlim(0, 1); a.set_ylim(0, 1)
167
- a.text(0.5, 0.80, "KSFGTPFGGG…", ha="center", va="center", fontsize=8.5,
168
  family="monospace", color=INK)
169
  a.imshow(np.linspace(0, 1, 128)[None, :], cmap="magma",
170
  extent=(0.06, 0.94, 0.30, 0.50), aspect="auto")
171
- a.text(0.5, 0.13, "low → high", ha="center", va="center", fontsize=7.5, color=MUTED)
172
 
173
  # 2. a ladder of images, not one -- three rungs stand in for all 512.
174
  # Size the axes square in inches; imshow would otherwise letterbox each frame
@@ -183,7 +183,7 @@ def plot_explainer():
183
  ax.imshow(_synthetic_cell(c, seed=k + 1), cmap="magma", vmin=0, vmax=1)
184
  frame(ax, boxed=False)
185
  stage.text(xs[1], y0 - 0.035, "low → high concentration", ha="center", va="top",
186
- fontsize=7.2, color=MUTED, zorder=4)
187
 
188
  # 3. the curve those calls become
189
  c_ax = panel(xs[2])
@@ -199,10 +199,19 @@ def plot_explainer():
199
  d.plot(x, flat, color=GAP, linewidth=1.0, linestyle="--", alpha=0.85)
200
  d.set_xscale("log"); d.set_xlim(x.min(), x.max()); d.set_ylim(-0.05, 1.08)
201
  d.set_facecolor("white"); frame(d)
202
- d.text(0.33, 0.28, "AUC", transform=d.transAxes, fontsize=9, color=CURVE,
203
- fontweight="bold", ha="center")
204
- d.text(0.80, 0.62, "AAC", transform=d.transAxes, fontsize=9, color=GAP,
205
- fontweight="bold", ha="center")
 
 
 
 
 
 
 
 
 
206
 
207
  return fig
208
 
 
92
  """A reentrant titration curve used only for the diagram's thumbnails."""
93
  x = pipeline.intensity_ladder(pipeline.NUM_IMAGES).numpy()
94
  lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
95
+ p = np.clip(1.55 * np.exp(-((lx - 0.42) ** 2) / (2 * 0.17 ** 2)) - 0.16, 0, 1)
96
  calls = (np.random.default_rng(0).random(p.size) < p).astype(int)
97
  curve = metrics.moving_average(calls, pipeline.NUM_IMAGES // pipeline.WINDOW_DIVISOR)
98
  return x, curve
 
111
  stage = fig.add_axes([0, 0, 1, 1]); stage.set_axis_off()
112
  stage.set_xlim(0, 1); stage.set_ylim(0, 1)
113
 
114
+ xs = [0.112, 0.371, 0.629, 0.888]
115
+ box_y, box_h, box_w = 0.30, 0.50, 0.196
116
 
117
  def node(cx, title, caption):
118
  stage.add_patch(FancyBboxPatch(
 
120
  boxstyle="round,pad=0.012,rounding_size=0.02",
121
  linewidth=1.1, edgecolor=MUTED, facecolor="white", zorder=2))
122
  stage.text(cx, box_y + box_h + 0.06, title, ha="center", va="bottom",
123
+ fontsize=12, color=INK, fontweight="bold", zorder=4)
124
  if caption:
125
  stage.text(cx, box_y - 0.05, caption, ha="center", va="top",
126
+ fontsize=9.2, color=MUTED, linespacing=1.5, zorder=4)
127
 
128
  def arrow(x0, x1, label):
129
  stage.annotate("", xy=(x1, box_y + box_h / 2), xytext=(x0, box_y + box_h / 2),
130
  arrowprops=dict(arrowstyle="-|>", lw=1.6, color=INK,
131
  shrinkA=0, shrinkB=0), zorder=3)
132
  stage.text((x0 + x1) / 2, box_y + box_h / 2 + 0.045, label, ha="center",
133
+ va="bottom", fontsize=9.5, color=CURVE, fontweight="bold",
134
  linespacing=1.35, zorder=4)
135
 
136
  node(xs[0], "IDP sequence\n+ expression level", f"{pipeline.SEQUENCE_LENGTH} aa, scanned over\n{pipeline.NUM_IMAGES} concentrations")
 
139
  node(xs[3], "AUC, AAC", None)
140
 
141
  arrow(xs[0] + box_w / 2, xs[1] - box_w / 2, "CELL-FM")
142
+ arrow(xs[1] + box_w / 2, xs[2] - box_w / 2, "ViT")
143
  arrow(xs[2] + box_w / 2, xs[3] - box_w / 2, "integrate")
144
 
145
  # the last node's caption replaces the bullets that used to sit under the title
146
  stage.text(xs[3], box_y - 0.05, "AUC — range spent condensed", ha="center", va="top",
147
+ fontsize=9.2, color=CURVE, fontweight="bold", zorder=4)
148
  stage.text(xs[3], box_y - 0.16, "AAC — condensation lost to\nreentrant dissolution",
149
+ ha="center", va="top", fontsize=9.2, color=GAP, fontweight="bold",
150
  linespacing=1.5, zorder=4)
151
 
152
  def panel(cx, pad_x=0.020, pad_y=0.055):
 
164
 
165
  # 1. inputs: the sequence, and the concentration ladder it is scanned over
166
  a = panel(xs[0]); a.set_axis_off(); a.set_xlim(0, 1); a.set_ylim(0, 1)
167
+ a.text(0.5, 0.80, "KSFGTPFGGG…", ha="center", va="center", fontsize=10.5,
168
  family="monospace", color=INK)
169
  a.imshow(np.linspace(0, 1, 128)[None, :], cmap="magma",
170
  extent=(0.06, 0.94, 0.30, 0.50), aspect="auto")
171
+ a.text(0.5, 0.13, "low → high", ha="center", va="center", fontsize=9, color=MUTED)
172
 
173
  # 2. a ladder of images, not one -- three rungs stand in for all 512.
174
  # Size the axes square in inches; imshow would otherwise letterbox each frame
 
183
  ax.imshow(_synthetic_cell(c, seed=k + 1), cmap="magma", vmin=0, vmax=1)
184
  frame(ax, boxed=False)
185
  stage.text(xs[1], y0 - 0.035, "low → high concentration", ha="center", va="top",
186
+ fontsize=8.6, color=MUTED, zorder=4)
187
 
188
  # 3. the curve those calls become
189
  c_ax = panel(xs[2])
 
199
  d.plot(x, flat, color=GAP, linewidth=1.0, linestyle="--", alpha=0.85)
200
  d.set_xscale("log"); d.set_xlim(x.min(), x.max()); d.set_ylim(-0.05, 1.08)
201
  d.set_facecolor("white"); frame(d)
202
+ # place each label inside its own shaded region, computed from the curve
203
+ plateau = np.flatnonzero(curve > 0.9)
204
+ auc_i = plateau[len(plateau) // 2] if plateau.size else int(np.argmax(curve))
205
+ d.text(x[auc_i], 0.42, "AUC", fontsize=12, color=CURVE, fontweight="bold",
206
+ ha="center", va="center")
207
+ clear = np.flatnonzero((flat - curve > 0.45) & (curve < 0.35))
208
+ if clear.size:
209
+ # Sit just inside the open part of the gap, but never so far right that a
210
+ # centred label overruns the axes -- the midpoint of the region does.
211
+ lx = (np.log10(x) - np.log10(x[0])) / (np.log10(x[-1]) - np.log10(x[0]))
212
+ target = min(max(lx[clear[0]] + 0.09, 0.66), 0.78)
213
+ d.text(x[int(np.argmin(np.abs(lx - target)))], 0.55, "AAC", fontsize=12,
214
+ color=GAP, fontweight="bold", ha="center", va="center")
215
 
216
  return fig
217