m0ksh commited on
Commit
387b341
·
verified ·
1 Parent(s): 0587c42

Sync from GitHub (preserve manual model files)

Browse files
Files changed (1) hide show
  1. StreamlitApp/StreamlitApp.py +60 -74
StreamlitApp/StreamlitApp.py CHANGED
@@ -86,14 +86,10 @@ if "predict_input_widget" not in st.session_state:
86
  st.session_state.predict_input_widget = ""
87
  if "analyze_input" not in st.session_state:
88
  st.session_state.analyze_input = "" # last analyze input
89
- if "analyze_input_widget" not in st.session_state:
90
- st.session_state.analyze_input_widget = st.session_state.get("analyze_input", "")
91
  if "analyze_output" not in st.session_state:
92
  st.session_state.analyze_output = None # (label, conf_display, comp, props, analysis)
93
  if "optimize_input" not in st.session_state:
94
  st.session_state.optimize_input = "" # last optimize input
95
- if "optimize_input_widget" not in st.session_state:
96
- st.session_state.optimize_input_widget = st.session_state.get("optimize_input", "")
97
  if "optimize_output" not in st.session_state:
98
  st.session_state.optimize_output = None # (orig_seq, orig_conf, improved_seq, improved_conf, history)
99
  if "visualize_sequences" not in st.session_state:
@@ -110,8 +106,8 @@ if st.sidebar.button("Clear All Fields"):
110
  # clear only our known keys
111
  keys = ["predictions", "predict_ran",
112
  "predict_input_widget",
113
- "analyze_input", "analyze_input_widget", "analyze_output",
114
- "optimize_input", "optimize_input_widget", "optimize_output",
115
  "visualize_sequences", "visualize_df"]
116
  for k in keys:
117
  if k in st.session_state:
@@ -135,7 +131,7 @@ if page == "Predict":
135
  preset_cols = st.columns(2)
136
  with preset_cols[0]:
137
  if st.button("Use strong AMP example"):
138
- st.session_state.predict_input_widget = "KWKLFKKIGAVLKVL"
139
  st.rerun()
140
  with preset_cols[1]:
141
  if st.button("Use weak sequence example"):
@@ -197,7 +193,7 @@ if page == "Predict":
197
 
198
  # If user hasn't just run predictions, show the last saved results (if any)
199
  if st.session_state.predictions and not (run and st.session_state.predict_ran is False):
200
- st.subheader("Predictions (last run)")
201
 
202
  top_candidate = choose_top_candidate(st.session_state.predictions)
203
  if top_candidate:
@@ -205,34 +201,23 @@ if page == "Predict":
205
  st.markdown(
206
  "<div style='border:1px solid #e6e6e6; border-radius:0.6rem; padding:0.8rem; background:#fafafa;'>"
207
  , unsafe_allow_html=True)
208
- st.markdown("### Top Candidate")
209
- st.code(top_candidate["Sequence"], language="text")
 
 
 
 
 
 
 
 
 
 
 
210
  st.write(f"Confidence: **{format_conf_percent(top_candidate['predicted_confidence'], digits=1)}**")
211
  st.write(f"Reason: {top_candidate['Reason']}")
212
  st.markdown("</div>", unsafe_allow_html=True)
213
 
214
- # Copy-to-clipboard + compact row view (buttons placed next to each sequence).
215
- st.markdown("### Results with Copy")
216
- for idx, row in enumerate(st.session_state.predictions):
217
- seq = row.get("Sequence", "")
218
- label = row.get("Prediction", "")
219
- conf = row.get("Confidence", 0.0)
220
- conf_display = round(float(conf) * 100, 1) if label == "AMP" else round((1 - float(conf)) * 100, 1)
221
- cols = st.columns([7, 2, 1])
222
- with cols[0]:
223
- st.code(seq, language="text")
224
- with cols[1]:
225
- st.write(f"**{label}**")
226
- st.caption(f"{conf_display}% confidence")
227
- with cols[2]:
228
- if st.button("Copy", key=f"copy_pred_{idx}"):
229
- _try_copy_to_clipboard(seq)
230
- toast_fn = getattr(st, "toast", None)
231
- if toast_fn is not None:
232
- toast_fn("Copied to clipboard")
233
- else:
234
- st.success("Copied to clipboard")
235
-
236
  # Keep the original dataframe for full overview/download compatibility.
