| name | bio-applied-rdkit-basics |
| description | Parse SMILES with RDKit, compute MW/LogP/TPSA/HBD/HBA descriptors and Lipinski Ro5, build Morgan/ECFP4 and MACCS fingerprints, score Tanimoto similarity. Use for cheminformatics, drug-likeness screening, or fingerprint similarity search. |
| tool_type | python |
| primary_tool | RDKit |
Molecular Representations with RDKit
When to Use
- Parsing, validating, or canonicalizing SMILES/InChI strings for a set of compounds
- Computing molecular descriptors (molecular weight, LogP, TPSA, HBD/HBA) for drug-likeness triage
- Checking Lipinski's Rule of Five for oral bioavailability screening
- Generating Morgan (ECFP4) or MACCS fingerprints for ML featurization or similarity search
- Computing pairwise Tanimoto similarity to find near-neighbors in a small compound library
Version Compatibility
- rdkit ≥ 2023.09 (pip package
rdkit, no separate rdkit-pypi needed since 2022)
- Python ≥ 3.9
- pandas ≥ 1.5, seaborn ≥ 0.12, matplotlib ≥ 3.6 (for the similarity heatmap)
Prerequisites
pip install rdkit pandas seaborn matplotlib
- Basic familiarity with SMILES notation and organic chemistry (functional groups, aromaticity)
Goal: Parse a set of SMILES strings into RDKit molecule objects and validate them.
Approach: Chem.MolFromSmiles returns None on invalid input instead of raising — always check for None before downstream use.
from rdkit import Chem
def parse_smiles(smiles_dict: dict[str, str]) -> dict[str, Chem.Mol]:
"""Parse a {name: SMILES} dict into RDKit Mol objects, dropping invalid entries.
RDKit's MolFromSmiles returns None (not an exception) for malformed SMILES,
so unchecked use silently propagates None into later steps and crashes there.
"""
mols = {}
for name, smi in smiles_dict.items():
mol = Chem.MolFromSmiles(smi)
if mol is None:
print(f"{name}: INVALID SMILES -> {smi!r}")
continue
mols[name] = mol
return mols
smiles_dict = {
"Aspirin": "CC(=O)Oc1ccccc1C(=O)O",
"Caffeine": "Cn1cnc2c1c(=O)n(C)c(=O)n2C",
"Ibuprofen": "CC(C)Cc1ccc(cc1)C(C)C(=O)O",
}
mols = parse_smiles(smiles_dict)
Goal: Draw a 2D grid image of the parsed molecules for visual inspection.
Approach: Draw.MolsToGridImage lays out molecules with per-molecule legends; useful in Jupyter or saved to disk with .save().
from typing import Optional
from rdkit.Chem import Draw
def draw_grid(mols: dict[str, Chem.Mol], mols_per_row: int = 3, path: Optional[str] = None):
"""Render molecules as a 2D grid image; saves to `path` if given, else returns the image."""
img = Draw.MolsToGridImage(
list(mols.values()),
legends=list(mols.keys()),
molsPerRow=mols_per_row,
subImgSize=(300, 250),
)
if path:
img.save(path)
return img
img = draw_grid(mols, path="/tmp/molecule_grid.png")
Goal: Compute drug-likeness descriptors and apply Lipinski's Rule of Five.
Approach: Pull MW/LogP/TPSA from Descriptors and HBD/HBA counts from rdMolDescriptors, then flag compounds violating Ro5 thresholds (MW≤500, LogP≤5, HBD≤5, HBA≤10).
import pandas as pd
from rdkit.Chem import Descriptors, rdMolDescriptors
def compute_descriptors(mols: dict[str, Chem.Mol]) -> pd.DataFrame:
"""Compute MW, LogP, HBD, HBA, TPSA and flag Lipinski Rule of Five pass/fail."""
rows = []
for name, mol in mols.items():
rows.append({
"Name": name,
"MW": Descriptors.MolWt(mol),
"LogP": Descriptors.MolLogP(mol),
"HBD": rdMolDescriptors.CalcNumHBD(mol),
"HBA": rdMolDescriptors.CalcNumHBA(mol),
"TPSA": Descriptors.TPSA(mol),
})
desc_df = pd.DataFrame(rows)
desc_df["Ro5_pass"] = (
(desc_df["MW"] <= 500) & (desc_df["LogP"] <= 5) &
(desc_df["HBD"] <= 5) & (desc_df["HBA"] <= 10)
)
return desc_df
desc_df = compute_descriptors(mols)
print(desc_df)
Goal: Generate Morgan (ECFP4) and MACCS fingerprints for similarity/ML use.
Approach: radius=2 with nBits=2048 reproduces the standard ECFP4 fingerprint; MACCS keys are a fixed 167-bit structural-key fingerprint requiring no parameters.
from rdkit.Chem import AllChem
from rdkit.Chem.MACCSkeys import GenMACCSKeys
def compute_fingerprints(mols: dict[str, Chem.Mol], radius: int = 2, n_bits: int = 2048):
"""Return (morgan_fps, maccs_fps) dicts keyed by molecule name.
radius=2, n_bits=2048 is the conventional "ECFP4" configuration.
"""
morgan_fps = {
name: AllChem.GetMorganFingerprintAsBitVect(mol, radius=radius, nBits=n_bits)
for name, mol in mols.items()
}
maccs_fps = {name: GenMACCSKeys(mol) for name, mol in mols.items()}
return morgan_fps, maccs_fps
morgan_fps, maccs_fps = compute_fingerprints(mols)
Goal: Compute a pairwise Tanimoto similarity matrix and visualize it as a heatmap.
Approach: DataStructs.TanimotoSimilarity compares two bit vectors; loop over all name pairs and plot with seaborn.
import seaborn as sns
import matplotlib.pyplot as plt
from rdkit import DataStructs
def tanimoto_matrix(fps: dict[str, "DataStructs.ExplicitBitVect"]) -> pd.DataFrame:
"""Build an all-pairs Tanimoto similarity matrix from a {name: fingerprint} dict."""
names = list(fps.keys())
sim_matrix = pd.DataFrame(index=names, columns=names, dtype=float)
for n1 in names:
for n2 in names:
sim_matrix.loc[n1, n2] = DataStructs.TanimotoSimilarity(fps[n1], fps[n2])
return sim_matrix
sim_matrix = tanimoto_matrix(morgan_fps)
sns.heatmap(sim_matrix.astype(float), annot=True, cmap="Blues", vmin=0, vmax=1)
plt.title("Tanimoto Similarity (ECFP4)")
plt.show()
Pitfalls
- SMILES canonicalization: Different SMILES strings can represent the same molecule; canonicalize with
Chem.MolToSmiles(mol) before string-based deduplication or comparison
- Silent parse failures:
MolFromSmiles returns None instead of raising — always check for None before calling any descriptor/fingerprint function on the result
- Stereochemistry: Default Morgan/MACCS fingerprints from 2D SMILES can merge enantiomers with different bioactivity; use
useChirality=True in GetMorganFingerprintAsBitVect when stereochemistry matters
- Descriptor scaling: MW, LogP, and TPSA live on very different numeric scales; standardize (z-score) before feeding descriptors into ML models
- Fingerprint type mismatch: Morgan and MACCS fingerprints are not comparable to each other — compute Tanimoto similarity only between fingerprints of the same type/parameters
- API deprecation:
AllChem.GetMorganFingerprintAsBitVect still works but is deprecated in newer RDKit releases in favor of rdFingerprintGenerator.GetMorganGenerator(radius, fpSize).GetFingerprint(mol) — prefer the generator API in new code
See Also
cheminformatics-drug-discovery — end-to-end RDKit + QSAR + docking + ADMET workflow
bio-applied-virtual-screening — docking ligand libraries and ranking hits by ADMET/QSAR
bio-applied-molecular-gnn — SMILES-to-graph GNN property prediction beyond fingerprint/RF
bio-applied-molecular-modeling — force-field energetics and structure QC for docked/modeled molecules