alterlab-datamol — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited alterlab-datamol (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Datamol is a Python library that provides a lightweight, Pythonic abstraction layer over RDKit for molecular cheminformatics. Simplify complex molecular operations with sensible defaults, efficient parallelization, and modern I/O capabilities. All molecular objects are native rdkit.Chem.Mol instances, ensuring full compatibility with the RDKit ecosystem.
Key capabilities:
Use this skill when the user wants to:
| Scenario | Use Instead |
|---|---|
| Low-level control: custom sanitization flags, atom-mapped reaction details, specialised fingerprint/descriptor algorithms | alterlab-rdkit |
| ML-ready feature matrices, pretrained embeddings, or featurizer benchmarking | alterlab-molfeat |
| Drug-likeness rule sets, PAINS / structural-alert and complexity filtering | alterlab-medchem |
| Downloading curated ADMET/DTI benchmark datasets with scaffold or cold splits | alterlab-pytdc |
Guide users to install datamol:
uv pip install datamolExamples here are verified against datamol 0.13.0 (current as of 2026-09; requires Python ≥ 3.11 and pulls in RDKit). 0.13 fixed the misspelled heterocycle descriptors: the compute_many_descriptors keys are now n_aromatic_heterocycles / n_aliphatic_heterocycles / n_saturated_heterocycles (formerly ..._heterocyles; the old function names survive only as deprecated aliases). Pin 'datamol>=0.13' if you depend on those keys.
Import convention:
import datamol as dmEach subsection below shows the primary call pattern. Full API signatures, parameters, and secondary examples live in the per-module reference files cited under each; complete multi-step pipelines live in references/workflow_recipes.md.
import datamol as dm
# Parse SMILES (returns None on failure)
mol = dm.to_mol("CCO") # Ethanol
mols = [dm.to_mol(smi) for smi in ["CCO", "c1ccccc1", "CC(=O)O"]]
if dm.to_mol("invalid_smiles") is None:
print("Failed to parse SMILES")
# Export to common formats (canonical + isomeric by default)
smiles = dm.to_smiles(mol) # keeps stereochemistry
flat = dm.to_smiles(mol, isomeric=False) # drops stereochemistry
inchi = dm.to_inchi(mol)
inchikey = dm.to_inchikey(mol)
selfies = dm.to_selfies(mol)
# Standardize user-provided molecules (recommended for datasets)
mol = dm.sanitize_mol(mol)
mol = dm.standardize_mol(mol, disconnect_metals=True, normalize=True, reionize=True)
clean_smiles = dm.standardize_smiles(smiles)Full conversion, sanitization, and standardization API: see references/core_api.md.
# Read (open_df auto-detects .sdf/.csv/.xlsx/.parquet/.json)
mols = dm.read_sdf("compounds.sdf") # default: list of Mols
df = dm.read_sdf("compounds.sdf", as_df=True, mol_column="mol") # DataFrame instead
df = dm.read_csv("data.csv", smiles_column="SMILES", mol_column="mol")
df = dm.open_df("file.sdf")
# Write
dm.to_sdf(mols, "output.sdf") # or dm.to_sdf(df, "output.sdf", mol_column="mol")
dm.to_smi(mols, "output.smi")
dm.to_xlsx(df, "output.xlsx", mol_column="mol") # renders molecule images in cells
# Remote paths work everywhere via fsspec (S3, GCS, HTTP)
mols = dm.read_sdf("s3://bucket/compounds.sdf")
dm.to_sdf(mols, "s3://bucket/output.sdf")Full reader/writer signatures (read_smi, read_excel, read_mol2file, read_pdbfile, save_df, shared parameters): see references/io_module.md.
# Single molecule -> ~22 keys. Note datamol's naming (NOT rdkit's):
desc = dm.descriptors.compute_many_descriptors(mol)
# {'mw': 46.04, 'clogp': -0.0, 'n_lipinski_hbd': 1, 'n_lipinski_hba': 1,
# 'tpsa': 20.23, 'n_rotatable_bonds': 0, 'qed': ..., 'fsp3': ..., 'sas': ..., ...}
# Gotcha: logP is 'clogp'; donors/acceptors are 'n_lipinski_hbd'/'n_lipinski_hba'.
# There is no 'logp', 'hbd', 'hba', or 'n_aromatic_atoms' key in this dict.
# Batch (parallel) -> DataFrame with the same keys. Pass an explicit batch_size
# when n_jobs != 1: the default batch_size=None is rejected by joblib >= 1.6
# ("batch_size must be 'auto' or a positive integer").
desc_df = dm.descriptors.batch_compute_many_descriptors(mols, n_jobs=-1, batch_size=256,
progress=True)
# Standalone descriptors not in the dict above
dm.descriptors.n_aromatic_atoms(mol)
dm.descriptors.n_stereo_centers(mol)
dm.descriptors.n_rigid_bonds(mol)
# Drug-likeness filter (Lipinski's Rule of Five) with datamol's exact key names
def is_druglike(mol):
d = dm.descriptors.compute_many_descriptors(mol)
return (d['mw'] <= 500 and d['clogp'] <= 5 and
d['n_lipinski_hbd'] <= 5 and d['n_lipinski_hba'] <= 10)
druglike_mols = [m for m in mols if is_druglike(m)]Full descriptor catalog, RDKit descriptor access, and ADME examples: see references/descriptors_viz.md.
# Fingerprints (ECFP/Morgan is the default; datamol's ecfp default is radius=3, i.e. ECFP6)
# Extra kwargs go straight to RDKit's rdFingerprintGenerator: use fpSize, not n_bits/nBits.
fp = dm.to_fp(mol, fp_type='ecfp', radius=2, fpSize=2048)
fp_maccs = dm.to_fp(mol, fp_type='maccs')
# Also available: 'topological', 'atompair', 'fcfp', 'rdkit', '*-count' variants
# (full list: dm.list_supported_fingerprints())
# Similarity as Tanimoto distance (distance = 1 - similarity; lower = more similar)
dist_matrix = dm.pdist(mols, n_jobs=-1) # square N x N matrix (squareform=True default)
condensed = dm.pdist(mols, squareform=False) # condensed vector, SciPy-style
distances = dm.cdist(query_mols, library_mols, n_jobs=-1) # between two setsFingerprint types and pdist / cdist details: see references/core_api.md.
# Butina clustering (cutoff = Tanimoto distance). Returns a TUPLE:
# (cluster_indices, cluster_mols) — one tuple of indices / list of Mols per cluster.
cluster_idx, cluster_mols = dm.cluster_mols(mols, cutoff=0.2, n_jobs=-1)
for i, members in enumerate(cluster_idx):
print(i, len(members))
# Diversity / representative selection — both return (indices, mols)
diverse_idx, diverse = dm.pick_diverse(mols, npick=100)
centroid_idx, centroids = dm.pick_centroids(mols, npick=50)Scale note: Butina builds a full distance matrix — fine for ~1,000 molecules, not 10,000+. Clustering parameters: see references/core_api.md.
# Bemis-Murcko scaffold (core ring systems + linkers)
scaffold = dm.to_scaffold_murcko(mol)
scaffold_smiles = dm.to_smiles(scaffold)Scaffold frequency counting, scaffold-to-molecule grouping, and scaffold-based train/test splitting for ML: see references/workflow_recipes.md. fuzzy_scaffolding and more: see references/fragments_scaffolds.md.
# BRICS (16 bond types) and RECAP (11 bond types) return lists of RDKit Mol
# fragments, parent molecule first unless remove_parent=True. The default
# fix=True caps the dummy atoms; pass fix=False to keep attachment points
# such as '[1*]C(C)=O' in the SMILES.
frags_brics = dm.fragment.brics(mol, remove_parent=True, fix=False)
frag_smiles = {dm.to_smiles(f) for f in frags_brics}
frags_recap = dm.fragment.recap(mol, remove_parent=True)Cross-library fragment frequency analysis and fragment-overlap scoring recipes: see references/workflow_recipes.md. MMPA fragmentation and a method comparison table: see references/fragments_scaffolds.md.
# Generate 3D conformers (ETKDGv3 is the default method; minimize_energy defaults
# to False — pass True for UFF minimization)
mol_3d = dm.conformers.generate(mol, n_confs=50, rms_cutoff=0.5,
minimize_energy=True, method='ETKDGv3')
mol_3d.GetNumConformers()
conf = mol_3d.GetConformer(0)
positions = conf.GetPositions() # Nx3 array of atom coordinates
# Cluster conformers by RMSD (Butina on symmetry-aware pairwise RMS)
centroid_mol = dm.conformers.cluster(mol_3d, rms_cutoff=1.0) # one Mol holding the centroid conformers
per_cluster = dm.conformers.cluster(mol_3d, rms_cutoff=1.0, centroids=False) # list of Mols, one per cluster
# Solvent accessible surface area
sasa_values = dm.conformers.sasa(mol_3d, n_jobs=-1)
sasa = mol_3d.GetConformer(0).GetDoubleProp('rdkit_free_sasa')Embedding methods, RMSD matrices, and low-level coordinate manipulation: see references/conformers_module.md.
# Grid image (SVG by default: use_svg=True). For a PNG file pass use_svg=False —
# otherwise SVG markup is written into the .png file.
dm.viz.to_image(mols[:20], legends=[dm.to_smiles(m) for m in mols[:20]],
n_cols=5, mol_size=(300, 300))
dm.viz.to_image(mols, outfile="molecules.png", use_svg=False)
dm.viz.to_image(mols, outfile="molecules.svg")
# Align by MCS for SAR series; highlight atoms/bonds; render conformers
dm.viz.to_image(similar_mols, align=True, legends=activity_labels, n_cols=4)
dm.viz.to_image(mol, highlight_atom=[0, 1, 2, 3], highlight_bond=[0, 1, 2])
dm.viz.conformers(mol_3d, n_confs=10, align_conf=True, n_cols=3)Full to_image / conformers / circle_grid parameters and best practices: see references/descriptors_viz.md.
from rdkit.Chem import rdChemReactions
# Build a reaction from SMARTS, then apply it to a reactant tuple. By default
# apply_reaction returns every product set (list of lists); ask for one Mol:
rxn = rdChemReactions.ReactionFromSmarts('[C:1](=[O:2])[OH:3]>>[C:1](=[O:2])[Cl:3]')
product = dm.reactions.apply_reaction(rxn, (dm.to_mol("CC(=O)O"),),
single_product_group=True, product_index=0,
sanitize=True)
product_smiles = dm.to_smiles(product) # 'CC(=O)Cl' (or pass as_smiles=True)Batch reaction application, common reaction templates (amide, Suzuki, esterification), and the toy datamol.data datasets: see references/reactions_data.md.
Datamol includes built-in parallelization for many operations. Use n_jobs parameter:
n_jobs=1: Sequential (no parallelization)n_jobs=-1: Use all available CPU coresn_jobs=4: Use 4 coresFunctions supporting parallelization:
dm.read_sdf(..., n_jobs=-1)dm.descriptors.batch_compute_many_descriptors(..., n_jobs=-1, batch_size=256) (explicit batch_size needed with joblib ≥ 1.6)dm.cluster_mols(..., n_jobs=-1)dm.pdist(..., n_jobs=-1)dm.conformers.sasa(..., n_jobs=-1)Progress bars: Many batch operations support progress=True parameter.
Full copy-ready worked pipelines — data loading → filtering → analysis, Structure-Activity Relationship (SAR) analysis, and virtual screening — plus machine-learning feature generation and robust error-handling wrappers, have moved out of this file to keep it lean. See references/workflow_recipes.md.
For detailed API documentation, consult these reference files:
mol = dm.standardize_mol(mol, disconnect_metals=True, normalize=True, reionize=True) mol = dm.to_mol(smiles)
if mol is None:
... # log and skip the invalid SMILES result = dm.operation(..., n_jobs=-1, progress=True) df = dm.read_sdf("s3://bucket/compounds.sdf")Issue: Molecule parsing fails
dm.standardize_smiles() first or try dm.fix_mol()Issue: Memory errors with clustering
dm.pick_diverse() instead of full clustering for large setsIssue: Slow conformer generation
n_confs or increase rms_cutoff to generate fewer conformersIssue: Remote file access fails
Part of the AlterLab Academic Skills suite.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.