この記事では、ネットワーク薬理学の具体的な手法として、ChEMBL Multitask Neural Networkモデルを用いたターゲット予測プロセスを詳細に解説します。特に、伝統的な漢方薬である黄芩(Scutellaria baicalensis)の成分がどのタンパク質に作用する可能性があるかについて予測しております。ぜひご覧ください。
Mac M1, Sequoia 15.3
自宅でできるin silico創薬の技術書を販売中
新薬探索を試したい方必読!
ITエンジニアである著者の視点から、wetな研究者からもdryの創薬研究をわかりやすく身近に感じられるように解説しています
自宅でできるin silico創薬の技術書を販売中
タンパク質デザイン・モデリングに焦点を当て、初めてこの分野に参入する方向けに、それぞれの手法の説明から、環境構築、実際の使い方まで網羅!
Network pharmacologyとは?
Network pharmacology(ネットワーク薬理学)は、漢方薬や機能性食品に含まれる複数の成分が、体内のさまざまな標的(タンパク質、遺伝子など)に同時に作用し、複雑な生理的効果をもたらす仕組みを「ネットワーク」として解析する手法です。
従来の薬理学が「1成分=1標的」の考え方に基づいていたのに対し、ネットワーク薬理学は「多成分=多標的=多経路」の全体像をとらえます。たとえば、漢方薬「黄芩」に含まれるバイカリンなどの成分が、乳がんに関与する複数の遺伝子やシグナル経路に作用している可能性を、各種データベースを用いて可視化できます。
これにより、伝統的処方の有効性を科学的に裏付けたり、新たな疾病への応用可能性を探索したりすることができます。
Network pharmacologyの流れ
- 成分取得:PubChemなど各種データベースを使って、漢方や食品中の有効成分を調査する。
- 標的予測:SwissTargetPrediction、ChEMBL Multitask Neural Network modelなどを用いて、成分が結合する可能性のある標的タンパク質を予測する。
- 疾患関連遺伝子の収集:DisGeNETやGeneCardsを使って、対象とする疾患に関係する遺伝子やタンパク質の情報を収集する。
- 共通ターゲットの抽出:成分の標的と疾患関連遺伝子を照合し、共通の遺伝子(ターゲット)を見つける。
- ネットワーク構築・可視化:成分、標的、疾患の関係性をCytoscapeなどでネットワーク図として可視化する。
- パスウェイ・GO解析:KEGGやDAVIDを使って、共通ターゲットが関与するシグナル伝達経路や生物学的機能を解析する。
以下の論文を参考にし、漢方のScutellaria baicalensis(オウゴン (黄芩))の成分と乳がんの標的タンパク質を明らかにし、黄芩のどの成分が、乳がんの標的に結合するか明らかにしていきます。
今回は2. 標的予測について、Multitask Neural Network modelを使いながら、ご説明します。
標的予測のツールとして、SwissTargetPrediction や Similarity ensemble approach (SEA) が有名です。しかしこれらは一気に沢山の化合物の標的を予測するには不便です。なので、今回はChEMBL Multitask Neural Network modelをご紹介します。
ChEMBL Multitask Neural Network modelとは
ChEMBL Multitask Neural Networkモデルは、ChEMBLデータベースに収録された化合物と標的タンパク質との活性情報をもとに、複数のターゲットに対する活性を同時に予測できるよう訓練されたディープラーニングモデルです。
入力には分子構造の特徴量であるMorganフィンガープリントを用い、出力として各標的に対する活性確率(0〜1のスコア)を返します。ターゲットごとにバイナリ分類(活性か非活性か)を行う形式で、数百〜数千のタスクを1つのモデルで扱うマルチタスク学習が特徴です。
構造類似性に基づかない予測も可能なため、標的予測や副作用予測、スクリーニング支援など幅広いin silico創薬に応用されています。モデルはONNX形式で公開されており、Pythonから容易に使用できます。
ブラウザベースでも試すことができますが、本記事ではChEMBL Multitask Neural Network modelを使って、複数化合物の標的を見てみたいと思います。
また標的の多い化合物を重要な化合物とし、標的が多い順にTop10の化合物を選出します。この化合物を後にスクリーニングに持って行きます。
また一方で、得られた標的については、後に疾患の標的と照合し、被るものを標的として選出します。
この記事ではTop10の化合物の選出と、標的の取得を行います。
環境構築
#仮想環境の構築
conda create -n chem_pred python=3.10 -y
conda activate chem_pred
# RDKitのインストール(conda-forgeチャンネル使用)
conda install -c conda-forge rdkit
# ONNX Runtimeのインストール
pip install onnxruntime
# その他必要なライブラリ
pip install numpy openpyxl requests tqdm
#gitのレポジトリファイルのダウンロード
git clone https://github.com/chembl/chembl_multitask_model.git
#chembl_multitask_modelディレクトリに入る。
cd chembl_multitask_model詳細なコード説明
仮想環境の構築とアクティベート
まず、プロジェクト固有の依存関係を管理するために、独立したPython仮想環境を作成します。これにより、システムのPython環境が汚染されるのを防ぎ、異なるプロジェクト間でライブラリのバージョン競合が発生するのを避けることができます。
conda create -n chem_pred python=3.10 -y
conda activate chem_predconda create -n chem_pred python=3.10 -y:conda create: 新しいconda仮想環境を作成するコマンド。n chem_pred: 作成する仮想環境の名前をchem_predと指定します。この名前は後で環境をアクティベートする際に使用します。python=3.10: この仮想環境にPython 3.10をインストールするように指定します。特定のPythonバージョンを固定することで、ライブラリの互換性の問題を避けることができます。y: 確認プロンプト(Yes/No)に対して自動的に「Yes」と回答し、インストールプロセスを非対話的に進めます。
conda activate chem_pred:- 作成した
chem_predという名前の仮想環境をアクティベート(有効化)します。このコマンドを実行すると、ターミナルのプロンプトの先頭に環境名が表示され(例:(chem_pred) user@hostname:~$)、以降のコマンドはこの仮想環境内で実行されます。
- 作成した
RDKitのインストール
RDKitは、ケモインフォマティクス(化学情報学)のための主要なオープンソースライブラリです。分子構造の操作、指紋の生成、機械学習のための特徴量抽出など、幅広い機能を提供します。
Bash
conda install -c conda-forge rdkitconda install: conda環境にパッケージをインストールするコマンド。c conda-forge:conda-forgeというチャネル(パッケージリポジトリ)からRDKitをインストールするように指定します。conda-forgeは、科学計算系のパッケージが豊富で、最新版のRDKitが提供されていることが多いです。rdkit: インストールするパッケージ名。
ONNX Runtimeのインストール
ONNX Runtimeは、Open Neural Network Exchange (ONNX) 形式の機械学習モデルを実行するための高性能な推論エンジンです。ChEMBLのマルチタスクモデルがONNX形式で提供されている場合、これをインストールする必要があります。
pip install onnxruntimepip install: Pythonパッケージ管理ツールpipを使ってパッケージをインストールするコマンド。onnxruntime: インストールするパッケージ名。pipは現在アクティブなconda仮想環境にパッケージをインストールします。
その他必要なライブラリのインストール
プロジェクトによっては、データ操作、ファイル処理、ネットワークリクエスト、進捗表示などのために追加のライブラリが必要になる場合があります。
pip install numpy openpyxl requests tqdmpip install: これらのライブラリもpipを使ってインストールします。numpy: 数値計算のための基本的なライブラリで、特に配列操作に優れています。科学計算で広く利用されます。openpyxl: Excel.xlsxファイルを読み書きするためのライブラリです。requests: HTTPリクエストを送信するためのシンプルなライブラリです。Web APIとの通信などに利用されます。tqdm: ループ処理の進捗バーを簡単に追加できるライブラリです。これにより、長時間かかる処理の状況を視覚的に把握できます。
Gitリポジトリファイルのダウンロードと移動
最後に、目的のChEMBLマルチタスクモデルのコードを含むGitリポジトリをダウンロードし、そのディレクトリに移動します。
git clone https://github.com/chembl/chembl_multitask_model.git
cd chembl_multitask_modelgit clone https://github.com/chembl/chembl_multitask_model.git:git clone: 指定されたURLのGitリポジトリを現在のディレクトリに複製(ダウンロード)するコマンドです。https://github.com/chembl/chembl_multitask_model.git: ChEMBLのマルチタスクモデルのGitHubリポジトリのURLです。このコマンドを実行すると、現在のディレクトリにchembl_multitask_modelという新しいディレクトリが作成され、その中にリポジトリのすべてのファイルがダウンロードされます。
cd chembl_multitask_model:cd:change directoryの略で、カレントディレクトリ(作業ディレクトリ)を変更するコマンドです。chembl_multitask_model: ダウンロードしたリポジトリのディレクトリ名です。このコマンドを実行することで、モデルのスクリプトやデータが配置されているディレクトリに移動し、そこから関連するPythonスクリプトを実行できるようになります。
ChEMBL Multitask Neural Network modeを使ったターゲット予測
このスクリプトは、入力CSVファイルに含まれるSMILES表記から化合物のMorganフィンガープリントを生成し、事前に訓練されたONNX形式の機械学習モデル(ChEMBLマルチタスクモデル)を使用して、特定のターゲットに対する活性確率を予測します。最終的に、予測確率が閾値を超える結果のみを抽出し、DataFrameとして表示します。
全コード
import pandas as pd
import numpy as np
from rdkit import Chem
from rdkit.Chem.rdFingerprintGenerator import GetMorganGenerator
import onnxruntime
# 定数
FP_SIZE = 1024
RADIUS = 2
THRESHOLD = 0.5
# モデルの読み込み
ort_session = onnxruntime.InferenceSession("trained_models/chembl_34_model/chembl_34_multitask.onnx", providers=['CPUExecutionProvider'])
def calc_morgan_fp(smiles): mol = Chem.MolFromSmiles(smiles) generator = GetMorganGenerator(radius=RADIUS, fpSize=FP_SIZE) fp = generator.GetFingerprint(mol) a = np.zeros((0,), dtype=np.float32) Chem.DataStructs.ConvertToNumpyArray(fp, a) return a
def format_preds(preds, targets): preds = np.concatenate(preds).ravel() np_preds = [(tar, pre) for tar, pre in zip(targets, preds)] dt = [('chembl_id','|U20'), ('pred', '<f4')] np_preds = np.array(np_preds, dtype=dt) np_preds[::-1].sort(order='pred') return np_preds # CSV読み込み
df = pd.read_csv("compound_DL_filtered.csv")
# 出力対象リスト
results = []
# 全SMILESをループして予測
for idx, row in df.iterrows(): cid = row['CID'] smiles = row['SMILES'] fp = calc_morgan_fp(smiles) if fp is None: print(f"⚠️ SMILES変換失敗: {cid} / {smiles}") continue ort_inputs = {ort_session.get_inputs()[0].name: fp} preds = ort_session.run(None, ort_inputs) formatted = format_preds(preds, [o.name for o in ort_session.get_outputs()]) filtered = [(cid, t, p) for t, p in formatted if p >= THRESHOLD] results.extend(filtered)
# 結果をDataFrameで表示
df_results = pd.DataFrame(results, columns=["CID", "Target", "Probability"])
df_results.sort_values(by="Probability", ascending=False, inplace=True)
df_resultsコード詳細説明
必要なモジュールのインポート
import pandas as pd
import numpy as np
from rdkit import Chem
from rdkit.Chem.rdFingerprintGenerator import GetMorganGenerator
import onnxruntimepandas(pd): データ分析と操作のための強力なライブラリで、特に表形式データ(DataFrame)の扱いに優れています。ここではCSVファイルの読み込みや結果の整理に使われます。numpy(np): 数値計算を行うためのPythonライブラリです。特に配列操作に特化しており、フィンガープリントやモデルの予測結果の処理に使われます。rdkit.Chem: RDKit(化学情報学ライブラリ)の中核モジュールで、分子構造の表現、操作、化学計算など、様々な化学関連機能を提供します。rdkit.Chem.rdFingerprintGenerator.GetMorganGenerator: RDKitからMorganフィンガープリント(いわゆるExtended Connectivity Fingerprint, ECFP)を生成するための関数をインポートします。onnxruntime: ONNX (Open Neural Network Exchange) 形式の機械学習モデルを実行するための推論エンジンです。訓練済みモデルの予測を行うために使用されます。
定数定義
FP_SIZE = 1024
RADIUS = 2
THRESHOLD = 0.5モデルの予測やフィンガープリント生成に影響する重要なパラメータを定数として定義しています。
FP_SIZE = 1024: 生成されるMorganフィンガープリントのビット長(サイズ)を指定します。フィンガープリントは分子の特徴を数値ベクトルで表現したもので、このサイズが大きいほどより多くの特徴を表現できますが、計算コストも増加します。RADIUS = 2: Morganフィンガープリントの生成において考慮する原子の周囲の結合距離(半径)を指定します。半径が大きいほど、より広範囲の分子構造情報をフィンガープリントに取り込みます。THRESHOLD = 0.5: モデルの予測確率をフィルタリングするための閾値です。予測結果がこの値以上の場合にのみ、関連する活性として考慮されます。
モデルの読み込み
ort_session = onnxruntime.InferenceSession("trained_models/chembl_34_model/chembl_34_multitask.onnx", providers=['CPUExecutionProvider'])ONNX形式で保存された訓練済み機械学習モデルを読み込み、推論準備をします。
onnxruntime.InferenceSession(...): ONNXモデルをロードし、推論を実行するためのセッションを作成します。"trained_models/chembl_34_model/chembl_34_multitask.onnx": 読み込むONNXモデルファイルのパスです。providers=['CPUExecutionProvider']: モデルの実行にCPUを使用することを明示的に指定します。GPUが利用可能な環境では、'CUDAExecutionProvider'なども指定できます。
Morganフィンガープリント計算関数
def calc_morgan_fp(smiles): mol = Chem.MolFromSmiles(smiles) generator = GetMorganGenerator(radius=RADIUS, fpSize=FP_SIZE) fp = generator.GetFingerprint(mol) a = np.zeros((0,), dtype=np.float32) Chem.DataStructs.ConvertToNumpyArray(fp, a) return aSMILES文字列からMorganフィンガープリントを計算する関数です。
mol = Chem.MolFromSmiles(smiles): 入力されたSMILES文字列からRDKitの分子オブジェクトを生成します。SMILESが不正な場合、Noneが返されることがあります。generator = GetMorganGenerator(radius=RADIUS, fpSize=FP_SIZE): 定義済みのRADIUSとFP_SIZEを使って、Morganフィンガープリントジェネレーターを初期化します。fp = generator.GetFingerprint(mol): 生成した分子オブジェクトからフィンガープリントを計算します。a = np.zeros((0,), dtype=np.float32): フィンガープリントを格納するための空のNumPy配列を初期化します。dtype=np.float32は、モデルの入力形式に合わせるための指定です。Chem.DataStructs.ConvertToNumpyArray(fp, a): RDKitのフィンガープリントオブジェクトfpをNumPy配列aに変換します。この関数は、fpの内容を直接aに書き込みます。return a: 生成されたNumPy配列形式のMorganフィンガープリントを返します。
予測結果フォーマット関数
def format_preds(preds, targets): preds = np.concatenate(preds).ravel() np_preds = [(tar, pre) for tar, pre in zip(targets, preds)] dt = [('chembl_id','|U20'), ('pred', '<f4')] np_preds = np.array(np_preds, dtype=dt) np_preds[::-1].sort(order='pred') return np_predsモデルの予測結果(数値配列)とターゲット名(ChEMBL IDなど)を組み合わせ、見やすい形式に整形する関数です。
preds = np.concatenate(preds).ravel(): モデルの出力predsはリストのリスト(または複数の配列)で返されることがあるため、np.concatenate()で結合し、ravel()で一次元配列に変換します。これにより、すべての予測確率が一つのNumPy配列にまとめられます。np_preds = [(tar, pre) for tar, pre in zip(targets, preds)]: ターゲット名(targets)とそれぞれの予測確率(preds)をペアにして、タプルのリストを作成します。dt = [('chembl_id','|U20'), ('pred', '<f4')]: 構造化NumPy配列を作成するためのデータ型(dtype)を定義します。('chembl_id','|U20'): ターゲットID用のフィールドで、最大20文字のUnicode文字列。('pred', '<f4'): 予測確率用のフィールドで、32ビット浮動小数点数。
np_preds = np.array(np_preds, dtype=dt): タプルのリストを、定義したデータ型を持つ構造化NumPy配列に変換します。これにより、各ターゲットの予測確率が対応する名前と関連付けられます。np_preds[::-1].sort(order='pred'): 予測確率('pred')に基づいて、結果を降順でソートします。[::-1]は配列を逆順にソートするためのトリックです。
CSV読み込みと予測ループ
# CSV読み込み
df = pd.read_csv("compound_DL_filtered.csv")
# 出力対象リスト
results = []
# 全SMILESをループして予測
for idx, row in df.iterrows(): cid = row['CID'] smiles = row['SMILES'] fp = calc_morgan_fp(smiles) if fp is None: print(f"⚠️ SMILES変換失敗: {cid} / {smiles}") continue ort_inputs = {ort_session.get_inputs()[0].name: fp} preds = ort_session.run(None, ort_inputs) formatted = format_preds(preds, [o.name for o in ort_session.get_outputs()]) filtered = [(cid, t, p) for t, p in formatted if p >= THRESHOLD] results.extend(filtered)入力CSVファイルからSMILESを読み込み、各SMILESに対して予測処理を実行します。
df = pd.read_csv("compound_DL_filtered.csv"):"compound_DL_filtered.csv"という名前のCSVファイルをPandas DataFrameとして読み込みます。このCSVファイルには、予測対象となる化合物のCIDとSMILESが含まれていると想定されます。results = []: 予測結果を一時的に格納するための空のリストを初期化します。for idx, row in df.iterrows():: DataFrameの各行をループ処理します。idxは行のインデックス、rowは各行のデータ(Pandas Seriesオブジェクト)です。cid = row['CID']: 現在の行から化合物ID(CID)を取得します。smiles = row['SMILES']: 現在の行からSMILES文字列を取得します。fp = calc_morgan_fp(smiles): 取得したSMILESからMorganフィンガープリントを計算します。if fp is None::calc_morgan_fp関数がSMILESから分子オブジェクトを生成できなかった場合(SMILESが不正な場合など)の処理です。- 警告メッセージを出力し、現在のループをスキップして次のSMILESに進みます。
ort_inputs = {ort_session.get_inputs()[0].name: fp}: ONNXモデルへの入力形式に合わせて、入力辞書を作成します。キーはモデルの入力層の名前(ort_session.get_inputs()[0].nameで取得)、値は計算されたフィンガープリントfpです。preds = ort_session.run(None, ort_inputs): ロードしたONNXモデルに対して推論を実行します。None: すべての出力層の値を返します。ort_inputs: 入力データ。
formatted = format_preds(preds, [o.name for o in ort_session.get_outputs()]): モデルの出力predsと、モデルの出力層の名前(ターゲット名)を使って、予測結果を整形します。filtered = [(cid, t, p) for t, p in formatted if p >= THRESHOLD]: 整形された予測結果から、予測確率pが定義済みのTHRESHOLD以上であるもののみをフィルタリングします。結果は(CID, ターゲット名, 確率)のタプルとしてリストに格納されます。results.extend(filtered): フィルタリングされた結果をresultsリストに追加します。
結果の表示
# 結果をDataFrameで表示
df_results = pd.DataFrame(results, columns=["CID", "Target", "Probability"])
df_results.sort_values(by="Probability", ascending=False, inplace=True)
df_results収集したすべての予測結果をPandas DataFrameとして整理し、表示します。
df_results = pd.DataFrame(results, columns=["CID", "Target", "Probability"]):resultsリストの内容を元に新しいPandas DataFrameを作成します。列名も明確に指定します。df_results.sort_values(by="Probability", ascending=False, inplace=True): 作成したDataFrameをProbability列の値に基づいて降順にソートします。inplace=Trueは、元のDataFrameを直接変更することを意味します。df_results: 最後に、整形・ソートされた結果のDataFrameを表示します。これにより、高確率で活性を示すと予測された化合物とそのターゲットが一目でわかるようになります。
このスクリプトは、化学データ処理、機械学習モデルの推論、そして結果の整理という一連のワークフローを効率的に実行します。特に、大量の化合物データに対して活性予測を行う場合に非常に有用です。
TargetのUniProt_IDの追加
ここでは、先に予測された化合物の活性ターゲット(ChEMBL ID)に対し、UniProt IDと遺伝子名 (Gene_Name) を追加することを目的としています。
全コード
# df_results作成直後にUniProt IDとGene_Nameを追加(キャッシュ機能付き)
import requests
from tqdm import tqdm
import pickle
import os
# UniProt ID取得関数
def get_gene_symbol(chembl_id): url = f"https://www.ebi.ac.uk/chembl/api/data/target/{chembl_id}.json" try: res = requests.get(url, timeout=10) if res.status_code == 200: data = res.json() components = data.get("target_components", []) if components: return components[0].get("accession", "") else: return "Not found" else: return f"HTTP {res.status_code}" except Exception as e: return str(e)
# Gene_Name取得関数
def get_gene_name_from_uniprot(accession): if accession in ["Not found", "Error", ""] or accession.startswith("HTTP"): return "Unknown" url = f"https://rest.uniprot.org/uniprotkb/{accession}.json" try: res = requests.get(url, timeout=10) if res.status_code == 200: data = res.json() genes = data.get("genes", []) if genes: return genes[0]["geneName"]["value"] else: return "Unknown" else: return "Not found" except: return "Error"
# キャッシュファイルのパス
cache_file = "target_uniprot_cache.pkl"
gene_cache_file = "target_gene_cache.pkl"
# 既存のキャッシュを読み込み(存在する場合)
if os.path.exists(cache_file): with open(cache_file, 'rb') as f: target2uniprot = pickle.load(f) print(f"📁 既存のUniProtキャッシュを読み込みました({len(target2uniprot)}件)")
else: target2uniprot = {} print("🆕 新しいUniProtキャッシュを作成します")
# Gene_Nameキャッシュを読み込み(存在する場合)
if os.path.exists(gene_cache_file): with open(gene_cache_file, 'rb') as f: uniprot2gene = pickle.load(f) print(f"📁 既存のGene_Nameキャッシュを読み込みました({len(uniprot2gene)}件)")
else: uniprot2gene = {} print("🆕 新しいGene_Nameキャッシュを作成します")
# Targetのユニークリストを作成
unique_targets = df_results["Target"].unique()
print(f"🎯 処理対象Target数: {len(unique_targets)}件")
# 新しいTargetのみを取得
new_targets = [target for target in unique_targets if target not in target2uniprot]
print(f"🆕 新規UniProt ID取得対象: {len(new_targets)}件")
if new_targets: # 新規TargetのUniProt IDを取得 for target in tqdm(new_targets, desc="UniProt ID取得中"): target2uniprot[target] = get_gene_symbol(target) # UniProtキャッシュを保存 with open(cache_file, 'wb') as f: pickle.dump(target2uniprot, f) print(f"💾 UniProtキャッシュを更新しました({len(target2uniprot)}件)")
else: print("✅ すべてのTargetがUniProtキャッシュに存在します")
# 新しいUniProt IDのGene_Nameを取得
unique_uniprot_ids = set(target2uniprot.values())
new_uniprot_ids = [uniprot_id for uniprot_id in unique_uniprot_ids if uniprot_id not in uniprot2gene and uniprot_id not in ["Not found", "Error", ""] and not uniprot_id.startswith("HTTP")]
print(f"🆕 新規Gene_Name取得対象: {len(new_uniprot_ids)}件")
if new_uniprot_ids: # 新規UniProt IDのGene_Nameを取得 for uniprot_id in tqdm(new_uniprot_ids, desc="Gene_Name取得中"): uniprot2gene[uniprot_id] = get_gene_name_from_uniprot(uniprot_id) # Gene_Nameキャッシュを保存 with open(gene_cache_file, 'wb') as f: pickle.dump(uniprot2gene, f) print(f"💾 Gene_Nameキャッシュを更新しました({len(uniprot2gene)}件)")
else: print("✅ すべてのUniProt IDがGene_Nameキャッシュに存在します")
# df_resultsにUniProt IDとGene_Nameを追加
df_results["UniProt_ID"] = df_results["Target"].map(target2uniprot)
df_results["Gene_Name"] = df_results["UniProt_ID"].map(lambda x: uniprot2gene.get(x, "Unknown"))
print(f"\n✅ df_resultsにUniProt IDとGene_Nameを追加しました")
print(f"📊 データ形状: {df_results.shape}")
print("\n🔍 最初の10行:")
print(df_results.head(10))
# CSVに保存
df_results.to_csv("df_compound_target_prediction.csv", index=False)これを実行すると、以下のようなdf_compound_target_prediction.csvが出力されています。

