Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Therapeutics Data Commons (TDC) AI-ready drug discovery datasets. Curated ADME, toxicity, DTI, DDI with scaffold/cold splits, standardized metrics, molecular oracles, and ADMET benchmarks for therapeutic ML and property prediction. For chemical database queries use chembl-database-bioactivity; for featurization use molfeat.
.claude/skills/jaechang-hits-pytdc-therapeutics-data-commons/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 211% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 128% | 0% |
| case-04 | ✗→✓ | ▲ Improved | 126% | 0% |
| case-16 | ✗→✓ | ▲ Improved | 190% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 232% | 0% |
PyTDC is an open-science platform providing AI-ready datasets and benchmarks for drug discovery. It organizes therapeutics data into three categories: single-instance prediction (molecular/protein properties), multi-instance prediction (drug-target interactions), and generation (molecule design, retrosynthesis). All datasets come with standardized splits, evaluation metrics, and molecular oracles.
chembl-database-bioactivity insteadmolfeat insteadbashuv pip install PyTDC # Core deps: numpy, pandas, scikit-learn, tqdm, fuzzywuzzy # Optional: rdkit (scaffold splits), torch-geometric (PyG conversion)
API Note: TDC downloads datasets on first access (~10-500 MB per dataset). Specify path='data/' to control download location. No API key required.
pythonfrom tdc.single_pred import ADME from tdc import Evaluator # Load dataset with scaffold split data = ADME(name='Caco2_Wang') split = data.get_split(method='scaffold', seed=42, frac=[0.7, 0.1, 0.2]) train, valid, test = split['train'], split['valid'], split['test'] print(f"Train: {len(train)}, Valid: {len(valid)}, Test: {len(test)}") # Train: ~640, Valid: ~91, Test: ~182 # Evaluate predictions evaluator = Evaluator(name='MAE') # score = evaluator(test['Y'].values, predictions)
Load datasets for predicting properties of individual molecules or proteins.
pythonfrom tdc.single_pred import ADME, Tox, HTS, QM # ADME — pharmacokinetic properties data = ADME(name='Caco2_Wang') # Intestinal permeability (regression) data = ADME(name='BBB_Martins') # Blood-brain barrier (binary) data = ADME(name='Lipophilicity_AstraZeneca') # LogD (regression) data = ADME(name='Solubility_AqSolDB') # Aqueous solubility # Toxicity — adverse effects data = Tox(name='hERG') # Cardiotoxicity (binary) data = Tox(name='AMES') # Mutagenicity (binary) data = Tox(name='DILI') # Drug-induced liver injury data = Tox(name='ClinTox') # Clinical trial toxicity # Access data as DataFrame df = data.get_data(format='df') print(df.columns.tolist()) # ['Drug_ID', 'Drug', 'Y'] — Drug is SMILES, Y is target label print(f"Dataset size: {len(df)}, Label range: [{df['Y'].min():.2f}, {df['Y'].max():.2f}]")
Other single-prediction tasks: HTS (screening), QM (quantum mechanics), Yields, Epitope, Develop, CRISPROutcome.
Load datasets for predicting interactions between pairs of biomedical entities.
pythonfrom tdc.multi_pred import DTI, DDI, PPI # Drug-Target Interaction — binding affinity data = DTI(name='BindingDB_Kd') # 52,284 pairs, Kd values data = DTI(name='DAVIS') # 30,056 pairs, kinase binding data = DTI(name='KIBA') # 118,254 pairs, kinase bioactivity # Drug-Drug Interaction — interaction type prediction data = DDI(name='DrugBank') # 191,808 pairs, 86 interaction types # Protein-Protein Interaction data = PPI(name='HuRI') # Multi-instance data format df = data.get_data(format='df') print(df.columns.tolist()) # ['Drug_ID', 'Drug', 'Target_ID', 'Target', 'Y'] # Drug=SMILES, Target=protein sequence, Y=binding affinity or class
Other multi-instance tasks: GDA, DrugRes, DrugSyn, PeptideMHC, AntibodyAff, MTI, Catalyst, TrialOutcome.
Load training sets and oracles for molecule generation and retrosynthesis.
pythonfrom tdc.generation import MolGen, RetroSyn, PairMolGen from tdc import Oracle # Molecule generation — training data data = MolGen(name='ChEMBL_V29') # 1.6M drug-like SMILES split = data.get_split() train_smiles = split['train']['Drug'].tolist() # Oracle scoring — evaluate generated molecules oracle = Oracle(name='GSK3B') # GSK3B inhibition predictor (0-1) score = oracle('CC(C)Cc1ccc(cc1)C(C)C(O)=O') print(f"GSK3B score: {score:.4f}") # Batch evaluation scores = oracle(['CCO', 'c1ccccc1', 'CC(=O)O']) print(f"Batch scores: {scores}") # Retrosynthesis — reaction prediction data = RetroSyn(name='USPTO') # 1.9M reactions split = data.get_split() # Paired generation — prodrug design data = PairMolGen(name='Prodrug')
Apply meaningful data splits and standardized evaluation metrics.
pythonfrom tdc.single_pred import ADME from tdc.multi_pred import DTI from tdc import Evaluator # Scaffold split — ensures chemical diversity between sets data = ADME(name='Caco2_Wang') split = data.get_split(method='scaffold', seed=42, frac=[0.7, 0.1, 0.2]) # Cold splits — for DTI (unseen drugs/targets in test set) data = DTI(name='BindingDB_Kd') cold_drug = data.get_split(method='cold_drug', seed=1) cold_target = data.get_split(method='cold_target', seed=1) # Verify no overlap in cold split train_drugs = set(cold_drug['train']['Drug_ID']) test_drugs = set(cold_drug['test']['Drug_ID']) print(f"Drug overlap: {len(train_drugs & test_drugs)}") # 0 # Evaluation metrics eval_mae = Evaluator(name='MAE') eval_auc = Evaluator(name='ROC-AUC') eval_spearman = Evaluator(name='Spearman') # score = eval_mae(y_true, y_pred)
Available split methods: random, scaffold (Bemis-Murcko), cold_drug, cold_target, cold_drug_target, temporal.
Available metrics: Classification — ROC-AUC, PR-AUC, F1, Accuracy, Kappa. Regression — RMSE, MAE, R2, MSE. Ranking — Spearman, Pearson. Multi-label — Micro-F1, Macro-F1.
Run standardized multi-seed evaluation protocols for model comparison.
pythonfrom tdc.benchmark_group import admet_group # Load ADMET benchmark (22 datasets) group = admet_group(path='data/') # Standard 5-seed evaluation protocol benchmark = group.get('Caco2_Wang') predictions = {} for seed in [1, 2, 3, 4, 5]: train_df = benchmark['train'] valid_df = benchmark['valid'] test_df = benchmark['test'] # Train your model on train_df, tune on valid_df # predictions[seed] = model.predict(test_df['Drug']) predictions[seed] = test_df['Y'].values # placeholder # Get benchmark results results = group.evaluate(predictions) print(f"Mean MAE: {results['Caco2_Wang'][0]:.4f} ± {results['Caco2_Wang'][1]:.4f}")
| Category | Import Path | Task Examples | Data Format | |----------|-------------|---------------|-------------| | Single-Instance | tdc.single_pred | ADME, Tox, HTS, QM | Drug (SMILES) + Y (label) | | Multi-Instance | tdc.multi_pred | DTI, DDI, PPI, DrugSyn | Drug + Target + Y | | Generation | tdc.generation | MolGen, RetroSyn | SMILES collections | | Benchmark | tdc.benchmark_group | admet_group | Curated splits |
| Category | Examples | Speed | Output Range | |----------|----------|-------|-------------| | Biochemical | DRD2, GSK3B, JNK3, 5HT2A | Medium (ML) | 0-1 probability | | Physicochemical | QED, SA, LogP, MW | Fast (rule-based) | Varies by metric | | Composite | Isomer_Meta, Median1/2, Rediscovery | Medium | 0-1 combined | | Specialized | ASKCOS, Docking, Vina | Slow (external) | Varies |
| Utility | Function | Example | |---------|----------|---------| | Format conversion | MolConvert(src, dst) | SMILES → PyG, ECFP, SELFIES, DGL | | Molecule filters | MolFilter(filters) | PAINS, BMS, Glaxo, drug-likeness | | Label binarization | label_transform() | Continuous → binary at threshold | | Unit conversion | label_transform(from_unit, to_unit) | nM → pIC50 | | ID resolution | cid2smiles(), uniprot2seq() | PubChem CID → SMILES | | Dataset listing | retrieve_dataset_names(task) | List all ADME datasets |
pythonfrom tdc.single_pred import ADME from tdc import Evaluator import numpy as np data = ADME(name='Caco2_Wang') evaluator = Evaluator(name='MAE') results = [] for seed in [1, 2, 3, 4, 5]: split = data.get_split(method='scaffold', seed=seed) train, valid, test = split['train'], split['valid'], split['test'] # model.fit(train['Drug'], train['Y']) # preds = model.predict(test['Drug']) preds = test['Y'].values + np.random.normal(0, 0.1, len(test)) # placeholder score = evaluator(test['Y'].values, preds) results.append(score) print(f"Seed {seed}: MAE = {score:.4f}") print(f"Mean MAE: {np.mean(results):.4f} ± {np.std(results):.4f}")
pythonfrom tdc import Oracle import numpy as np # Define multi-objective scoring oracles = { 'QED': (Oracle(name='QED'), 0.3), # drug-likeness 'SA': (Oracle(name='SA'), 0.3), # synthetic accessibility 'GSK3B': (Oracle(name='GSK3B'), 0.4), # target activity } test_smiles = ['CC(C)Cc1ccc(cc1)C(C)C(O)=O', 'c1ccc2c(c1)cc1ccc3cccc4ccc2c1c34'] for smi in test_smiles: scores = {} weighted_sum = 0 for name, (oracle, weight) in oracles.items(): score = oracle(smi) scores[name] = score weighted_sum += score * weight print(f"SMILES: {smi[:30]}...") print(f" Scores: {scores}") print(f" Weighted: {weighted_sum:.4f}")
DTI(name='BindingDB_Kd') (Core API Module 2)data.get_split(method='cold_drug', seed=seed) (Core API Module 4)Evaluator(name='Spearman') (Module 4)| Parameter | Function/Module | Default | Range/Options | Effect | |-----------|----------------|---------|---------------|--------| | method | get_split() | 'scaffold' | random, scaffold, cold_drug, cold_target, temporal | Split strategy for train/test | | seed | get_split() | 42 | 1-5 for benchmarks | Reproducibility; use 5 seeds for benchmarks | | frac | get_split() | [0.7, 0.1, 0.2] | Sum must equal 1.0 | Train/valid/test proportions | | name | Evaluator() | — | MAE, RMSE, ROC-AUC, Spearman, etc. | Evaluation metric | | name | Oracle() | — | QED, SA, GSK3B, DRD2, etc. | Scoring function for molecules | | src/dst | MolConvert() | — | SMILES, SELFIES, PyG, DGL, ECFP4 | Molecular representation formats | | path | admet_group() | 'data/' | Any directory | Dataset download/cache location | | format | get_data() | 'df' | 'df', 'dict' | Output data format |
cold_drug tests generalization to unseen drugs, cold_target to unseen targetsMolFilter (PAINS, drug-likeness) before training to remove problematic compoundspath — avoids re-downloading large datasets across sessionspythonfrom tdc.single_pred import ADME from tdc.utils import retrieve_dataset_names # List all ADME datasets datasets = retrieve_dataset_names('ADME') print(f"Available ADME datasets: {datasets}") # Load and inspect data = ADME(name='Caco2_Wang') df = data.get_data(format='df') print(f"Size: {len(df)}") print(f"Label stats: mean={df['Y'].mean():.2f}, std={df['Y'].std():.2f}") print(f"SMILES example: {df['Drug'].iloc[0]}")
pythonfrom tdc.chem_utils import MolConvert # SMILES to multiple representations smiles = 'CC(C)Cc1ccc(cc1)C(C)C(O)=O' converter_ecfp = MolConvert(src='SMILES', dst='ECFP4') converter_selfies = MolConvert(src='SMILES', dst='SELFIES') ecfp = converter_ecfp(smiles) selfies = converter_selfies(smiles) print(f"ECFP4 shape: {ecfp.shape}") # (1024,) binary fingerprint print(f"SELFIES: {selfies}")
pythonfrom tdc import Oracle # Define property constraints constraints = { 'QED': (Oracle(name='QED'), 0.5, 1.0), # min, max 'SA': (Oracle(name='SA'), 1.0, 4.0), 'LogP': (Oracle(name='LogP'), -0.5, 5.0), } def check_constraints(smiles): """Check if molecule satisfies all property constraints.""" results = {} all_pass = True for name, (oracle, lo, hi) in constraints.items(): score = oracle(smiles) passed = lo <= score <= hi results[name] = {'score': score, 'passed': passed} all_pass = all_pass and passed return all_pass, results passed, details = check_constraints('CC(C)Cc1ccc(cc1)C(C)C(O)=O') for name, info in details.items(): status = "PASS" if info['passed'] else "FAIL" print(f"{name}: {info['score']:.3f} [{status}]")
| Problem | Cause | Solution | |---------|-------|----------| | ModuleNotFoundError: tdc | Package not installed | uv pip install PyTDC | | Scaffold split fails | Missing RDKit dependency | uv pip install rdkit for scaffold decomposition | | Dataset download timeout | Large dataset or slow connection | Set path='data/' for persistent cache; retry | | KeyError on dataset name | Wrong name or task category | Use retrieve_dataset_names('ADME') to list valid names | | Oracle returns NaN | Invalid SMILES or RDKit parse failure | Validate SMILES with RDKit MolFromSmiles() first | | Cold split empty test set | Too few unique entities | Use frac=[0.7, 0.1, 0.2] with larger datasets | | Benchmark evaluation error | Wrong prediction format | Pass dict with seeds as keys: {1: preds1, 2: preds2, ...} | | Memory error on large dataset | Full dataset loaded to memory | Process in chunks or use smaller split fractions | | PyG conversion fails | torch-geometric not installed | uv pip install torch-geometric for graph conversion |
Covers: complete catalog of all TDC datasets organized by task category (single-instance, multi-instance, generation) with dataset names, sizes, label types, and data sources. Relocated inline: top ADME/Tox/DTI datasets with code examples consolidated into Core API Modules 1-2. Omitted: None — all dataset entries preserved in catalog format.
Covers: detailed oracle documentation (all 17+ oracles with parameters, speed tiers, output ranges, custom oracle template) and data processing utilities (format conversion targets, molecule filter types, label transformation, entity resolution). Consolidated from original oracles.md + utilities.md. Relocated inline: Quick Start oracle usage pattern, top evaluation metrics, split methods, MolConvert pattern → Core API Modules 3-4. Omitted: distribution learning KS-test example — niche statistical comparison; leaderboard submission guide — platform-specific.
load_and_split_data.py (215 lines): scaffold/cold split patterns → Core API Module 4; custom split fractions → Key Parameters; evaluation examples → Workflow 1. Thin wrappers around get_split() and Evaluator().benchmark_evaluation.py (328 lines): 5-seed protocol → Core API Module 5 + Workflow 1; multi-dataset evaluation → Workflow 1 pattern; leaderboard guide → omitted (platform-specific).molecular_generation.py (405 lines): single/batch oracle usage → Core API Module 3; multi-objective scoring → Workflow 2; constraint satisfaction → Recipe 3; distribution learning → omitted (niche).| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→pass | 11,597 | 16,497 | +42% | 1 | 1 | 0% | 2,214 | 6,882 | +211% | 0 | 0 | — |
case-02 | pass→pass | 14,827 | 11,255 | -24% | 1 | 1 | 0% | 3,081 | 7,446 | +142% | 0 | 0 | — |
case-03 | fail→pass | 17,801 | 10,160 | -43% | 1 | 1 | 0% | 3,098 | 7,050 | +128% | 0 | 0 | — |
case-04 | fail→pass | 17,416 | 10,177 | -42% | 1 | 1 | 0% | 3,082 | 6,950 | +126% | 0 | 0 | — |
case-16 | fail→pass | 11,931 | 4,921 | -59% | 1 | 1 | 0% | 2,054 | 5,966 | +190% | 0 | 0 | — |
case-05 | fail→pass | 10,241 | 5,726 | -44% | 1 | 1 | 0% | 1,870 | 6,213 | +232% | 0 | 0 | — |
case-06 | fail→pass | 6,426 | 1,971 | -69% | 1 | 1 | 0% | 1,019 | 5,418 | +432% | 0 | 0 | — |
case-07 | fail→pass | 8,357 | 3,223 | -61% | 1 | 1 | 0% | 1,302 | 5,722 | +339% | 0 | 0 | — |
case-08 | fail→pass | 17,919 | 5,029 | -72% | 1 | 1 | 0% | 3,220 | 6,018 | +87% | 0 | 0 | — |
case-09 | fail→pass | 11,145 | 4,696 | -58% | 1 | 1 | 0% | 1,939 | 5,958 | +207% | 0 | 0 | — |
case-10 | fail→pass | 17,288 | 5,896 | -66% | 1 | 1 | 0% | 3,475 | 6,204 | +79% | 0 | 0 | — |
case-11 | pass→pass | 4,829 | 1,927 | -60% | 1 | 1 | 0% | 731 | 5,415 | +641% | 0 | 0 | — |
case-12 | pass→pass | 14,731 | 5,676 | -61% | 1 | 1 | 0% | 2,660 | 6,083 | +129% | 0 | 0 | — |
case-13 | fail→pass | 10,242 | 8,549 | -17% | 1 | 1 | 0% | 2,041 | 6,878 | +237% | 0 | 0 | — |
case-14 | pass→pass | 7,412 | 3,610 | -51% | 1 | 1 | 0% | 1,238 | 5,731 | +363% | 0 | 0 | — |
case-15 | fail→pass | 7,226 | 3,780 | -48% | 1 | 1 | 0% | 1,280 | 5,773 | +351% | 0 | 0 | — |
case-17 | fail→pass | 17,006 | 8,972 | -47% | 1 | 1 | 0% | 3,281 | 5,931 | +81% | 0 | 0 | — |
case-18 | fail→pass | 9,371 | 3,694 | -61% | 1 | 1 | 0% | 1,758 | 5,773 | +228% | 0 | 0 | — |
case-19 | pass→pass | 3,989 | 2,364 | -41% | 1 | 1 | 0% | 675 | 5,502 | +715% | 0 | 0 | — |
case-20 | pass→pass | 8,102 | 9,230 | +14% | 1 | 1 | 0% | 1,594 | 6,895 | +333% | 0 | 0 | — |
case-21 | pass→pass | 8,417 | 6,651 | -21% | 1 | 1 | 0% | 1,516 | 6,334 | +318% | 0 | 0 | — |
case-22 | pass→pass | 10,054 | 6,201 | -38% | 1 | 1 | 0% | 1,746 | 6,259 | +258% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +64 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.