237
  st.dataframe(pd.DataFrame(st.session_state.predictions), use_container_width=True)
238
  csv = pd.DataFrame(st.session_state.predictions).to_csv(index=False)
@@ -242,22 +227,11 @@ if page == "Predict":
242
  elif page == "Analyze":
243
  st.header("Sequence Analysis")
244
 
245
- preset_cols = st.columns(2)
246
- with preset_cols[0]:
247
- if st.button("Use strong AMP example"):
248
- st.session_state.analyze_input_widget = "KWKLFKKIGAVLKVL"
249
- st.rerun()
250
- with preset_cols[1]:
251
- if st.button("Use weak sequence example"):
252
- st.session_state.analyze_input_widget = "GAGAGAGAGAGA"
253
- st.rerun()
254
-
255
  # show the last saved analyze output if user navigated back
256
  last_seq = st.session_state.analyze_input
257
  seq = st.text_input(
258
  "Enter a peptide sequence to analyze:",
259
- value=st.session_state.get("analyze_input_widget", last_seq),
260
- key="analyze_input_widget",
261
  )
262
 
263
  warn = sequence_length_warning(seq)
@@ -349,21 +323,40 @@ elif page == "Analyze":
349
  "Net Charge": "Favorable" if charge > 0 else "Neutral" if charge == 0 else "Unfavorable",
350
  "Molecular Weight": "Acceptable" if 500 <= mw <= 5000 else "Extreme"
351
  }
352
- st.table(pd.DataFrame([
353
- {"Property": "Length", "Value": length, "Favorability": favorability["Length"]},
354
- {"Property": "Hydrophobic Fraction", "Value": hydro, "Favorability": favorability["Hydrophobic Fraction"]},
355
- {"Property": "Net Charge", "Value": charge, "Favorability": favorability["Net Charge"]},
356
- {"Property": "Molecular Weight", "Value": mw, "Favorability": favorability["Molecular Weight"]}
357
- ]))
 
 
 
 
358
 
359
- _tooltip_label("Hydrophobic Fraction", "Fraction of residues that prefer non-aqueous environments")
360
- _tooltip_label("Net Charge", "Positive charge helps peptides bind bacterial membranes")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
  st.subheader("Property Radar Chart")
363
- _tooltip_label(
364
- "Radar",
365
- "Radar chart compares length, hydrophobicity, net charge, and molecular weight against ideal AMP ranges.",
366
- )
367
  categories = ["Length", "Hydrophobic Fraction", "Net Charge", "Molecular Weight"]
368
  values = [min(length / 50, 1), min(hydro, 1), 1 if charge > 0 else 0, min(mw / 5000, 1)]
369
  values += values[:1]
@@ -390,7 +383,6 @@ elif page == "Analyze":
390
  st.write(f"- {line}")
391
 
392
  # Export analysis report
393
- st.markdown("---")
394
  st.subheader("Export Analysis Report")
395
  export_format = st.radio("Format", ["CSV", "TXT"], horizontal=True)
396
 
@@ -437,23 +429,10 @@ elif page == "Analyze":
437
  elif page == "Optimize":
438
  st.header("AMP Sequence Optimizer")
439
 
440
- preset_cols = st.columns(2)
441
- with preset_cols[0]:
442
- if st.button("Use strong AMP example"):
443
- st.session_state.optimize_input_widget = "KWKLFKKIGAVLKVL"
444
- st.session_state.optimize_input = "KWKLFKKIGAVLKVL"
445
- st.rerun()
446
- with preset_cols[1]:
447
- if st.button("Use weak sequence example"):
448
- st.session_state.optimize_input_widget = "GAGAGAGAGAGA"
449
- st.session_state.optimize_input = "GAGAGAGAGAGA"
450
- st.rerun()
451
-
452
- # Single entry point: text input retained across navigation (widget uses a dedicated key)
453
  seq = st.text_input(
454
  "Enter a peptide sequence to optimize:",
455
- value=st.session_state.get("optimize_input_widget", ""),
456
- key="optimize_input_widget",
457
  )
458
 
459
  # Run optimization when user changes input and clicks button
@@ -626,6 +605,13 @@ elif page == "Visualize":
626
  elif page == "About":
