Claude Skill

alterlab-datamol

Wraps RDKit in a high-level, pandas-friendly datamol interface with sensible defaults for everyday drug discovery — SMILES/SDF loading into DataFrames, molecule standardization, descriptors, fingerprints, Butina clustering, 3D conformer generation, scaffold analysis, and parallel

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download alterlab-ieu-alterlab-academic-skills-skills_cheminformatics_alterlab-datamol-e4836c0.zip · 25 KB
Part of alterlab-ieu/alterlab-academic-skills — 94 skills

Install

skills CLI npx skills add https://github.com/AlterLab-IEU/AlterLab-Academic-Skills/tree/main/skills/cheminformatics/alterlab-datamol
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install alterlab-ieu-alterlab-academic-skills@llmmart
Git git clone https://github.com/AlterLab-IEU/AlterLab-Academic-Skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole alterlab-ieu/alterlab-academic-skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

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

When to Use This Skill

Use this skill when the user wants to:

  • Load, standardize, and de-duplicate molecule tables (CSV/SDF/Excel/Parquet, local or cloud) with little boilerplate
  • Compute datamol's descriptor set, fingerprints, and Tanimoto distance matrices for a compound set
  • Cluster, pick diverse subsets, extract Murcko scaffolds, or fragment (BRICS/RECAP) a library
  • Generate and cluster 3D conformers, or render aligned molecule grids for SAR review

Does NOT Trigger

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

Installation and Setup

Guide users to install datamol:

uv pip install datamol

Examples 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 dm

Core Workflows

Each 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.

1. Basic Molecule Handling

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.

2. Reading and Writing Molecular Files

# 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.

3. Molecular Descriptors and Properties

# 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.

4. Molecular Fingerprints and Similarity

# 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 sets

Fingerprint types and pdist / cdist details: see references/core_api.md.

5. Clustering and Diversity Selection

# 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.

6. Scaffold Analysis

# 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.

7. Molecular Fragmentation

# 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.

8. 3D Conformer Generation

# 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.

9. Visualization

# 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.

10. Chemical Reactions

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.

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 cores
  • n_jobs=4: Use 4 cores

Functions 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.

Common Workflows and Patterns

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.

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
  • references/workflow_recipes.md: End-to-end pipelines, SAR/screening recipes, ML integration, error handling

Best Practices

  1. Always standardize molecules from external sources:

    mol = dm.standardize_mol(mol, disconnect_metals=True, normalize=True, reionize=True)
    
  2. Check for None values after molecule parsing:

    mol = dm.to_mol(smiles)
    if mol is None:
        ...  # log and skip the invalid SMILES
    
  3. Use parallel processing for large datasets:

    result = dm.operation(..., n_jobs=-1, progress=True)
    
  4. Leverage fsspec for cloud storage:

    df = dm.read_sdf("s3://bucket/compounds.sdf")
    
  5. Use appropriate fingerprints for similarity:

    • ECFP (Morgan): General purpose, structural similarity
    • MACCS: Fast, smaller feature space
    • Atom pairs: Considers atom pairs and distances
  6. Consider scale limitations:

    • Butina clustering: ~1,000 molecules (full distance matrix)
    • For larger datasets: Use diversity selection or hierarchical methods
  7. Scaffold splitting for ML: Ensure proper train/test separation by scaffold

  8. Align molecules when visualizing SAR series

Troubleshooting

Issue: Molecule parsing fails

  • Solution: Use dm.standardize_smiles() first or try dm.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_confs or increase rms_cutoff to generate fewer conformers

Issue: Remote file access fails

  • Solution: Ensure fsspec and appropriate cloud provider libraries are installed (s3fs, gcsfs, etc.)

Additional Resources

Part of the AlterLab Academic Skills suite.