コード詳細説明
必要なモジュールのインポート
import requests
from tqdm import tqdm
import pickle
import osrequests: ウェブサイトやAPIからデータを取得するためにHTTPリクエストを送信するためのPythonライブラリです。ここではEBI ChEMBL APIとUniProt APIへのアクセスに使われます。tqdm: ループ処理の進捗状況をプログレスバーで表示するためのライブラリです。APIからのデータ取得のような時間のかかる処理の際に、ユーザーに視覚的なフィードバックを提供します。pickle: Pythonオブジェクトをバイトストリームに直列化(シリアライズ)し、ファイルに保存したり、読み込んだりするためのモジュールです。ここでは、APIから取得した情報をキャッシュファイルとして保存・読み込みするために使われます。os: オペレーティングシステムと対話するためのモジュールです。ファイルの存在チェック (os.path.exists()) などに使われます。
UniProt ID取得関数 (get_gene_symbol)
def get_gene_symbol(chembl_id): url = f"https://www.ebi.ac.uk/chembl/api/data/target/{chembl_id}.json" try: res = requests.get(url, timeout=10) if res.status_code == 200: data = res.json() components = data.get("target_components", []) if components: return components[0].get("accession", "") else: return "Not found" else: return f"HTTP {res.status_code}" except Exception as e: return str(e)ChEMBL ID を入力として受け取り、対応するUniProt ID をEBI ChEMBL APIから取得する関数です。
url = f"https://www.ebi.ac.uk/chembl/api/data/target/{chembl_id}.json": ChEMBL APIのエンドポイントURLを構築します。{chembl_id}の部分には、引数として渡されたChEMBL IDが入ります。requests.get(url, timeout=10): 指定されたURLにGETリクエストを送信し、最大10秒のタイムアウトを設定します。if res.status_code == 200:: HTTPステータスコードが200(成功)であれば、レスポンスのJSONデータを解析します。data.get("target_components", []): レスポンスから"target_components"キーの値を取得します。もしキーが存在しない場合は空のリストを返します。components[0].get("accession", ""):target_componentsリストの最初の要素から"accession"(これがUniProt IDに相当します)を取得します。見つからない場合は空文字列を返します。- エラー発生時 (
elseやexceptブロック内) には、ステータスコードやエラーメッセージを文字列として返します。
Gene_Name取得関数 (get_gene_name_from_uniprot)
def get_gene_name_from_uniprot(accession): if accession in ["Not found", "Error", ""] or accession.startswith("HTTP"): return "Unknown" url = f"https://rest.uniprot.org/uniprotkb/{accession}.json" try: res = requests.get(url, timeout=10) if res.status_code == 200: data = res.json() genes = data.get("genes", []) if genes: return genes[0]["geneName"]["value"] else: return "Unknown" else: return "Not found" except: return "Error"UniProt ID を入力として受け取り、対応する遺伝子名 (Gene_Name) をUniProt APIから取得する関数です。
if accession in [...]:get_gene_symbol関数からの不正な入力(例:"Not found","Error")を事前にチェックし、"Unknown"を返します。url = f"https://rest.uniprot.org/uniprotkb/{accession}.json": UniProt APIのエンドポイントURLを構築します。requests.get(url, timeout=10): UniProt APIにGETリクエストを送信します。data.get("genes", []): レスポンスから"genes"キーの値を取得します。genes[0]["geneName"]["value"]:genesリストの最初の要素から、遺伝子名を取得します。- エラー発生時 (
elseやexceptブロック内) には、"Not found"や"Error"を返します。
キャッシュファイルのパス設定
cache_file = "target_uniprot_cache.pkl"
gene_cache_file = "target_gene_cache.pkl"APIから取得した情報を保存するキャッシュファイルのパスを定義しています。
target_uniprot_cache.pkl: ChEMBL IDとUniProt IDのマッピングを保存するためのファイル。target_gene_cache.pkl: UniProt IDと遺伝子名のマッピングを保存するためのファイル。.pkl拡張子は、Pythonのpickleモジュールで保存されたファイルによく使われます。
キャッシュの読み込みと初期化
# 既存のキャッシュを読み込み(存在する場合)
if os.path.exists(cache_file): with open(cache_file, 'rb') as f: target2uniprot = pickle.load(f) print(f"📁 既存のUniProtキャッシュを読み込みました({len(target2uniprot)}件)")
else: target2uniprot = {} print("🆕 新しいUniProtキャッシュを作成します")
# Gene_Nameキャッシュを読み込み(存在する場合)
if os.path.exists(gene_cache_file): with open(gene_cache_file, 'rb') as f: uniprot2gene = pickle.load(f) print(f"📁 既存のGene_Nameキャッシュを読み込みました({len(uniprot2gene)}件)")
else: uniprot2gene = {} print("🆕 新しいGene_Nameキャッシュを作成します")スクリプト実行時に、以前に保存されたキャッシュファイルが存在するかどうかを確認し、存在すれば読み込みます。なければ新しい空の辞書を初期化します。
os.path.exists(cache_file): 指定されたパスにファイルが存在するかどうかをチェックします。with open(cache_file, 'rb') as f:: キャッシュファイルをバイナリ読み込みモード ('rb') で開きます。pickle.load(f): ファイルから保存されたPythonオブジェクトを読み込みます。- キャッシュが読み込まれた場合、読み込んだ件数が表示され、なければ新しいキャッシュを作成する旨のメッセージが表示されます。
新規ターゲット情報の取得とキャッシュ更新
# Targetのユニークリストを作成
unique_targets = df_results["Target"].unique()
print(f"🎯 処理対象Target数: {len(unique_targets)}件")
# 新しいTargetのみを取得
new_targets = [target for target in unique_targets if target not in target2uniprot]
print(f"🆕 新規UniProt ID取得対象: {len(new_targets)}件")
if new_targets: # 新規TargetのUniProt IDを取得 for target in tqdm(new_targets, desc="UniProt ID取得中"): target2uniprot[target] = get_gene_symbol(target) # UniProtキャッシュを保存 with open(cache_file, 'wb') as f: pickle.dump(target2uniprot, f) print(f"💾 UniProtキャッシュを更新しました({len(target2uniprot)}件)")
else: print("✅ すべてのTargetがUniProtキャッシュに存在します")df_results からユニークなターゲット(ChEMBL ID)を抽出し、まだキャッシュに存在しないものだけを対象にUniProt IDを取得します。
unique_targets = df_results["Target"].unique():df_resultsDataFrameの"Target"列から重複のない値(ユニークなChEMBL ID)を取得します。new_targets = [target for target in unique_targets if target not in target2uniprot]:unique_targetsの中から、既存のtarget2uniprotキャッシュにまだキーとして含まれていないChEMBL IDのみを抽出します。for target in tqdm(new_targets, desc="UniProt ID取得中"): 新しいターゲットが存在する場合、tqdmで進捗バーを表示しながら各ターゲットのUniProt IDを取得し、target2uniprot辞書に格納します。with open(cache_file, 'wb') as f: pickle.dump(target2uniprot, f): 取得が完了したら、更新されたtarget2uniprot辞書をキャッシュファイルに保存します。
新規Gene_Name情報の取得とキャッシュ更新
# 新しいUniProt IDのGene_Nameを取得
unique_uniprot_ids = set(target2uniprot.values())
new_uniprot_ids = [uniprot_id for uniprot_id in unique_uniprot_ids if uniprot_id not in uniprot2gene and uniprot_id not in ["Not found", "Error", ""] and not uniprot_id.startswith("HTTP")]
print(f"🆕 新規Gene_Name取得対象: {len(new_uniprot_ids)}件")
if new_uniprot_ids: # 新規UniProt IDのGene_Nameを取得 for uniprot_id in tqdm(new_uniprot_ids, desc="Gene_Name取得中"): uniprot2gene[uniprot_id] = get_gene_name_from_uniprot(uniprot_id) # Gene_Nameキャッシュを保存 with open(gene_cache_file, 'wb') as f: pickle.dump(uniprot2gene, f) print(f"💾 Gene_Nameキャッシュを更新しました({len(uniprot2gene)}件)")
else: print("✅ すべてのUniProt IDがGene_Nameキャッシュに存在します")UniProt IDのキャッシュ (target2uniprot) に格納されたUniProt IDの中から、まだ遺伝子名キャッシュに存在しないものだけを対象に遺伝子名を取得します。
unique_uniprot_ids = set(target2uniprot.values()):target2uniprot辞書の値(UniProt ID)から重複のないセットを作成します。new_uniprot_ids = [...]:unique_uniprot_idsの中から、不正なID ("Not found","Error","","HTTP...") を除外し、かつuniprot2geneキャッシュにまだキーとして含まれていないUniProt IDのみを抽出します。for uniprot_id in tqdm(new_uniprot_ids, desc="Gene_Name取得中"): 新しいUniProt IDが存在する場合、tqdmで進捗バーを表示しながら各UniProt IDの遺伝子名を取得し、uniprot2gene辞書に格納します。with open(gene_cache_file, 'wb') as f: pickle.dump(uniprot2gene, f): 取得が完了したら、更新されたuniprot2gene辞書をキャッシュファイルに保存します。
df_resultsへの情報追加と表示
# df_resultsにUniProt IDとGene_Nameを追加
df_results["UniProt_ID"] = df_results["Target"].map(target2uniprot)
df_results["Gene_Name"] = df_results["UniProt_ID"].map(lambda x: uniprot2gene.get(x, "Unknown"))
print(f"\n✅ df_resultsにUniProt IDとGene_Nameを追加しました")
print(f"📊 データ形状: {df_results.shape}")
print("\n🔍 最初の10行:")
print(df_results.head(10))
# CSVに保存
df_results.to_csv("df_compound_target_prediction.csv", index=False)取得したUniProt IDと遺伝子名を、元の予測結果DataFrame (df_results) に新しい列として追加し、最終的な結果を表示してCSVファイルに保存します。
df_results["UniProt_ID"] = df_results["Target"].map(target2uniprot):df_resultsの"Target"列の各ChEMBL IDをキーとしてtarget2uniprot辞書を検索し、対応するUniProt IDを新しい"UniProt_ID"列として追加します。df_results["Gene_Name"] = df_results["UniProt_ID"].map(lambda x: uniprot2gene.get(x, "Unknown")): 新しく作成された"UniProt_ID"列の各UniProt IDをキーとしてuniprot2gene辞書を検索し、対応する遺伝子名を新しい"Gene_Name"列として追加します。もしUniProt IDが見つからない場合(例えば、ChEMBL APIから取得できなかった場合など)は"Unknown"が代入されます。print(...): 更新されたDataFrameの形状と最初の10行を表示し、処理が成功したことをユーザーに知らせます。df_results.to_csv("df_compound_target_prediction.csv", index=False): 最終的なdf_resultsDataFrameをdf_compound_target_prediction.csvという名前でCSVファイルに保存します。index=Falseは、DataFrameのインデックスをCSVファイルに書き込まないように指定します。
このスクリプトは、化学的な活性予測の結果を、より生物学的な文脈で理解しやすい情報(UniProt IDや遺伝子名)で補完するのに役立ちます。キャッシュ機能により、大規模なデータセットを扱う際の効率性も考慮されています。
標的の多い化合物の抽出
ここでは、特定の化合物(CID)が、機械学習モデルによってどれだけの数の異なるターゲットに活性を持つと予測されたか、つまり**ネットワークにおける「次数(Degree)」**を計算します。これにより、広範囲のターゲットに作用する可能性のある化合物を特定することができます。
全コード
import pandas as pd
# 1. CSVファイルを読み込む
df = pd.read_csv("df_compound_target_prediction.csv")
# 2. CIDごとの出現回数(= ターゲット数 = Degree)をカウント
cid_degree = df['CID'].value_counts().reset_index()
cid_degree.columns = ['CID', 'Degree']
# 3. 上位10件を表示
print(cid_degree.head(10))
# 4. CSVとして保存(Name列なし)
cid_degree.to_csv("cid_degree.csv", index=False)
print("✅ CSVファイル 'cid_degree.csv' を作成しました。")これを実行すると以下のような標的の多い化合物を特定します。

必要なモジュールのインポート
import pandas as pdpandas(pd): データ分析と操作のための主要なライブラリです。CSVファイルの読み込み、データフレームの操作、集計処理を行うために使用されます。
CSVファイルの読み込み
df = pd.read_csv("df_compound_target_prediction.csv")事前に保存された予測結果を含むCSVファイル df_compound_target_prediction.csv を読み込み、Pandas DataFrameとして変数 df に格納します。このDataFrameは、化合物ID (CID)、ターゲット、予測確率などの情報を含んでいます。
CIDごとのターゲット数 (Degree) のカウント
cid_degree = df['CID'].value_counts().reset_index()
cid_degree.columns = ['CID', 'Degree']この部分がスクリプトの中核で、各CIDが予測されたターゲットの数を計算します。
df['CID'].value_counts(): DataFramedfの'CID'列に存在する各ユニークなCIDの出現回数をカウントします。結果はSeriesオブジェクトとして返され、インデックスがCID、値がその出現回数(=ターゲット数)となります。.reset_index():value_counts()の結果をDataFrameに変換します。このとき、元のSeriesのインデックス(CID)は新しい列として追加されます。デフォルトの列名はindexとなります。cid_degree.columns = ['CID', 'Degree']: 新しく作成されたDataFrameの列名を、分かりやすく'CID'と'Degree'に変更します。ここでいう'Degree'は、そのCIDが予測されたターゲットの数を意味します。
上位10件の表示
print(cid_degree.head(10))計算された cid_degree DataFrameの先頭10行を表示します。これにより、どの化合物が最も多くのターゲットに作用する可能性を秘めているか、おおよその傾向をすぐに確認できます。
結果のCSV保存
cid_degree.to_csv("cid_degree.csv", index=False)
print("✅ CSVファイル 'cid_degree.csv' を作成しました。")最終的な cid_degree DataFrameを cid_degree.csv という名前の新しいCSVファイルとして保存します。
cid_degree.to_csv(...): DataFrameをCSVファイルとして書き出すメソッドです。"cid_degree.csv": 保存するファイル名です。index=False: DataFrameのインデックス(自動で付与される行番号)をCSVファイルに書き込まないように指定します。これにより、CSVがよりクリーンになります。- ファイルが正常に作成されたことを示す確認メッセージも表示されます。
標的の抽出
ここでは後に疾患の標的タンパク質との照合のために、重複なく遺伝子名を抽出して行きます。
全コード
import pandas as pd
# 1. CSVファイルを読み込む
df = pd.read_csv("df_compound_target_prediction.csv")
# 2. Gene_Name列のユニークな値を抽出(NaNを除く)
unique_genes = df['Gene_Name'].dropna().unique()
# 3. DataFrameに変換
df_unique_genes = pd.DataFrame(unique_genes, columns=['Gene_Name'])
# 4. CSVとして保存
df_unique_genes.to_csv("unique_gene_names.csv", index=False)
print(f"Gene個数: {len(df_unique_genes)}")
print("✅ ユニークなGene_Nameを抽出し、'unique_gene_names.csv' に保存しました。")これを実行すると、Gene_nameのリストがcsvで出力されます。
詳細説明
必要なモジュールのインポート
import pandas as pdpandas(pd): データ分析と操作に不可欠なPythonライブラリです。ここでは、CSVファイルの読み込みやDataFrameの操作、そしてデータの抽出に利用されます。
CSVファイルの読み込み
df = pd.read_csv("df_compound_target_prediction.csv")事前に保存された化合物予測結果のCSVファイル df_compound_target_prediction.csv を読み込み、その内容をPandasの DataFrame df としてメモリに格納します。このDataFrameには、予測された化合物ID (CID)、ターゲット、確率、そして追加されたUniProt IDとGene_Nameが含まれています。
Gene_Name列のユニークな値の抽出(NaNを除く)
unique_genes = df['Gene_Name'].dropna().unique()この行がスクリプトの核心であり、目的とするユニークな遺伝子名のリストを作成します。
df['Gene_Name']: DataFramedfから'Gene_Name'という名前の列を選択します。この列には、各予測結果に対応する遺伝子名が格納されています。.dropna(): 欠損値 (NaN, Not a Number) を含む行を除外します。これは、APIから遺伝子名が取得できなかった場合などに発生する可能性のある「Unknown」や空文字列、あるいはPandasが自動的に挿入するNaN値をリストに含めないようにするために重要です。.unique(): 選択された列の中から、重複しない(ユニークな)値のみを抽出します。結果はNumPy配列として返されます。
DataFrameへの変換
df_unique_genes = pd.DataFrame(unique_genes, columns=['Gene_Name'])抽出されたユニークな遺伝子名のNumPy配列を、単一の列を持つPandas DataFrameに変換します。これにより、結果をCSVファイルとして簡単に保存できるようになります。
pd.DataFrame(unique_genes, columns=['Gene_Name']):unique_genes配列を新しいDataFrameのデータとし、その列名を'Gene_Name'と指定します。
CSVとして保存と件数表示
df_unique_genes.to_csv("unique_gene_names.csv", index=False)
print(f"Gene個数: {len(df_unique_genes)}")
print("✅ ユニークなGene_Nameを抽出し、'unique_gene_names.csv' に保存しました。")最後に、作成されたユニークな遺伝子名のDataFrameをCSVファイルとして保存し、その総数を表示します。
df_unique_genes.to_csv("unique_gene_names.csv", index=False):df_unique_genesDataFrameをunique_gene_names.csvという名前のCSVファイルとして保存します。index=Falseは、DataFrameのインデックスをCSVファイルに含めないようにする指示です。print(f"Gene個数: {len(df_unique_genes)}"): ユニークな遺伝子名の総数を表示します。これは、今回の予測で関連付けられた遺伝子の種類がいくつあるかを示します。- 処理が正常に完了したことを示すメッセージも出力されます。
最後に
これで、ChEMBL Multitask Neural Networkモデルを用いた黄芩成分のターゲット予測、関連遺伝子情報の取得、そして主要な化合物の特定までの一連のプロセスが完了しました。得られた「標的の多い化合物」のリストは、今後の実験的な検証における貴重な候補となるでしょう。また、「ユニークな遺伝子名」のリストは、次に続く「疾患関連遺伝子との照合」ステップで重要な役割を果たします。
ネットワーク薬理学の概念と、実践的なデータ処理・解析スキルが、皆さんの研究の一助となれば幸いです。この後のステップで、これらの情報がどのように活用されるか、ぜひご期待ください!
参考文献
MIT License
自宅でできるin silico創薬の技術書を販売中
新薬探索を試したい方必読!
ITエンジニアである著者の視点から、wetな研究者からもdryの創薬研究をわかりやすく身近に感じられるように解説しています
自宅でできるin silico創薬の技術書を販売中
タンパク質デザイン・モデリングに焦点を当て、初めてこの分野に参入する方向けに、それぞれの手法の説明から、環境構築、実際の使い方まで網羅!







