Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Tutorial 2: Human Health Impact Prediction Using ANN Model


In this code you will be able to estimate the impact to Human Health using the ANN Model. Then you will be able to see how changing the amount of that chemical will affect the predicted impact.


Code Overview:

This code will download the human health impact weight of the model locally and give this file to the code, which then will take the input of a chemical using the SMILES identification to tell you the impact in kg to human health. Then you will be able to see the impact scaled according to how much mass in kg you have of that chemical.

The libraries / packages listed in cell 1 will have a brief explanation of their function in the code, but for more information, please use the links below

Library / PackageLink to Documentation
numpyNumPy Documentation
pandasPandas Documentation
matplotlib.pyplotMatplotlib.pyplot
ipywidgetsJupyter Widgets
IPython.displayDisplay Module
tensorflowTensorflow
osOS
requestsRequests

Click the button below to open this code in Google Colab

Open in Colab

Outputs Should Appear Like This:

# Cell 1: Import Required Libraries / Packages

import numpy as np                    # Stores numbers in arrays and runs fast calculations on them
import pandas as pd                   # Opens the data file as a table and finds rows by their SMILES string
import matplotlib.pyplot as plt       # Plot Graphs
import ipywidgets as widgets          # Creates UI elements such as the slider below
from IPython.display import display   # Displays UI elements

import tensorflow as tf               # Loads the trained neural network (ANN) and runs it to make predictions

import os                             # Allows interactions with the file system
import requests                       # Makes requests to internet files

import joblib                         # Parallel Computing
# Cell 2: Define Function to Download Weight (.h5 file) of ANN Model

def download_if_needed(url, local_path):
    """Download a file from GitHub if it doesn't already exist."""
    if os.path.exists(local_path):
        print(f"✓ Using existing {local_path}")
        return

    print(f"Downloading {local_path}...")
    response = requests.get(url)
    response.raise_for_status()

    with open(local_path, "wb") as f:
        f.write(response.content)

    print("Download complete.")
# Cell 3: Create Path to Dowloaded Files and Database

# URL / path to download (.h5 file)
HH_MODEL_URL = ("https://raw.githubusercontent.com/cacherowan/CACHE-Rowan/main/Reference_Files/Chemical_Property_Database/model_v5_HH_okayvaltest.h5")

# Name of weight file
HH_MODEL_PATH = "model_v5_HH_okayvaltest.h5"

# URL / path to download (.joblib file)
MIN_MAX_SCALAR_URL = ("https://raw.githubusercontent.com/cacherowan/CACHE-Rowan/main/Reference_Files/Chemical_Property_Database/minmax_scaler_0_5-1.joblib")

# Name of min_max_scalar file
MIN_MAX_SCALAR_PATH = "minmax_scaler_0_5-1.joblib"

# URL / path to database (the spreadsheet holding the molecular properties)
DATABASE_PATH = "https://raw.githubusercontent.com/cacherowan/CACHE-Rowan/main/Reference_Files/Chemical_Property_Database/Processed_Solvent_DF_v6_TEST.xlsx"

# Call to Function to Download Weight (.h5 file)
download_if_needed(HH_MODEL_URL, HH_MODEL_PATH)

# Call to Function to Download Min Max Scalar
download_if_needed(MIN_MAX_SCALAR_URL, MIN_MAX_SCALAR_PATH)

# The trained model file (.h5) that predicts Human Health Impact
HH_MODEL = HH_MODEL_PATH

# Min Max Scalar
Min_Max_Scalar = MIN_MAX_SCALAR_PATH
Expected output
Downloading model_v5_HH_okayvaltest.h5...
Download complete.
Downloading minmax_scaler_0_5-1.joblib...
Download complete.
# Cell 4: Load the trained model
# Load the .h5 file, which contains the trained network: its structure and its learned weights.  
# This rebuilds the model as "model_CC," ready to make predictions.  
# compile=false in the code below helps skip the training setup, since we are only using the model to predict (not train it).  

import warnings
warnings.filterwarnings("ignore")

model_HH = tf.keras.models.load_model(
    HH_MODEL_PATH,
    custom_objects={"LeakyReLU": tf.keras.layers.LeakyReLU},
    compile=False
)
# Cell 5: Load the molecule database
# Opens the spreadshet into a table, and uses each molecule's SMILES code as its row name so molecules are easy to look up

db = pd.read_excel(DATABASE_PATH)
db = db.set_index('SMILES')
# Cell 6: Select the thermodynamic properties and the molecular descriptors used as inputs for the Human Health model
# The Human Health model was trained on a specific set of 10 features.
# We must feed the model the exact same features, in the exact same order, every time we ask it to make a prediction.

