-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixed rf, changed name in navbar and some more stylistic choices done
- Loading branch information
1 parent
7feac19
commit f7531f1
Showing
13 changed files
with
202 additions
and
4,430 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,17 @@ | ||
# QSAR IN BROWSER | ||
# QSAR IN THE BROWSER (QITB) | ||
|
||
### Why is QITB? | ||
This project was borne out of a virtual screening campaign that was conducted for my MPhil thesis. I was given a biological target and told to explore AI/ML strategies to find agonists for it. I learned a ton and attended a lot of talks and conferences. There I met a man who after a very complex talk was like, do you have a UI for this where I could just press a button? And, from there this project was borne to strip all the programming and only keep the chemistry, with some options to dig into the complexities of various algorithms. | ||
For some, it will be a quick and dirty tool to analyze various targets and drugs to work on while for others it will be their foray into cheminformatics and analysis of small molecules. The hope is to make this website a powerful and versatile tool. | ||
|
||
### Where is QITB? | ||
Just in your browser. All the code runs in the browser. Using WASM and JS The technologies include: | ||
|
||
- Next.js. Don't ask me why. I think it's for SEO. SSR with normal React is horrendous. SveltKit is in its infancy compared to React and I don't know how to use Vue. I have a penchant for headaches. That too! | ||
- (RDKit JS)[https://github.com/rdkit/rdkit/tree/master/Code/MinimalLib]. This is a custom build of the JS version with hopes for more contributions to the RDKit JS repo to import more functionalities into QITB. | ||
- Pyodide and Scikit-Learn. | ||
- tSNE adapted for (Karpathy's)[https://github.com/karpathy/tsnejs] JavaScript implementation. | ||
- PCA from ml-pca | ||
|
||
### How is QITB? | ||
You tell? Found an issue and want to fix it? Please feel free to make a PR. I am a noob when it comes to programming so will look forward to people turning and tossing my code to make it better. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,127 +1,106 @@ | ||
import { useContext, useEffect, useState } from "react"; | ||
import LigandContext from "../context/LigandContext"; | ||
import { RandomForestRegression as RFRegression } from 'ml-random-forest'; | ||
import { initRDKit } from './utils/rdkit_loader' | ||
import * as tf from '@tensorflow/tfjs' | ||
import * as sk from 'scikitjs' | ||
sk.setBackend(tf) | ||
|
||
import { initRDKit } from './utils/rdkit_loader'; | ||
import Script from "next/script"; | ||
import Loader from './Loader'; | ||
import GroupedBarChart from "./BarChart"; | ||
|
||
export default function RandomForest(){ | ||
const { ligand } = useContext(LigandContext); | ||
const [rfRun, setRFRun] = useState(false); | ||
const [rfMAE, setRFMAE] = useState(0); | ||
const [rfModel, setRFModelState] = useState({}) | ||
const [testSMILES, settestSMILES] = useState('C1=NC(=C2C(=N1)N(C=N2)C3C(C(C(O3)CO)O)O)N') | ||
|
||
const [RDKit, setRDKit] = useState(null); | ||
const [stateOfRDKit, setStateOfRDKit] = useState(false); | ||
|
||
const [predictedRFMAE, setPredictedRFMAE] = useState('Yet to be predicted') | ||
|
||
useEffect(() => { | ||
setTimeout(() => { | ||
rfRunner(); | ||
}, 1000) | ||
|
||
async function loadRDKit() { | ||
const RDK = await initRDKit() | ||
setRDKit(RDK); | ||
setStateOfRDKit(true); | ||
} | ||
loadRDKit(); | ||
}, []); | ||
|
||
const rfRunner = () => { | ||
setRFRun(false) | ||
var X = ligand.map((obj) => obj.fingerprint); | ||
var y = ligand.map((obj) => obj.pKi); | ||
|
||
const kf = new sk.KFold({ nSplits: 5 }); | ||
|
||
const mae_through_folds = [] | ||
const r2_through_folds = [] | ||
for (const { trainIndex, testIndex } of kf.split(X, y)) { | ||
try { | ||
const regression = new RFRegression({ | ||
seed: 3, | ||
maxFeatures: 0.5, | ||
replacement: false, | ||
nEstimators: 80 | ||
}); | ||
|
||
const XTrain = Array.from(trainIndex.dataSync()).map(i => X[i]); | ||
const yTrain= Array.from(trainIndex.dataSync()).map(index => y[index]); | ||
regression.train(XTrain, yTrain); | ||
|
||
const XTest = Array.from(testIndex.dataSync()).map(i => X[i]); | ||
const yTest= Array.from(testIndex.dataSync()).map(i => y[i]); | ||
const result = regression.predict(XTest); | ||
|
||
let mae_test = sk.metrics.meanAbsoluteError(yTest, result); | ||
let r2_test = sk.metrics.r2Score(yTest, result); | ||
|
||
console.log(mae_test, r2_test) | ||
|
||
mae_through_folds.push(mae_test) | ||
r2_through_folds.push(r2_test) | ||
} | ||
finally { | ||
trainIndex.dispose() | ||
testIndex.dispose() | ||
} | ||
} | ||
import GroupedBarChart from './BarChart'; | ||
|
||
export default function RandomForest() { | ||
const { ligand } = useContext(LigandContext); | ||
const [pyodide_ins, setPyodide] = useState(false) | ||
const [pyodideState, setPyodideState] = useState(false) | ||
|
||
const [cpuNum, setCpuNum] = useState(-1); | ||
const [maxFeats, setMaxFeats] = useState('None'); | ||
const [criterion, setCriterion] = useState('poisson'); | ||
const [nEstimators, setNEstimators] = useState(120); | ||
|
||
const [results, setResults] = useState([]) | ||
|
||
globalThis.fp = ligand.map((obj) => obj.fingerprint); | ||
globalThis.pKi = ligand.map((obj) => obj.pKi); | ||
|
||
async function pyodideLoaded() { | ||
loadPyodide().then((pyodide) => { | ||
pyodide.loadPackage(['scikit-learn', 'numpy']).then(() => { | ||
setPyodide(pyodide) | ||
setPyodideState(true) | ||
}) | ||
}) | ||
} | ||
|
||
function runRandForestModel() { | ||
pyodide_ins.runPython(` | ||
import js | ||
from sklearn.ensemble import RandomForestRegressor | ||
from sklearn.metrics import mean_absolute_error, r2_score | ||
from sklearn.model_selection import KFold | ||
import numpy as np | ||
param = { | ||
"n_estimators": 120, | ||
"criterion": "poisson", | ||
"max_features" : None, | ||
} | ||
model = RandomForestRegressor(**param, n_jobs = -1) | ||
const regression2 = new RFRegression({ | ||
seed: 3, | ||
maxFeatures: 0.5, | ||
replacement: false, | ||
nEstimators: 80 | ||
}); | ||
X = (js.globalThis.fp).to_py() | ||
y = (js.globalThis.pKi).to_py() | ||
regression2.train(X, y); | ||
setRFModelState(regression2.toJSON()); | ||
setRFMAE([mae_through_folds, r2_through_folds]); | ||
setRFRun(true) | ||
} | ||
X = np.array(X) | ||
y = np.array(y) | ||
const predictionerRF = () => { | ||
setPredictedRFMAE('Processing....'); | ||
const mol = RDKit.get_mol(testSMILES); | ||
const mol_fp = mol.get_morgan_fp_as_uint8array(JSON.stringify({ radius: 2, nBits: 2048 })); | ||
const regression = RFRegression.load(rfModel); | ||
const result = regression.predict([mol_fp]); | ||
setPredictedRFMAE(result); | ||
mol?.delete() | ||
} | ||
kf = KFold(n_splits=10, shuffle=True) | ||
if (!rfRun) { | ||
return( | ||
<div className="main-container"> | ||
<div className="container"> | ||
<Loader /> | ||
</div> | ||
</div> | ||
) | ||
} | ||
metrics = [] | ||
for train, test in kf.split(X, y): | ||
trainX = X[train] | ||
trainY = y[train] | ||
testX = X[test] | ||
testY = y[test] | ||
params = {'n_estimators': ${nEstimators}, 'criterion': '${criterion}', 'max_features': ${maxFeats}, 'n_jobs' : ${cpuNum}} | ||
model = RandomForestRegressor(**params) | ||
return( | ||
<div className="main-container"> | ||
<div className="container"> | ||
{stateOfRDKit ? (<span>RDKit is Loaded ✅</span>) : (<span>Loading RDKit</span>)} | ||
model.fit(trainX, trainY) | ||
pred = model.predict(testX) | ||
metric = [mean_absolute_error(testY, pred), r2_score(testY, pred)] | ||
<h3>Random Forest performance across 5 folds</h3> | ||
<GroupedBarChart mae = {rfMAE[0]} r2={rfMAE[1]} /> | ||
print(metric) | ||
metrics.append(metric) | ||
<input className="input" type="text" placeholder = {testSMILES} onChange={(e) => settestSMILES(e.target.value)}></input> | ||
<br></br> | ||
<button className="button" onClick={predictionerRF}>Predict on SMILES</button> | ||
<br></br><br></br> | ||
<span>{predictedRFMAE}</span> | ||
</div> | ||
</div> | ||
js.metrics = metrics | ||
` | ||
) | ||
const results = metrics.toJs(); | ||
const results_mae = results.map((arr) => arr[0]); | ||
const results_r2 = results.map((arr) => arr[1]); | ||
setResults([results_mae, results_r2]) | ||
} | ||
return ( | ||
<div className="main-container"> | ||
<Script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js" onLoad={pyodideLoaded}></Script> | ||
{pyodideState ? ( | ||
<div className="container"> | ||
<div> | ||
<label>N_Estimators</label> | ||
<input className="input" value = {nEstimators} onChange={(e) => setNEstimators(e.target.value)}></input> | ||
<br /> | ||
<label>Criterion</label> | ||
<input className="input" value = {criterion} onChange={(e) => setCriterion(e.target.value)}></input> | ||
<br /> | ||
<label>Max_features</label> | ||
<input className="input" value = {maxFeats} onChange={(e) => setMaxFeats(e.target.value)}></input> | ||
<br /> | ||
<label>CPU No:</label> | ||
<input type = 'number' value = {cpuNum} onChange={(e) => setCpuNum(e.target.value)} className="input"></input> | ||
</div> | ||
<br /> | ||
<button onClick={runRandForestModel} className="button">Run Random Forest Model</button> | ||
{results.length > 0 ? <GroupedBarChart mae = {results[0]} r2 = {results[1]} /> : <></>} | ||
</div> | ||
): <Loader loadingText="Loading Scikit and Numpy"/>} | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,34 +1,11 @@ | ||
const CopyPlugin = require("copy-webpack-plugin"); | ||
const { PyodidePlugin } = require("@pyodide/webpack-plugin"); | ||
|
||
const nextConfig = { | ||
// webpack(config, { isServer }) { | ||
// config.plugins.push( | ||
// new CopyPlugin({ | ||
// patterns: [ | ||
// { | ||
// from: "node_modules/@rdkit/rdkit/dist/RDKit_minimal.wasm", | ||
// to: "static/chunks" | ||
// } | ||
// ] | ||
// }), | ||
// new PyodidePlugin(), | ||
// ); | ||
|
||
// if (!isServer) { | ||
// config.resolve.fallback = { | ||
// fs: false | ||
// }; | ||
// } | ||
|
||
// return config; | ||
// }, | ||
|
||
const nextConfig = { | ||
output: "export", | ||
basePath: "/sar-in-browser", | ||
basePath: "/qsar-in-browser", | ||
images: { | ||
unoptimized: true, | ||
}, | ||
}; | ||
|
||
module.exports = nextConfig; | ||
module.exports = nextConfig; |
Oops, something went wrong.