Sync from GitHub (preserve manual model files)
Browse files- StreamlitApp/StreamlitApp.py +31 -53
- StreamlitApp/utils/peptide_extras.py +70 -29
StreamlitApp/StreamlitApp.py
CHANGED
|
@@ -156,30 +156,6 @@ if st.sidebar.button("Clear All Fields"):
|
|
| 156 |
st.stop()
|
| 157 |
|
| 158 |
|
| 159 |
-
def _prefill_peptide_viz_sequence() -> str:
|
| 160 |
-
"""Best-effort sequence for Visualize Peptide: optimized result → optimize input → analyze → Predict top."""
|
| 161 |
-
out = st.session_state.get("optimize_output")
|
| 162 |
-
if out and len(out) >= 3:
|
| 163 |
-
improved = str(out[2] or "").strip()
|
| 164 |
-
if improved:
|
| 165 |
-
return improved
|
| 166 |
-
oi = (st.session_state.get("optimize_input") or "").strip()
|
| 167 |
-
if oi:
|
| 168 |
-
return oi
|
| 169 |
-
ai = (st.session_state.get("analyze_input") or "").strip()
|
| 170 |
-
if ai:
|
| 171 |
-
return ai
|
| 172 |
-
preds = st.session_state.get("predictions") or []
|
| 173 |
-
top = choose_top_candidate(preds)
|
| 174 |
-
if top and top.get("Sequence"):
|
| 175 |
-
return str(top["Sequence"]).strip()
|
| 176 |
-
pi = st.session_state.get("predict_input_widget", "") or ""
|
| 177 |
-
lines = [ln.strip() for ln in pi.splitlines() if ln.strip()]
|
| 178 |
-
if lines:
|
| 179 |
-
return lines[0]
|
| 180 |
-
return ""
|
| 181 |
-
|
| 182 |
-
|
| 183 |
# Load model once
|
| 184 |
model = load_model()
|
| 185 |
|
|
@@ -460,6 +436,29 @@ elif page == "Analyze":
|
|
| 460 |
ax.legend(loc='lower center', bbox_to_anchor=(0.85, 1.15), ncol=2, fontsize=7)
|
| 461 |
st.pyplot(fig, use_container_width=False)
|
| 462 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 463 |
st.divider()
|
| 464 |
# Analysis Summary
|
| 465 |
st.subheader("Analysis Summary")
|
|
@@ -611,25 +610,19 @@ elif page == "Visualize Peptide":
|
|
| 611 |
"and functional residue map."
|
| 612 |
)
|
| 613 |
|
| 614 |
-
if not (st.session_state.get("visualize_peptide_input") or "").strip():
|
| 615 |
-
sug = _prefill_peptide_viz_sequence()
|
| 616 |
-
if sug:
|
| 617 |
-
st.session_state.visualize_peptide_input = sug
|
| 618 |
-
|
| 619 |
st.checkbox("Auto-run when sequence changes", value=False, key="viz_peptide_auto_run")
|
| 620 |
|
| 621 |
st.text_input(
|
| 622 |
"Peptide sequence",
|
| 623 |
key="visualize_peptide_input",
|
| 624 |
-
placeholder="
|
| 625 |
-
help="When empty, your last optimized / analyzed / predicted sequence is filled in automatically.",
|
| 626 |
)
|
| 627 |
|
| 628 |
seq_viz = (st.session_state.get("visualize_peptide_input") or "").strip()
|
| 629 |
clean_viz = "".join(c for c in seq_viz.upper() if not c.isspace())
|
| 630 |
if not clean_viz:
|
| 631 |
st.session_state.viz_peptide_last_computed = ""
|
| 632 |
-
st.info("Enter a sequence above
|
| 633 |
else:
|
| 634 |
run_viz = st.button("Run visualization", type="primary", key="viz_peptide_run_btn")
|
| 635 |
auto_on = bool(st.session_state.get("viz_peptide_auto_run"))
|
|
@@ -641,7 +634,7 @@ elif page == "Visualize Peptide":
|
|
| 641 |
need_compute = True
|
| 642 |
|
| 643 |
if need_compute:
|
| 644 |
-
with st.spinner("Building 3D view
|
| 645 |
st.session_state.viz_peptide_last_computed = clean_viz
|
| 646 |
|
| 647 |
if clean_viz == st.session_state.get("viz_peptide_last_computed"):
|
|
@@ -669,7 +662,9 @@ elif page == "Visualize Peptide":
|
|
| 669 |
|
| 670 |
with col_r:
|
| 671 |
st.subheader("Helical wheel")
|
| 672 |
-
st.caption(
|
|
|
|
|
|
|
| 673 |
fig_wheel = plot_helical_wheel(clean_viz)
|
| 674 |
st.pyplot(fig_wheel, use_container_width=True)
|
| 675 |
plt.close(fig_wheel)
|
|
@@ -683,23 +678,6 @@ elif page == "Visualize Peptide":
|
|
| 683 |
with st.expander("Map · legend", expanded=False):
|
| 684 |
st.markdown(COMPACT_MAP_LEGEND)
|
| 685 |
|
| 686 |
-
st.subheader("Most similar known AMP")
|
| 687 |
-
st.caption(
|
| 688 |
-
f"Compared to **{len(KNOWN_AMPS)}** unique AMP sequences (label = 1 in `Data/ampData.csv`)."
|
| 689 |
-
)
|
| 690 |
-
match_seq, sim_score = find_most_similar(clean_viz)
|
| 691 |
-
if match_seq is not None:
|
| 692 |
-
st.write(f"**Best match:** `{match_seq}`")
|
| 693 |
-
st.write(f"**Similarity score:** **{sim_score:.3f}** (position match / max length)")
|
| 694 |
-
if sim_score > 0.6:
|
| 695 |
-
st.success("High similarity to a known AMP in the reference set.")
|
| 696 |
-
elif sim_score > 0.3:
|
| 697 |
-
st.warning("Moderate similarity — interpret with care.")
|
| 698 |
-
else:
|
| 699 |
-
st.error("Low similarity — sequence is distant from reference AMPs.")
|
| 700 |
-
else:
|
| 701 |
-
st.warning("Could not compute similarity (empty sequence after cleaning).")
|
| 702 |
-
|
| 703 |
elif clean_viz != (st.session_state.get("viz_peptide_last_computed") or ""):
|
| 704 |
st.caption("Click **Run visualization** or enable **Auto-run** to update the figures.")
|
| 705 |
|
|
@@ -806,9 +784,9 @@ PeptideAI is a lightweight Streamlit app for exploring antimicrobial peptide (AM
|
|
| 806 |
|
| 807 |
It uses a trained neural network to estimate whether a peptide is likely to be antimicrobial, then helps you interpret and improve candidates:
|
| 808 |
- **Predict**: batch predictions from multi-line or FASTA input, length warnings, persisted results, top-candidate highlight, and CSV export.
|
| 809 |
-
- **Analyze**: single-sequence numerical & textual analysis — AMP prediction, composition, physicochemical table + radar, and exportable report (no 3D on this page).
|
| 810 |
- **Optimize**: guided sequence optimization with mutation heatmap, step table, and confidence vs. step plot.
|
| 811 |
-
- **Visualize Peptide**: one sequence with consistent coloring across functional map, helical wheel, and optional 3D approximation
|
| 812 |
- **Visualize t-SNE**: upload many sequences, embed with the model, run t-SNE, and explore clusters with filters and hover metadata.
|
| 813 |
- **About**: this overview and disclaimer.
|
| 814 |
|
|
|
|
| 156 |
st.stop()
|
| 157 |
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
# Load model once
|
| 160 |
model = load_model()
|
| 161 |
|
|
|
|
| 436 |
ax.legend(loc='lower center', bbox_to_anchor=(0.85, 1.15), ncol=2, fontsize=7)
|
| 437 |
st.pyplot(fig, use_container_width=False)
|
| 438 |
|
| 439 |
+
st.divider()
|
| 440 |
+
st.subheader("Most similar known AMP")
|
| 441 |
+
st.caption(
|
| 442 |
+
f"Compared to **{len(KNOWN_AMPS)}** unique AMP sequences (label = 1 in `Data/ampData.csv`)."
|
| 443 |
+
)
|
| 444 |
+
seq_sim = str(st.session_state.analyze_input or "").strip()
|
| 445 |
+
seq_clean_sim = "".join(c for c in seq_sim.upper() if not c.isspace())
|
| 446 |
+
if seq_clean_sim:
|
| 447 |
+
match_seq, sim_score = find_most_similar(seq_clean_sim)
|
| 448 |
+
if match_seq is not None:
|
| 449 |
+
st.write(f"**Best match:** `{match_seq}`")
|
| 450 |
+
st.write(f"**Similarity score:** **{sim_score:.3f}** (position match / max length)")
|
| 451 |
+
if sim_score > 0.6:
|
| 452 |
+
st.success("High similarity to a known AMP in the reference set.")
|
| 453 |
+
elif sim_score > 0.3:
|
| 454 |
+
st.warning("Moderate similarity — interpret with care.")
|
| 455 |
+
else:
|
| 456 |
+
st.error("Low similarity — sequence is distant from reference AMPs.")
|
| 457 |
+
else:
|
| 458 |
+
st.warning("Could not compute similarity.")
|
| 459 |
+
else:
|
| 460 |
+
st.caption("Run analysis with a sequence to compare against known AMPs.")
|
| 461 |
+
|
| 462 |
st.divider()
|
| 463 |
# Analysis Summary
|
| 464 |
st.subheader("Analysis Summary")
|
|
|
|
| 610 |
"and functional residue map."
|
| 611 |
)
|
| 612 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 613 |
st.checkbox("Auto-run when sequence changes", value=False, key="viz_peptide_auto_run")
|
| 614 |
|
| 615 |
st.text_input(
|
| 616 |
"Peptide sequence",
|
| 617 |
key="visualize_peptide_input",
|
| 618 |
+
placeholder="Paste or type a one-letter amino-acid sequence",
|
|
|
|
| 619 |
)
|
| 620 |
|
| 621 |
seq_viz = (st.session_state.get("visualize_peptide_input") or "").strip()
|
| 622 |
clean_viz = "".join(c for c in seq_viz.upper() if not c.isspace())
|
| 623 |
if not clean_viz:
|
| 624 |
st.session_state.viz_peptide_last_computed = ""
|
| 625 |
+
st.info("Enter a sequence above, then click **Run visualization** (or enable **Auto-run**).")
|
| 626 |
else:
|
| 627 |
run_viz = st.button("Run visualization", type="primary", key="viz_peptide_run_btn")
|
| 628 |
auto_on = bool(st.session_state.get("viz_peptide_auto_run"))
|
|
|
|
| 634 |
need_compute = True
|
| 635 |
|
| 636 |
if need_compute:
|
| 637 |
+
with st.spinner("Building 3D view and helical wheel..."):
|
| 638 |
st.session_state.viz_peptide_last_computed = clean_viz
|
| 639 |
|
| 640 |
if clean_viz == st.session_state.get("viz_peptide_last_computed"):
|
|
|
|
| 662 |
|
| 663 |
with col_r:
|
| 664 |
st.subheader("Helical wheel")
|
| 665 |
+
st.caption(
|
| 666 |
+
"Radial spokes per residue, black connectors along the sequence, colored disks (same scheme as 3D)."
|
| 667 |
+
)
|
| 668 |
fig_wheel = plot_helical_wheel(clean_viz)
|
| 669 |
st.pyplot(fig_wheel, use_container_width=True)
|
| 670 |
plt.close(fig_wheel)
|
|
|
|
| 678 |
with st.expander("Map · legend", expanded=False):
|
| 679 |
st.markdown(COMPACT_MAP_LEGEND)
|
| 680 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 681 |
elif clean_viz != (st.session_state.get("viz_peptide_last_computed") or ""):
|
| 682 |
st.caption("Click **Run visualization** or enable **Auto-run** to update the figures.")
|
| 683 |
|
|
|
|
| 784 |
|
| 785 |
It uses a trained neural network to estimate whether a peptide is likely to be antimicrobial, then helps you interpret and improve candidates:
|
| 786 |
- **Predict**: batch predictions from multi-line or FASTA input, length warnings, persisted results, top-candidate highlight, and CSV export.
|
| 787 |
+
- **Analyze**: single-sequence numerical & textual analysis — AMP prediction, composition, physicochemical table + radar, similarity to known AMPs, and exportable report (no 3D on this page).
|
| 788 |
- **Optimize**: guided sequence optimization with mutation heatmap, step table, and confidence vs. step plot.
|
| 789 |
+
- **Visualize Peptide**: one sequence with consistent coloring across functional map, detailed helical wheel, and optional 3D approximation.
|
| 790 |
- **Visualize t-SNE**: upload many sequences, embed with the model, run t-SNE, and explore clusters with filters and hover metadata.
|
| 791 |
- **About**: this overview and disclaimer.
|
| 792 |
|
StreamlitApp/utils/peptide_extras.py
CHANGED
|
@@ -152,14 +152,14 @@ def get_residue_color(aa: str) -> str:
|
|
| 152 |
|
| 153 |
|
| 154 |
def residue_color_mpl(aa: str) -> str:
|
| 155 |
-
"""Matplotlib-compatible hex colors matching `get_residue_color` categories."""
|
| 156 |
cat = get_residue_color(aa)
|
| 157 |
return {
|
| 158 |
-
"blue": "#
|
| 159 |
-
"red": "#
|
| 160 |
-
"green": "#
|
| 161 |
-
"gray": "#
|
| 162 |
-
}.get(cat, "#
|
| 163 |
|
| 164 |
|
| 165 |
HELIX_WHEEL_LEGEND_MARKDOWN: str = """
|
|
@@ -178,7 +178,8 @@ COMPACT_3D_LEGEND: str = (
|
|
| 178 |
"Helix trace is an approximation, not an experimental structure."
|
| 179 |
)
|
| 180 |
COMPACT_WHEEL_LEGEND: str = (
|
| 181 |
-
"**
|
|
|
|
| 182 |
)
|
| 183 |
COMPACT_MAP_LEGEND: str = (
|
| 184 |
"Same scheme as 3D and wheel: charged vs hydrophobic distribution along the sequence."
|
|
@@ -187,8 +188,8 @@ COMPACT_MAP_LEGEND: str = (
|
|
| 187 |
|
| 188 |
def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2)) -> Any:
|
| 189 |
"""
|
| 190 |
-
|
| 191 |
-
|
| 192 |
"""
|
| 193 |
import matplotlib.pyplot as plt
|
| 194 |
from matplotlib import patheffects as pe
|
|
@@ -198,54 +199,94 @@ def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2))
|
|
| 198 |
fig, ax = plt.subplots(figsize=figsize, subplot_kw={"projection": "polar"})
|
| 199 |
fig.patch.set_facecolor("white")
|
| 200 |
if n == 0:
|
| 201 |
-
ax.set_facecolor("#
|
| 202 |
ax.set_title("Helical wheel (empty sequence)", pad=12)
|
| 203 |
return fig
|
| 204 |
|
| 205 |
-
ax.set_facecolor("#
|
| 206 |
|
| 207 |
angles_deg = np.array([i * 100.0 for i in range(n)], dtype=float) % 360.0
|
| 208 |
angles_rad = np.deg2rad(angles_deg)
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 213 |
th = angles_rad[i]
|
| 214 |
-
|
| 215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 216 |
t = ax.text(
|
| 217 |
-
|
| 218 |
-
|
| 219 |
aa,
|
| 220 |
ha="center",
|
| 221 |
va="center",
|
| 222 |
fontsize=fs,
|
| 223 |
-
color="#
|
| 224 |
fontweight="bold",
|
|
|
|
| 225 |
)
|
| 226 |
-
t.set_path_effects([pe.withStroke(linewidth=2.
|
| 227 |
|
| 228 |
-
ax.
|
| 229 |
-
ax.set_theta_direction(-1)
|
| 230 |
-
ax.set_ylim(0, 1.05)
|
| 231 |
ax.set_yticklabels([])
|
| 232 |
ax.set_xticklabels([])
|
| 233 |
ax.grid(False)
|
| 234 |
-
ax.set_title(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 235 |
return fig
|
| 236 |
|
| 237 |
|
| 238 |
def get_residue_style(aa: str) -> str:
|
|
|
|
| 239 |
positive = ["K", "R", "H"]
|
| 240 |
negative = ["D", "E"]
|
| 241 |
hydrophobic = ["A", "V", "I", "L", "M", "F", "W", "Y"]
|
| 242 |
if aa in positive:
|
| 243 |
-
return "background-color:
|
| 244 |
if aa in negative:
|
| 245 |
-
return "background-color:
|
| 246 |
if aa in hydrophobic:
|
| 247 |
-
return "background-color:
|
| 248 |
-
return "background-color:
|
| 249 |
|
| 250 |
|
| 251 |
def build_importance_map_html(sequence: str) -> str:
|
|
|
|
| 152 |
|
| 153 |
|
| 154 |
def residue_color_mpl(aa: str) -> str:
|
| 155 |
+
"""Matplotlib-compatible hex colors matching `get_residue_color` categories (high-contrast for plots)."""
|
| 156 |
cat = get_residue_color(aa)
|
| 157 |
return {
|
| 158 |
+
"blue": "#1D4ED8",
|
| 159 |
+
"red": "#DC2626",
|
| 160 |
+
"green": "#16A34A",
|
| 161 |
+
"gray": "#57534E",
|
| 162 |
+
}.get(cat, "#57534E")
|
| 163 |
|
| 164 |
|
| 165 |
HELIX_WHEEL_LEGEND_MARKDOWN: str = """
|
|
|
|
| 178 |
"Helix trace is an approximation, not an experimental structure."
|
| 179 |
)
|
| 180 |
COMPACT_WHEEL_LEGEND: str = (
|
| 181 |
+
"**Radial lines:** each residue around the wheel. **Black segments:** connect residue i → i+1 in sequence order "
|
| 182 |
+
"(classic helical-wheel “star” pattern). **Colors** match 3D: **blue** K,R,H · **red** D,E · **green** hydrophobic · **gray** other."
|
| 183 |
)
|
| 184 |
COMPACT_MAP_LEGEND: str = (
|
| 185 |
"Same scheme as 3D and wheel: charged vs hydrophobic distribution along the sequence."
|
|
|
|
| 188 |
|
| 189 |
def plot_helical_wheel(sequence: str, figsize: Tuple[float, float] = (6.2, 6.2)) -> Any:
|
| 190 |
"""
|
| 191 |
+
Detailed helical wheel (matplotlib polar): radial spokes, sequence-order connectors (i→i+1),
|
| 192 |
+
and colored residue disks — same chemistry classes as 3D / HTML maps (high-contrast colors).
|
| 193 |
"""
|
| 194 |
import matplotlib.pyplot as plt
|
| 195 |
from matplotlib import patheffects as pe
|
|
|
|
| 199 |
fig, ax = plt.subplots(figsize=figsize, subplot_kw={"projection": "polar"})
|
| 200 |
fig.patch.set_facecolor("white")
|
| 201 |
if n == 0:
|
| 202 |
+
ax.set_facecolor("#ffffff")
|
| 203 |
ax.set_title("Helical wheel (empty sequence)", pad=12)
|
| 204 |
return fig
|
| 205 |
|
| 206 |
+
ax.set_facecolor("#ffffff")
|
| 207 |
|
| 208 |
angles_deg = np.array([i * 100.0 for i in range(n)], dtype=float) % 360.0
|
| 209 |
angles_rad = np.deg2rad(angles_deg)
|
| 210 |
+
r_inner, r_ring = 0.06, 0.88
|
| 211 |
+
fs = max(7, min(11, int(220 / max(n, 1))))
|
| 212 |
+
pt_size = float(np.clip(8000.0 / max(n, 1), 130.0, 420.0))
|
| 213 |
+
|
| 214 |
+
ax.set_theta_zero_location("N")
|
| 215 |
+
ax.set_theta_direction(-1)
|
| 216 |
+
|
| 217 |
+
# Radial spokes (residue positions)
|
| 218 |
+
for i in range(n):
|
| 219 |
th = angles_rad[i]
|
| 220 |
+
ax.plot(
|
| 221 |
+
[th, th],
|
| 222 |
+
[r_inner, r_ring],
|
| 223 |
+
color="#1a1a1a",
|
| 224 |
+
linewidth=0.65,
|
| 225 |
+
alpha=0.45,
|
| 226 |
+
zorder=1,
|
| 227 |
+
)
|
| 228 |
+
|
| 229 |
+
# Sequence-order connections (straight chords in the plane — classic wheel “star”)
|
| 230 |
+
for i in range(n - 1):
|
| 231 |
+
ax.plot(
|
| 232 |
+
[angles_rad[i], angles_rad[i + 1]],
|
| 233 |
+
[r_ring, r_ring],
|
| 234 |
+
color="#0a0a0a",
|
| 235 |
+
linewidth=1.05,
|
| 236 |
+
solid_capstyle="round",
|
| 237 |
+
zorder=2,
|
| 238 |
+
)
|
| 239 |
+
|
| 240 |
+
colors = [residue_color_mpl(aa) for aa in clean]
|
| 241 |
+
ax.scatter(
|
| 242 |
+
angles_rad,
|
| 243 |
+
np.full(n, r_ring),
|
| 244 |
+
s=pt_size,
|
| 245 |
+
c=colors,
|
| 246 |
+
edgecolors="#111111",
|
| 247 |
+
linewidths=1.2,
|
| 248 |
+
zorder=4,
|
| 249 |
+
)
|
| 250 |
+
|
| 251 |
+
for i, aa in enumerate(clean):
|
| 252 |
t = ax.text(
|
| 253 |
+
angles_rad[i],
|
| 254 |
+
r_ring,
|
| 255 |
aa,
|
| 256 |
ha="center",
|
| 257 |
va="center",
|
| 258 |
fontsize=fs,
|
| 259 |
+
color="#0a0a0a",
|
| 260 |
fontweight="bold",
|
| 261 |
+
zorder=5,
|
| 262 |
)
|
| 263 |
+
t.set_path_effects([pe.withStroke(linewidth=2.2, foreground="white")])
|
| 264 |
|
| 265 |
+
ax.set_ylim(0, 1.0)
|
|
|
|
|
|
|
| 266 |
ax.set_yticklabels([])
|
| 267 |
ax.set_xticklabels([])
|
| 268 |
ax.grid(False)
|
| 269 |
+
ax.set_title(
|
| 270 |
+
"Helical wheel (α-helix, 100°/residue) — spokes + sequence connectors",
|
| 271 |
+
pad=14,
|
| 272 |
+
fontsize=11,
|
| 273 |
+
color="#111111",
|
| 274 |
+
)
|
| 275 |
return fig
|
| 276 |
|
| 277 |
|
| 278 |
def get_residue_style(aa: str) -> str:
|
| 279 |
+
"""Inline styles for sequence map — colors aligned with wheel / 3D categories (high contrast)."""
|
| 280 |
positive = ["K", "R", "H"]
|
| 281 |
negative = ["D", "E"]
|
| 282 |
hydrophobic = ["A", "V", "I", "L", "M", "F", "W", "Y"]
|
| 283 |
if aa in positive:
|
| 284 |
+
return "background-color: #1D4ED8; color: #ffffff; padding: 2px 3px; border-radius: 2px;"
|
| 285 |
if aa in negative:
|
| 286 |
+
return "background-color: #DC2626; color: #ffffff; padding: 2px 3px; border-radius: 2px;"
|
| 287 |
if aa in hydrophobic:
|
| 288 |
+
return "background-color: #16A34A; color: #ffffff; padding: 2px 3px; border-radius: 2px;"
|
| 289 |
+
return "background-color: #57534E; color: #ffffff; padding: 2px 3px; border-radius: 2px;"
|
| 290 |
|
| 291 |
|
| 292 |
def build_importance_map_html(sequence: str) -> str:
|