Files (alterlab-academic-skills)
  • evals
    • evals.json 5.5 KB
      {
        "skill": "alterlab-datamol",
        "evals": [
          {
            "id": "standardize-and-descriptors",
            "prompt": "I have a CSV of ~3000 vendor compounds with a SMILES column. Standardize them (disconnect metals, normalize, reionize), drop anything that fails to parse, and compute MW, logP, HBD, HBA and TPSA for the survivors so I can filter by Lipinski.",
            "expected_output": "Invokes alterlab-datamol: reads the CSV with dm.read_csv, standardizes each molecule via dm.standardize_mol (disconnect_metals/normalize/reionize) while dropping None parses from dm.to_mol, then runs dm.descriptors.batch_compute_many_descriptors with n_jobs=-1 (and an explicit batch_size, which joblib >= 1.6 requires) to get datamol's keys mw/clogp/n_lipinski_hbd/n_lipinski_hba/tpsa, returning a clean DataFrame ready for a Rule-of-Five mask.",
            "assertions": [
              { "type": "should_trigger", "value": true },
              { "type": "behavior", "value": "Uses datamol (dm.*) for parsing, standardization, and dm.descriptors.batch_compute_many_descriptors rather than raw RDKit, handles failed SMILES parses, and references the descriptors by datamol's own key names (clogp, n_lipinski_hbd, n_lipinski_hba) — not RDKit's logp/hbd/hba." }
            ]
          },
          {
            "id": "butina-clustering-diversity",
            "prompt": "Cluster these 800 hits by structural similarity at a Tanimoto distance cutoff of 0.2, then pick the 50 most diverse representatives for follow-up.",
            "expected_output": "Invokes alterlab-datamol: runs dm.cluster_mols with cutoff=0.2 and n_jobs=-1 (Butina clustering, noting it builds a full distance matrix suitable at this ~800 scale), unpacks its (cluster_indices, cluster_mols) return value to report cluster membership, and uses dm.pick_diverse / dm.pick_centroids (each returning (indices, mols)) to select 50 representatives.",
            "assertions": [
              { "type": "should_trigger", "value": true },
              { "type": "behavior", "value": "Uses dm.cluster_mols (Butina) at the 0.2 cutoff and a datamol diversity picker, and notes the full-distance-matrix scale limit." }
            ]
          },
          {
            "id": "scaffold-grouping-sar",
            "prompt": "Extract the Bemis-Murcko scaffold for each molecule in my series, group the compounds by scaffold, and show me the 10 most common scaffolds.",
            "expected_output": "Invokes alterlab-datamol: computes Bemis-Murcko scaffolds with dm.to_scaffold_murcko, canonicalizes each via dm.to_smiles, groups molecules per scaffold and uses a Counter to surface the 10 most frequent scaffold SMILES.",
            "assertions": [
              { "type": "should_trigger", "value": true },
              { "type": "output_contains", "value": "dm.to_scaffold_murcko" }
            ]
          },
          {
            "id": "conformers-and-viz",
            "prompt": "Generate 3D conformers for this molecule with ETKDGv3, minimize them, filter near-duplicates by RMSD, and save an aligned grid image of the molecules to a PNG.",
            "expected_output": "Invokes alterlab-datamol: calls dm.conformers.generate (method='ETKDGv3', minimize_energy=True, rms_cutoff to drop duplicates), then renders with dm.viz.to_image (align=True, outfile PNG with use_svg=False, since SVG is the default). 3D conformer embedding plus visualization are datamol's wheelhouse.",
            "assertions": [
              { "type": "should_trigger", "value": true },
              { "type": "behavior", "value": "Uses dm.conformers.generate with ETKDGv3 and dm.viz.to_image to write the PNG grid." }
            ]
          },
          {
            "id": "near-miss-molfeat",
            "prompt": "I want to turn these SMILES into ML-ready feature matrices and benchmark ECFP vs MACCS vs ChemBERTa embeddings for a QSAR random forest. Build the featurizers and a scikit-learn pipeline.",
            "expected_output": "Does NOT invoke this skill; defers to alterlab-molfeat. The user wants ML featurizer objects, pretrained ChemBERTa embeddings, and a scikit-learn-compatible transformer pipeline to compare representations, which is molfeat's territory, not datamol's everyday descriptor/cleanup wrapper.",
            "assertions": [
              { "type": "should_not_trigger", "value": true },
              { "type": "output_contains", "value": "alterlab-molfeat" }
            ]
          },
          {
            "id": "near-miss-rdkit",
            "prompt": "I need fine-grained control over a custom RDKit reaction with explicit atom-map sanitization flags and a hand-tuned conformer embedding using my own ETKDG parameters and bounds matrix. Show me the raw RDKit calls.",
            "expected_output": "Does NOT invoke this skill; defers to alterlab-rdkit. The user explicitly wants low-level, custom-parameter RDKit control rather than datamol's sensible-default wrapper, which the datamol skill itself recommends deferring to for advanced control.",
            "assertions": [
              { "type": "should_not_trigger", "value": true },
              { "type": "output_contains", "value": "alterlab-rdkit" }
            ]
          },
          {
            "id": "near-miss-medchem",
            "prompt": "Before I order these 2,000 screening hits, flag PAINS and other structural alerts, apply the NIBR filters, and drop anything failing Rule of Five or Veber.",
            "expected_output": "Does NOT invoke alterlab-datamol; defers to alterlab-medchem. Rule-based drug-likeness triage with PAINS / structural-alert catalogs and NIBR filters is medchem's job (RuleFilters, CommonAlertsFilters, NIBRFilters); datamol only supplies the underlying molecule handling and descriptors.",
            "assertions": [
              { "type": "should_not_trigger", "value": true },
              { "type": "output_contains", "value": "alterlab-medchem" }
            ]
          }
        ]
      }
      
  • references
    • conformers_module.md 5 KB
      # Datamol Conformers Module Reference
      
      The `datamol.conformers` module provides tools for generating and analyzing 3D molecular conformations.
      
      ## Conformer Generation
      
      ### `dm.conformers.generate(mol, n_confs=None, rms_cutoff=None, minimize_energy=False, method=None, add_hs=True, ...)`
      Generate 3D molecular conformers.
      - **Parameters**:
        - `mol`: Input molecule
        - `n_confs`: Number of conformers to generate (auto-determined based on rotatable bonds if None)
        - `rms_cutoff`: RMS threshold in Ångströms for filtering similar conformers (removes duplicates)
        - `minimize_energy`: Apply UFF energy minimization (default: **False** — pass `True` to minimize)
        - `method`: Embedding method - options:
          - `'ETDG'` - Experimental Torsion Distance Geometry
          - `'ETKDG'` - ETDG with additional basic knowledge
          - `'ETKDGv2'` - Enhanced version 2
          - `'ETKDGv3'` - Enhanced version 3 (used when `method=None`; recommended)
        - `add_hs`: Add hydrogens before embedding (default: True, critical for quality)
        - `random_seed`: Set for reproducibility
      - **Returns**: Molecule with embedded conformers
      - **Example**:
        ```python
        mol = dm.to_mol("CCO")
        mol_3d = dm.conformers.generate(mol, n_confs=10, rms_cutoff=0.5)
        conformers = mol_3d.GetConformers()  # Access all conformers
        ```
      
      ## Conformer Clustering
      
      ### `dm.conformers.cluster(mol, rms_cutoff=1.0, already_aligned=False, centroids=True)`
      Group conformers by RMS distance.
      - **Parameters**:
        - `rms_cutoff`: Clustering threshold in Ångströms (default: 1.0)
        - `already_aligned`: Whether conformers are pre-aligned
        - `centroids`: Return centroid conformers (True, default) or cluster groups (False)
      - **Returns**: Cluster information or centroid conformers
      - **Use case**: Identify distinct conformational families
      
      ### `dm.conformers.return_centroids(mol, conf_clusters, centroids=True)`
      Extract representative conformers from clusters.
      - **Parameters**:
        - `conf_clusters`: Sequence of index clusters (e.g. from RDKit `Butina.ClusterData`); `cluster()` calls this internally and already returns its result
        - `centroids`: Return single molecule (True) or list of molecules (False)
      - **Returns**: Centroid conformer(s)
      
      ## Conformer Analysis
      
      ### `dm.conformers.rmsd(mol)`
      Calculate pairwise RMSD matrix across all conformers.
      - **Requirements**: Minimum 2 conformers
      - **Returns**: NxN matrix of RMSD values
      - **Use case**: Quantify conformer diversity
      
      ### `dm.conformers.sasa(mol, n_jobs=1, ...)`
      Calculate Solvent Accessible Surface Area (SASA) using FreeSASA.
      - **Parameters**:
        - `n_jobs`: Parallelization for multiple conformers
      - **Returns**: Array of SASA values (one per conformer)
      - **Storage**: Values stored in each conformer as property `'rdkit_free_sasa'`
      - **Example**:
        ```python
        sasa_values = dm.conformers.sasa(mol_3d)
        # Or access from conformer properties
        conf = mol_3d.GetConformer(0)
        sasa = conf.GetDoubleProp('rdkit_free_sasa')
        ```
      
      ## Low-Level Conformer Manipulation
      
      ### `dm.conformers.center_of_mass(mol, conf_id=-1, use_atoms=True, round_coord=None)`
      Calculate molecular center.
      - **Parameters**:
        - `conf_id`: Conformer index (-1 for first conformer)
        - `use_atoms`: Use atomic masses (True) or geometric center (False)
        - `round_coord`: Decimal precision for rounding
      - **Returns**: 3D coordinates of center
      - **Use case**: Centering molecules for visualization or alignment
      
      ### `dm.conformers.get_coords(mol, conf_id=-1)`
      Retrieve atomic coordinates from a conformer.
      - **Returns**: Nx3 numpy array of atomic positions
      - **Example**:
        ```python
        positions = dm.conformers.get_coords(mol_3d, conf_id=0)
        # positions.shape: (num_atoms, 3)
        ```
      
      ### `dm.conformers.translate(mol, conf_id=-1, transform_matrix=None)`
      Reposition conformer using transformation matrix.
      - **Modification**: Operates in-place
      - **Use case**: Aligning or repositioning molecules
      
      ## Workflow Example
      
      ```python
      import datamol as dm
      
      # 1. Create molecule and generate conformers
      mol = dm.to_mol("CC(C)CCO")  # Isopentanol
      mol_3d = dm.conformers.generate(
          mol,
          n_confs=50,           # Generate 50 initial conformers
          rms_cutoff=0.5,       # Filter similar conformers
          minimize_energy=True   # Minimize energy
      )
      
      # 2. Analyze conformers
      n_conformers = mol_3d.GetNumConformers()
      print(f"Generated {n_conformers} unique conformers")
      
      # 3. Calculate SASA
      sasa_values = dm.conformers.sasa(mol_3d)
      
      # 4. Cluster conformers
      clusters = dm.conformers.cluster(mol_3d, rms_cutoff=1.0, centroids=False)
      
      # 5. Get representative conformers
      centroids = dm.conformers.return_centroids(mol_3d, clusters)
      
      # 6. Access 3D coordinates
      coords = dm.conformers.get_coords(mol_3d, conf_id=0)
      ```
      
      ## Key Concepts
      
      - **Distance Geometry**: Method for generating 3D structures from connectivity information
      - **ETKDG**: Uses experimental torsion angle preferences and additional chemical knowledge
      - **RMS Cutoff**: Lower values = more unique conformers; higher values = fewer, more distinct conformers
      - **Energy Minimization**: Relaxes structures to nearest local energy minimum
      - **Hydrogens**: Critical for accurate 3D geometry - always include during embedding
      
    • core_api.md 4.7 KB
      # Datamol Core API Reference
      
      This document covers the main functions available in the datamol namespace.
      
      ## Molecule Creation and Conversion
      
      ### `to_mol(mol, ...)`
      Convert SMILES string or other molecular representations to RDKit molecule objects.
      - **Parameters**: Accepts SMILES strings, InChI, or other molecular formats
      - **Returns**: `rdkit.Chem.Mol` object
      - **Common usage**: `mol = dm.to_mol("CCO")`
      
      ### `from_inchi(inchi)`
      Convert InChI string to molecule object.
      
      ### `from_smarts(smarts)`
      Convert SMARTS pattern to molecule object.
      
      ### `from_selfies(selfies)`
      Convert SELFIES string to molecule object.
      
      ### `copy_mol(mol)`
      Create a copy of a molecule object to avoid modifying the original.
      
      ## Molecule Export
      
      ### `to_smiles(mol, ...)`
      Convert molecule object to SMILES string.
      - **Common parameters**: `canonical=True`, `isomeric=True`
      
      ### `to_inchi(mol, ...)`
      Convert molecule to InChI string representation.
      
      ### `to_inchikey(mol)`
      Convert molecule to InChI key (fixed-length hash).
      
      ### `to_smarts(mol)`
      Convert molecule to SMARTS pattern.
      
      ### `to_selfies(mol)`
      Convert molecule to SELFIES (Self-Referencing Embedded Strings) format.
      
      ## Sanitization and Standardization
      
      ### `sanitize_mol(mol, ...)`
      Enhanced version of RDKit's sanitize operation using mol→SMILES→mol conversion and aromatic nitrogen fixing.
      - **Purpose**: Fix common molecular structure issues
      - **Returns**: Sanitized molecule or None if sanitization fails
      
      ### `standardize_mol(mol, disconnect_metals=False, normalize=True, reionize=True, ...)`
      Apply comprehensive standardization procedures including:
      - Metal disconnection
      - Normalization (charge corrections)
      - Reionization
      - Fragment handling (largest fragment selection)
      
      ### `standardize_smiles(smiles, ...)`
      Apply SMILES standardization procedures directly to a SMILES string.
      
      ### `fix_mol(mol)`
      Attempt to fix molecular structure issues automatically.
      
      ### `fix_valence(mol)`
      Correct valence errors in molecular structures.
      
      ## Molecular Properties
      
      ### `reorder_atoms(mol, ...)`
      Ensure consistent atom ordering for the same molecule regardless of original SMILES representation.
      - **Purpose**: Maintain reproducible feature generation
      
      ### `remove_hs(mol, ...)`
      Remove hydrogen atoms from molecular structure.
      
      ### `add_hs(mol, ...)`
      Add explicit hydrogen atoms to molecular structure.
      
      ## Fingerprints and Similarity
      
      ### `to_fp(mol, fp_type='ecfp', ...)`
      Generate molecular fingerprints for similarity calculations.
      - **Fingerprint types**:
        - `'ecfp'` - Extended Connectivity Fingerprints (Morgan)
        - `'fcfp'` - Functional Connectivity Fingerprints
        - `'maccs'` - MACCS keys
        - `'topological'` - Topological fingerprints
        - `'atompair'` - Atom pair fingerprints
      - **Common parameters**: `radius`, `fpSize` — extra kwargs are passed straight to RDKit's `rdFingerprintGenerator` (so `fpSize`, not `n_bits`/`nBits`); datamol's `ecfp` default is `radius=3` (ECFP6), `fpSize=2048`
      - **Returns**: Numpy array (default `as_array=True`) or RDKit fingerprint object
      
      ### `pdist(mols, n_jobs=1, squareform=True, **fp_args)`
      Calculate pairwise Tanimoto distances between all molecules in a list.
      - **Supports**: Parallel processing via `n_jobs` parameter
      - **Returns**: Square N×N distance matrix by default; `squareform=False` gives the condensed vector
      
      ### `cdist(mols1, mols2, ...)`
      Calculate Tanimoto distances between two sets of molecules.
      
      ## Clustering and Diversity
      
      ### `cluster_mols(mols, cutoff=0.2, feature_fn=None, n_jobs=1)`
      Cluster molecules using Butina clustering algorithm.
      - **Parameters**:
        - `cutoff`: Distance threshold (default 0.2)
        - `feature_fn`: Custom function for molecular features
        - `n_jobs`: Parallelization (-1 for all cores)
      - **Important**: Builds full distance matrix - suitable for ~1000 structures, not for 10,000+
      - **Returns**: A tuple `(cluster_indices, cluster_mols)` — one tuple of indices and one list of Mols per cluster
      
      ### `pick_diverse(mols, npick, ...)`
      Select diverse subset of molecules based on fingerprint diversity (MaxMin).
      - **Returns**: `(picked_indices, picked_mols)`
      
      ### `pick_centroids(mols, npick=0, threshold=0.5, method='sphere', ...)`
      Select centroid molecules representing clusters (sphere exclusion by default; `maxmin` or RDKit hierarchical methods also accepted).
      - **Returns**: `(centroid_indices, centroid_mols)`
      
      ## Graph Operations
      
      ### `to_graph(mol)`
      Convert molecule to graph representation for graph-based analysis.
      
      ### `get_all_path_between(mol, start, end)`
      Find all paths between two atoms in molecular structure.
      
      ## DataFrame Integration
      
      ### `to_df(mols, smiles_column='smiles', mol_column='mol')`
      Convert list of molecules to pandas DataFrame.
      
      ### `from_df(df, smiles_column='smiles', mol_column='mol')`
      Convert pandas DataFrame to list of molecules.
      
    • descriptors_viz.md 7.9 KB
      # Datamol Descriptors and Visualization Reference
      
      ## Descriptors Module (`datamol.descriptors`)
      
      The descriptors module provides tools for computing molecular properties and descriptors.
      
      ### Specialized Descriptor Functions
      
      #### `dm.descriptors.n_aromatic_atoms(mol)`
      Calculate the number of aromatic atoms.
      - **Returns**: Integer count
      - **Use case**: Aromaticity analysis
      
      #### `dm.descriptors.n_aromatic_atoms_proportion(mol)`
      Calculate ratio of aromatic atoms to total heavy atoms.
      - **Returns**: Float between 0 and 1
      - **Use case**: Quantifying aromatic character
      
      #### `dm.descriptors.n_charged_atoms(mol)`
      Count atoms with nonzero formal charge.
      - **Returns**: Integer count
      - **Use case**: Charge distribution analysis
      
      #### `dm.descriptors.n_rigid_bonds(mol)`
      Count non-rotatable bonds (neither single bonds nor ring bonds).
      - **Returns**: Integer count
      - **Use case**: Molecular flexibility assessment
      
      #### `dm.descriptors.n_stereo_centers(mol)`
      Count stereogenic centers (chiral centers).
      - **Returns**: Integer count
      - **Use case**: Stereochemistry analysis
      
      #### `dm.descriptors.n_stereo_centers_unspecified(mol)`
      Count stereocenters lacking stereochemical specification.
      - **Returns**: Integer count
      - **Use case**: Identifying incomplete stereochemistry
      
      ### Batch Descriptor Computation
      
      #### `dm.descriptors.compute_many_descriptors(mol, properties_fn=None, add_properties=True)`
      Compute multiple molecular properties for a single molecule.
      - **Parameters**:
        - `properties_fn`: Custom list of descriptor functions
        - `add_properties`: Include additional computed properties
      - **Returns**: Dictionary of descriptor name → value pairs (~22 keys)
      - **Default keys** (datamol's own names, verified on 0.13.0): `mw`, `fsp3`,
        `n_lipinski_hba`, `n_lipinski_hbd`, `n_rings`, `n_hetero_atoms`,
        `n_heavy_atoms`, `n_rotatable_bonds`, `n_radical_electrons`, `tpsa`, `qed`,
        `clogp`, `sas`, plus aliphatic/aromatic/saturated carbocycle/heterocycle/ring
        counts (`n_aromatic_heterocycles` etc. — 0.13 fixed the old `..._heterocyles`
        misspelling, so the dict keys changed).
      - **Naming gotcha**: logP is `clogp`; H-bond donors/acceptors are
        `n_lipinski_hbd` / `n_lipinski_hba`. There is **no** `logp`, `hbd`, `hba`, or
        `n_aromatic_atoms` key (use the standalone `n_aromatic_atoms(mol)` function for
        that count).
      - **Example**:
        ```python
        mol = dm.to_mol("CCO")
        descriptors = dm.descriptors.compute_many_descriptors(mol)
        # {'mw': 46.04, 'clogp': -0.0, 'n_lipinski_hbd': 1, 'n_lipinski_hba': 1,
        #  'tpsa': 20.23, ...}
        ```
      
      #### `dm.descriptors.batch_compute_many_descriptors(mols, properties_fn=None, add_properties=True, n_jobs=1, batch_size=None, progress=False)`
      Compute descriptors for multiple molecules in parallel.
      - **Parameters**:
        - `mols`: List of molecules
        - `n_jobs`: Number of parallel jobs (-1 for all cores)
        - `batch_size`: Chunk size for parallel processing — set it explicitly (e.g. `256`) whenever `n_jobs != 1`; the `None` default is rejected by joblib ≥ 1.6
        - `progress`: Show progress bar
      - **Returns**: Pandas DataFrame with one row per molecule
      - **Example**:
        ```python
        mols = [dm.to_mol(smi) for smi in smiles_list]
        df = dm.descriptors.batch_compute_many_descriptors(
            mols,
            n_jobs=-1,
            batch_size=256,
            progress=True
        )
        ```
      
      ### RDKit Descriptor Access
      
      #### `dm.descriptors.any_rdkit_descriptor(name)`
      Retrieve any descriptor function from RDKit by name.
      - **Parameters**: `name` - Descriptor function name (e.g., 'MolWt', 'TPSA')
      - **Returns**: RDKit descriptor function
      - **Available descriptors**: From `rdkit.Chem.Descriptors` and `rdkit.Chem.rdMolDescriptors`
      - **Example**:
        ```python
        tpsa_fn = dm.descriptors.any_rdkit_descriptor('TPSA')
        tpsa_value = tpsa_fn(mol)
        ```
      
      ### Common Use Cases
      
      **Drug-likeness Filtering (Lipinski's Rule of Five)**:
      ```python
      descriptors = dm.descriptors.compute_many_descriptors(mol)
      is_druglike = (
          descriptors['mw'] <= 500 and
          descriptors['clogp'] <= 5 and
          descriptors['n_lipinski_hbd'] <= 5 and
          descriptors['n_lipinski_hba'] <= 10
      )
      ```
      
      **ADME Property Analysis**:
      ```python
      df = dm.descriptors.batch_compute_many_descriptors(compound_library)
      # Filter by TPSA for blood-brain barrier penetration
      bbb_candidates = df[df['tpsa'] < 90]
      ```
      
      ---
      
      ## Visualization Module (`datamol.viz`)
      
      The viz module provides tools for rendering molecules and conformers as images.
      
      ### Main Visualization Function
      
      #### `dm.viz.to_image(mols, legends=None, n_cols=4, use_svg=True, mol_size=(300, 300), highlight_atom=None, highlight_bond=None, outfile=None, max_mols=32, copy=True, indices=False, align=False, ...)`
      Generate image grid from molecules.
      - **Parameters**:
        - `mols`: Single molecule or list of molecules
        - `legends`: String or list of strings as labels (one per molecule)
        - `n_cols`: Number of molecules per row (default: 4)
        - `use_svg`: Output SVG (True, **default**) or PNG (False) — set `use_svg=False` when writing a `.png` file
        - `mol_size`: Tuple (width, height) or single int for square images
        - `highlight_atom`: Atom indices to highlight (list or dict)
        - `highlight_bond`: Bond indices to highlight (list or dict)
        - `outfile`: Save path (local or remote, supports fsspec)
        - `max_mols`: Maximum number of molecules to display
        - `indices`: Draw atom indices on structures (default: False)
        - `align`: Align molecules using MCS (Maximum Common Substructure)
      - **Returns**: Image object (can be displayed in Jupyter) or saves to file
      - **Example**:
        ```python
        # Basic grid
        dm.viz.to_image(mols[:10], legends=[dm.to_smiles(m) for m in mols[:10]])
      
        # Save to file (PNG needs use_svg=False; SVG is the default)
        dm.viz.to_image(mols, outfile="molecules.png", n_cols=5, use_svg=False)
      
        # Highlight substructure
        dm.viz.to_image(mol, highlight_atom=[0, 1, 2], highlight_bond=[0, 1])
      
        # Aligned visualization
        dm.viz.to_image(mols, align=True, legends=activity_labels)
        ```
      
      ### Conformer Visualization
      
      #### `dm.viz.conformers(mol, n_confs=None, align_conf=True, n_cols=3, sync_views=True, remove_hs=True, ...)`
      Display multiple conformers in grid layout.
      - **Parameters**:
        - `mol`: Molecule with embedded conformers
        - `n_confs`: Number or list of conformer indices to display (None = all)
        - `align_conf`: Align conformers for comparison (default: True)
        - `n_cols`: Grid columns (default: 3)
        - `sync_views`: Synchronize 3D views when interactive (default: True)
        - `remove_hs`: Remove hydrogens for clarity (default: True)
      - **Returns**: Grid of conformer visualizations
      - **Use case**: Comparing conformational diversity
      - **Example**:
        ```python
        mol_3d = dm.conformers.generate(mol, n_confs=20)
        dm.viz.conformers(mol_3d, n_confs=10, align_conf=True)
        ```
      
      ### Circle Grid Visualization
      
      #### `dm.viz.circle_grid(center_mol, circle_mols, mol_size=200, circle_margin=50, act_mapper=None, ...)`
      Create concentric ring visualization with central molecule.
      - **Parameters**:
        - `center_mol`: Molecule at center
        - `circle_mols`: List of molecule lists (one list per ring)
        - `mol_size`: Image size per molecule
        - `circle_margin`: Spacing between rings (default: 50)
        - `act_mapper`: Activity mapping dictionary for color-coding
      - **Returns**: Circular grid image
      - **Use case**: Visualizing molecular neighborhoods, SAR analysis, similarity networks
      - **Example**:
        ```python
        # Show a reference molecule surrounded by similar compounds
        dm.viz.circle_grid(
            center_mol=reference,
            circle_mols=[nearest_neighbors, second_tier]
        )
        ```
      
      ### Visualization Best Practices
      
      1. **Use legends for clarity**: Always label molecules with SMILES, IDs, or activity values
      2. **Align related molecules**: Use `align=True` in `to_image()` for SAR analysis
      3. **Adjust grid size**: Set `n_cols` based on molecule count and display width
      4. **Use SVG for publications**: Set `use_svg=True` for scalable vector graphics
      5. **Highlight substructures**: Use `highlight_atom` and `highlight_bond` to emphasize features
      6. **Save large grids**: Use `outfile` parameter to save rather than display in memory
      
    • fragments_scaffolds.md 6.3 KB
      # Datamol Fragments and Scaffolds Reference
      
      ## Scaffolds Module (`datamol.scaffold`)
      
      Scaffolds represent the core structure of molecules, useful for identifying structural families and analyzing structure-activity relationships (SAR).
      
      ### Murcko Scaffolds
      
      #### `dm.to_scaffold_murcko(mol)`
      Extract Bemis-Murcko scaffold (molecular framework).
      - **Method**: Removes side chains, retaining ring systems and linkers
      - **Returns**: Molecule object representing the scaffold
      - **Use case**: Identify core structures across compound series
      - **Example**:
        ```python
        mol = dm.to_mol("c1ccc(cc1)CCN")  # Phenethylamine
        scaffold = dm.to_scaffold_murcko(mol)
        scaffold_smiles = dm.to_smiles(scaffold)
        # Returns: 'c1ccccc1' — the ethylamine chain is a side chain (not a linker
        # between two ring systems), so it is stripped, leaving only the ring.
        ```
      
      **Workflow for scaffold analysis**:
      ```python
      # Extract scaffolds from compound library
      scaffolds = [dm.to_scaffold_murcko(mol) for mol in mols]
      scaffold_smiles = [dm.to_smiles(s) for s in scaffolds]
      
      # Count scaffold frequency
      from collections import Counter
      scaffold_counts = Counter(scaffold_smiles)
      most_common = scaffold_counts.most_common(10)
      ```
      
      ### Fuzzy Scaffolds
      
      #### `dm.scaffold.fuzzy_scaffolding(mol, ...)`
      Generate fuzzy scaffolds with enforceable groups that must appear in the core.
      - **Purpose**: More flexible scaffold definition allowing specified functional groups
      - **Use case**: Custom scaffold definitions beyond Murcko rules
      
      ### Applications
      
      **Scaffold-based splitting** (for ML model validation):
      ```python
      # Group compounds by scaffold
      scaffold_to_mols = {}
      for mol, scaffold in zip(mols, scaffolds):
          smi = dm.to_smiles(scaffold)
          if smi not in scaffold_to_mols:
              scaffold_to_mols[smi] = []
          scaffold_to_mols[smi].append(mol)
      
      # Ensure train/test sets have different scaffolds
      ```
      
      **SAR analysis**:
      ```python
      # Group by scaffold and analyze activity
      for scaffold_smi, molecules in scaffold_to_mols.items():
          activities = [get_activity(mol) for mol in molecules]
          print(f"Scaffold: {scaffold_smi}, Mean activity: {np.mean(activities)}")
      ```
      
      ---
      
      ## Fragments Module (`datamol.fragment`)
      
      Molecular fragmentation breaks molecules into smaller pieces based on chemical rules, useful for fragment-based drug design and substructure analysis.
      
      ### BRICS Fragmentation
      
      #### `dm.fragment.brics(mol, ...)`
      Fragment molecule using BRICS (Breaking Retrosynthetically Interesting Chemical Substructures).
      - **Method**: Dissects based on 16 chemically meaningful bond types
      - **Consideration**: Considers chemical environment and surrounding substructures
      - **Returns**: List of RDKit `Mol` fragments — the parent molecule comes first unless `remove_parent=True`; the default `fix=True` caps dummy atoms, so pass `fix=False` to keep attachment points
      - **Use case**: Retrosynthetic analysis, fragment-based design
      - **Example**:
        ```python
        mol = dm.to_mol("c1ccccc1CCN")
        fragments = dm.fragment.brics(mol, remove_parent=True, fix=False)
        frag_smiles = {dm.to_smiles(f) for f in fragments}
        # e.g. '[4*]CCN', '[16*]c1ccccc1' — [n*] marks attachment points
        ```
      
      ### RECAP Fragmentation
      
      #### `dm.fragment.recap(mol, ...)`
      Fragment molecule using RECAP (Retrosynthetic Combinatorial Analysis Procedure).
      - **Method**: Dissects based on 11 predefined bond types
      - **Rules**:
        - Leaves alkyl groups smaller than 5 carbons intact
        - Preserves cyclic bonds
      - **Returns**: List of RDKit `Mol` fragments (parent first unless `remove_parent=True`; `fix=False` keeps `*` attachment points)
      - **Use case**: Combinatorial library design
      - **Example**:
        ```python
        mol = dm.to_mol("CCCCCc1ccccc1")
        fragments = dm.fragment.recap(mol)
        ```
      
      ### MMPA Fragmentation
      
      #### `dm.fragment.mmpa_frag(mol, ...)`
      Fragment for Matched Molecular Pair Analysis.
      - **Purpose**: Generate fragments suitable for identifying molecular pairs
      - **Use case**: Analyzing how small structural changes affect properties
      - **Example**:
        ```python
        fragments = dm.fragment.mmpa_frag(mol)
        # Used to find pairs of molecules differing by single transformation
        ```
      
      ### Comparison of Methods
      
      | Method | Bond Types | Preserves Cycles | Best For |
      |--------|-----------|------------------|----------|
      | BRICS  | 16        | Yes              | Retrosynthetic analysis, fragment recombination |
      | RECAP  | 11        | Yes              | Combinatorial library design |
      | MMPA   | Variable  | Depends          | Structure-activity relationship analysis |
      
      ### Fragmentation Workflow
      
      ```python
      import datamol as dm
      
      # 1. Fragment a molecule
      mol = dm.to_mol("CC(=O)Oc1ccccc1C(=O)O")  # Aspirin
      brics_frags = dm.fragment.brics(mol)
      recap_frags = dm.fragment.recap(mol)
      
      # 2. Analyze fragment frequency across library (count SMILES, not Mol objects)
      all_fragments = []
      for mol in molecule_library:
          frags = dm.fragment.brics(mol, remove_parent=True, fix=False)
          all_fragments.extend(dm.to_smiles(f) for f in frags)
      
      # 3. Identify common fragments
      from collections import Counter
      fragment_counts = Counter(all_fragments)
      common_fragments = fragment_counts.most_common(20)
      
      # 4. Convert fragments back to molecules (remove attachment points)
      def clean_fragment(frag_smiles):
          # Cap every [n*] / * attachment point with hydrogen
          import re
          clean = re.sub(r"\[\d*\*\]|\*", "[H]", frag_smiles)
          return dm.to_mol(clean)
      ```
      
      ### Advanced: Fragment-Based Virtual Screening
      
      ```python
      # Build fragment library from known actives (as canonical SMILES)
      def brics_smiles(mol):
          return {dm.to_smiles(f) for f in dm.fragment.brics(mol, remove_parent=True, fix=False)}
      
      active_fragments = set()
      for active_mol in active_compounds:
          active_fragments.update(brics_smiles(active_mol))
      
      # Screen compounds for presence of active fragments
      def score_by_fragments(mol, fragment_set):
          mol_frags = brics_smiles(mol)
          overlap = mol_frags.intersection(fragment_set)
          return len(overlap) / len(mol_frags) if mol_frags else 0.0
      
      # Score screening library
      scores = [score_by_fragments(mol, active_fragments) for mol in screening_lib]
      ```
      
      ### Key Concepts
      
      - **Attachment Points**: Marked with [1*], [2*], etc. in fragment SMILES
      - **Retrosynthetic**: Fragmentation mimics synthetic disconnections
      - **Chemically Meaningful**: Breaks occur at typical synthetic bonds
      - **Recombination**: Fragments can theoretically be recombined into valid molecules
      
    • io_module.md 4.5 KB
      # Datamol I/O Module Reference
      
      The `datamol.io` module provides comprehensive file handling for molecular data across multiple formats.
      
      ## Reading Molecular Files
      
      ### `dm.read_sdf(urlpath, sanitize=True, as_df=False, smiles_column='smiles', mol_column=None, remove_hs=True, n_jobs=1, ...)`
      Read Structure-Data File (SDF) format.
      - **Parameters**:
        - `filename`: Path to SDF file (supports local and remote paths via fsspec)
        - `sanitize`: Apply sanitization to molecules
        - `remove_hs`: Remove explicit hydrogens
        - `as_df`: Return a DataFrame (True) or a list of molecules (False, **default**)
        - `mol_column`: Name of molecule column in the DataFrame (only with `as_df=True`)
        - `n_jobs`: Enable parallel processing
      - **Returns**: List of molecules, or a DataFrame with `as_df=True`
      - **Example**: `mols = dm.read_sdf("compounds.sdf")`; `df = dm.read_sdf("compounds.sdf", as_df=True, mol_column="mol")`
      
      ### `dm.read_smi(urlpath)`
      Read a SMILES file (SMILES optionally followed by an ID/name).
      - **Returns**: List of molecules (no DataFrame option; wrap with `dm.to_df(mols)` if needed)
      - **Example**: `mols = dm.read_smi("molecules.smi")`
      
      ### `dm.read_csv(filename, smiles_column='smiles', mol_column=None, ...)`
      Read CSV file with optional automatic SMILES-to-molecule conversion.
      - **Parameters**:
        - `smiles_column`: Column containing SMILES strings
        - `mol_column`: If specified, creates molecule objects from SMILES column
      - **Example**: `df = dm.read_csv("data.csv", smiles_column="SMILES", mol_column="mol")`
      
      ### `dm.read_excel(filename, sheet_name=0, smiles_column='smiles', mol_column=None, ...)`
      Read Excel files with molecule handling.
      - **Parameters**:
        - `sheet_name`: Sheet to read (index or name)
        - Other parameters similar to `read_csv`
      - **Example**: `df = dm.read_excel("compounds.xlsx", sheet_name="Sheet1")`
      
      ### `dm.read_molblock(molblock, sanitize=True, remove_hs=True)`
      Parse MOL block string (molecular structure text representation).
      
      ### `dm.read_mol2file(filename, sanitize=True, remove_hs=True, cleanupSubstructures=True)`
      Read Mol2 format files.
      
      ### `dm.read_pdbfile(filename, sanitize=True, remove_hs=True, proximityBonding=True)`
      Read Protein Data Bank (PDB) format files.
      
      ### `dm.read_pdbblock(pdbblock, sanitize=True, remove_hs=True, proximityBonding=True)`
      Parse PDB block string.
      
      ### `dm.open_df(filename, ...)`
      Universal DataFrame reader - automatically detects format.
      - **Supported formats**: CSV, Excel, Parquet, JSON, SDF
      - **Example**: `df = dm.open_df("data.csv")` or `df = dm.open_df("molecules.sdf")`
      
      ## Writing Molecular Files
      
      ### `dm.to_sdf(mols, filename, mol_column=None, ...)`
      Write molecules to SDF file.
      - **Input types**:
        - List of molecules
        - DataFrame with molecule column
        - Sequence of molecules
      - **Parameters**:
        - `mol_column`: Column name if input is DataFrame
      - **Example**:
        ```python
        dm.to_sdf(mols, "output.sdf")
        # or from DataFrame
        dm.to_sdf(df, "output.sdf", mol_column="mol")
        ```
      
      ### `dm.to_smi(mols, filename, mol_column=None, ...)`
      Write molecules to SMILES file with optional validation.
      - **Format**: SMILES strings with optional molecule names/IDs
      
      ### `dm.to_xlsx(mols, urlpath, smiles_column='smiles', mol_column='mol', mol_size=[300, 300])`
      Export molecules or a DataFrame to Excel with rendered molecular images.
      - **Parameters**:
        - `mol_column`: The (single) column containing molecules to render as images
      - **Special feature**: Automatically renders molecules as images in Excel cells
      - **Example**: `dm.to_xlsx(df, "molecules.xlsx", mol_column="mol")`
      
      ### `dm.to_molblock(mol, ...)`
      Convert molecule to MOL block string.
      
      ### `dm.to_pdbblock(mol, ...)`
      Convert molecule to PDB block string.
      
      ### `dm.save_df(df, filename, ...)`
      Save DataFrame in multiple formats (CSV, Excel, Parquet, JSON).
      
      ## Remote File Support
      
      All I/O functions support remote file paths through fsspec integration:
      - **Supported protocols**: S3 (AWS), GCS (Google Cloud), Azure, HTTP/HTTPS
      - **Example**:
        ```python
        dm.read_sdf("s3://bucket/compounds.sdf")
        dm.read_csv("https://example.com/data.csv")
        ```
      
      ## Key Parameters Across Functions
      
      - **`sanitize`**: Apply molecule sanitization (default: True)
      - **`remove_hs`**: Remove explicit hydrogens (default: True)
      - **`as_df`**: Return DataFrame vs list (`read_sdf` defaults to a list, `as_df=False`)
      - **`n_jobs`**: Parallel processing (`-1` = all cores; `0`, `1`, or `None` = sequential)
      - **`mol_column`**: Name of molecule column in DataFrames
      - **`smiles_column`**: Name of SMILES column in DataFrames
      
    • reactions_data.md 7.2 KB
      # Datamol Reactions and Data Modules Reference
      
      ## Reactions Module (`datamol.reactions`)
      
      The reactions module enables programmatic application of chemical transformations using SMARTS reaction patterns.
      
      ### Applying Chemical Reactions
      
      #### `dm.reactions.apply_reaction(rxn, reactants, product_index=None, single_product_group=False, as_smiles=False, rm_attach=False, sanitize=True)`
      Apply a chemical reaction to reactant molecules.
      - **Parameters**:
        - `rxn`: Reaction object (from SMARTS pattern)
        - `reactants`: Tuple of reactant molecules
        - `as_smiles`: Return SMILES strings (True) or molecule objects (False)
        - `sanitize`: Sanitize product molecules
        - `single_product_group`: Return one product set (True) or all product sets (False, default)
        - `rm_attach`: Remove attachment point markers (default False)
        - `product_index`: Which product to return from each set (default None = all)
      - **Returns**: With the defaults, a list of product lists; with `single_product_group=True, product_index=0`, a single Mol (or SMILES with `as_smiles=True`)
      - **Example**:
        ```python
        from rdkit import Chem
      
        # Define reaction: alcohol + carboxylic acid → ester
        rxn = Chem.rdChemReactions.ReactionFromSmarts(
            '[C:1][OH:2].[C:3](=[O:4])[OH:5]>>[C:1][O:2][C:3](=[O:4])'
        )
      
        # Apply to reactants
        alcohol = dm.to_mol("CCO")
        acid = dm.to_mol("CC(=O)O")
        product = dm.reactions.apply_reaction(rxn, (alcohol, acid),
                                              single_product_group=True, product_index=0)
        ```
      
      ### Creating Reactions
      
      Reactions are typically created from SMARTS patterns using RDKit:
      ```python
      from rdkit.Chem import rdChemReactions
      
      # Reaction pattern: [reactant1].[reactant2]>>[product]
      rxn = rdChemReactions.ReactionFromSmarts(
          '[1*][*:1].[1*][*:2]>>[*:1][*:2]'
      )
      ```
      
      ### Validation Functions
      
      The module includes functions to:
      - **Check if molecule is reactant**: Verify if molecule matches reactant pattern
      - **Validate reaction**: Check if reaction is synthetically reasonable
      - **Process reaction files**: Load reactions from files or databases
      
      ### Common Reaction Patterns
      
      **Amide formation**:
      ```python
      # Amine + carboxylic acid → amide
      amide_rxn = rdChemReactions.ReactionFromSmarts(
          '[N:1].[C:2](=[O:3])[OH]>>[N:1][C:2](=[O:3])'
      )
      ```
      
      **Suzuki coupling**:
      ```python
      # Aryl halide + boronic acid → biaryl
      suzuki_rxn = rdChemReactions.ReactionFromSmarts(
          '[c:1][Br].[c:2][B]([OH])[OH]>>[c:1][c:2]'
      )
      ```
      
      **Functional group transformations**:
      ```python
      # Alcohol → ester
      esterification = rdChemReactions.ReactionFromSmarts(
          '[C:1][OH:2].[C:3](=[O:4])[Cl]>>[C:1][O:2][C:3](=[O:4])'
      )
      ```
      
      ### Workflow Example
      
      ```python
      import datamol as dm
      from rdkit.Chem import rdChemReactions
      
      # 1. Define reaction
      rxn_smarts = '[C:1](=[O:2])[OH:3]>>[C:1](=[O:2])[Cl:3]'  # Acid → acid chloride
      rxn = rdChemReactions.ReactionFromSmarts(rxn_smarts)
      
      # 2. Apply to molecule library
      acids = [dm.to_mol(smi) for smi in acid_smiles_list]
      acid_chlorides = []
      
      for acid in acids:
          try:
              product = dm.reactions.apply_reaction(
                  rxn,
                  (acid,),  # Single reactant as tuple
                  single_product_group=True,
                  product_index=0,
                  sanitize=True
              )
              acid_chlorides.append(product)
          except Exception as e:
              print(f"Reaction failed: {e}")
      
      # 3. Validate products
      valid_products = [p for p in acid_chlorides if p is not None]
      ```
      
      ### Key Concepts
      
      - **SMARTS**: SMiles ARbitrary Target Specification - pattern language for reactions
      - **Atom Mapping**: Numbers like [C:1] preserve atom identity through reaction
      - **Attachment Points**: [1*] represents generic connection points
      - **Reaction Validation**: Not all SMARTS reactions are chemically reasonable
      
      ---
      
      ## Data Module (`datamol.data`)
      
      The data module provides convenient access to curated molecular datasets for testing and learning.
      
      ### Available Datasets
      
      #### `dm.data.cdk2(as_df=True, mol_column='mol')`
      RDKit CDK2 dataset - kinase inhibitor data.
      - **Parameters**:
        - `as_df`: Return as DataFrame (True) or list of molecules (False)
        - `mol_column`: Name for molecule column
      - **Returns**: Dataset with molecular structures and activity data
      - **Use case**: Small dataset for algorithm testing
      - **Example**:
        ```python
        cdk2_df = dm.data.cdk2(as_df=True)
        print(cdk2_df.shape)
        print(cdk2_df.columns)
        ```
      
      #### `dm.data.freesolv()`
      FreeSolv dataset - experimental and calculated hydration free energies.
      - **Contents**: 642 molecules with:
        - IUPAC names
        - SMILES strings
        - Experimental hydration free energy values
        - Calculated values
      - **Warning**: "Only meant to be used as a toy dataset for pedagogic and testing purposes"
      - **Not suitable for**: Benchmarking or production model training
      - **Example**:
        ```python
        freesolv_df = dm.data.freesolv()
        # Columns: iupac, smiles, expt (kcal/mol), calc (kcal/mol)
        ```
      
      #### `dm.data.solubility(as_df=True, mol_column='mol')`
      RDKit solubility dataset with train/test splits.
      - **Contents**: Aqueous solubility data with pre-defined splits
      - **Columns**: Includes 'split' column with 'train' or 'test' values
      - **Use case**: Testing ML workflows with proper train/test separation
      - **Example**:
        ```python
        sol_df = dm.data.solubility(as_df=True)
        # Columns: mol, ID, NAME, SOL, SOL_classification, smiles, split
        # The numeric solubility target is the 'SOL' column.
      
        # Split into train/test
        train_df = sol_df[sol_df['split'] == 'train']
        test_df = sol_df[sol_df['split'] == 'test']
      
        # Featurize: to_fp takes ONE mol, so stack per-row into a matrix.
        import numpy as np
        X_train = np.array([dm.to_fp(m) for m in train_df['mol']])
        y_train = train_df['SOL'].values
        ```
      
      ### Usage Guidelines
      
      **For testing and tutorials**:
      ```python
      # Quick dataset for testing code
      df = dm.data.cdk2()
      mols = df['mol'].tolist()
      
      # Test descriptor calculation
      descriptors_df = dm.descriptors.batch_compute_many_descriptors(mols)
      
      # Test clustering (returns (cluster_indices, cluster_mols))
      cluster_idx, cluster_mols = dm.cluster_mols(mols, cutoff=0.3)
      ```
      
      **For learning workflows**:
      ```python
      # Complete ML pipeline example
      sol_df = dm.data.solubility()
      
      # Preprocessing
      train = sol_df[sol_df['split'] == 'train']
      test = sol_df[sol_df['split'] == 'test']
      
      # Featurization: to_fp is per-molecule, so build the matrix row by row
      import numpy as np
      X_train = np.array([dm.to_fp(m) for m in train['mol']])
      X_test = np.array([dm.to_fp(m) for m in test['mol']])
      
      # Model training (example) — 'SOL' is the numeric solubility target column
      from sklearn.ensemble import RandomForestRegressor
      model = RandomForestRegressor()
      model.fit(X_train, train['SOL'])
      predictions = model.predict(X_test)
      ```
      
      ### Important Notes
      
      - **Toy Datasets**: Designed for pedagogical purposes, not production use
      - **Small Size**: Limited number of compounds suitable for quick tests
      - **Pre-processed**: Data already cleaned and formatted
      - **Citations**: Check dataset documentation for proper attribution if publishing
      
      ### Best Practices
      
      1. **Use for development only**: Don't draw scientific conclusions from toy datasets
      2. **Validate on real data**: Always test production code on actual project data
      3. **Proper attribution**: Cite original data sources if using in publications
      4. **Understand limitations**: Know the scope and quality of each dataset
      
    • workflow_recipes.md 6.1 KB
      # Datamol Worked Workflow Recipes
      
      End-to-end, copy-ready pipelines and multi-step recipes extracted from the skill body so `SKILL.md` stays lean; each block is self-contained and assumes `import datamol as dm`.
      
      ## Complete Pipeline: Data Loading → Filtering → Analysis
      
      ```python
      import datamol as dm
      import pandas as pd
      
      # 1. Load molecules (read_sdf returns a list unless as_df=True)
      df = dm.read_sdf("compounds.sdf", as_df=True, mol_column="mol")
      
      # 2. Standardize
      df['mol'] = df['mol'].apply(lambda m: dm.standardize_mol(m) if m else None)
      df = df[df['mol'].notna()]  # Remove failed molecules
      
      # 3. Compute descriptors
      desc_df = dm.descriptors.batch_compute_many_descriptors(
          df['mol'].tolist(),
          n_jobs=-1,
          batch_size=256,   # required with n_jobs != 1 under joblib >= 1.6
          progress=True
      )
      
      # 4. Filter by drug-likeness (batch_compute_many_descriptors uses the same keys
      #    as compute_many_descriptors: clogp, n_lipinski_hbd, n_lipinski_hba)
      druglike = (
          (desc_df['mw'] <= 500) &
          (desc_df['clogp'] <= 5) &
          (desc_df['n_lipinski_hbd'] <= 5) &
          (desc_df['n_lipinski_hba'] <= 10)
      )
      filtered_df = df[druglike.values]
      
      # 5. Cluster and select diverse subset (returns (indices, mols))
      diverse_idx, diverse_mols = dm.pick_diverse(
          filtered_df['mol'].tolist(),
          npick=100
      )
      
      # 6. Visualize results (PNG output needs use_svg=False)
      dm.viz.to_image(
          diverse_mols,
          legends=[dm.to_smiles(m) for m in diverse_mols],
          outfile="diverse_compounds.png",
          use_svg=False,
          n_cols=10
      )
      ```
      
      ## Structure-Activity Relationship (SAR) Analysis
      
      ```python
      # Group by scaffold
      scaffolds = [dm.to_scaffold_murcko(mol) for mol in mols]
      scaffold_smiles = [dm.to_smiles(s) for s in scaffolds]
      
      # Create DataFrame with activities
      sar_df = pd.DataFrame({
          'mol': mols,
          'scaffold': scaffold_smiles,
          'activity': activities  # User-provided activity data
      })
      
      # Analyze each scaffold series
      for scaffold, group in sar_df.groupby('scaffold'):
          if len(group) >= 3:  # Need multiple examples
              print(f"\nScaffold: {scaffold}")
              print(f"Count: {len(group)}")
              print(f"Activity range: {group['activity'].min():.2f} - {group['activity'].max():.2f}")
      
              # Visualize with activities as legends
              dm.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
      
      ```python
      # 1. Calculate Tanimoto distances between query actives and the library.
      #    dm.cdist takes the molecules directly (it fingerprints internally),
      #    returning an (n_query, n_library) distance matrix.
      import numpy as np
      
      distances = 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 hits
      top_indices = np.argsort(similarities)[::-1][:100]  # Top 100
      top_hits = [library_mols[i] for i in top_indices]
      top_scores = [similarities[i] for i in top_indices]
      
      # 5. Visualize hits
      dm.viz.to_image(
          top_hits[:20],
          legends=[f"Sim: {score:.3f}" for score in top_scores[:20]],
          outfile="screening_hits.png"
      )
      ```
      
      ## Scaffold-Based Analysis
      
      ```python
      # Group compounds by scaffold
      from collections import Counter
      
      scaffolds = [dm.to_scaffold_murcko(mol) for mol in mols]
      scaffold_smiles = [dm.to_smiles(s) for s in scaffolds]
      
      # Count scaffold frequency
      scaffold_counts = Counter(scaffold_smiles)
      most_common = scaffold_counts.most_common(10)
      
      # Create scaffold-to-molecules mapping
      scaffold_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)
      
      ```python
      # Ensure train and test sets have different scaffolds
      scaffold_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/test
      import random
      scaffolds = 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 split
      train_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]]
      ```
      
      ## Fragment Frequency and Overlap Scoring
      
      ```python
      # Find common fragments across compound library
      from collections import Counter
      
      def brics_smiles(mol):
          # brics() returns Mol objects (parent first); compare fragments as SMILES
          return {dm.to_smiles(f) for f in dm.fragment.brics(mol, remove_parent=True, fix=False)}
      
      all_fragments = []
      for mol in mols:
          all_fragments.extend(brics_smiles(mol))
      
      fragment_counts = Counter(all_fragments)
      common_frags = fragment_counts.most_common(20)
      
      # Fragment-based scoring
      def fragment_score(mol, reference_fragments):
          mol_frags = brics_smiles(mol)
          overlap = mol_frags.intersection(reference_fragments)
          return len(overlap) / len(mol_frags) if mol_frags else 0
      ```
      
      ## Integration with Machine Learning
      
      ```python
      # Feature generation
      X = np.array([dm.to_fp(mol) for mol in mols])
      
      # Or descriptors
      desc_df = dm.descriptors.batch_compute_many_descriptors(mols, n_jobs=-1, batch_size=256)
      X = desc_df.values
      
      # Train model
      from sklearn.ensemble import RandomForestRegressor
      model = RandomForestRegressor()
      model.fit(X, y_target)
      
      # Predict
      predictions = model.predict(X_test)
      ```
      
      ## Robust Error Handling Wrappers
      
      ```python
      # Safe molecule creation
      def safe_to_mol(smiles):
          try:
              mol = dm.to_mol(smiles)
              if mol is not None:
                  mol = dm.standardize_mol(mol)
              return mol
          except Exception as e:
              print(f"Failed to process {smiles}: {e}")
              return None
      
      # Safe batch processing
      valid_mols = []
      for smiles in smiles_list:
          mol = safe_to_mol(smiles)
          if mol is not None:
              valid_mols.append(mol)
      ```
      
  • SKILL.md 15.4 KB
    ---
    name: alterlab-datamol
    description: Wraps RDKit in a high-level, pandas-friendly datamol interface with sensible defaults for everyday drug discovery — SMILES/SDF loading into DataFrames, molecule standardization, descriptors, fingerprints, Butina clustering, 3D conformer generation, scaffold analysis, and parallel batch processing, returning native rdkit.Chem.Mol objects. Use when running standard cheminformatics pipelines on molecule tables with minimal boilerplate; for low-level control, custom sanitization, or specialized algorithms prefer alterlab-rdkit. Part of the AlterLab Academic Skills suite.
    license: Apache-2.0
    allowed-tools: Read Write Edit Bash(python:*) Bash(uv:*)
    compatibility: "Self-contained — runs under `uv run python` with the skill's Python package installed; no API key or account required."
    metadata:
        skill-author: AlterLab
        version: "1.1.0"
        last_updated: "2026-09-23"
    ---
    
    # 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
    
    ## When to Use This Skill
    
    Use this skill when the user wants to:
    - Load, standardize, and de-duplicate molecule tables (CSV/SDF/Excel/Parquet, local or cloud) with little boilerplate
    - Compute datamol's descriptor set, fingerprints, and Tanimoto distance matrices for a compound set
    - Cluster, pick diverse subsets, extract Murcko scaffolds, or fragment (BRICS/RECAP) a library
    - Generate and cluster 3D conformers, or render aligned molecule grids for SAR review
    
    ### Does NOT Trigger
    
    | 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` |
    
    ## Installation and Setup
    
    Guide users to install datamol:
    
    ```bash
    uv pip install datamol
    ```
    
    Examples 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**:
    ```python
    import datamol as dm
    ```
    
    ## Core Workflows
    
    Each 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`.
    
    ### 1. Basic Molecule Handling
    
    ```python
    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`.
    
    ### 2. Reading and Writing Molecular Files
    
    ```python
    # 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`.
    
    ### 3. Molecular Descriptors and Properties
    
    ```python
    # 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`.
    
    ### 4. Molecular Fingerprints and Similarity
    
    ```python
    # 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 sets
    ```
    
    Fingerprint types and `pdist` / `cdist` details: see `references/core_api.md`.
    
    ### 5. Clustering and Diversity Selection
    
    ```python
    # 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`.
    
    ### 6. Scaffold Analysis
    
    ```python
    # 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`.
    
    ### 7. Molecular Fragmentation
    
    ```python
    # 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`.
    
    ### 8. 3D Conformer Generation
    
    ```python
    # 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`.
    
    ### 9. Visualization
    
    ```python
    # 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`.
    
    ### 10. Chemical Reactions
    
    ```python
    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`.
    
    ## 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 cores
    - `n_jobs=4`: Use 4 cores
    
    **Functions 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.
    
    ## Common Workflows and Patterns
    
    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`.
    
    ## 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
    - **`references/workflow_recipes.md`**: End-to-end pipelines, SAR/screening recipes, ML integration, error handling
    
    ## Best Practices
    
    1. **Always standardize molecules** from external sources:
       ```python
       mol = dm.standardize_mol(mol, disconnect_metals=True, normalize=True, reionize=True)
       ```
    
    2. **Check for None values** after molecule parsing:
       ```python
       mol = dm.to_mol(smiles)
       if mol is None:
           ...  # log and skip the invalid SMILES
       ```
    
    3. **Use parallel processing** for large datasets:
       ```python
       result = dm.operation(..., n_jobs=-1, progress=True)
       ```
    
    4. **Leverage fsspec** for cloud storage:
       ```python
       df = dm.read_sdf("s3://bucket/compounds.sdf")
       ```
    
    5. **Use appropriate fingerprints** for similarity:
       - ECFP (Morgan): General purpose, structural similarity
       - MACCS: Fast, smaller feature space
       - Atom pairs: Considers atom pairs and distances
    
    6. **Consider scale limitations**:
       - Butina clustering: ~1,000 molecules (full distance matrix)
       - For larger datasets: Use diversity selection or hierarchical methods
    
    7. **Scaffold splitting for ML**: Ensure proper train/test separation by scaffold
    
    8. **Align molecules** when visualizing SAR series
    
    ## Troubleshooting
    
    **Issue**: Molecule parsing fails
    - **Solution**: Use `dm.standardize_smiles()` first or try `dm.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_confs` or increase `rms_cutoff` to 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
    
    Part of the AlterLab Academic Skills suite.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related