Minjun Kang commited on
Commit
e15df09
Β·
1 Parent(s): b90da4a

add smoothing

Browse files
Files changed (1) hide show
  1. app.py +49 -12
app.py CHANGED
@@ -210,6 +210,15 @@ def model_predict(feat: np.ndarray, cond: np.ndarray) -> float:
210
  return float(model.predict_proba(x)[0, 1])
211
 
212
 
 
 
 
 
 
 
 
 
 
213
  # ── Matplotlib helpers ────────────────────────────────────────────────────────
214
  def prob_gauge_figure(prob: float) -> plt.Figure:
215
  """Horizontal probability bar gauge."""
@@ -236,21 +245,34 @@ def prob_gauge_figure(prob: float) -> plt.Figure:
236
 
237
 
238
  def screening_figure(xvals: np.ndarray, probs: np.ndarray,
239
- screen_name: str) -> plt.Figure:
240
- """Line graph for condition screening result."""
 
 
 
 
 
241
  xlabel = SCREEN_LABELS[screen_name]
242
  LLPS_COLOR = "#e74c3c"
243
  NON_COLOR = "#2980b9"
244
 
 
 
 
245
  fig, ax = plt.subplots(figsize=(9, 5))
246
- ax.plot(xvals, probs, lw=2.5, color=NON_COLOR, label="LLPS Probability", zorder=3)
 
 
 
 
 
247
  ax.axhline(0.5, color=LLPS_COLOR, lw=1.8, ls="--",
248
  label="Threshold 0.5", zorder=4)
249
- ax.fill_between(xvals, probs, 0.5,
250
- where=(probs >= 0.5), alpha=0.22,
251
  color=LLPS_COLOR, label="LLPS region", zorder=2)
252
- ax.fill_between(xvals, probs, 0.5,
253
- where=(probs < 0.5), alpha=0.15,
254
  color=NON_COLOR, label="Non-LLPS region", zorder=2)
255
  ax.set_xlim(xvals[0], xvals[-1])
256
  ax.set_ylim(0, 1)
