jannisborn's picture
update
0fcd1da unverified
Raw
History Blame
3.61 kB
import html
import logging
from typing import List, Tuple
import numpy as np
import pandas as pd
from rdkit import Chem
from rdkit.Chem import Draw
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
def _draw_molecule_svg(smiles: str, size: Tuple[int, int]) -> str:
mol = Chem.MolFromSmiles(smiles)
if mol is None:
return f"<pre class='gt4sd-smiles'>{html.escape(smiles)}</pre>"
svg = Draw.MolsToGridImage([mol], molsPerRow=1, subImgSize=size, useSVG=True)
return str(svg).replace("<?xml version='1.0' encoding='iso-8859-1'?>", "")
def _draw_static_grid(
result_df: pd.DataFrame, n_cols: int, size: Tuple[int, int]
) -> str:
columns = [column for column in result_df.columns if column != "SMILES"]
cards = []
for _, row in result_df.iterrows():
smiles = str(row["SMILES"])
details = "".join(
f"<dt>{html.escape(str(column))}</dt>"
f"<dd>{html.escape(str(row[column]))}</dd>"
for column in columns
)
cards.append(
"<section class='gt4sd-molecule-card'>"
f"<div class='gt4sd-molecule-image'>{_draw_molecule_svg(smiles, size)}</div>"
f"<dl class='gt4sd-molecule-details'>{details}</dl>"
"</section>"
)
return f"""
<style>
.gt4sd-molecule-grid {{
display: grid;
grid-template-columns: repeat({max(1, n_cols)}, minmax(180px, 1fr));
gap: 12px;
width: 100%;
max-height: 900px;
overflow: auto;
}}
.gt4sd-molecule-card {{
border: 1px solid #e5e7eb;
border-radius: 6px;
background: #fff;
padding: 12px;
min-width: 0;
}}
.gt4sd-molecule-image svg {{
width: 100%;
max-width: {size[0]}px;
height: auto;
display: block;
margin: 0 auto 8px;
}}
.gt4sd-molecule-details {{
display: grid;
grid-template-columns: max-content minmax(0, 1fr);
gap: 4px 10px;
margin: 0;
font: 13px/1.35 system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}}
.gt4sd-molecule-details dt {{ color: #6b7280; font-weight: 600; }}
.gt4sd-molecule-details dd {{ margin: 0; min-width: 0; overflow-wrap: anywhere; }}
.gt4sd-smiles {{ white-space: pre-wrap; overflow-wrap: anywhere; }}
@media (max-width: 720px) {{
.gt4sd-molecule-grid {{ grid-template-columns: 1fr; }}
}}
</style>
<div class='gt4sd-molecule-grid'>
{''.join(cards)}
</div>
"""
def draw_grid_predict(
sequences: List[str], properties: np.array, property_names: List[str], domain: str
) -> str:
"""
Uses RDKit SVGs to draw a HTML grid for the prediction
Args:
sequences: Sequences for which properties are predicted.
properties: Predicted properties. Array of shape (n_samples, n_properties).
names: List of property names
domain: Domain of the prediction (molecules or proteins).
Returns:
HTML to display
"""
if domain not in ["Molecules", "Proteins"]:
raise ValueError(f"Unsupported domain {domain}")
if domain == "Proteins":
converter = lambda x: Chem.MolToSmiles(Chem.MolFromFASTA(x))
else:
converter = lambda x: x
smiles = []
for sequence in sequences:
try:
seq = converter(sequence)
smiles.append(seq)
except Exception:
logger.warning(f"Could not draw sequence {seq}")
result = pd.DataFrame({"SMILES": smiles})
for i, name in enumerate(property_names):
result[name] = properties[:, i]
n_cols = min(3, len(result))
size = (140, 200) if len(result) > 3 else (600, 700)
return _draw_static_grid(result, n_cols=n_cols, size=size)