# Thermodynamic properties:
thermo_feat_HH = [
    'Heat of Vaporization(J/mol)',    # energy needed to turn the liquid into vapour
    'Heat Capacity (kJ/kgC)',         # heat needed to raise the molecule's temperature
    'XLogP',                          # a measure of how "fat-loving" vs "water-loving" it is
    'Pitzer’s Acentric Factor [-]',   # a measure of how non-spherical the molecule is
    'Critical Temperature [K]'        # temperature above which it can't be liquefied
]
# Molecular descriptors:
mol_desc_feat_HH = [
    'Chi0n',                # a connectivity index describing molecular structure
    'HallKierAlpha',        # a shape-related descriptor
    'SMR_VSA7',             # surface-area descriptor related to molar refractivity
    'VSA_EState6',          # surface-area descriptor related to electronic state
    'NumValenceElectrons'   # total number of valence electrons in the molecule
]
# Cell 7: Load the scaler
scaler = joblib.load(Min_Max_Scalar)

feature_names = list(scaler.feature_names_in_)
data_min = scaler.data_min_
data_max = scaler.data_max_
lo, hi = scaler.feature_range

def scale_subset(values):
    """Apply the fitted MinMax scaler to a subset of columns, by name."""
    scaled_values = []

    for feature_name in values.index:
        if feature_name not in feature_names:
            print(f"Warning: '{feature_name}' was not seen by the scaler at fit time.")
            continue

        # Find where this feature is in the scaler's arrays
        position = feature_names.index(feature_name)

        x = values[feature_name]
        x_min = data_min[position]
        x_max = data_max[position]

        # MinMax formula: rescale x from [x_min, x_max] to [lo, hi]
        x_scaled = (x - x_min) / (x_max - x_min) * (hi - lo) + lo

        scaled_values.append(x_scaled)

    return pd.Series(scaled_values, index=values.index)
# Cell 8: Predict Human Health Impact for one molecule
# This function takes a molecule, looks up its properties in the database, feeds them to the trained model, and returns the predicted Human Health Impact.
# Run this cell once.  It won't show any output on its own, it just sets yp the function so the cells below can use it.

def predict_human_health_impact(molecule_name, smiles):
    """
    Predict the Human Health Impact of one molecule.

    molecule_name : name for printing only (e.g. "Methanol") — doesn't affect the result.
    smiles        : SMILES string; must exactly match a row in the database.
    Returns the predicted Human Health Impact in DALY per kg of chemical.
    """
    descriptors = db.loc[smiles]
    descriptors_hh = descriptors.loc[thermo_feat_HH + mol_desc_feat_HH]
    descriptors_hh_scaled = scale_subset(descriptors_hh)

    print(f"\n{molecule_name}  ({smiles})")
    print("-" * 40)
    for feature_name, value in descriptors_hh.items():
        print(f"  {feature_name:<32} {value:>8.4f}")
    print("-" * 40)

    model_input = np.array([list(descriptors_hh)])
    prediction = model_HH.predict(model_input, verbose=0)
    impact_value = prediction.item()

    print(f"  Human Health Impact: {round(impact_value, 4)} DALY/kg\n")
    return impact_value
# Cell 9: Calculate the Human Health Impact of a chemical
# Give the function below two things: the chemical's name (for display) and its SMILES code, which the model uses to look it up.  Run the cell to see its features and its predicted human health impact impact.
# To calculate the impact for another chemical, copy the line below into a new cell and change the name and SMILES.

predict_human_health_impact("Methanol", "CO");
Expected output
Methanol  (CO)
----------------------------------------
  Heat of Vaporization(J/mol)      37460.1385
  Heat Capacity (kJ/kgC)             2.5318
  XLogP                             -0.5000
  Pitzer’s Acentric Factor [-]       0.5560
  Critical Temperature [K]         512.5000
  Chi0n                              1.4472
  HallKierAlpha                     -0.0400
  SMR_VSA7                           0.0000
  VSA_EState6                        0.0000
  NumValenceElectrons               14.0000
----------------------------------------
  Human Health Impact: 0.9432 DALY/kg
# Cell 10: Example with Ethanol

predict_human_health_impact("Ethanol", "CCO");
Expected output
Ethanol  (CCO)
----------------------------------------
  Heat of Vaporization(J/mol)      42319.9941
  Heat Capacity (kJ/kgC)             2.5700
  XLogP                             -0.1000
  Pitzer’s Acentric Factor [-]       0.6440
  Critical Temperature [K]         514.0000
  Chi0n                              2.1543
  HallKierAlpha                     -0.0400
  SMR_VSA7                           0.0000
  VSA_EState6                        0.0000
  NumValenceElectrons               20.0000
