Embed molecular editor into Streamlit app #streamlit #chemoinformatics #RDKit

I wrote some posts about usage of combination chemoinformatics and streamlit. One was predictive model application which was used rdkit and scikit-learn. When I tweeted that, Jan Jansen (who is Great quantum chemist and I met him RDKit UGM!!!) commented me that it is useful that if molecular drawer can use in the app ;) I think so too and I would like to do it. However I didn’t know how to do that.

Now I’m in winter vacation so I tried to implement JSME of streamlit.

To communicate Python <> Javascript interactively through the streamlit, node.js React.js should be used. For convenience, I used jsme-react to make front end. Fortunately simple template example is provided from streamlit documentation page. So most of code is borrowed from the example code and added compound drawer page for my app.

The folder structure is below.

├── app.py
├── datamaker.py
├── frontend
│   ├── package.json
│   ├── package-lock.json
│   ├── public
│   │   ├── index.html
│   │   └── manifest.json
│   ├── src
│   │   ├── App.js
│   │   ├── index.css
│   │   └── index.js
│   └── yarn.lock
├── __init__.py
├── makerf.py
└── requirements.txt

I’m not so familiar to typescript so I didn’t use type script ( need learn ASAP … :) ) . Front end application is stored in frontend holder. And to launch the app, I installed node and npm then type following command.

$cd frontend
$npm install
$npm start
# service will launch at localhost:3001
To prevent launch the browser, .env file should be put in frontend folder with following code.
# Run the component's dev server on :3001
# (The Streamlit dev server already runs on :3000)
PORT=3001

# Don't automatically open the web browser on `npm run start`.
#BROWSER=none
~               

And main part of front end is App.js.

#App.js
import React, { Component } from 'react'

import { Jsme } from 'jsme-react'
import {
  Streamlit,
  StreamlitComponentBase,
  withStreamlitConnection
} from "streamlit-component-lib";

export class App extends  StreamlitComponentBase {
  logSmiles(smiles) {
    console.log(smiles)
    Streamlit.setComponentValue(smiles)
  }
  render () {
    return (
      <div hieght="350px" width="500px">
        <Jsme height="300px" width="500px" smiles='CC=O' options="oldlook,star" onChange={this.logSmiles} />
      </div>
    )
  }
}

export default withStreamlitConnection(App)

Streamlit.setComponentValue function store SMILES string from jsme and pass it to python. And end of the code, withStreamlitConnenction is very important. It connect python-js interactively, it means that smiles value will be changed when user change the target structure.

Ok, next go to python code app.py

#app.py
import pickle
import datamaker
import streamlit as st
st.title('Streamlit + RDKit :rocket:')
from rdkit import Chem
from rdkit.Chem import Draw
from rdkit.Chem.Draw import SimilarityMaps
from io import BytesIO
from functools import partial
from PIL import Image
from rdkit.Chem.Draw import rdDepictor
rdDepictor.SetPreferCoordGen(True)

import streamlit.components.v1 as components

_RELEASE = False

if not _RELEASE:
    _component_func = components.declare_component(
            "chemstreamlit",
            url="http://localhost:3001"
            )
else:
    parent_dir = os.path.dirname(os.path.abspath(__file__))
    build_dir = os.path.join(parent_dir, "frontend/build")
    _component_func = components.declare_component("my_component", path=build_dir)

def my_component():
    component_value = _component_func()
    return component_value

res = my_component()
rfc = pickle.load(open('rf.pkl', 'rb'))
fpfunc = partial(SimilarityMaps.GetMorganFingerprint, radius=2)


mol = Chem.MolFromSmiles(res)
fp = [datamaker.mol2fp(mol)]
kls = rfc.predict(fp)

st.write('predicted class:', datamaker.rclasses[kls[0]])
img = Draw.MolToImage(mol)
bio = BytesIO()
img.save(bio, format='png')
st.image(img)

components.declare_component makes front end component and embed it in app and call my_component, python side streamlit get SMILES strings from JSME. Now app.py can use SMILES with rdkit! After that almost there. I think it’s not required for details about mol2fp and how to use fp to prediction.

Here is an example of the app. Predicted solubility class is dynamically changed depends on drawings.

example movie

In summary, embed JS on streamlit expands possibility of application but it is little bit complex for not JS familiar people like me… :)

Today’s code is uploaded following URL

https://github.com/iwatobipen/chem_streamlit/tree/main/chemstreamlit_js

Published by iwatobipen

I'm medicinal chemist in mid size of pharmaceutical company. I love chemoinfo, cording, organic synthesis, my family.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.