627
  st.header("About the Project")
628
  st.markdown("""
629
- **Problem:** Antimicrobial resistance is a global health threat. Traditional peptide screening is slow and costly.
630
- **Solution:** This tool predicts antimicrobial activity directly from sequence using deep learning, speeding up AMP discovery.
 
 
 
 
 
 
 
631
  """)
 
86
  st.session_state.predict_input_widget = ""
87
  if "analyze_input" not in st.session_state:
88
  st.session_state.analyze_input = "" # last analyze input
 
 
89
  if "analyze_output" not in st.session_state:
90
  st.session_state.analyze_output = None # (label, conf_display, comp, props, analysis)
91
  if "optimize_input" not in st.session_state:
92
  st.session_state.optimize_input = "" # last optimize input
 
 
93
  if "optimize_output" not in st.session_state:
94
  st.session_state.optimize_output = None # (orig_seq, orig_conf, improved_seq, improved_conf, history)
95
  if "visualize_sequences" not in st.session_state:
 
106
  # clear only our known keys
107
  keys = ["predictions", "predict_ran",
108
  "predict_input_widget",
109
+ "analyze_input", "analyze_output",
110
+ "optimize_input", "optimize_output",
111
  "visualize_sequences", "visualize_df"]
112
  for k in keys:
113
  if k in st.session_state:
 
131
  preset_cols = st.columns(2)
132
  with preset_cols[0]:
133
  if st.button("Use strong AMP example"):
134
+ st.session_state.predict_input_widget = "ILPWKWPWWPWRR"
135
  st.rerun()
136
  with preset_cols[1]:
137
  if st.button("Use weak sequence example"):
 
193
 
194
  # If user hasn't just run predictions, show the last saved results (if any)
195
  if st.session_state.predictions and not (run and st.session_state.predict_ran is False):
196
+ st.write("**Predictions (last run)**")
197
 
198
  top_candidate = choose_top_candidate(st.session_state.predictions)
199
  if top_candidate:
 
201
  st.markdown(
202
  "<div style='border:1px solid #e6e6e6; border-radius:0.6rem; padding:0.8rem; background:#fafafa;'>"
203
  , unsafe_allow_html=True)
204
+ st.write("**Top Candidate**")
205
+ seq = top_candidate.get("Sequence", "")
206
+ cc = st.columns([9, 1])
207
+ with cc[0]:
208
+ st.code(seq, language="text")
209
+ with cc[1]:
210
+ if st.button("Copy", key="copy_top_candidate"):
211
+ _try_copy_to_clipboard(seq)
212
+ toast_fn = getattr(st, "toast", None)
213
+ if toast_fn is not None:
214
+ toast_fn("Copied to clipboard")
215
+ else:
216
+ st.success("Copied to clipboard")
217
  st.write(f"Confidence: **{format_conf_percent(top_candidate['predicted_confidence'], digits=1)}**")
218
  st.write(f"Reason: {top_candidate['Reason']}")
219
  st.markdown("</div>", unsafe_allow_html=True)
220
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
221
  # Keep the original dataframe for full overview/download compatibility.
222
  st.dataframe(pd.DataFrame(st.session_state.predictions), use_container_width=True)
223
  csv = pd.DataFrame(st.session_state.predictions).to_csv(index=False)
 
227
  elif page == "Analyze":
228
  st.header("Sequence Analysis")
229
 
 
 
 
 
 
 
 
 
 
 
230
  # show the last saved analyze output if user navigated back
231
  last_seq = st.session_state.analyze_input
232
  seq = st.text_input(
233
  "Enter a peptide sequence to analyze:",
234
+ value=last_seq,
 
235
  )
236
 
237
  warn = sequence_length_warning(seq)
 
323
  "Net Charge": "Favorable" if charge > 0 else "Neutral" if charge == 0 else "Unfavorable",
324
  "Molecular Weight": "Acceptable" if 500 <= mw <= 5000 else "Extreme"
325
  }
326
+ def _info_icon(tooltip_text: str) -> str:
327
+ safe = _html.escape(tooltip_text, quote=True)
328
+ return (
329
+ "<span "
330
+ f"title='{safe}' "
331
+ "style=\"display:inline-flex; align-items:center; justify-content:center; "
332
+ "margin-left:6px; width:16px; height:16px; border-radius:50%; "
333
+ "background:#f2f2f2; border:1px solid #d9d9d9; color:#333; "
334
+ "font-size:12px; font-weight:700; cursor:help;\">(i)</span>"
335
+ )
336
 