@@ -350,7 +372,8 @@ def cb_predict(feat,
350
  def cb_screen(feat, screen_name,
351
  fix_temp, fix_conc, fix_pH,
352
  nacl, mgcl2, kcl, glyc,
353
- peg1, peg2, peg3, ficoll, dext40, dext70):
 
354
  """Tab 2: Screen LLPS across a range of one condition."""
355
  if feat is None:
356
  return None, "⚠️ Please extract the T5 feature first (Step 2)."
@@ -368,15 +391,22 @@ def cb_screen(feat, screen_name,
368
  probs.append(model_predict(feat, cond))
369
 
370
  probs = np.array(probs)
371
- fig = screening_figure(xvals, probs, screen_name)
372
 
373
- peak_idx = probs.argmax()
 
 
 
 
 
 
374
  xlabel = SCREEN_LABELS[screen_name]
375
  txt = (f"Screening completed. \n"
376
- f"Peak probability **{probs[peak_idx]:.4f}** "
377
  f"at {xlabel} = **{xvals[peak_idx]:.1f}** \n"
378
  f"LLPS-positive range: "
379
- f"**{(probs >= 0.5).sum()}** / {len(probs)} points β‰₯ 0.5")
 
 
380
  return fig, txt
381
  except Exception as e:
382
  return None, f"❌ Screening failed: {e}"
@@ -614,6 +644,12 @@ with gr.Blocks(
614
  s_dext40 = gr.Slider(0, 50, value=0, step=1, label="Dextran ≀40 kDa (%)")
615
  s_dext70 = gr.Slider(0, 50, value=0, step=1, label="Dextran β‰₯70 kDa (%)")
616
 
 
 
 
 
 
 
617
  screen_btn = gr.Button("πŸ“Š Run Condition Screening", variant="primary")
618
  screen_plot = gr.Plot(label="Screening Result")
619
  screen_text = gr.Markdown("")
@@ -625,6 +661,7 @@ with gr.Blocks(
625
  s_temp, s_conc, s_pH,
626
  s_nacl, s_mgcl2, s_kcl, s_glyc,
627
  s_peg1, s_peg2, s_peg3, s_ficoll, s_dext40, s_dext70,
 
628
  ],
629
  outputs=[screen_plot, screen_text],
630
  )
 
210
  return float(model.predict_proba(x)[0, 1])
211
 
212
 
213
+ # ── Smoothing (same as LLPSXG.py's moving_average) ────────────────────────────
214
+ def moving_average(y: np.ndarray, window_size: int) -> np.ndarray:
215
+ if window_size % 2 == 0:
216
+ raise ValueError("Window size should be odd to ensure symmetry.")
217
+ window = np.ones(int(window_size)) / float(window_size)
218
+ y_padded = np.pad(y, (window_size // 2, window_size // 2), mode="edge")
219
+ return np.convolve(y_padded, window, "valid")
220
+
221
+
222
  # ── Matplotlib helpers ────────────────────────────────────────────────────────
223
  def prob_gauge_figure(prob: float) -> plt.Figure:
224
  """Horizontal probability bar gauge."""
 
245
 
246
 
247
  def screening_figure(xvals: np.ndarray, probs: np.ndarray,
248
+ screen_name: str, probs_smooth: np.ndarray = None) -> plt.Figure:
249
+ """Line graph for condition screening result.
250
+
251
+ If probs_smooth differs from probs (smoothing window > 1), the raw curve
252
+ is drawn as a faint dotted reference line and the smoothed curve becomes
253
+ the main plotted/filled line.
254
+ """
255
  xlabel = SCREEN_LABELS[screen_name]
256
  LLPS_COLOR = "#e74c3c"
257
  NON_COLOR = "#2980b9"
258
 
259
+ smoothed = probs_smooth is not None and not np.array_equal(probs, probs_smooth)
260
+ plot_probs = probs_smooth if smoothed else probs
261
+
262
  fig, ax = plt.subplots(figsize=(9, 5))
263
+ if smoothed:
264
+ ax.plot(xvals, probs, lw=1.2, color=NON_COLOR, alpha=0.35, ls=":",
265
+ label="Raw", zorder=2)
266
+ ax.plot(xvals, plot_probs, lw=2.5, color=NON_COLOR,
267
+ label="Smoothed LLPS Probability" if smoothed else "LLPS Probability",
268
+ zorder=3)
269
  ax.axhline(0.5, color=LLPS_COLOR, lw=1.8, ls="--",
270
  label="Threshold 0.5", zorder=4)
271
+ ax.fill_between(xvals, plot_probs, 0.5,
272
+ where=(plot_probs >= 0.5), alpha=0.22,
273
  color=LLPS_COLOR, label="LLPS region", zorder=2)
274
+ ax.fill_between(xvals, plot_probs, 0.5,
275
+ where=(plot_probs < 0.5), alpha=0.15,
276
  color=NON_COLOR, label="Non-LLPS region", zorder=2)
277
  ax.set_xlim(xvals[0], xvals[-1])
278
  ax.set_ylim(0, 1)
 
372
  def cb_screen(feat, screen_name,
373
  fix_temp, fix_conc, fix_pH,
374
  nacl, mgcl2, kcl, glyc,
375
+ peg1, peg2, peg3, ficoll, dext40, dext70,
376
+ smooth_window):
377
  """Tab 2: Screen LLPS across a range of one condition."""
378
  if feat is None:
379
  return None, "⚠️ Please extract the T5 feature first (Step 2)."
 
391
  probs.append(model_predict(feat, cond))
392
 
393
  probs = np.array(probs)
 
394
 
395
+ # Slider step keeps this odd (1, 3, 5, ...); window=1 is a no-op average.
396
+ window = int(smooth_window)
397
+ probs_plot = moving_average(probs, window) if window > 1 else probs
398
+
399
+ fig = screening_figure(xvals, probs, screen_name, probs_plot)
400
+
401
+ peak_idx = probs_plot.argmax()
402
  xlabel = SCREEN_LABELS[screen_name]
403
  txt = (f"Screening completed. \n"
404
+ f"Peak probability **{probs_plot[peak_idx]:.4f}** "
405
  f"at {xlabel} = **{xvals[peak_idx]:.1f}** \n"
406
  f"LLPS-positive range: "
407
+ f"**{(probs_plot >= 0.5).sum()}** / {len(probs_plot)} points β‰₯ 0.5")
408
+ if window > 1:
409
+ txt += f" \n*(Smoothed with moving-average window size {window})*"
410
  return fig, txt
411
  except Exception as e:
412
  return None, f"❌ Screening failed: {e}"
 
644
  s_dext40 = gr.Slider(0, 50, value=0, step=1, label="Dextran ≀40 kDa (%)")
645
  s_dext70 = gr.Slider(0, 50, value=0, step=1, label="Dextran β‰₯70 kDa (%)")
646
 
647
+ s_smooth = gr.Slider(
648
+ 1, 21, value=15, step=2,
649
+ label="Smoothing Window Size",
650
+ info="Odd window size for moving-average smoothing of the screening curve (1 = no smoothing).",
651
+ )
652
+
653
  screen_btn = gr.Button("πŸ“Š Run Condition Screening", variant="primary")
654
  screen_plot = gr.Plot(label="Screening Result")
655
  screen_text = gr.Markdown("")
 
661
  s_temp, s_conc, s_pH,
662
  s_nacl, s_mgcl2, s_kcl, s_glyc,
663
  s_peg1, s_peg2, s_peg3, s_ficoll, s_dext40, s_dext70,
664
+ s_smooth,
665
  ],
666
  outputs=[screen_plot, screen_text],
667
  )