Skill v1.0.0
currentAutomated scan100/100version: "1.0.0" name: datamol description: Pythonic wrapper around RDKit with simplified interface and sensible defaults. Preferred for standard drug discovery including SMILES parsing, standardization, descriptors, fingerprints, clustering, 3D conformers, parallel processing. Returns native rdkit.Chem.Mol objects. For advanced control or custom parameters, use rdkit directly. license: Apache-2.0 license tags: [scientific-skills, datamol, python, rdkit, cheminformatics] metadata: skill-author: K-Dense Inc.
Datamol Cheminformatics Skill
Overview
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:
- Molecular format conversion (SMILES, SELFIES, InChI)
- Structure standardization and sanitization
- Molecular descriptors and fingerprints
- 3D conformer generation and analysis
- Clustering and diversity selection
- Scaffold and fragment analysis
- Chemical reaction application
- Visualization and alignment
- Batch processing with parallelization
- Cloud storage support via fsspec
Installation and Setup
Guide users to install datamol:
uv pip install datamol
Import convention:
import datamol as dm
Core Workflows
1. Basic Molecule Handling
Creating molecules from SMILES:
import datamol as dm# Single moleculemol = dm.to_mol("CCO") # Ethanol# From list of SMILESsmiles_list = ["CCO", "c1ccccc1", "CC(=O)O"]mols = [dm.to_mol(smi) for smi in smiles_list]# Error handlingmol = dm.to_mol("invalid_smiles") # Returns Noneif mol is None:print("Failed to parse SMILES")
Converting molecules to SMILES:
# Canonical SMILESsmiles = dm.to_smiles(mol)# Isomeric SMILES (includes stereochemistry)smiles = dm.to_smiles(mol, isomeric=True)# Other formatsinchi = dm.to_inchi(mol)inchikey = dm.to_inchikey(mol)selfies = dm.to_selfies(mol)
Standardization and sanitization (always recommend for user-provided molecules):
# Sanitize moleculemol = dm.sanitize_mol(mol)# Full standardization (recommended for datasets)mol = dm.standardize_mol(mol,disconnect_metals=True,normalize=True,reionize=True)# For SMILES strings directlyclean_smiles = dm.standardize_smiles(smiles)
2. Reading and Writing Molecular Files
Refer to references/io_module.md for comprehensive I/O documentation.
Reading files:
# SDF files (most common in chemistry)df = dm.read_sdf("compounds.sdf", mol_column='mol')# SMILES filesdf = dm.read_smi("molecules.smi", smiles_column='smiles', mol_column='mol')# CSV with SMILES columndf = dm.read_csv("data.csv", smiles_column="SMILES", mol_column="mol")# Excel filesdf = dm.read_excel("compounds.xlsx", sheet_name=0, mol_column="mol")# Universal reader (auto-detects format)df = dm.open_df("file.sdf") # Works with .sdf, .csv, .xlsx, .parquet, .json
Writing files:
# Save as SDFdm.to_sdf(mols, "output.sdf")# Or from DataFramedm.to_sdf(df, "output.sdf", mol_column="mol")# Save as SMILES filedm.to_smi(mols, "output.smi")# Excel with rendered molecule imagesdm.to_xlsx(df, "output.xlsx", mol_columns=["mol"])
Remote file support (S3, GCS, HTTP):
# Read from cloud storagedf = dm.read_sdf("s3://bucket/compounds.sdf")df = dm.read_csv("https://example.com/data.csv")# Write to cloud storagedm.to_sdf(mols, "s3://bucket/output.sdf")
3. Molecular Descriptors and Properties
Refer to references/descriptors_viz.md for detailed descriptor documentation.
Computing descriptors for a single molecule:
# Get standard descriptor setdescriptors = dm.descriptors.compute_many_descriptors(mol)# Returns: {'mw': 46.07, 'logp': -0.03, 'hbd': 1, 'hba': 1,# 'tpsa': 20.23, 'n_aromatic_atoms': 0, ...}
Batch descriptor computation (recommended for datasets):
# Compute for all molecules in paralleldesc_df = dm.descriptors.batch_compute_many_descriptors(mols,n_jobs=-1, # Use all CPU coresprogress=True # Show progress bar)
Specific descriptors:
# Aromaticityn_aromatic = dm.descriptors.n_aromatic_atoms(mol)aromatic_ratio = dm.descriptors.n_aromatic_atoms_proportion(mol)# Stereochemistryn_stereo = dm.descriptors.n_stereo_centers(mol)n_unspec = dm.descriptors.n_stereo_centers_unspecified(mol)# Flexibilityn_rigid = dm.descriptors.n_rigid_bonds(mol)
Drug-likeness filtering (Lipinski's Rule of Five):
# Filter compoundsdef is_druglike(mol):desc = dm.descriptors.compute_many_descriptors(mol)return (desc['mw'] <= 500 anddesc['logp'] <= 5 anddesc['hbd'] <= 5 anddesc['hba'] <= 10)druglike_mols = [mol for mol in mols if is_druglike(mol)]
4. Molecular Fingerprints and Similarity
Generating fingerprints:
# ECFP (Extended Connectivity Fingerprint, default)fp = dm.to_fp(mol, fp_type='ecfp', radius=2, n_bits=2048)# Other fingerprint typesfp_maccs = dm.to_fp(mol, fp_type='maccs')fp_topological = dm.to_fp(mol, fp_type='topological')fp_atompair = dm.to_fp(mol, fp_type='atompair')
Similarity calculations:
# Pairwise distances within a setdistance_matrix = dm.pdist(mols, n_jobs=-1)# Distances between two setsdistances = dm.cdist(query_mols, library_mols, n_jobs=-1)# Find most similar moleculesfrom scipy.spatial.distance import squareformdist_matrix = squareform(dm.pdist(mols))# Lower distance = higher similarity (Tanimoto distance = 1 - Tanimoto similarity)
5. Clustering and Diversity Selection
Refer to references/core_api.md for clustering details.
Butina clustering:
# Cluster molecules by structural similarityclusters = dm.cluster_mols(mols,cutoff=0.2, # Tanimoto distance threshold (0=identical, 1=completely different)n_jobs=-1 # Parallel processing)# Each cluster is a list of molecule indicesfor i, cluster in enumerate(clusters):print(f"Cluster {i}: {len(cluster)} molecules")cluster_mols = [mols[idx] for idx in cluster]
Important: Butina clustering builds a full distance matrix - suitable for ~1000 molecules, not for 10,000+.
Diversity selection:
# Pick diverse subsetdiverse_mols = dm.pick_diverse(mols,npick=100 # Select 100 diverse molecules)# Pick cluster centroidscentroids = dm.pick_centroids(mols,npick=50 # Select 50 representative molecules)
6. Scaffold Analysis
Refer to references/fragments_scaffolds.md for complete scaffold documentation.
Extracting Murcko scaffolds:
# Get Bemis-Murcko scaffold (core structure)scaffold = dm.to_scaffold_murcko(mol)scaffold_smiles = dm.to_smiles(scaffold)
Scaffold-based analysis:
# Group compounds by scaffoldfrom collections import Counterscaffolds = [dm.to_scaffold_murcko(mol) for mol in mols]scaffold_smiles = [dm.to_smiles(s) for s in scaffolds]# Count scaffold frequencyscaffold_counts = Counter(scaffold_smiles)most_common = scaffold_counts.most_common(10)# Create scaffold-to-molecules mappingscaffold_groups = {}for mol, scaf_smi in zip(mols, scaffold_smiles):if scaf_smi not in scaffold_groups:scaffold_groups[scaf_smi] = []scaffold_groups[scaf_smi].append(mol)
Scaffold-based train/test splitting (for ML):
# Ensure train and test sets have different scaffoldsscaffold_to_mols = {}for mol, scaf in zip(mols, scaffold_smiles):if scaf not in scaffold_to_mols:scaffold_to_mols[scaf] = []scaffold_to_mols[scaf].append(mol)# Split scaffolds into train/testimport randomscaffolds = list(scaffold_to_mols.keys())random.shuffle(scaffolds)split_idx = int(0.8 * len(scaffolds))train_scaffolds = scaffolds[:split_idx]test_scaffolds = scaffolds[split_idx:]# Get molecules for each splittrain_mols = [mol for scaf in train_scaffolds for mol in scaffold_to_mols[scaf]]test_mols = [mol for scaf in test_scaffolds for mol in scaffold_to_mols[scaf]]
7. Molecular Fragmentation
Refer to references/fragments_scaffolds.md for fragmentation details.
BRICS fragmentation (16 bond types):
# Fragment moleculefragments = dm.fragment.brics(mol)# Returns: set of fragment SMILES with attachment points like '[1*]CCN'
RECAP fragmentation (11 bond types):
fragments = dm.fragment.recap(mol)
Fragment analysis:
# Find common fragments across compound libraryfrom collections import Counterall_fragments = []for mol in mols:frags = dm.fragment.brics(mol)all_fragments.extend(frags)fragment_counts = Counter(all_fragments)common_frags = fragment_counts.most_common(20)# Fragment-based scoringdef fragment_score(mol, reference_fragments):mol_frags = dm.fragment.brics(mol)overlap = mol_frags.intersection(reference_fragments)return len(overlap) / len(mol_frags) if mol_frags else 0
8. 3D Conformer Generation
Refer to references/conformers_module.md for detailed conformer documentation.
Generating conformers:
# Generate 3D conformersmol_3d = dm.conformers.generate(mol,n_confs=50, # Number to generate (auto if None)rms_cutoff=0.5, # Filter similar conformers (Ångströms)minimize_energy=True, # Minimize with UFF force fieldmethod='ETKDGv3' # Embedding method (recommended))# Access conformersn_conformers = mol_3d.GetNumConformers()conf = mol_3d.GetConformer(0) # Get first conformerpositions = conf.GetPositions() # Nx3 array of atom coordinates
Conformer clustering:
# Cluster conformers by RMSDclusters = dm.conformers.cluster(mol_3d,rms_cutoff=1.0,centroids=False)# Get representative conformerscentroids = dm.conformers.return_centroids(mol_3d, clusters)
SASA calculation:
# Calculate solvent accessible surface areasasa_values = dm.conformers.sasa(mol_3d, n_jobs=-1)# Access SASA from conformer propertiesconf = mol_3d.GetConformer(0)sasa = conf.GetDoubleProp('rdkit_free_sasa')
9. Visualization
Refer to references/descriptors_viz.md for visualization documentation.
Basic molecule grid:
# Visualize moleculesdm.viz.to_image(mols[:20],legends=[dm.to_smiles(m) for m in mols[:20]],n_cols=5,mol_size=(300, 300))# Save to filedm.viz.to_image(mols, outfile="molecules.png")# SVG for publicationsdm.viz.to_image(mols, outfile="molecules.svg", use_svg=True)
Aligned visualization (for SAR analysis):
# Align molecules by common substructuredm.viz.to_image(similar_mols,align=True, # Enable MCS alignmentlegends=activity_labels,n_cols=4)
Highlighting substructures:
# Highlight specific atoms and bondsdm.viz.to_image(mol,highlight_atom=[0, 1, 2, 3], # Atom indiceshighlight_bond=[0, 1, 2] # Bond indices)
Conformer visualization:
# Display multiple conformersdm.viz.conformers(mol_3d,n_confs=10,align_conf=True,n_cols=3)
10. Chemical Reactions
Refer to references/reactions_data.md for reactions documentation.
Applying reactions:
from rdkit.Chem import rdChemReactions# Define reaction from SMARTSrxn_smarts = '[C:1](=[O:2])[OH:3]>>[C:1](=[O:2])[Cl:3]'rxn = rdChemReactions.ReactionFromSmarts(rxn_smarts)# Apply to moleculereactant = dm.to_mol("CC(=O)O") # Acetic acidproduct = dm.reactions.apply_reaction(rxn,(reactant,),sanitize=True)# Convert to SMILESproduct_smiles = dm.to_smiles(product)
Batch reaction application:
# Apply reaction to libraryproducts = []for mol in reactant_mols:try:prod = dm.reactions.apply_reaction(rxn, (mol,))if prod is not None:products.append(prod)except Exception as e:print(f"Reaction failed: {e}")
Parallelization
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 cores
Functions supporting parallelization:
dm.read_sdf(..., n_jobs=-1)dm.descriptors.batch_compute_many_descriptors(..., n_jobs=-1)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.
Common Workflows and Patterns
Complete Pipeline: Data Loading → Filtering → Analysis
import datamol as dmimport pandas as pd# 1. Load moleculesdf = dm.read_sdf("compounds.sdf")# 2. Standardizedf['mol'] = df['mol'].apply(lambda m: dm.standardize_mol(m) if m else None)df = df[df['mol'].notna()] # Remove failed molecules# 3. Compute descriptorsdesc_df = dm.descriptors.batch_compute_many_descriptors(df['mol'].tolist(),n_jobs=-1,progress=True)# 4. Filter by drug-likenessdruglike = ((desc_df['mw'] <= 500) &(desc_df['logp'] <= 5) &(desc_df['hbd'] <= 5) &(desc_df['hba'] <= 10))filtered_df = df[druglike]# 5. Cluster and select diverse subsetdiverse_mols = dm.pick_diverse(filtered_df['mol'].tolist(),npick=100)# 6. Visualize resultsdm.viz.to_image(diverse_mols,legends=[dm.to_smiles(m) for m in diverse_mols],outfile="diverse_compounds.png",n_cols=10)
Structure-Activity Relationship (SAR) Analysis
# Group by scaffoldscaffolds = [dm.to_scaffold_murcko(mol) for mol in mols]scaffold_smiles = [dm.to_smiles(s) for s in scaffolds]# Create DataFrame with activitiessar_df = pd.DataFrame({'mol': mols,'scaffold': scaffold_smiles,'activity': activities # User-provided activity data})# Analyze each scaffold seriesfor scaffold, group in sar_df.groupby('scaffold'):if len(group) >= 3: # Need multiple examplesprint(f"\nScaffold: {scaffold}")print(f"Count: {len(group)}")print(f"Activity range: {group['activity'].min():.2f} - {group['activity'].max():.2f}")# Visualize with activities as legendsdm.viz.to_image(group['mol'].tolist(),legends=[f"Activity: {act:.2f}" for act in group['activity']],align=True # Align by common substructure)
Virtual Screening Pipeline
# 1. Generate fingerprints for query and libraryquery_fps = [dm.to_fp(mol) for mol in query_actives]library_fps = [dm.to_fp(mol) for mol in library_mols]# 2. Calculate similaritiesfrom scipy.spatial.distance import cdistimport numpy as npdistances = dm.cdist(query_actives, library_mols, n_jobs=-1)# 3. Find closest matches (min distance to any query)min_distances = distances.min(axis=0)similarities = 1 - min_distances # Convert distance to similarity# 4. Rank and select top hitstop_indices = np.argsort(similarities)[::-1][:100] # Top 100top_hits = [library_mols[i] for i in top_indices]top_scores = [similarities[i] for i in top_indices]# 5. Visualize hitsdm.viz.to_image(top_hits[:20],legends=[f"Sim: {score:.3f}" for score in top_scores[:20]],outfile="screening_hits.png")
Reference Documentation
For detailed API documentation, consult these reference files:
- `references/core_api.md`: Core namespace functions (conversions, standardization, fingerprints, clustering)
- `references/io_module.md`: File I/O operations (read/write SDF, CSV, Excel, remote files)
- `references/conformers_module.md`: 3D conformer generation, clustering, SASA calculations
- `references/descriptors_viz.md`: Molecular descriptors and visualization functions
- `references/fragments_scaffolds.md`: Scaffold extraction, BRICS/RECAP fragmentation
- `references/reactions_data.md`: Chemical reactions and toy datasets
Best Practices
- Always standardize molecules from external sources:
``python mol = dm.standardize_mol(mol, disconnect_metals=True, normalize=True, reionize=True) ``
- Check for None values after molecule parsing:
``python mol = dm.to_mol(smiles) if mol is None: # Handle invalid SMILES ``
- Use parallel processing for large datasets:
``python result = dm.operation(..., n_jobs=-1, progress=True) ``
- Leverage fsspec for cloud storage:
``python df = dm.read_sdf("s3://bucket/compounds.sdf") ``
- Use appropriate fingerprints for similarity:
- ECFP (Morgan): General purpose, structural similarity
- MACCS: Fast, smaller feature space
- Atom pairs: Considers atom pairs and distances
- Consider scale limitations:
- Butina clustering: ~1,000 molecules (full distance matrix)
- For larger datasets: Use diversity selection or hierarchical methods
- Scaffold splitting for ML: Ensure proper train/test separation by scaffold
- Align molecules when visualizing SAR series
Error Handling
# Safe molecule creationdef safe_to_mol(smiles):try:mol = dm.to_mol(smiles)if mol is not None:mol = dm.standardize_mol(mol)return molexcept Exception as e:print(f"Failed to process {smiles}: {e}")return None# Safe batch processingvalid_mols = []for smiles in smiles_list:mol = safe_to_mol(smiles)if mol is not None:valid_mols.append(mol)
Integration with Machine Learning
# Feature generationX = np.array([dm.to_fp(mol) for mol in mols])# Or descriptorsdesc_df = dm.descriptors.batch_compute_many_descriptors(mols, n_jobs=-1)X = desc_df.values# Train modelfrom sklearn.ensemble import RandomForestRegressormodel = RandomForestRegressor()model.fit(X, y_target)# Predictpredictions = model.predict(X_test)
Troubleshooting
Issue: Molecule parsing fails
- Solution: Use
dm.standardize_smiles()first or trydm.fix_mol()
Issue: Memory errors with clustering
- Solution: Use
dm.pick_diverse()instead of full clustering for large sets
Issue: Slow conformer generation
- Solution: Reduce
n_confsor increaserms_cutoffto generate fewer conformers
Issue: Remote file access fails
- Solution: Ensure fsspec and appropriate cloud provider libraries are installed (s3fs, gcsfs, etc.)
Additional Resources
- Datamol Documentation: https://docs.datamol.io/
- RDKit Documentation: https://www.rdkit.org/docs/
- GitHub Repository: https://github.com/datamol-io/datamol