medchem について
medchemは、創薬における分子のフィルタリングと優先順位付けを行うPythonライブラリです。薬化学ルール、構造アラート、化学基の検出、組み合わせ可能なクエリで化合物ライブラリを評価できます。
主な機能
- 01ドラッグライクネスルールの適用
- 02構造アラートのスクリーニング
- 03化学基の検出
- 04分子複雑度のスコア計算
- 05カスタム物性制約
- 06ブール式フィルタークエリ
活用シーン
- 01化合物ライブラリの選別
- 02分子の構造アラート検査
- 03官能基や反応性基の特定
- 04複雑度と物性による候補の優先順位付け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jaechang-hits/ sciagent-skills --skill medchemインストール後、Claude Code で/medchemを入力して読み込まれたか確認します。
同梱ファイルreferences/rules_catalog.md
詳細
Medchem
概要
Medchem は、創薬における分子のフィルタリングと優先順位付けのための Python ライブラリです。確立された数百種類のメディシナルケミストリーのルール、構造アラート、化学基の検出機能を提供し、化合物ライブラリを大規模にトリアージできます。すべてのフィルターは並列実行に対応しており、構造化された結果を返します。
使用する場面
- 化合物ライブラリにドラッグライク性ルール(Lipinski、Veber、Oprea、CNS、REOS)を適用する
- 構造アラート(PAINS、NIBR、Lilly Demerits)によって分子をフィルタリングする
- 特定の化学基(ヒンジ結合基、Michael 受容体、反応性基)を検出する
- 分子の複雑さの指標(Bertz、Whitlock、Barone)を計算する
- カスタムの物性制約(MW、LogP、TPSA、回転可能結合)を適用する
- ブール論理を使って、複雑な複数ルールのフィルタークエリを組み立てる
- SMILES/SDF の解析、記述子、フィンガープリントには rdkit-cheminformatics を使用する
- 高度な分子操作には datamol-cheminformatics を使用する
前提条件
pip install medchem datamol
Medchem は RDKit と datamol に依存します。すべての分子入力は RDKit の Chem.Mol オブジェクトです。SMILES から変換するには datamol.to_mol() を使用してください。
クイックスタート
import datamol as dm
import medchem as mc
# Convert SMILES to molecules
smiles_list = ["CC(=O)OC1=CC=CC=C1C(=O)O", "c1ccccc1N", "O=C(O)c1ccccc1"]
mols = [dm.to_mol(s) for s in smiles_list]
# Apply Rule of Five + structural alerts in one pass
rule_filter = mc.rules.RuleFilters(rule_list=["rule_of_five"])
alert_filter = mc.structural.CommonAlertsFilters()
rule_results = rule_filter(mols=mols, n_jobs=-1)
alert_results = alert_filter(mols=mols, n_jobs=-1)
print(f"Rule results: {rule_results}")
print(f"Alert results: {[r['has_alerts'] for r in alert_results]}")
コア API
1. ドラッグライク性ルール
mc.rules を使って、確立されたメディシナルケミストリーのルールを適用します。個々のルールは bool を返し、RuleFilters は複数のルールを一括で適用します。
import medchem as mc
# Single rule on a SMILES string
passes = mc.rules.basic_rules.rule_of_five("CC(=O)OC1=CC=CC=C1C(=O)O")
print(f"Passes Ro5: {passes}") # True
# Available individual rules:
# rule_of_five, rule_of_three, rule_of_oprea, rule_of_cns,
# rule_of_leadlike_soft, rule_of_leadlike_strict, rule_of_veber,
# rule_of_reos, rule_of_drug, golden_triangle, pains_filter
import datamol as dm
import medchem as mc
# Batch application with RuleFilters
mols = [dm.to_mol(s) for s in smiles_list]
rfilter = mc.rules.RuleFilters(
rule_list=["rule_of_five", "rule_of_oprea", "rule_of_cns"]
)
results = rfilter(mols=mols, n_jobs=-1, progress=True)
# Returns list of dicts: [{"rule_of_five": True, "rule_of_oprea": False, ...}, ...]
print(f"First molecule: {results[0]}")
2. 構造アラートフィルター
mc.structural を使って、問題となる構造パターンを検出します。対象範囲と厳格さが異なる 3 つのフィルターセットがあります。
import datamol as dm
import medchem as mc
mol = dm.to_mol("c1ccc(N)cc1")
mols = [dm.to_mol(s) for s in smiles_list]
# Common Alerts — general structural alerts from ChEMBL / literature
alert_filter = mc.structural.CommonAlertsFilters()
has_alerts, details = alert_filter.check_mol(mol) # single molecule
batch_results = alert_filter(mols=mols, n_jobs=-1, progress=True)
# Each result: {"has_alerts": bool, "alert_details": [...], "num_alerts": int}
print(f"Alerts: {batch_results[0]}")
import medchem as mc
# NIBR Filters — Novartis industrial filter set (returns bool list)
nibr_filter = mc.structural.NIBRFilters()
nibr_results = nibr_filter(mols=mols, n_jobs=-1)
print(f"NIBR pass: {nibr_results}") # [True, False, ...]
# Lilly Demerits — 275 patterns, molecules rejected at >100 demerits
lilly_filter = mc.structural.LillyDemeritsFilters()
lilly_results = lilly_filter(mols=mols, n_jobs=-1)
# Each result: {"demerits": int, "passes": bool, "matched_patterns": [...]}
print(f"Lilly: {lilly_results[0]}")
3. 化学基
mc.groups.ChemicalGroup を使って、特定の官能基モチーフを検出します。
定義済みのグループ:hinge_binders、phosphate_binders、michael_acceptors、reactive_groups。
import medchem as mc
# Check for kinase hinge binders and Michael acceptors
group = mc.groups.ChemicalGroup(
groups=["hinge_binders", "michael_acceptors"]
)
has_matches = group.has_match(mols) # List[bool]
match_info = group.get_matches(mols[0]) # {group_name: [(atom_indices), ...]}
all_matches = group.get_all_matches(mols) # List[Dict]
print(f"Has hinge binder: {has_matches}")
# Custom SMARTS patterns
custom = mc.groups.ChemicalGroup(
groups=["reactive_groups"],
custom_smarts={"trifluoromethyl_ketone": "[C;H0](=O)C(F)(F)F"}
)
4. 名前付きカタログ
mc.catalogs を使って、キュレーションされた化学構造カタログにアクセスします。
利用可能なカタログ:functional_groups、protecting_groups、reagents、fragments。
import medchem as mc
catalog = mc.catalogs.NamedCatalogs.get("functional_groups")
matches = catalog.get_matches(mol)
print(f"Functional group matches: {matches}")
5. 分子の複雑さ
mc.complexity を使って、合成容易性の代理指標を計算します。
手法:bertz(トポロジカル)、whitlock、barone。
import datamol as dm
import medchem as mc
mol = dm.to_mol("CC(=O)OC1=CC=CC=C1C(=O)O")
# Single molecule complexity
score = mc.complexity.calculate_complexity(mol, method="bertz")
print(f"Bertz complexity: {score:.1f}")
# Batch filtering by complexity threshold
cfilter = mc.complexity.ComplexityFilter(max_complexity=500, method="bertz")
results = cfilter(mols=mols, n_jobs=-1)
print(f"Passes complexity: {results}") # List[bool]
6. 物性制約
mc.constraints.Constraints を使って、カスタムの物性ベースの制約を適用します。
import medchem as mc
constraints = mc.constraints.Constraints(
mw_range=(200, 500),
logp_range=(-2, 5),
tpsa_max=140,
rotatable_bonds_max=10,
hbd_max=5,
hba_max=10,
rings_range=(1, 5),
aromatic_rings_max=3,
)
results = constraints(mols=mols, n_jobs=-1)
# Each result: {"passes": bool, "violations": ["mw_range", ...]}
print(f"Violations: {results[0]}")
7. クエリ言語
mc.query を使って、ブール式で複雑なフィルターロジックを組み立てます。
import medchem as mc
# Parse a query combining rules, alerts, and property checks
query = mc.query.parse("rule_of_five AND NOT common_alerts")
results = query.apply(mols=mols, n_jobs=-1) # List[bool]
print(f"Passing: {sum(results)}/{len(results)}")
# More complex queries
q2 = mc.query.parse("rule_of_cns AND complexity < 400")
q3 = mc.query.parse("(rule_of_five OR rule_of_oprea) AND NOT pains_filter")
q4 = mc.query.parse("mw > 200 AND mw < 500 AND logp < 5")
8. 関数 API とユーティリティ
mc.functional のショートカット関数と、mc.utils のユーティリティを使用します。
import medchem as mc
# Functional API — one-liner filters
nibr_ok = mc.functional.nibr_filter(mols=mols, n_jobs=-1) # List[bool]
alerts = mc.functional.common_alerts_filter(mols=mols, n_jobs=-1)
lilly = mc.functional.lilly_demerits_filter(mols=mols, n_jobs=-1)
# Utilities
standardized = mc.utils.standardize_mol(mol) # sanitize, neutralize charges
batch_out = mc.utils.batch_process(
mols=mols, func=mc.complexity.calculate_complexity,
n_jobs=-1, progress=True, batch_size=100
)
主な概念
ルールのしきい値早見表
| ルール | MW | LogP | HBD | HBA | RotBonds | TPSA | その他 |
|---|---|---|---|---|---|---|---|
| Ro5(Lipinski) | ≤500 | ≤5 | ≤5 | ≤10 | — | — | — |
| Veber | — | — | — | — | ≤10 | ≤140 | — |
| Oprea(リード) | 200-350 | -2 to 4 | — | — | ≤7 | — | 環 ≤4 |
| Leadlike Soft | 250-450 | -3 to 4 | — | — | ≤10 | — | — |
| Leadlike Strict | 200-350 | -2 to 3.5 | — | — | ≤7 | — | 環 1-3 |
| CNS | ≤450 | -1 to 5 | ≤2 | — | — | ≤90 | — |
| REOS | 200-500 | -5 to 5 | 0-5 | 0-10 | — | — | — |
| Ro3(フラグメント) | ≤300 | ≤3 | ≤3 | ≤3 | ≤3 | ≤60 | — |
| Golden Triangle | 200-50*LogP+400 | -2 to 5 | — | — | — | — | — |
| Rule of Drug | Ro5 + Veber + PAINS なし |
創薬段階別のフィルター選択
| 段階 | 推奨フィルター | 理由 |
|---|---|---|
| 初期スクリーニング | Ro5、PAINS、Common Alerts | 広範なトリアージを行い、明らかな問題要因を除外 |
| ヒットからリードへの展開 | Oprea または Leadlike Soft、NIBR、Lilly | リードライクな領域、業界向けフィルター |
| リード最適化 | Rule of Drug、Leadlike Strict、Complexity | 厳格なドラッグライク性と合成可能性 |
| CNS 標的 | Rule of CNS、TPSA ≤90、HBD ≤2 | BBB 透過性の要件 |
| フラグメントベース | Ro3、低複雑性(≤250) | フラグメントには「成長の余地」がある |
ルールは絶対ではなく、指針です
市販されている医薬品の約10%はRo5に違反しています。天然物、抗生物質、PROTAC、プロドラッグでは例外がよく見られます。ルールベースのフィルタリングは、必ず専門知識や標的クラスに関する知識と組み合わせてください。投与経路(経口、IV、局所)や標的クラス(キナーゼ、GPCR、イオンチャネル)によって、最適な物性範囲は異なります。
一般的なワークフロー
ワークフロー 1:化合物ライブラリのトリアージ
生のSMILESから、フィルタリング済みのドラッグライク候補を得るまでの完全なパイプラインです。
import pandas as pd
import datamol as dm
import medchem as mc
# Load compound library
df = pd.read_csv("compounds.csv")
mols = [dm.to_mol(s) for s in df["smiles"]]
print(f"Input: {len(mols)} molecules")
# Step 1: Drug-likeness rules
rfilter = mc.rules.RuleFilters(rule_list=["rule_of_five", "rule_of_veber"])
rule_results = rfilter(mols=mols, n_jobs=-1, progress=True)
# Step 2: Structural alerts
alert_filter = mc.structural.CommonAlertsFilters()
alert_results = alert_filter(mols=mols, n_jobs=-1, progress=True)
# Step 3: Combine results
df["passes_rules"] = [all(r.values()) for r in rule_results]
df["has_alerts"] = [r["has_alerts"] for r in alert_results]
df["drug_like"] = df["passes_rules"] & ~df["has_alerts"]
filtered = df[df["drug_like"]]
print(f"Output: {len(filtered)} drug-like molecules ({len(filtered)/len(df)*100:.1f}%)")
filtered.to_csv("filtered_compounds.csv", index=False)
ワークフロー 2:リード最適化フィルターのカスケード
段階的に厳しいフィルターを適用し、詳細なレポートを作成します。
import datamol as dm
import medchem as mc
import pandas as pd
mols = [dm.to_mol(s) for s in smiles_list]
# Cascade: rules → structural alerts → complexity → Lilly demerits
filters = [
("Leadlike Strict", mc.rules.RuleFilters(rule_list=["rule_of_leadlike_strict"])),
("NIBR Alerts", mc.structural.NIBRFilters()),
("Complexity ≤400", mc.complexity.ComplexityFilter(max_complexity=400)),
("Lilly Demerits", mc.structural.LillyDemeritsFilters()),
]
surviving = list(range(len(mols)))
for name, filt in filters:
results = filt(mols=[mols[i] for i in surviving], n_jobs=-1)
# Handle different result formats
if isinstance(results[0], dict):
passed = [i for i, r in zip(surviving, results)
if r.get("passes", not r.get("has_alerts", True))]
else:
passed = [i for i, r in zip(surviving, results) if r]
print(f"{name}: {len(surviving)} → {len(passed)}")
surviving = passed
print(f"Final candidates: {len(surviving)} / {len(mols)}")
主なパラメーター
| パラメーター | モジュール | デフォルト | 範囲 | 効果 |
|---|---|---|---|---|
rule_list | RuleFilters | — | ルールの表を参照 | 適用するドラッグライクネスルール |
n_jobs | すべてのフィルター | 1 | -1 から N | 並列ワーカー数(-1 = すべてのコア) |
progress | すべてのフィルター | False | bool | 進捗バーを表示 |
max_complexity | ComplexityFilter | — | 0-1000+ | Bertz複雑度のしきい値 |
method | calculate_complexity | "bertz" | bertz/whitlock/barone | 複雑度の指標 |
mw_range | Constraints | None | tuple(float, float) | 分子量の範囲(Da) |
logp_range | Constraints | None | tuple(float, float) | LogPの範囲 |
tpsa_max | Constraints | None | 0-200+ | トポロジカル極性表面積の上限 |
groups | ChemicalGroup | — | 名前のリスト | 検出する定義済み化学基 |
custom_smarts | ChemicalGroup | None | dict | カスタムSMARTSパターン {name: SMARTS} |
ベストプラクティス
-
広く始めてから絞り込む:まず許容範囲の広いフィルター(Ro5、PAINS)を適用し、パイプラインで候補が絞られるにつれて、段階的に条件を厳しくします(NIBR、Lilly、複雑度)。
-
必ず並列化する:分子数が1000を超えるライブラリでは、すべてのCPUコアを使用するために
n_jobs=-1を設定します。 -
ルールと構造アラートを組み合わせる:ルールは物理化学的性質を確認し、アラートは部分構造パターンを確認します。堅牢なトリアージには両方が必要です。
-
アンチパターン — 無差別なフィルタリング:Ro5に違反するものをすべて無条件に除外しないでください。フィルタリングの前に、標的クラスと投与経路を考慮してください。
-
アンチパターン — プロドラッグの無視:プロドラッグは意図的に標準ルールに違反します。除外するのではなく、例外として記録してください。
-
フィルタリングの判断を記録する:再現性と規制遵守のため、どの分子を、なぜ除外したかを追跡してください。
-
既知の活性化合物で検証する:対象の既知の活性化合物にフィルターカスケードを適用し、偽陽性率を推定してください。
よく使うレシピ
レシピ:DataFrameとの統合
import pandas as pd
import datamol as dm
import medchem as mc
df = pd.read_csv("molecules.csv")
df["mol"] = df["smiles"].apply(dm.to_mol)
rfilter = mc.rules.RuleFilters(rule_list=["rule_of_five", "rule_of_cns"])
results = rfilter(mols=df["mol"].tolist(), n_jobs=-1)
df["passes_ro5"] = [r["rule_of_five"] for r in results]
df["passes_cns"] = [r["rule_of_cns"] for r in results]
filtered = df[df["passes_ro5"] & df["passes_cns"]]
print(f"CNS drug-like: {len(filtered)}")
レシピ:MLスコアリングとの組み合わせ
import medchem as mc
# Rule-based pre-filter
rule_results = mc.rules.RuleFilters(rule_list=["rule_of_five"])(mols, n_jobs=-1)
filtered_mols = [mol for mol, r in zip(mols, rule_results) if r["rule_of_five"]]
# ML model scoring on filtered set (reduces compute cost)
ml_scores = ml_model.predict(filtered_mols)
candidates = [mol for mol, score in zip(filtered_mols, ml_scores) if score > 0.8]
print(f"ML-scored candidates: {len(candidates)}")
レシピ:カスタムSMARTSによるグループスクリーニング
import medchem as mc
# Define project-specific warheads for covalent inhibitor screening
custom_warheads = {
"acrylamide": "[C;H1](=O)[CH]=[CH2]",
"vinyl_sulfonamide": "[NH]S(=O)(=O)[CH]=[CH2]",
"chloroacetamide": "ClCC(=O)N",
}
group = mc.groups.ChemicalGroup(groups=[], custom_smarts=custom_warheads)
has_warhead = group.has_match(mols)
warhead_mols = [mol for mol, match in zip(mols, has_warhead) if match]
print(f"Covalent warhead candidates: {len(warhead_mols)}")
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
分子リストに None が含まれる | 入力に無効なSMILESがある | 事前にフィルタリングします:mols = [m for m in mols if m is not None] |
| すべての分子がRo5に不合格となる | ライブラリがフラグメント様、またはPPI空間にある | 代わりに rule_of_three または rule_of_leadlike_soft を使用します |
| PAINSアラートが見つからない | 分子が単純、またはフラグメント様である | 想定どおりです。PAINSパターンはスクリーニングヒットサイズの分子を対象としています |
| Lillyの減点がすべて100を超える | 高度に官能基化された分子である | 個別のパターンを確認し、しきい値を引き上げるか、代わりにNIBRを使用することを検討します |
| 処理が遅い | 並列化せずに大規模ライブラリを処理している | 並列実行のために n_jobs=-1 を設定します |
ImportError: medchem | 依存関係が不足している | pip install medchem datamol(RDKitが必要です) |
| クエリの解析エラー | クエリ構文が無効である | 演算子を確認します:AND、OR、NOT、比較:<、>、== |
| 結果の形式に一貫性がない | フィルタークラスごとに戻り値の型が異なる | ドキュメントを確認します:RuleFilters → dict、NIBRFilters → bool、LillyDemeritsFilters → dict |
同梱リソース
references/rules_catalog.md
すべてのメディシナルケミストリーのルールと構造アラートフィルターを網羅したカタログです。文献の参照情報、閾値の基準、化学基のパターン、カスタム SMARTS の例、ステージごとのフィルター選択ガイドラインを掲載しています。API 関数のシグネチャは、上記の Core API コードブロックにまとめています。
関連スキル
- rdkit-cheminformatics — 低レベルのケモインフォマティクス:SMILES/SDF の解析、記述子、フィンガープリント、部分構造検索
- datamol-cheminformatics — 高レベルの分子操作:標準化、骨格、フラグメンテーション、3D 配座
- pubchem-compound-search — データベースクエリ:検証用の化合物特性および生物活性データの取得
参考文献
- Lipinski CA et al. Adv Drug Deliv Rev (1997) 23:3-25 — Rule of Five
- Veber DF et al. J Med Chem (2002) 45:2615-2623 — Veber ルール
- Oprea TI et al. J Chem Inf Comput Sci (2001) 41:1308-1315 — リードライクネス
- Baell JB & Holloway GA. J Med Chem (2010) 53:2719-2740 — PAINS フィルター
- Congreve M et al. Drug Discov Today (2003) 8:876-877 — Rule of Three
- Johnson TW et al. J Med Chem (2009) 52:5487-5500 — Golden Triangle
- Walters WP & Murcko MA. Adv Drug Deliv Rev (2002) 54:255-271 — REOS
- 公式ドキュメント:https://medchem-docs.datamol.io/
- GitHub:https://github.com/datamol-io/medchem