Skip to content

Instantly share code, notes, and snippets.

@pstjohn
Last active September 5, 2019 22:53
Show Gist options
  • Save pstjohn/b7e2f05136bf8bfd22f68f375437452d to your computer and use it in GitHub Desktop.
Save pstjohn/b7e2f05136bf8bfd22f68f375437452d to your computer and use it in GitHub Desktop.

YSI Prediction and Analysis code

Flattened jupyter notebooks containing the analysis workflow in the manuscript A quantitative model for the prediction of sooting tendency from molecular structure.

Full repository (with data files) is available at pstjohn/ysi_qsar_energy_fuels

name: ysi
channels:
- https://conda.anaconda.org/rdkit
dependencies:
- python=3.5
- jupyter
- pandas
- seaborn
- rdkit
- jinja2
- pip:
- https://github.com/pstjohn/scikit-learn/archive/pstjohn-changes.zip
- pubchempy
- keras==0.3.3
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\n",
"import pandas as pd\n",
"\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"sns.set_style('darkgrid')\n",
"sns.set_context('talk', font_scale=1.5)\n",
"sns.set(color_codes=True)\n",
"\n",
"%matplotlib inline"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"collapsed": false
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"Using Theano backend.\n"
]
}
],
"source": [
"import warnings\n",
"with warnings.catch_warnings():\n",
" warnings.simplefilter(\"ignore\")\n",
"\n",
" from ysi_utils.models import outlier_model, bagging_model, ensemble_predict\n",
" ensemble_predict.bagging_model = bagging_model()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from ysi_utils.descriptors import dragon_ron\n",
"from ysi_utils.data import ron, low, addl_mols\n",
"from ysi_utils.tools.descriptors import get_element_dict, get_DBE\n",
"from ysi_utils.tools import chemical_conversions\n",
"\n",
"#ron.SMILES = ron.SMILES.apply(chemical_conversions.canonicalize_smiles)\n",
"\n",
"valid_ron = ron[~ron.RON.str.contains('>')].copy()\n",
"valid_ron.RON = valid_ron.RON.astype('float')"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"ron_df = valid_ron.set_index('SMILES')\n",
"ron_df['inlier'] = outlier_model.predict(dragon_ron.loc[ron_df.index]) == 1"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>IUPAC name</th>\n",
" <th>CAS</th>\n",
" <th>RON</th>\n",
" <th>source RON</th>\n",
" <th>inlier</th>\n",
" <th>YSI</th>\n",
" <th>YSI_std</th>\n",
" </tr>\n",
" <tr>\n",
" <th>SMILES</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>C=CCCCC</th>\n",
" <td>1-Hexene</td>\n",
" <td>592-41-6</td>\n",
" <td>76.0</td>\n",
" <td>API Report</td>\n",
" <td>True</td>\n",
" <td>15.235844</td>\n",
" <td>1.730438</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CC(C)=CC(C)C</th>\n",
" <td>2,4-dimethyl-2-pentene</td>\n",
" <td>625-65-0</td>\n",
" <td>100.0</td>\n",
" <td>API Report</td>\n",
" <td>True</td>\n",
" <td>58.909860</td>\n",
" <td>5.397653</td>\n",
" </tr>\n",
" <tr>\n",
" <th>Cc1ccc(C)o1</th>\n",
" <td>2,5-Dimethylfuran</td>\n",
" <td>625-86-5</td>\n",
" <td>101.3</td>\n",
" <td>API Report</td>\n",
" <td>False</td>\n",
" <td>73.031421</td>\n",
" <td>11.812839</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CC(C)CCCC(C)C</th>\n",
" <td>2,6- Dimethylheptane</td>\n",
" <td>1072-05-5</td>\n",
" <td>36.0</td>\n",
" <td>Ind. Eng. Chem., 1941, 33 (4), pp 554-560</td>\n",
" <td>True</td>\n",
" <td>49.168216</td>\n",
" <td>3.759263</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CCC(C)O</th>\n",
" <td>2-butanol</td>\n",
" <td>78-92-2</td>\n",
" <td>107.0</td>\n",
" <td>Christensen, 2011</td>\n",
" <td>True</td>\n",
" <td>-7.394225</td>\n",
" <td>1.031740</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" IUPAC name CAS RON \\\n",
"SMILES \n",
"C=CCCCC 1-Hexene 592-41-6 76.0 \n",
"CC(C)=CC(C)C 2,4-dimethyl-2-pentene 625-65-0 100.0 \n",
"Cc1ccc(C)o1 2,5-Dimethylfuran 625-86-5 101.3 \n",
"CC(C)CCCC(C)C 2,6- Dimethylheptane 1072-05-5 36.0 \n",
"CCC(C)O 2-butanol 78-92-2 107.0 \n",
"\n",
" source RON inlier YSI \\\n",
"SMILES \n",
"C=CCCCC API Report True 15.235844 \n",
"CC(C)=CC(C)C API Report True 58.909860 \n",
"Cc1ccc(C)o1 API Report False 73.031421 \n",
"CC(C)CCCC(C)C Ind. Eng. Chem., 1941, 33 (4), pp 554-560 True 49.168216 \n",
"CCC(C)O Christensen, 2011 True -7.394225 \n",
"\n",
" YSI_std \n",
"SMILES \n",
"C=CCCCC 1.730438 \n",
"CC(C)=CC(C)C 5.397653 \n",
"Cc1ccc(C)o1 11.812839 \n",
"CC(C)CCCC(C)C 3.759263 \n",
"CCC(C)O 1.031740 "
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"predictions = pd.DataFrame(ensemble_predict(dragon_ron.loc[ron_df.index]), index=ron_df.index)\n",
"ron_df['YSI'] = predictions.mean(1)\n",
"ron_df['YSI_std'] = predictions.std(1)\n",
"ron_df.head()"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"low_join = low.append(addl_mols.rename(columns={'Name': 'Species'}))"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"measured_ysi = low_join.loc[low_join.SMILES.isin(ron_df.index)].set_index('SMILES')['YSI']\n",
"ron_df['measured_ysi'] = ron_df.index.isin(measured_ysi.index)\n",
"ron_df.loc[measured_ysi.index, 'YSI'] = measured_ysi\n",
"ron_df.loc[measured_ysi.index, 'YSI_std'] = 0.58"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/plain": [
"'Measured values for 58 of 92 fuels'"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"'Measured values for {0} of {1} fuels'.format(len(measured_ysi),len(ron_df))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Drop points that neither have a measured YSI value nor pass the applicability domain test"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"ron_df = ron_df[~((~ron_df.inlier) & (~ron_df.measured_ysi))]"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"from ysi_utils.tools.descriptors import get_element_dict\n",
"is_oxygenate = ron_df.reset_index().SMILES.apply(get_element_dict).fillna(0)['O'] > 0\n",
"is_oxygenate.index = ron_df.index\n",
"\n",
"ron_df['type'] = 'hydrocarbon'\n",
"ron_df.loc[is_oxygenate, 'type'] = 'oxygenate'"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"colors = pd.Series(['b', 'r']).loc[is_oxygenate.astype(int)].values"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"image/png": "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\n",
"text/plain": [
"<matplotlib.figure.Figure at 0x164a64dd8>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from matplotlib.lines import Line2D\n",
"\n",
"with sns.axes_style('ticks'):\n",
" fig = plt.figure(figsize=(4,4))\n",
" ax = fig.add_subplot(111)\n",
" \n",
" for ron, ysi, err, m, c in zip(ron_df.RON, ron_df.YSI, ron_df.YSI_std, markers, colors):\n",
" \n",
" \n",
" #ax.errorbar(ron, ysi, yerr=1.96*err, marker=m, c=c, markersize=ms,\n",
" # markeredgewidth=lw, elinewidth=1, capsize=0)\n",
" \n",
" ax.plot(ron, ysi, marker='o', c=c, markersize=4.5, markeredgewidth=0)\n",
" \n",
" hydrocarbon = Line2D([1],[1], color='b', marker='o', markerfacecolor='b', lw=0, markersize=4.5)\n",
" oxygenate = Line2D([1],[1], color='b', marker='o', markerfacecolor='r', lw=0, markersize=4.5)\n",
" #aromatic = Line2D([1],[1], color='b', marker='x', markerfacecolor='r', lw=0, markersize=5, markeredgewidth=2)\n",
" #oaromatic = Line2D([1],[1], color='r', marker='x', markerfacecolor='r', lw=0, markersize=5, markeredgewidth=2)\n",
"\n",
"\n",
" \n",
" ax.set_xlim([0, 140])\n",
" ax.set_ylim([-40, 100])\n",
"\n",
" \n",
" ax.legend((hydrocarbon, oxygenate),\n",
" ('Hydrocarbon', 'Oxygenate'), loc='lower left', ncol=1)\n",
" sns.despine()\n",
" \n",
" ax.set_xlabel('Research Octane Number')\n",
" ax.set_ylabel('Yield Sooting Index')\n",
" \n",
" fig.tight_layout()\n",
" fig.savefig('ysi_vs_ron.svg')"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"output_df = ron_df.copy()\n",
"output_df['YSI source'] = 'Model Prediction'\n",
"output_df.loc[measured_ysi.index, 'YSI source'] = 'McEnally & Pfefferle, 2011'\n",
"output_df['RON source'] = output_df['source RON']\n",
"output_df = output_df.drop(['type', 'source RON'], 1)\n",
"output_df.reset_index(drop=True).to_csv('ron_ysi_predictions.csv', index=False, float_format='%.3f')"
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>IUPAC name</th>\n",
" <th>CAS</th>\n",
" <th>RON</th>\n",
" <th>source RON</th>\n",
" <th>inlier</th>\n",
" <th>YSI</th>\n",
" <th>YSI_std</th>\n",
" <th>measured_ysi</th>\n",
" <th>type</th>\n",
" </tr>\n",
" <tr>\n",
" <th>SMILES</th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" <th></th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>CO</th>\n",
" <td>Methanol</td>\n",
" <td>67-56-1</td>\n",
" <td>109.0</td>\n",
" <td>Anderson, et al., 2010</td>\n",
" <td>False</td>\n",
" <td>-36.9</td>\n",
" <td>0.58</td>\n",
" <td>True</td>\n",
" <td>oxygenate</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CCO</th>\n",
" <td>Ethanol</td>\n",
" <td>64-17-5</td>\n",
" <td>109.0</td>\n",
" <td>Hunwartzen, I SAE Paper 82002, 1982. DOI: 10.4...</td>\n",
" <td>False</td>\n",
" <td>-31.1</td>\n",
" <td>0.58</td>\n",
" <td>True</td>\n",
" <td>oxygenate</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CC(C)=O</th>\n",
" <td>2-Propanone</td>\n",
" <td>67-64-1</td>\n",
" <td>111.3</td>\n",
" <td>NaN</td>\n",
" <td>False</td>\n",
" <td>-26.9</td>\n",
" <td>0.58</td>\n",
" <td>True</td>\n",
" <td>oxygenate</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CCOC(C)=O</th>\n",
" <td>Acetic acid ethyl ester</td>\n",
" <td>141-78-6</td>\n",
" <td>118.0</td>\n",
" <td>NREL, March 2016 JCE-4-42309-01, WO #56</td>\n",
" <td>True</td>\n",
" <td>-26.4</td>\n",
" <td>0.58</td>\n",
" <td>True</td>\n",
" <td>oxygenate</td>\n",
" </tr>\n",
" <tr>\n",
" <th>CCCO</th>\n",
" <td>n-Propanol</td>\n",
" <td>71-23-8</td>\n",
" <td>104.0</td>\n",
" <td>Christensen, 2011</td>\n",
" <td>True</td>\n",
" <td>-22.0</td>\n",
" <td>0.58</td>\n",
" <td>True</td>\n",
" <td>oxygenate</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" IUPAC name CAS RON \\\n",
"SMILES \n",
"CO Methanol 67-56-1 109.0 \n",
"CCO Ethanol 64-17-5 109.0 \n",
"CC(C)=O 2-Propanone 67-64-1 111.3 \n",
"CCOC(C)=O Acetic acid ethyl ester 141-78-6 118.0 \n",
"CCCO n-Propanol 71-23-8 104.0 \n",
"\n",
" source RON inlier YSI \\\n",
"SMILES \n",
"CO Anderson, et al., 2010 False -36.9 \n",
"CCO Hunwartzen, I SAE Paper 82002, 1982. DOI: 10.4... False -31.1 \n",
"CC(C)=O NaN False -26.9 \n",
"CCOC(C)=O NREL, March 2016 JCE-4-42309-01, WO #56 True -26.4 \n",
"CCCO Christensen, 2011 True -22.0 \n",
"\n",
" YSI_std measured_ysi type \n",
"SMILES \n",
"CO 0.58 True oxygenate \n",
"CCO 0.58 True oxygenate \n",
"CC(C)=O 0.58 True oxygenate \n",
"CCOC(C)=O 0.58 True oxygenate \n",
"CCCO 0.58 True oxygenate "
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"ron_df[ron_df.type == 'oxygenate'].sort_values('YSI').head()"
]
}
],
"metadata": {
"anaconda-cloud": {},
"kernelspec": {
"display_name": "Python [conda env:ysi]",
"language": "python",
"name": "conda-env-ysi-py"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 0
}
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
import numpy as np
np.random.seed(0)
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.wrappers.scikit_learn import KerasRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import (StandardScaler, Normalizer, RobustScaler,
MaxAbsScaler, Imputer)
from sklearn.decomposition import PCA
from sklearn.feature_selection import VarianceThreshold, RFE, SelectKBest, f_regression
from sklearn import svm
from ysi_utils.descriptors import dragon
from ysi_utils.validation import y_train as y
from ysi_utils.validation import y_test
X = dragon.loc[y.index]
X_test = dragon.loc[y_test.index]
# These are the optimal parameters as selected by the hyperopt fitting routine.
params = \
{'activation': 'relu',
'batch_size': 39,
'dropout1': 0.36632971139053216,
'dropout2': 0.030115015871571206,
'feature_extractor': {'n_features_to_select': 390, 'step': 10, 'type': 'RFE'},
'nb_epochs': 100,
'num_layers': {'dropout3': 0.31435729897990794,
'layers': 'three',
'units3': 161},
'optimizer': 'adam',
'preprocessor': 'MaxAbsScaler',
'units1': 140,
'units2': 140}
def get_input_size(params):
if 'n_components' in params['feature_extractor']:
return params['feature_extractor']['n_components']
elif 'k' in params['feature_extractor']:
return params['feature_extractor']['k']
elif 'n_features_to_select' in params['feature_extractor']:
return params['feature_extractor']['n_features_to_select']
def build_model(params=params):
model = Sequential()
model.add(Dense(output_dim=params['units1'],
input_dim=get_input_size(params)))
model.add(Activation(params['activation']))
model.add(Dropout(params['dropout1']))
model.add(Dense(output_dim=params['units2'], init="glorot_uniform"))
model.add(Activation(params['activation']))
model.add(Dropout(params['dropout2']))
if params['num_layers']['layers']== 'three':
model.add(Dense(output_dim=params['num_layers']['units3'], init="glorot_uniform"))
model.add(Activation(params['activation']))
model.add(Dropout(params['num_layers']['dropout3']))
model.add(Dense(output_dim=1))
model.compile(loss='mae', optimizer=params['optimizer'])
return model
ffann = KerasRegressor(build_fn=build_model,
nb_epoch=params['nb_epochs'],
batch_size=params['batch_size'],
verbose=0)
# Set up preprocessing pipeline
imputer = Imputer()
var_filter = VarianceThreshold()
preprocessor_dict = {
'StandardScaler' : StandardScaler,
'MaxAbsScaler' : MaxAbsScaler,
'Normalizer' : Normalizer,
'RobustScaler' : RobustScaler,
}
scaler = preprocessor_dict[params['preprocessor']]()
if params['feature_extractor']['type'] == 'pca':
opts = dict(params['feature_extractor'])
del opts['type']
feature_extraction = PCA(**opts)
elif params['feature_extractor']['type'] == 'RFE':
opts = dict(params['feature_extractor'])
del opts['type']
svr = svm.SVR(kernel='linear')
feature_extraction = RFE(estimator=svr, **opts)
elif params['feature_extractor']['type'] == 'SelectKBest':
opts = dict(params['feature_extractor'])
del opts['type']
feature_extraction = SelectKBest(score_func=f_regression, **opts)
model = Pipeline(steps=[
('imputer', imputer),
('filter', var_filter),
('scaler', scaler),
('feature_extraction', feature_extraction),
('ffann', ffann)
])
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment