Open Targets入門 #2:Platform UIで疾患から創薬ターゲットを探すでは、アルツハイマー病から創薬ターゲット候補を画面上で探索しました。APP、APOE、PSEN1などのターゲットをAssociated Targets・Target Prioritisation・Evidenceページで深掘りする流れを見ました。
今回は同じ発想を、Python + GraphQL APIで再現します。UIで見ていたAssociated Targetsの一覧をプログラムから取得し、pandasのDataFrameに整形してCSVとして保存します。
本記事はOpen Targetsシリーズの第3回です。
| 回 | タイトル | 主なゴール |
|---|---|---|
| 第1回 | Open Targets入門 #1:概要と仕組みを理解する | Platformの位置づけ、データソース、association scoreの考え方を理解する |
| 第2回 | Open Targets入門 #2:Platform UIで疾患から創薬ターゲットを探す | アルツハイマー病を例に、UIでAssociated TargetsとEvidenceを読む |
| 第3回(本記事) | Python + GraphQL APIでデータを取得する | GraphQL APIから疾患関連ターゲットを取得し、解析しやすい表に変換する |
- GraphQL APIのendpoint・query・variablesの関係を理解できる
- Pythonでページネーションしながら全件取得できる
- overall scoreとdatatype別スコアをDataFrameで読んで解釈できる
- 大量取得にはAPIではなくParquetを使うべき理由がわかる
例として使用する疾患は、前回と同じアルツハイマー病(Alzheimer disease)です。APIクエリでは MONDO_0004975 を指定します。
Open Targets v26.03では疾患IDにMONDO Ontologyを使用しています。旧来のEFO IDは使用できません。
分析環境を準備する
必要なものはPython本体と、HTTPリクエスト・表形式データ処理用の最小限のライブラリです。
| 項目 | 推奨 |
|---|---|
| Python | 3.8以上 |
| HTTPクライアント | requests |
| 表形式データ | pandas |
| 出力形式 | CSV(必要に応じてParquetへ変更) |
インストールは以下で十分です。
python -m pip install requests pandas
仮想環境を使う場合は、先に以下のように作成してからインストールします。
python -m venv .venv
source .venv/bin/activate
python -m pip install requests pandas
GraphQL APIでデータを取得する仕組み
Open Targets PlatformのGraphQL API endpointは以下です。
https://api.platform.opentargets.org/api/v4/graphql
GraphQLでは、REST APIのようにURLパスを細かく切り替えるのではなく、1つのendpointに対して「どのフィールドが欲しいか」をqueryとして送ります。
| 要素 | 役割 | 本記事での例 |
|---|---|---|
| endpoint | リクエスト送信先 | https://api.platform.opentargets.org/api/v4/graphql |
| query | 取得したいデータ構造 | disease { associatedTargets { rows { ... } } } |
| variables | queryに渡す引数 | efoId, index, size |
| page | ページネーション指定 | { index: 0, size: 20 } |
本記事で使うqueryは以下です。ページネーションは page: { index, size } 形式で指定します。
query AssociatedTargets($efoId: String!, $index: Int!, $size: Int!) {
disease(efoId: $efoId) {
associatedTargets(page: { index: $index, size: $size }) {
count
rows {
target { id approvedSymbol approvedName }
score
datatypeScores { id score }
}
}
}
}
変数は以下のように渡します。
{
"efoId": "MONDO_0004975",
"index": 0,
"size": 20
}
curlで最小動作確認をする
Pythonを書く前に、curl で最小限のPOSTリクエストを確認できます。
curl -sS -X POST https://api.platform.opentargets.org/api/v4/graphql \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"query": "query AssociatedTargets($efoId: String!, $index: Int!, $size: Int!) { disease(efoId: $efoId) { associatedTargets(page: { index: $index, size: $size }) { count rows { target { id approvedSymbol approvedName } score datatypeScores { id score } } } } }",
"variables": {
"efoId": "MONDO_0004975",
"index": 0,
"size": 20
}
}
JSON
GraphQLではHTTPステータスコードが200でもレスポンス内に errors が含まれることがあります。Python側ではステータスコードだけでなく、JSON内の errors キーも必ず確認してください。
Pythonで全件取得するコード
以下は、アルツハイマー病に関連するターゲット一覧を取得し、datatype別スコアを列に展開してCSV保存する完全なコードです。
import requests
import pandas as pd
ENDPOINT = "https://api.platform.opentargets.org/api/v4/graphql"
QUERY = """
query AssociatedTargets($efoId: String!, $index: Int!, $size: Int!) {
disease(efoId: $efoId) {
associatedTargets(page: { index: $index, size: $size }) {
count
rows {
target { id approvedSymbol approvedName }
score
datatypeScores { id score }
}
}
}
}
"""
def fetch_associated_targets(efo_id, page_size=500):
rows = []
index = 0
total_count = None
while True:
variables = {
"efoId": efo_id,
"index": index,
"size": page_size,
}
response = requests.post(
ENDPOINT,
json={"query": QUERY, "variables": variables},
timeout=60,
)
response.raise_for_status()
payload = response.json()
if "errors" in payload:
raise RuntimeError(payload["errors"])
associated_targets = payload["data"]["disease"]["associatedTargets"]
if total_count is None:
total_count = associated_targets["count"]
page_rows = associated_targets["rows"]
rows.extend(page_rows)
if not page_rows or len(rows) >= total_count:
break
index += 1
return rows
def rows_to_dataframe(rows):
records = []
for row in rows:
target = row["target"]
record = {
"target_id": target["id"],
"approved_symbol": target["approvedSymbol"],
"approved_name": target["approvedName"],
"overall_score": row["score"],
}
for datatype_score in row.get("datatypeScores", []):
column_name = f"score_{datatype_score['id']}"
record[column_name] = datatype_score["score"]
records.append(record)
df = pd.DataFrame(records)
df = df.sort_values("overall_score", ascending=False).reset_index(drop=True)
return df
def main():
EFO_ID = "MONDO_0004975"
rows = fetch_associated_targets(EFO_ID)
df = rows_to_dataframe(rows)
output_path = f"open_targets_associated_targets_{EFO_ID}.csv"
df.to_csv(output_path, index=False)
print(f"Saved: {output_path}")
print(df.head(20))
if __name__ == "__main__":
main()
APIから返る associatedTargets.count を見ながら index を1ずつ増やしてページを取得します。1ページ目だけを取る場合は page_size=20 でも十分ですが、後で疾患ごとの全候補を比較したい場合に備えて、関数側のデフォルトは page_size=500 にしています。
実行するとどうなるか
python main.py を実行すると、まずAPIへのリクエストが走り(アルツハイマー病では全件取得に数秒かかります)、以下のような出力が得られます。
Saved: open_targets_associated_targets_MONDO_0004975.csv
target_id approved_symbol ... overall_score
0 ENSG00000142192 APP ... 0.870
1 ENSG00000080815 PSEN1 ... 0.866
2 ENSG00000164458 PSEN2 ... 0.817
3 ENSG00000130203 APOE ... 0.775
4 ENSG00000176884 GRIN1 ... 0.700
...
保存されるCSVの列構成は以下のとおりです。datatypeのエビデンスがないターゲットでは、対応する score_* 列が NaN になります。
target_id, approved_symbol, approved_name, overall_score,
score_genetic_association, score_genetic_literature, score_clinical,
score_affected_pathway, score_literature, score_animal_model,
score_rna_expression, score_somatic_mutation
結果のDataFrameを読む
rows_to_dataframe() の出力では、ネストしたGraphQLレスポンスを解析しやすい横持ちの表に変換します。
| 列 | 内容 | 読み方 |
|---|---|---|
target_id | Ensembl gene ID | ターゲットを一意に識別するID |
approved_symbol | HGNC approved symbol | 一般的に使う遺伝子シンボル |
approved_name | HGNC approved name | 遺伝子・タンパク質の正式名称 |
overall_score | 総合association score | 疾患とターゲットの関連性を0〜1で表す総合スコア |
score_genetic_association | 遺伝的関連スコア | GWAS、ClinVar、gene burdenなどの寄与 |
score_known_drug | 既知薬・臨床エビデンス | ChEMBL等に基づくclinical precedence |
score_literature | 文献エビデンス | Europe PMCなどのテキストマイニング |
Open Targets入門 #1:概要と仕組みを理解するで説明したように、association scoreは単純な件数カウントではありません。各データソース内のエビデンスを**harmonic sum(調和和)**で統合し、datatypeや全体のassociation scoreへ集約します。
overall_score が高いターゲットは「強い証拠がある」「複数の独立した証拠がある」可能性がありますが、「創薬ターゲットとして必ず正しい」という意味ではありません。datatype別スコアを見ることで、根拠の種類と強さを判断できます。
datatypeScoresに出てくる主な id は以下です(v26.03実測値)。
| datatypeScoresのid | 日本語での意味 | 主なデータソース・解釈 |
|---|---|---|
genetic_association | 遺伝的関連(統計的) | GWAS associations、ClinVar、Gene Burdenなど。GWAS credible setをL2Gスコアでターゲットへ変換したエビデンス |
genetic_literature | 遺伝的関連(文献キュレーション) | Gene2Phenotype、PanelApp、ClinGenなど。メンデル遺伝病・希少疾患の遺伝子-疾患キュレーション |
clinical | 臨床・薬剤エビデンス | ChEMBL、ClinicalTrials.gov等に基づく臨床先行性(旧バージョンでは known_drug と表記されることもある) |
affected_pathway | パスウェイ・機能 | Reactome、CRISPR Screens、Project Scoreなど |
literature | 文献テキストマイニング | Europe PMCのテキストマイニング |
animal_model | 動物モデル | IMPCなどのモデル生物表現型 |
rna_expression | RNA発現変動 | Expression Atlasなどの疾患組織・対照組織比較 |
somatic_mutation | 体細胞変異 | Cancer Gene Census、IntOGenなど。主にがん関連疾患で寄与 |
前回のUI探索では、APP、APOE、PSEN1をEvidenceページで深掘りしました。APIから得たDataFrameでも、同じターゲットが上位に現れ、どのdatatypeがスコアを支えているかを列として確認できます。
API出力の上位10ターゲット例(v26.03)
以下は MONDO_0004975(Alzheimer disease)を page: { index: 0, size: 10 } で取得した実測結果です。スコアは小数第3位に丸めています。
| rank | symbol | overall | genetic_assoc | genetic_lit | clinical | pathway | literature | animal_model | rna_exp |
|---|---|---|---|---|---|---|---|---|---|
| 1 | APP | 0.870 | 0.924 | 0.608 | 0.973 | 0.608 | 0.995 | 0.391 | — |
| 2 | PSEN1 | 0.866 | 0.954 | 0.886 | 0.622 | — | 0.971 | 0.571 | 0.050 |
| 3 | PSEN2 | 0.817 | 0.909 | 0.772 | 0.622 | — | 0.693 | — | — |
| 4 | APOE | 0.775 | 0.896 | 0.608 | — | 0.267 | 0.999 | 0.269 | 0.017 |
| 5 | GRIN1 | 0.700 | — | — | 0.987 | 0.582 | 0.845 | — | — |
| 6 | SORL1 | 0.689 | 0.761 | 0.304 | — | — | 0.972 | 0.633 | 0.048 |
| 7 | ADAM10 | 0.682 | 0.782 | 0.608 | — | — | 0.918 | — | 0.020 |
| 8 | CDK5 | 0.680 | — | — | — | 0.877 | 0.931 | — | 0.090 |
| 9 | GRIN3B | 0.669 | 0.439 | — | 0.987 | — | 0.179 | — | — |
| 10 | ACE | 0.634 | 0.863 | 0.608 | 0.122 | — | 0.941 | — | — |
(—はそのdatatypeのエビデンスがゼロまたは返却なし。列見出しは列名の略記)
APPやPSEN1/PSEN2は clinical(臨床先行性)・genetic_association・literature など複数のdatatypeから強く支えられています。APOEは genetic_association と literature が特に高く、GWAS由来の遺伝的支持と文献蓄積が順位を押し上げています。
各列の解釈の基準は以下のとおりです。
| 見る場所 | 何を判断するか | 注意点 |
|---|---|---|
overall_score | 疾患との関連の総合順位 | 研究量の多い疾患・遺伝子では高くなりやすい |
score_genetic_association | ヒト遺伝学的な支持(GWAS・Gene Burden) | GWAS locusでは因果遺伝子推定の不確実性が残る |
score_genetic_literature | 遺伝的関連(文献キュレーション) | メンデル遺伝病は高くなりやすい |
score_clinical | 臨床・薬剤開発の先行性 | 既存薬があることは有望材料にも競合材料にもなる |
score_literature | 文献での言及量 | 文献バイアスを受けやすい |
score_animal_model | モデル生物での表現型 | ヒト疾患との対応関係を別途確認する |
score_rna_expression | 発現変動 | 原因か結果かはこの列だけでは判断できない |
大量取得にはParquet/BigQueryを使う
GraphQL APIは、1つの疾患・ターゲット・ターゲット-疾患ペアを探索的に調べる用途に向いています。数百〜数千の疾患を横断して全ターゲットを集めるような処理には向きません。
| 用途 | 推奨アクセス方法 | 理由 |
|---|---|---|
| 1疾患の上位ターゲットを確認 | GraphQL API | 必要なフィールドだけを柔軟に取得できる |
| UIで見つけたターゲットの詳細確認 | GraphQL API | Evidence、known drugs、annotationを追加で取得しやすい |
| 多数の疾患・ターゲットを系統的に集計 | Parquet / BigQuery | API連打を避け、再現性の高いバルク解析ができる |
| 社内パイプラインに組み込む | Parquet / BigQuery | バージョン固定、差分管理、SQL処理に向く |
Open TargetsのバルクデータはGoogle Cloud Storageでも公開されています。
gs://open-targets-data-releases/
全疾患×全ターゲットのassociation matrix、全evidence、全target annotationを扱う場合は、最初からParquet/BigQueryを前提に設計した方が安定します。APIをループで叩き続けるのは避けてください。
RユーザーはotargenのAPIを確認する
Rから同じGraphQL APIを呼ぶためのパッケージとして otargen(CRANで公開)があります。ただし、v2.0.0(2025年7月リリース)はOpen Targets GeneticsのPlatform統合に伴う大幅な作り直しが行われており、v1.x系にあった associatedTargets() などの高レベル関数が廃止されています。v2.0.0では clinVarQuery()・gwasCredibleSetsQuery()・chemblQuery() などデータソース別の関数群に再編されており、「疾患からターゲット一覧をoverall score付きで取得する」ような一発関数は現時点では存在しません。
Rを使う場合は本記事のGraphQLクエリをそのまま httr2 + jsonlite で呼び出すか、v2.0.0以降のotargenの動向を確認してください。
まとめ:API・UI・Parquetの使い分け
本記事では、Open Targets PlatformのGraphQL APIを使って、アルツハイマー病に関連するターゲット一覧をPythonで取得しました。
| ポイント | 内容 |
|---|---|
| API endpoint | https://api.platform.opentargets.org/api/v4/graphql |
| クエリ対象 | disease(efoId: "MONDO_0004975") の associatedTargets |
| ページネーション | page: { index, size } を使う |
| Python実装 | requests.post() でqueryとvariablesを送る |
| 整形 | datatypeScores を score_{id} 列へ展開する |
| 解釈 | overall scoreだけでなくdatatype別スコアを見る |
| 大量取得 | GraphQL APIではなくParquet/BigQueryを使う |
Open Targets Platformは、UIで仮説を立て、APIで小規模に検証し、Parquet/BigQueryで大規模解析へ広げるという流れで使うと実務に組み込みやすいツールです。
関連記事
関連記事Open Targets入門 #1:概要と仕組みを理解する →
関連記事Open Targets入門 #2:Platform UIで疾患から創薬ターゲットを探す →
参考: - Open Targets Platform - GraphQL API Documentation - Data and code access - Target–disease associations Documentation