337
+ # Render the favorability table with working inline tooltips.
338
+ hydro_label = f"Hydrophobic Fraction{_info_icon('Fraction of residues that prefer non-aqueous environments')}"
339
+ charge_label = f"Net Charge{_info_icon('Positive charge helps peptides bind bacterial membranes')}"
340
+ table_html = (
341
+ "<table style='width:100%; border-collapse:collapse;'>"
342
+ "<thead>"
343
+ "<tr>"
344
+ "<th style='text-align:left; padding:8px; border-bottom:1px solid #e6e6e6;'>Property</th>"
345
+ "<th style='text-align:right; padding:8px; border-bottom:1px solid #e6e6e6;'>Value</th>"
346
+ "<th style='text-align:left; padding:8px; border-bottom:1px solid #e6e6e6;'>Favorability</th>"
347
+ "</tr>"
348
+ "</thead>"
349
+ "<tbody>"
350
+ f"<tr><td style='padding:8px;'>{_html.escape('Length')}</td><td style='padding:8px; text-align:right;'>{_html.escape(str(length))}</td><td style='padding:8px;'>{_html.escape(favorability['Length'])}</td></tr>"
351
+ f"<tr><td style='padding:8px;'>{hydro_label}</td><td style='padding:8px; text-align:right;'>{_html.escape(str(hydro))}</td><td style='padding:8px;'>{_html.escape(favorability['Hydrophobic Fraction'])}</td></tr>"
352
+ f"<tr><td style='padding:8px;'>{charge_label}</td><td style='padding:8px; text-align:right;'>{_html.escape(str(charge))}</td><td style='padding:8px;'>{_html.escape(favorability['Net Charge'])}</td></tr>"
353
+ f"<tr><td style='padding:8px;'>{_html.escape('Molecular Weight')}</td><td style='padding:8px; text-align:right;'>{_html.escape(str(mw))}</td><td style='padding:8px;'>{_html.escape(favorability['Molecular Weight'])}</td></tr>"
354
+ "</tbody>"
355
+ "</table>"
356
+ )
357
+ st.markdown(table_html, unsafe_allow_html=True)
358
 
359
  st.subheader("Property Radar Chart")
 
 
 
 
360
  categories = ["Length", "Hydrophobic Fraction", "Net Charge", "Molecular Weight"]
361
  values = [min(length / 50, 1), min(hydro, 1), 1 if charge > 0 else 0, min(mw / 5000, 1)]
362
  values += values[:1]
 
383
  st.write(f"- {line}")
384
 
385
  # Export analysis report
 
386
  st.subheader("Export Analysis Report")
387
  export_format = st.radio("Format", ["CSV", "TXT"], horizontal=True)
388
 
 
429
  elif page == "Optimize":
430
  st.header("AMP Sequence Optimizer")
431
 
432
+ # Single entry point: text input retained across navigation
 
 
 
 
 
 
 
 
 
 
 
 
433
  seq = st.text_input(
434
  "Enter a peptide sequence to optimize:",
435
+ value=st.session_state.get("optimize_input", ""),
 
436
  )
437
 
438
  # Run optimization when user changes input and clicks button
 
605
  elif page == "About":
606
  st.header("About the Project")
607
  st.markdown("""
608
+ PeptideAI is a lightweight Streamlit app for exploring antimicrobial peptide (AMP) sequences.
609
+
610
+ It uses a trained neural network to estimate whether a peptide is likely to be antimicrobial, then helps you interpret and improve candidates:
611
+ - **Predict**: run batch predictions and highlights the best candidate using simple profile heuristics.
612
+ - **Analyze**: show amino-acid composition, physicochemical properties, and an AMP-range radar chart.
613
+ - **Optimize**: iteratively propose residue mutations and summarize how confidence/charge/hydrophobic balance changes.
614
+ - **Visualize**: embed sequences and inspect clusters with t-SNE.
615
+
616
+ Note: predictions are model-based heuristics and are not a substitute for wet-lab validation.
617
  """)