----------------------------------------
  Human Health Impact: 0.9486 DALY/kg
# Cell 11: Example with Benzene

predict_human_health_impact("Benzene", "c1ccccc1");
Expected output
Benzene  (c1ccccc1)
----------------------------------------
  Heat of Vaporization(J/mol)      33879.4484
  Heat Capacity (kJ/kgC)             1.7469
  XLogP                              2.1000
  Pitzer’s Acentric Factor [-]       0.2120
  Critical Temperature [K]         562.0500
  Chi0n                              3.4641
  HallKierAlpha                     -0.7800
  SMR_VSA7                          36.3982
  VSA_EState6                       12.0000
  NumValenceElectrons               30.0000
----------------------------------------
  Human Health Impact: 0.9432 DALY/kg
# Cell 12: Example with Toluene

predict_human_health_impact("Toluene", "Cc1ccccc1");
Expected output
Toluene  (Cc1ccccc1)
----------------------------------------
  Heat of Vaporization(J/mol)      38009.5928
  Heat Capacity (kJ/kgC)             1.7190
  XLogP                              2.7000
  Pitzer’s Acentric Factor [-]       0.2630
  Critical Temperature [K]         591.7500
  Chi0n                              4.3868
  HallKierAlpha                     -0.7800
  SMR_VSA7                          35.8953
  VSA_EState6                       10.2616
  NumValenceElectrons               36.0000
----------------------------------------
  Human Health Impact: 0.9432 DALY/kg
# Cell 13: Example using Phenol (Which will be displayed later on a slider and graph)

# 1. Example: Phenol
MOLECULE = "PHENOL"
SMILES = "Oc1ccccc1"

impact_value_slider = predict_human_health_impact(MOLECULE, SMILES)
Expected output
PHENOL  (Oc1ccccc1)
----------------------------------------
  Heat of Vaporization(J/mol)      57735.3635
  Heat Capacity (kJ/kgC)             2.1408
  XLogP                              1.5000
  Pitzer’s Acentric Factor [-]       0.4380
  Critical Temperature [K]         694.2500
  Chi0n                              3.8340
  HallKierAlpha                     -0.9800
  SMR_VSA7                          30.3318
  VSA_EState6                        8.7127
  NumValenceElectrons               36.0000
----------------------------------------
  Human Health Impact: 0.9596 DALY/kg
# Cell 14: Phenol Continued, Creating Slider to Visualize Impact

kg_slider = widgets.FloatSlider(value=(round(impact_value_slider, 4)), min=(round(impact_value_slider, 4)) * 0.5, max=(round(impact_value_slider, 4)) * 1.5, step=0.1, description='kg amount:')
output_slider = widgets.Output()

def update_slider(change):
    with output_slider:
        output_slider.clear_output()
        kg_slider_kg = kg_slider.value
        total_hh_slider = (round(impact_value_slider, 4)) * kg_slider_kg
        print(f"Health impact:  {total_hh_slider:.6f} Disability-Adjusted Life Years (DALY)")

kg_slider.observe(update_slider, names='value')
display(kg_slider, output_slider)
update_slider(None)
Expected output
# Cell 15: Phenol Continued, Creating Slider and Graph to Visualize Impact

kg_slider_graph = widgets.FloatSlider(value=(round(impact_value_slider, 4)), min=(round(impact_value_slider, 4)) * 0.5, max=(round(impact_value_slider, 4)) * 1.5, step=0.1, description='kg amount:')
output_graph = widgets.Output()

hh_per_kg = (round(impact_value_slider, 4))
kg_range = np.linspace((round(impact_value_slider, 4)) * 0.5, (round(impact_value_slider, 4)) * 1.5, 20)

def update_graph(change):
    with output_graph:
        output_graph.clear_output(wait=True)
        kg_graph_kg = kg_slider_graph.value
        total_hh_graph = hh_per_kg * kg_graph_kg

        fig, ax = plt.subplots(figsize=(5, 3.5))

        ax.plot(kg_range, hh_per_kg * kg_range, color='#e0685c')
        ax.scatter([kg_graph_kg], [total_hh_graph], color='#e0685c', zorder=5, s=80)
        ax.set_title(f"Health: {total_hh_graph:.6f} Disability-Adjusted Life Years (DALY)")
        ax.set_xlabel(f"kg {MOLECULE}")

        plt.tight_layout()
        plt.show()

kg_slider_graph.observe(update_graph, names='value')
display(kg_slider_graph, output_graph)
update_graph(None)
Expected output

Discussion / Analysis

In this code, you were able to see how a chemical impacts human health using the ANN Model. Then you could see how changing the amount of that chemical affects the human health impact prediction.