肿瘤模型归一化目录 — 刚过一万,却仍凑不成一张完整对照表

EMBL-EBI × JAX 合建 / 别名归一 / 分子数据缺口

来源 https://www.cancermodels.org/发布时间: 2026-10-26最后更新: 2026-10-26 阅读 19
肿瘤模型归一化目录 — 刚过一万,却仍凑不成一张完整对照表

信息速览

数据集名称肿瘤模型归一化目录 — 刚过一万,却仍凑不成一张完整对照表
数据类型元数据,本体映射,原始数据链接,文献引用
规模不适用:库内主体为 PDX 小鼠模型,人类样本仅为模型的来源端;模型记录数一万余条
接入方式https://www.cancermodels.org/
AI 就绪度

患者来源异种移植模型(PDX)是肿瘤药物研发里少数能在活体上验证疗效的资产。它的麻烦不在于稀有,而在于分散:一家机构的模型由一个实验室养着,另一家的模型由一个联合体保管,合同研究组织手里还压着一批。更麻烦的是命名——同一份肿瘤组织传代到两家机构,会长成两个名字;不同的两份肿瘤,可能恰好被起成同一个编号。当研究者想问「这个候选药在哪些 PDX 上测过」,问题会立即退化成一场跨机构的电话沟通。

CancerModels.org 就是针对这个退化设计的。它由 EMBL-EBI 与 Jackson Laboratory 联合建设,原名 PDX Finder,2019 年在《核酸研究》的数据库专刊上首次系统介绍。它的产品形态并不复杂:一个可检索的目录,给每个模型挂一个稳定的编号,再把这个编号和来源机构、供者描述、分子数据链接、给药响应归在一起。真正有分量的部分,是「归一化」这三个字背后的工作量——各机构提交的字段名、术语用法、模型命名习惯都不一致,要能跨机构检索,就得先把它们压到同一套属性上。

这一篇要回答的问题,是这套归一化做到了什么程度、在哪一段断了。库内模型数在 2026 年 10 月刚越过一万,这个数字对一个全球性目录来说不算大,原因要到 §2 和 §7 里找。一屏速览见下表。

维度 内容
官方名 CancerModels.org,原 PDX Finder
维护方 EMBL-EBI 与 The Jackson Laboratory 联合
建站时间 原 PDX Finder 约 2016 年起建,2019 年见刊
首发论文 Nucleic Acids Research 2019,doi:10.1093/nar/gky984,PMID 30535239
库内记录数 2026-10-26 查询,10348 条(BioStudies 接口 totalHits)
模型类型 以 PDX 为主;按 Model type:PDX 过滤为 6856 条
标识形式 BioStudies accession(S-CMO 加五位数字)+ Model ID 两套并存
标题形式 [机构缩写] [模型类型] [模型名] 组织学,四段拼接
元数据存储 EMBL-EBI BioStudies,集合名 CancerModelsOrg
分子数据形态 只存链接,不镜像文件;可用性字段非二值
许可 逐条声明,多数 CC0,部分 EMBL-EBI 条款
取数方式 公共 REST 接口,无需注册、无需密钥
主要来源库 PDMR、PDXNet、EurOPDX、JAX、BCM 等
最大短板 分子数据与给药响应的覆盖率低,别名合并依赖申报

§1 它要解决的具体麻烦

1.1 一门药的回溯问题,卡在一个名字上

设想一个最普通的临床前问题:某个团队拿到一株新合成的抑制剂,想先看看同类靶点的药物在哪些患者来源模型上做过体内验证,以便挑几个模型开始自己的实验。这个问题在文献里查不到,因为论文正文只写「我们用了三个乳腺癌 PDX」,不会写清这三个模型的来源与编号。查文献不成,就去查各机构的目录,于是撞上第一个障碍:每个机构给模型的命名规则都不一样,有的是字母加流水号,有的是癌种缩写加患者编号,有的是合同研究组织内部的商品编号。同一种肿瘤,在三个目录里就是三串互不相干的字符。

第二个障碍接踵而至。即便能查到三个目录里都有乳腺癌模型,也无法判断它们之间有没有重复——两家机构可能从同一家医院取到了同一份组织,各自传代、各自命名。对使用者来说,这两份是同一件事;对数据库来说,这是两条记录。没有一层归一的映射,任何关于「覆盖多少个独特患者」的统计都是错的。

第三个障碍最隐蔽:模型名对上了,描述没有对上。同样叫「乳腺癌 PDX」,一个机构记录的是原发灶取材,另一个记录的是肝转移灶取材;一个写的是初治患者,另一个写的是多线治疗后的复发患者。这些差别在后续的药效判断里是决定性的,但它们在原始目录里往往藏在自由文本里,或者干脆没记。

CancerModels.org 站在第三层,试图把上面三层一起处理:用一个稳定字段承接编号,用一个受控词表承接描述,再用一条链接承接数据。它不生产模型,也不养小鼠,它做的全部事情就是「把别人的描述压进同一套槽位」。理解这个定位很重要,因为它决定了这个库的能力上限——凡是来源方没提交的信息,它就没有。

1.2 三条常见的错误期待

把 CancerModels.org 当成「PDX 数据仓库」是最常见的误判。它更接近图书馆的联合目录:告诉你哪本书在哪个馆、索书号是什么,书本身还在原馆。分子数据、给药响应原始记录都在各自的来源库或论文补充材料里,这个目录给的是指针。

第二条错误期待是把它当成「别名解析服务」。直觉上,一个专门做归一化的库,应该能告诉你「文献里的 X 模型就是库里的 Y 记录」。实际上它给出的是一套提交格式与一个稳定 accession,别名是否被写进来,取决于提交方愿不愿意写。库内很多记录的 Model ID 就是机构自己的原始命名,没有额外的映射层。

第三条是把它当成「可以放心合并的训练集」。它的记录确实能批量导出,但导出的是元数据,不是特征矩阵。想做药物响应预测的人,拿到这批元数据后还要自己去各来源库抓分子数据和响应表,再处理跨库的标签不一致。这一段的工程量往往被严重低估。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
按癌种找到一批可用模型 按 Histology、Cancer System、Primary Site 检索,返回记录列表 够,但需要人工核对取材部位与治疗史
判断两个模型是不是同一份肿瘤 一个 Model ID 字段与一个复合标题 不够,跨机构同模型合并依赖申报
拿到模型的分子数据 Dataset Available 字段加外链 多数不够,字段非二值,且相当比例是 N/A
拿到给药响应曲线 部分来源库的记录带响应数据链接 不够,方案与终点不统一
判断模型能否对外分发 Available for distribution 字段加供应商表单链接 够,字段取值明确为 Yes/No
引用某个模型 BioStudies accession 可稳定引用 够,但学术惯例通常引原始论文
做跨库去重 没有公开的别名映射表 不够,需要自建映射
直接训练模型 仅元数据 远远不够,特征是外链的

这张表里有两个「够」、六个「不够」。这个比例值得先接受下来,再去谈它为什么仍然重要。它的价值不在于替你做完全部工作,而在于把「哪些模型存在、它们大致是什么」这一步从电话沟通变成了一次查询。

1.4 你的处境与它能帮到的事

你的处境 它能帮你什么 下一步该做什么
课题刚立项,要从零挑模型 按癌种、取材部位、宿主品系缩小候选范围 用 §5 的检索接口拉候选清单,再逐个开供应商表单
手上有十几个模型名,来路不明 用模型名反查记录,确认所属机构与组织学 把机构缩写一并带上再查,避免跨机构同名撞车
想复现某篇论文的体内实验 能查到模型是否在库内、来源机构是谁 直接联系记录里的联系人邮箱,或回到论文补充材料
要建一个药物响应的多机构训练集 提供跨机构的统一元数据骨架 先按 Dataset Available 与响应链接筛,再逐库下载特征
要评估模型库之间的重叠 提供各来源库的统一视图 自己做去重,联合主键用机构缩写加 Model ID
要给一组模型补分子注释 提供模型到外部数据的指针 顺着外链去原始库取,注意原始库的许可
只想知道这个领域有多少模型 查询接口直接给计数 见 §2.4 的分布表,注意口径叠加问题
做监管材料里的模型清单 记录可稳定引用,字段可追溯 引用时写明 accession 与查询日期

这张表的左列全部是处境,不是特性。它的用意是:使用者通常不是从「我要一个 PDX 目录」出发的,而是从某个具体动作出发的。同一个库在八个处境里扮演八个不同角色,把角色分清楚,才不会在错的环节等一个不会到来的答案。

1.5 一句话判断

它把一个散落的领域拉进了同一张表,但只拉进了「存在性」这一列;「可比性」那一列还空着。

1.6 不适合谁:四条边界

不适合把元数据当特征用的人。库内主体是描述性字段:组织学、取材部位、宿主品系、传代情况。这些字段能用来分层、能用来做协变量,但不能直接喂进预测模型。分子层面的特征要顺着外链去原始库取,且各库的预处理流程完全不同。

不适合需要完整别名映射表的人。如果你手上有一批来自文献的模型名,指望一次查询全部对上,结果会让人失望。库内只对提交方愿意申报的别名做归一,未申报的一律查不到。这类需求更现实的路径是去找具体联合体自己的映射表,比如 PDXNet 内部的数据协调文档。

不适合把它当临床前结论依据的人。所有模型都是免疫缺陷小鼠上的异种移植,缺少完整的免疫微环境,人源基质在传代中会逐渐被小鼠基质替代。任何在 PDX 上得到的疗效结论,都不能跳过这一步直接写成临床建议。

不适合只想要大样本量的人。一万余条听起来不少,但扣掉无分子数据的、无响应数据的、重复登记的,能进入建模的规模会小一个量级。如果你的设计需要数万级别的配对样本,这个库给不了,该去找单细胞图谱或大规模细胞系筛选。

§2 数据构成与规格

2.1 每一条记录长什么样

库的关键设计在于它有两套标识同时在用。外层是 BioStudies 的 accession,形如 S-CMO 加五位数字,这是存储层的容器编号,任何一条记录被提交进 EMBL-EBI 的 BioStudies 系统时都会得到。内层是 Model ID,这是模型本体的名字,通常就是来源机构自己的命名。两套并存带来一个直接后果:同一模型若被两家机构各提交一次,会得到两个 accession,却可能带着同一个 Model ID。反过来说,两条记录 Model ID 相同,也不能推出它们是同一模型,因为编号只有机构内部唯一。

标题是第三层信息,也是人眼最先看到的东西。库里把标题拼成固定四段:方括号里是机构缩写,第二个方括号是模型类型,第三个方括号是模型名,最后一段是组织学。这种拼法的好处是一眼能看出归属,坏处是模型名里若含空格或斜杠,掐取会出错。做正则解析时必须按方括号边界切,不能按空格切。

记录的结构分两层:一层是平铺的属性,一层是可选的子节。子节包括 Organization、Patient / Tumour Metadata,以及可选的 Immune markers、Related models、Molecular data。前两个子节在大多数记录里存在,后三个不是每条都有。这一点是理解覆盖率的关键——分子数据不是主表的一个字段,而是一个可能缺席的子节。

2.2 字段/记录的完整字典

字段名 所在层 取值形态 典型值 用途 缺失情况
accession 容器 S-CMO 加五位数字 S-CMO10361 稳定引用 不会缺
Title 平铺 四段方括号拼接 [AHS] [PDX] [PDX_AML-979] Acute Myeloid Leukemia 人工浏览、正则解析 不会缺
Model ID 平铺 自由文本 PDX_AML-979 模型本体名 不会缺
Model type 平铺 受控短词 PDX 类型过滤 多为 PDX
Histology 平铺 受控词表 Acute Myeloid Leukemia 按病种检索 少数 Not Provided
Cancer System 子节 受控词表 Unclassified / Breast Cancer / Thoracic Cancer 按系统检索 部分为 Unclassified
Primary Site 子节 半受控 bone marrow / breast 区分原发部位 常见 Not Provided
Collection Site 子节 半受控 bone marrow / lung 区分取材部位 常见 Not Provided
Tumour Type 子节 受控短词 Primary / Recurrent 区分初治与复发 部分缺失
Cancer Stage 子节 混合格式 Intermediate / Stage IB / T1N0M0 分期 格式混杂
Cancer Grade 子节 受控短词 Not Provided 分级 缺失率高
Patient Sex 子节 female / male female 供者性别 少量缺失
Patient Age 子节 十岁区间 50 - 59 / 0 - 23 months 供者年龄 少数缺失
Patient Ethnicity 子节 受控词表 European 族裔 常见 Not Provided
Host Strain Name 平铺 受控短词 nod scid gamma 宿主品系 部分缺失
Host Strain Nomenclature 平铺 标准命名 NOD.Cg-Prkdcscid Il2rgtm1Wjl/SzJ 精确品系 部分缺失
Engraftment Site 平铺 受控短词 Bone Marrow 移植部位 部分缺失
Type 平铺 受控短词 Orthotopic 原位或皮下 部分缺失
Passage 平铺 混合格式 All / all 传代数 取值不统一
Dataset Available 平铺 非二值自由描述 N/A / immune markers 判断有无分子数据 大量为 N/A
Available for distribution 平铺 Yes / No Yes 能否获取模型 取值明确
Supplier type 平铺 受控短词 Academic 学术或商业 取值明确
License 平铺 受控短词 CC0 / EMBL-EBI 许可 逐条不同
Provider URL 平铺 URL 或 Not Provided compendium.criver.com 链接 跳回来源库 常见 Not Provided
Contact Names 平铺 人名 Binje Vick 联系提交人 部分缺失
Contact Emails 平铺 邮箱 机构邮箱 直接联系 部分缺失
Paediatric 平铺 Yes / No No 儿科模型筛选 取值明确
Has Related Models 平铺 Yes / No Yes 是否存在关联模型 取值明确

这张字典里最值得盯着看的是 Dataset Available 这一行。它的取值形态写着「非二值自由描述」,实际取值里既有 N/A,也有 Yes 这样的肯定,还有 immune markers 这种只说明某一类数据存在的描述。用等值匹配过滤会漏掉一部分,用非空过滤又会把 N/A 当成有数据。正确做法是先枚举这个字段的全部取值再决定规则,而这一步没有现成文档,只能自己扫。

2.3 一条记录的完整解剖

下面这条记录来自德国一个血液学实验室,是库内结构最典型的一种:模型是急性髓系白血病,没有分子数据,不可对外分发。把它拆开看,能看清字段的组织方式。

{
  "accno": "S-CMO10361",
  "type": "study",
  "attributes": [
    {"name": "Title", "value": "[AHS] [PDX] [PDX_AML-979] Acute Myeloid Leukemia"},
    {"name": "ReleaseDate", "value": "2025-12-09"},
    {"name": "AttachTo", "value": "CancerModelsOrg"}
  ],
  "section": {
    "attributes": [
      {"name": "Model ID", "value": "PDX_AML-979"},
      {"name": "Model type", "value": "PDX"},
      {"name": "Histology", "value": "Acute Myeloid Leukemia"},
      {"name": "Dataset Available", "value": "N/A"},
      {"name": "Available for distribution", "value": "No"},
      {"name": "Supplier type", "value": "Academic"},
      {"name": "License", "value": "CC0"},
      {"name": "Paediatric", "value": "No"}
    ],
    "subsections": [
      {"type": "Organization", "attributes": [{"name": "Name", "value": "Research Unit Apoptosis in Hematopoietic Stem Cells (AHS)"}]},
      {"type": "Patient / Tumour Metadata", "attributes": [
        {"name": "Patient Sex", "value": "female"},
        {"name": "Patient Age", "value": "50 - 59"},
        {"name": "Cancer System", "value": "Unclassified"},
        {"name": "Primary Site", "value": "bone marrow"},
        {"name": "Collection Site", "value": "bone marrow"}
      ]}
    ]
  }
}
#-- 读这条记录时,要看的是四件事
#-- 1 归属:Title 第一段的 AHS 是机构缩写,Model ID 是机构内部命名
#-- 2 可用性:Dataset Available 为 N/A,说明这条记录不带分子数据
#-- 3 可获得性:Available for distribution 为 No,模型拿不到,只能读描述
#-- 4 粗粒度:Cancer System 是 Unclassified,只能靠 Histology 与 Primary Site 判病种

这四点合起来说明一件事:一条在库内检索得到的记录,未必是一条能用的记录。可检索、可获取、有数据是三件独立的事,字段里各有各的位置。很多使用者第一次查询时会默认三者一致,然后在实验设计阶段才发现拿不到模型,或者拿到了却没有测序数据。

再对照一条有数据的记录。下面这条来自美国国家癌症研究所的患者来源模型库,模型是肺腺癌,有免疫标志物数据、可分发、有关联模型。

{
  "accno": "S-CMO5794",
  "section": {
    "attributes": [
      {"name": "Model ID", "value": "LG0567-F671"},
      {"name": "Model type", "value": "PDX"},
      {"name": "Histology", "value": "Lung Adenocarcinoma"},
      {"name": "Dataset Available", "value": "immune markers"},
      {"name": "Available for distribution", "value": "Yes"},
      {"name": "Has Related Models", "value": "Yes"},
      {"name": "License", "value": "EMBL-EBI"}
    ],
    "subsections": [
      {"type": "Organization", "attributes": [{"name": "Name", "value": "Patient-Derived Model Repository (PDMR)"}]},
      {"type": "Patient / Tumour Metadata", "attributes": [
        {"name": "Patient Sex", "value": "male"},
        {"name": "Patient Age", "value": "50 - 59"},
        {"name": "Cancer System", "value": "Thoracic Cancer"},
        {"name": "Cancer Stage", "value": "Stage IB"},
        {"name": "Primary Site", "value": "respiratory/thoracic"}
      ]},
      {"type": "Immune markers"},
      {"type": "Related models"}
    ]
  }
}
#-- 两条记录并列看,能看出库的设计取舍
#-- 同一条记录里,Dataset Available 的取值可以是 N/A、Yes 或 immune markers
#-- 这意味着「有分子数据」这件事不是布尔量,而是一个自由描述的槽位
#-- 做筛选时,正确做法是先统计该字段的全部取值,再决定匹配规则
import json
import urllib.request

def fetch(acc):
    url = f"https://www.ebi.ac.uk/biostudies/api/v1/studies/{acc}"
    with urllib.request.urlopen(url, timeout=30) as r:
        return json.load(r)

def flat(sec, out=None):
    if out is None:
        out = {}
    if isinstance(sec, list):
        for x in sec:
            flat(x, out)
        return out
    for a in sec.get("attributes") or []:
        out.setdefault(a["name"], []).append(a.get("value"))
    subs = sec.get("subsections") or []
    if isinstance(subs, dict):
        subs = [subs]
    for s in subs:
        flat(s, out)
    return out

for acc in ("S-CMO10361", "S-CMO5794"):
    d = flat(fetch(acc)["section"])
    print(acc, "|", d["Model ID"], "|", d.get("Dataset Available"), "|",
          d.get("Available for distribution"), "|", d.get("License"))

这段代码读出的两行输出,恰好对应库内两种记录形态。第一种没有数据、不能分发,第二种有部分数据、可以分发。它们在同一套字段下并排存在,检索接口不会自动区分。用接口取回一百条记录,不做这一步拆分就去做统计,得到的「有分子数据的模型数」一定是错的。

2.4 分布表:规模、癌种与数据可用性

先说规模。2026 年 10 月 26 日通过 BioStudies 的 CancerModelsOrg 集合检索接口查询,返回 totalHits 为 10348,接口同时声明这个总数是精确的。这个数字包含集合内全部记录,不区分模型类型。

再说类型。用 Model type 字段过滤 PDX,得到 6856 条。这里有一个先前没料到的现象:把过滤条件换成 PDOX 同样写法,返回的计数完全相同。合理的解释是检索层对这两个写法做了同义展开,而不是两种类型恰好等量。这个细节提醒一件事——计数结果对写法敏感,换一种写法可能得到完全不同的数,做统计时要把查询字符串一起记录下来。

癌种分布方面,只按组织学关键词做了四个高频癌种的计数,结果如下表。这只是四个点,不是完整分布,完整的分布需要逐个组织学术语去查,超出了本次查询的范围。

过滤条件 返回计数 与 PDX 总数的关系 备注
集合全部记录 10348 基线 含全部类型
Model type:PDX 6856 占基线的六成七 主体
Model type:PDOX 6856 与上一行相同 疑为同义展开
组织学含 Colorectal 1026 占 PDX 的一成五 最高
组织学含 Lung 934 占 PDX 的一成四 次高
组织学含 Pancreatic 590 占 PDX 的百分之九 第三
组织学含 Breast 551 占 PDX 的百分之八 第四
Dataset Available:Yes 13 占基线的千分之一 口径明显偏窄

最后一行需要解释。按 Dataset Available 等于 Yes 精确过滤,只得到 13 条。这与「库内很多记录有分子数据」的印象冲突。冲突的原因在 2.2 的字典里已经埋下——这个字段的取值不统一,很多有数据的记录填的是别的描述,比如上一条剖析里看到的 immune markers。所以 13 这个数不能读成「只有 13 个模型有分子数据」,只能读成「恰好把该字段填成 Yes 的只有 13 条」。这个例子是全文最该记住的一条方法论:字段名看起来是在回答一个问题,实际回答的可能是另一个问题。

数据可用性写法 含义 能否直接当布尔量用
N/A 未提供,或无数据 否,需与缺失区分
Yes 有数据 是,但漏检
immune markers 仅某类数据存在 否,需归一
空子节(只有类型名) 声明了类别但内容为空 否

2.5 与同类资源的字段差异

PDX 领域的资源不止这一家。理解它的位置,要把它和三类东西并排看:单个大型模型库、细胞系筛选平台、临床基因组注册库。

资源 主体对象 标识方式 分子数据 与 CancerModels.org 的关系
CancerModels.org 跨机构 PDX 目录 BioStudies accession 加 Model ID 只存链接 目录层
PDMR(美国 NCI) 自有 PDX 库 库内标本序号 自有平台产出 上游来源之一
EurOPDX 欧洲联合体 PDX 库 成员机构各自命名 联合体统一测序 上游来源之一
PDXNet 美国联合体 成员机构各自命名 联合体统一测序 上游来源之一
细胞系筛选平台 商品化细胞系 细胞系标准名 表达与药敏矩阵 平行路线,非替代
临床基因组注册库 患者肿瘤测序 样本编号 测序与临床变量 患者端,非模型端

这张表说明它做的是横向整合,不是纵向深挖。单个来源库在自家数据上的完整性远高于目录,因为目录只能收录对方愿意提交的字段。真正要取特征矩阵的人,最终一定会回到上游库,目录的作用是告诉他该去哪一家、找哪个编号。

§3 它的身份层:一个模型怎样被认出

3.1 编号的三重身份

在 CancerModels.org 里,「编号」这个词至少指三样东西,混用会直接导致错误。第一重是存储容器的 accession,S-CMO 开头,由 EMBL-EBI 在接收提交时分配,与模型本身无关,只与提交动作有关。第二重是 Model ID,模型的业务主键,来自产生机构,反映的是机构的命名习惯。第三重是标题里的模型名段,也就是第三个方括号里的那串字符,它在多数记录里与 Model ID 相同,但不是全部。

把这三重关系理清之后,跨库连接的规则就自然浮现:accession 只用于引用单条记录,Model ID 只用于机构内部定位,要做跨机构的模型身份判断,必须把机构缩写与 Model ID 组合起来。机构缩写来自标题第一段,或来自 Organization 子节的 Name。两者并不总是能对上,因为标题里的缩写有时是项目代号而非机构名。

身份层 来源 唯一性范围 适用场景 误用后果
accession EMBL-EBI 分配 全局唯一 引用单条记录 无
Model ID 产生机构 机构内唯一 机构内定位模型 跨机构同名撞车
标题模型名段 提交方填写 无保证 人工浏览 正则掐取出错
机构缩写 提交方填写 项目级 与 Model ID 组合 与机构全名不一致

第三行是最容易出事的地方。标题是人读的,格式由提交方决定,而提交方并不总是严格按四段拼。有的记录里模型名段带了空格,有的在组织学段里又出现方括号,有的干脆只有三段。写解析代码时若假定格式绝对规整,遇到几百条之后必然崩溃。

3.2 归一化在哪个环节发生

归一化这个词在这套系统里具体发生在三处,每一处的程度不同。

第一处是字段名归一。各来源机构提交时的字段名五花八门,进入 BioStudies 之前会被映射到一套统一的属性名。这一处做得最彻底,因为它纯粹是技术性的,不涉及内容判断。副作用是原始字段名被丢弃,一旦映射有偏差,无法回溯。

第二处是术语归一。组织学、癌种、取材部位都往受控词表上靠。这一处做了但不满。比如 Cancer System 字段里会出现 Unclassified,说明映射目标本身允许「说不清」这个取值;Cancer Stage 更是三种格式混用,既有 Intermediate 这样的分级词,也有 Stage IB 这样的分期串,还有 T1N0M0 这样的 TNM 组合。

第三处是模型身份归一,也就是把不同机构对同一模型的不同叫法合并。这一处做得最少。库里没有公开的别名映射资源,同一模型的跨机构合并靠提交方主动申报关联。Has Related Models 字段存在,说明系统承认关联模型这个概念,但它记录的是「有没有关联」,不是「关联到哪一条」。

归一化环节 做得如何 判断依据 残余风险
字段名 彻底 所有记录共用同一套属性名 原始名不可回溯
组织学 较彻底 检索可跨机构命中同一术语 边缘病种可能落空
癌种系统 不彻底 存在 Unclassified 取值 按系统统计会漏
分期 不彻底 三种格式并存 无法直接排序
模型身份 最少 无公开映射表 跨机构去重需自建
分子数据指向 部分 只存链接,字段非二值 覆盖率统计易错

3.3 它凭什么值得信任

一个只做索引的库,可信度取决于两件事:来源是否可追溯,映射是否可复核。CancerModels.org 在两件事上的处理是:来源可追溯到具体机构与联系人邮箱,这部分做得好,几乎每条记录都有 Organization 子节;映射不可复核,因为映射规则与原始提交内容都不公开。

这个组合带来一个实际后果:当你在库里看到某条记录的 Histology 是某个具体亚型时,你无法判断这是提交方原文如此,还是映射环节做的判断。前者可以引用,后者需要谨慎。想确认,只能顺着 Provider URL 或联系人去找来源方核对。这也是为什么记录里的 Contact Emails 字段值得重视——它是这套系统里唯一一条能穿出目录、回到原始事实的通道。

§4 它的质量层:可用性比完整性更值得看

4.1 完整并不等于可用

评估一个目录类资源,容易掉进一个陷阱:看记录数、看字段数,然后给出「覆盖全面」的结论。CancerModels.org 的字段确实多,一条记录摊开有近三十个属性。但这些属性的填充质量差异极大,把填充率平均一下会得到一个毫无意义的中位数。

正确的看法是分三档。第一档是结构性字段,几乎必然存在:Title、Model ID、Model type、Histology、License、Available for distribution。第二档是描述性字段,多数存在但有缺失:Patient Sex、Patient Age、Primary Site、Collection Site。第三档是可选的子节,存在与否决定这条记录能不能用于下游:Molecular data、Immune markers、Related models。

档位 字段举例 存在比例 缺失后果
结构性 Title、Model ID、License 接近全部 记录不可用
描述性 性别、年龄段、原发部位 多数 分层能力下降
可选子节 分子数据、免疫标志物 少数 无法建模

第三档是决定性的。一条记录即使结构化程度很高,只要没有分子数据,它就只能用于「这个模型存在」这一类判断,不能进入任何以分子特征为输入的流程。而第三档的判定又恰恰最难,因为前面已经说过,Dataset Available 字段的取值不统一。

4.2 案例:一条肺腺癌模型的三次核对

这个案例取自库内一条真实记录,编号 LG0567-F671,来自美国国家癌症研究所的患者来源模型库。选它是因为它的字段填充度在库内属于较好的一档,恰好可以看清「好」到什么程度。

第一次核对是身份。标题三段拼成 [PDMR] [PDX] [LG0567-F671] Lung Adenocarcinoma。机构缩写 PDMR 与 Organization 子节里的 Patient-Derived Model Repository 对得上,说明这条记录的归属没有歧义。Model ID 与标题模型名段一致,没有额外的别名层。也就是说,如果另一篇论文里出现 LG0567-F671 这个名字,它指的就是这条记录——前提是那篇论文确实用了 PDMR 的模型。

第二次核对是可得性。Available for distribution 为 Yes,Supplier type 未填但 Organization 表明是学术机构,Provider URL 指向 NCI 的标本查询页面。这三点合起来说明模型可以申请,路径清楚。这一步在 §4.2 的案例里是最顺利的一环,因为 PDMR 作为国家级资源,对外分发的政策本身比较明确。

第三次核对是数据。Dataset Available 填的是 immune markers,说明这条记录关联了免疫标志物数据,但并不意味着有全外显子或转录组。子节里确实有一个 Immune markers 空节,内容为空的子节说明类别被声明了,具体项要回来源库取。Has Related Models 为 Yes,说明这个患者来源可能对应多个模型,但库内没有列出关联到哪几条。

三次核对做完,这条记录的可用边界就清楚了:能拿到模型,能拿到一部分免疫标志物数据,拿不到完整的分子谱,也无法在库内直接找到它的姊妹模型。这个结论不是「好」或「不好」,而是一组具体的能与不能。§4.2 的案例之所以值得反复回看,就因为它把「一条记录能支撑什么研究」拆成了可枚举的判断项,而不是一个笼统的评分。

4.3 覆盖率为什么低

分子数据覆盖率低,原因不在目录方,而在提交方。PDX 的分子表征成本很高:一个模型要做全外显子、RNA 测序、有时还要做拷贝数,单模型成本在数千美元量级,而模型本身往往由学术实验室以低成本维持。大多数实验室承担不起给每个模型都做全套表征,于是形成一种常态——先建模型、先做药效,分子数据等有经费了再补,或者永远不补。

联合体模式的资源情况好一些。PDMR、EurOPDX、PDXNet 这类大型项目有统一表征的预算,模型进库时通常带着一套分子数据。目录里分子数据覆盖率的不均衡,本质上是来源机构经费结构的不均衡在数据上的投影。这一点决定了任何试图提高覆盖率的努力,都不能只靠目录方去催提交,而要落到上游的资助安排上。

4.4 给药响应的三种记录方式

给药响应在这套系统里有三种存在形式,各自的可用性完全不同。

第一种是记录里带响应数据的链接,指向来源库的响应表。这种最好用,因为能追到原始数值,但数量少。第二种是记录里只写有响应数据,链接指向论文或项目主页,需要人工去找补充材料。第三种是没有任何响应相关字段,模型的药效信息只存在于某篇论文的图里,库内查不到。

响应记录形式 可取性 数据形态 典型来源
记录内带响应链接 直接 表格数值 大型联合体
只说明有响应数据 间接 需去论文找 学术实验室
无响应字段 无 不可得 多数小机构

这三种形式在检索接口上看不出区别,因为响应数据不在主表字段里。要判断一条记录有没有响应数据,只能打开记录看子节,这一步无法批量。这是这个库在「能不能用来做响应建模」这个问题上最实质的障碍——不是没数据,而是数据的存在性判断本身需要逐条人工。

§5 取数层:接口、查询与导出

5.1 入口在哪

官方站点的域名指向 EMBLEBI 的 BioStudies 系统,浏览界面是前端渲染的,直接抓 HTML 拿到的是空壳。可用的入口是 BioStudies 的公共 REST 接口,路径形如 biostudies 的 api/v1 前缀加集合名。这个集合名是 CancerModelsOrg,注意大小写与拼接方式,写成带点的形式会 404。

接口不需要注册、不需要密钥、没有速率文档。它是 EMBL-EBI 公共基础设施的一部分,稳定性随主站。检索用 search 端点,单条记录用 studies 端点加 accession。两个端点都返回 JSON。

5.2 检索接口的基本用法

检索接口支持一个 query 参数,语法是字段名加冒号加值,多个条件用 AND 连接。不做任何过滤时返回集合内全部记录。返回体里有两个计数字段,totalHits 是命中数,isTotalHitsExact 标明这个数是否精确。做统计时应当以 isTotalHitsExact 为真为前提。

#-- 集合总数
curl -s "https://www.ebi.ac.uk/biostudies/api/v1/CancerModelsOrg/search?pageSize=1" \
  | python3 -c "import json,sys; d=json.load(sys.stdin); print(d['totalHits'], d['isTotalHitsExact'])"
#-- 按模型类型过滤,比较 PDX 与 PDOX 两种写法
for t in PDX PDOX; do
  curl -s "https://www.ebi.ac.uk/biostudies/api/v1/CancerModelsOrg/search?query=Model%20type:${t}&pageSize=1" \
    | python3 -c "import json,sys; d=json.load(sys.stdin); print('${t}', d['totalHits'])"
done
#-- 按癌种关键词加类型过滤
for k in Colorectal Lung Pancreatic Breast; do
  curl -s "https://www.ebi.ac.uk/biostudies/api/v1/CancerModelsOrg/search?query=%22${k}%22%20AND%20Model%20type:PDX&pageSize=1" \
    | python3 -c "import json,sys; d=json.load(sys.stdin); print('${k}', d['totalHits'])"
done

三段脚本读出的就是 §2.4 那张分布表的全部数字。注意第三段里关键词加了引号,不加引号时检索会退化为分词匹配,命中数会明显偏大。这个差别很容易被忽略,然后在两份报告里出现两个对不上的数字。

5.3 分页取回记录

需要拿全量元数据时,用分页拉取。接口返回 hits 数组,每个元素带 accession、title、release_date 等摘要字段,不含完整属性。要拿完整属性得对每条再请求一次 studies 端点,这意味着一万条记录要一万次请求。分批做、加间隔、失败重试是必须的。

#-- 分页拉取摘要列表,再按 accession 逐条取完整记录
import json
import time
import urllib.request

BASE = "https://www.ebi.ac.uk/biostudies/api/v1"

def search(page, size=100, query=None):
    url = f"{BASE}/CancerModelsOrg/search?page={page}&pageSize={size}"
    if query:
        url += "&query=" + urllib.parse.quote(query)
    with urllib.request.urlopen(url, timeout=60) as r:
        return json.load(r)

def record(acc):
    with urllib.request.urlopen(f"{BASE}/studies/{acc}", timeout=60) as r:
        return json.load(r)

import urllib.parse
first = search(1)
total = first["totalHits"]
pages = (total + 99) // 100
accs = []
for p in range(1, pages + 1):
    for h in search(p)["hits"]:
        accs.append(h["accession"])
    time.sleep(0.3)
print("摘要条数", len(accs), "声明总数", total)

full = []
for i, a in enumerate(accs):
    full.append(record(a))
    if i % 500 == 499:
        time.sleep(2)
with open("cancermodels_full.json", "w", encoding="utf-8") as f:
    json.dump(full, f, ensure_ascii=False)
print("完整记录", len(full))

这段代码有一个容易被忽略的地方:分页时集合可能在增长,翻页过程中新提交的记录会打乱偏移。稳妥做法是先按 release_date 倒序取第一页,记下时间戳,后续只增量取更新的部分。若要一次性做快照,最好在同一分钟内跑完,并记录查询时刻。

5.4 从记录里抽出可用性信号

拿到完整记录后,第一件要做的事不是统计,而是把 Dataset Available 的取值枚举出来。这个字段的自由度决定了后续所有筛选规则的有效性。

#-- 枚举 Dataset Available 的全部取值,再决定筛选规则
import json
from collections import Counter

docs = json.load(open("cancermodels_full.json", encoding="utf-8"))

def flat(sec, out=None):
    if out is None:
        out = {}
    if isinstance(sec, list):
        for x in sec:
            flat(x, out)
        return out
    for a in sec.get("attributes") or []:
        out.setdefault(a.get("name"), []).append(a.get("value"))
    subs = sec.get("subsections") or []
    if isinstance(subs, dict):
        subs = [subs]
    for s in subs:
        flat(s, out)
    return out

g = [flat(d["section"]) for d in docs]
cnt = Counter((v[0] if v else None) for v in (x.get("Dataset Available") for x in g))
for k, n in cnt.most_common(15):
    print(repr(k), n)

这一步的输出会决定两件事:哪些取值算「有数据」,哪些算「无数据」。以库内常见形态推演,取值大致会落在三个区间:明确的否定(N/A、Not Provided、空)、明确的肯定(Yes)、以及描述性的部分肯定(immune markers 之类)。三个区间之外的零星取值需要人工判读,判读结果要写成规则并留档,否则下次重跑会得到另一套标准。

5.5 按病种与取材部位组合筛选

单靠组织学术语筛选,会把原发灶与转移灶混在一起。要区分,得再加 Primary Site 与 Collection Site 两个条件。这两个字段的取值是半受控的,同一个器官存在多种写法,检索时需要用 OR 把变体连起来。

Primary Site:(breast OR "breast cancer" OR mammary) AND Collection Site:liver

这行的意思是找原发于乳腺、取材于肝的模型,通常对应肝转移。库内这类组合查询是否支持括号与 OR,取决于后端检索语法,实际使用时先用小样本验证命中是否合理,再放开全量。这一步没有捷径,因为检索语法的文档不在官方站点上,只能从 BioStudies 的通用文档里推。

5.6 导出的三种形态

导出没有专门的按钮,只有三种自建路径。

第一种是接口拉全量 JSON,适合需要全部字段的人,代价是请求次数多。第二种是浏览器端导出检索结果,适合只需要摘要列表的人,几千条以内可以接受。第三种是顺着 Provider URL 回来源库下载,适合只需要某一批模型的人,数据最全但最不统一。

导出方式 适合规模 字段完整度 主要代价
接口全量 JSON 全库 全部属性 请求次数等于记录数
检索结果导出 千条以内 摘要字段 无完整属性
回来源库下载 单批模型 最全 需逐个申请
第三方镜像 不确定 不确定 时效与许可风险

§6 与库内词条的关系

6.1 关系图谱

这个库在千方医数集的词条网络里,处在「临床前模型」这一侧,与「患者端数据」和「细胞系端数据」并列。三者的关系不是替代,而是同一研究链条上的不同环节。

库内词条 关系 差异点
AACR GENIE 患者端对照 记录的是患者肿瘤测序,没有模型
CancerLinQ 患者端对照 记录真实世界诊疗,无实验模型
CZ CELLxGENE Census 表征方法参照 转录组数据的标准化方式可借鉴
Cellosaurus 类细胞系资源 平行路线 细胞系不可替代体内模型
CancerModels.org 本体 模型端目录

把它放在这张图里,能看清一个常被混淆的边界:GENIE 与 CancerLinQ 记录的是人,CancerModels 记录的是小鼠身上的肿瘤。同一个患者可能同时出现在患者端注册库与模型端目录里,但两边没有任何连接字段。想在两边做连接,只能靠机构、癌种、年龄段的模糊匹配,这一步的可靠性很低。

6.2 检索路径

从站内进到这篇词条,通常有三条路。一条是从「肿瘤模型」这个主题词出发,经分类页到达。一条是从同癌种的患者端词条出发,在对比表里跳转。一条是从单细胞图谱词条出发,在「如何验证细胞层面的发现」这个话题下跳转。

三条路的读者动机不同。第一条是想找模型的人,需要的是筛选路径,也就是 §1.4 那张处境表。第二条是已经做过患者数据分析、想找体内验证的人,需要的是模型的可获取性信息。第三条是想把体外发现推到体内的人,需要的是模型与细胞系的差别说明。同一个词条要同时服务这三种动机,靠的是把「能做什么」写清楚,而不是把「有什么字段」罗列完整。

§7 质量评估与局限性

7.0 阅读本节前的一个前置校正

在给分之前,得先纠正一个坐标系错误。评估这种目录类资源时,最自然的做法是拿它和成熟的数据库比字段完整度、比更新频率、比文档质量,然后得出「它不如某某库」的结论。这个比法的前提是两者在做同一件事,而实际上它们做的是两件不同的事:成熟数据库通常同时管数据和索引,这个库只管索引。

坐标系一摆正,很多看似是缺陷的东西会变成设计选择。分子数据只存链接,不是因为懒,而是因为原始数据的所有权在各来源机构手里,镜像下来会带来许可与维护的双重负担。别名映射不公开,不是因为藏,而是因为映射本身依赖提交方申报,公开一份不完整的映射表比不公开更容易造成误判。

但这套校正不能走得太远,不能把「只管索引」当成一块免罪金牌。索引类资源同样有它的下限:索引必须准,指向必须活,计数口径必须自洽。这三条它做到了第一条的一半、第二条的大半、第三条的小半。所以下面这一节,评分标准是「作为一个索引它做得怎样」,而不是「作为一个数据库它做得怎样」。带着这个前置认识再往下看五维评分,才不会把结构性的取舍当成个体的失误。

7.1 DAIMS 五维评分

下面这张表用的是 DAIMS 框架,即数据可获取性、可互操作、元数据质量与可持续性这一组常被引用的评估维度,再补一维可发现性。五个维度各自 1 到 10 分,评分依据必须能在库内找到对应字段或接口行为,不能凭印象给分。

维度 评分(1–10) 依据 扣分原因
可发现性 8 官方站点与 BioStudies 双重入口,论文有正式引用 站点前端为渲染式,直接抓取无效
可获取性 9 无需注册、无需密钥、接口稳定 无官方批量导出,需自建脚本
可互操作 7 字段名统一、组织学受控、用 BioStudies 标准容器 无公开别名映射,跨库连接困难
元数据质量 6 结构性字段齐备,供者描述粒度合理 可选子节缺失严重,取值不统一
可持续性 8 两家国家级机构联合、纳入 EMBL-EBI 长期基础设施 原域名已重定向,历史链接需维护
综合 7.6 索引层做得扎实,数据层依赖于上游 见右列

这个分数要结合 7.0 读。它在索引层的表现接近上限,在数据层的表现受制于上游,两者平均下来得到 7.6。如果有人拿它去做「分子数据覆盖率」的评估,会得到一个远低于 7.6 的结论,那是因为问的问题不在它擅长的层。

7.2 三个结构性短板

第一是别名归一不彻底。这是本文开头就点明的主轴,也是它最核心的功能承诺里最没兑现的一块。库内确实有 Model ID,确实有统一标题,但这两样都不构成跨机构的别名映射。同一模型在不同机构各有一条记录,系统不会自动合并。Has Related Models 字段只有是/否,没有指向。

第二是数据可用性判断无法批量化。Dataset Available 字段的取值自由度,使得任何自动化筛选都要附带一个人工判读环节。这在规模上万条时不算致命,但它决定了一件事:这个库不适合做需要反复重算的自动化 pipeline,适合做一次性的候选集构建。

第三是计数口径不自洽。集合总数、类型过滤、可用性过滤三个数之间的关系,官方没有给出说明文档。按 PDX 过滤得到约占六成七,剩下三成三是什么,要么是其他模型类型,要么是类型字段未填,官方页面未说明。这个空白本身不算错,但它意味着任何引用这个库规模数字的人,都要自己说明用的是哪个口径。

7.3 已知的、可枚举的偏差

偏差 表现 影响范围 可否缓解
机构偏斜 大型联合体贡献占比高 全库 不可,取决于提交意愿
癌种偏斜 结直肠、肺、胰腺、乳腺四类占多数 全库 可通过检索绕开
分子数据偏斜 有数据的集中在联合体模型 数据层 不可,取决于经费
分期格式偏差 三种格式混用 亚组分析 可自建归一规则
族裔字段偏差 大量 Not Provided 人群分析 不可
儿科模型稀少 Paediatric 为 Yes 的占比低 儿科研究 需去专门库
时间偏差 新提交记录集中在近两年 时间序列分析 可读 release_date
地域偏差 北美与欧洲机构为主 全球代表性 不可

第三行与第八行是这套数据在人群层面的根本限制。模型的供者以欧美人群为主,族裔字段又大量缺失,这意味着任何基于这批模型的药物反应外推,都隐含了一个未经检验的人群假设。这个偏差不是目录方造成的,而是全球 PDX 建库格局的直接反映。

7.4 外部验证矩阵

验证项 验证方式 结论 证据层级
集合计数 接口 totalHits 与 isTotalHitsExact 一万余条,声明精确 官方接口
类型计数 两种写法对照 写法不同结果相同 官方接口
字段结构 逐条打开两条记录 属性与子节两层并存 官方接口
字段取值自由度 读取具体记录 Dataset Available 非二值 官方接口
建库动机 论文摘要 各机构元数据标准不一 论文摘要
初始规模 论文摘要 2019 年报告近两千个模型 论文摘要
别名映射 未找到公开资源 未核实 未核实
许可统一性 逐条查看 两种声明并存 官方接口

第七行必须诚实标注。本文没有找到官方的别名映射文件,也没有找到系统性地把同一模型的跨机构记录合并起来的机制说明。这可能是能力问题,也可能是可发现性问题——文件存在但没有被列出。无论哪种,结论都只能是未核实。

7.5 它为何能持续:因为它站的是两家机构的公共基础设施位置

这类目录的存续,很少由使用量决定,更多由它在机构里的位置决定。CancerModels.org 由 EMBL-EBI 与 Jackson Laboratory 联合维护,前者是欧洲生物信息学的基础设施方,后者是美国的老牌模式动物机构。两家都不是靠这个项目谋生的商业体,项目挂在各自的数据服务序列里,属于「应该有人做、我们来做」的那一类。

这个位置带来两件事。一是抗中断能力强,单个课题组解散不会带走整个库;二是不以增长为目标,没有商业压力去刷记录数,也没有动力去为了完整性而降低门槛。从这个角度看,它存活下来的理由和它做不大的理由,是同一件事:它被当成公共品来维护,公共品的逻辑是够用就行,而不是越大越好。

这也解释了 §7.3 那些偏差为什么长期存在。它们不是待修的 bug,而是这套治理结构的必然产物。想改变覆盖率,需要改的是上游的资助安排,而不是这个库的代码。

§8 生态位与历史

8.1 从 PDX Finder 到 CancerModels.org

这个项目起初叫 PDX Finder,由两家机构在 2016 年前后开始合作建设。2019 年 1 月,《核酸研究》的数据库专刊刊出了它的系统介绍,DOI 为 10.1093/nar/gky984,PMID 30535239。那篇摘要把问题说得很直白:PDX 由学术实验室、多机构联合体和合同研究组织分散产生,各方用的元数据标准不同,这使得「找到与研究问题相关的 PDX 模型」成为一件难事。它给出的方案是:按一个事先制定好的社区最小信息标准,把模型属性归一后整合,让跨来源的一致检索成为可能。

摘要里报告当时的规模是近两千个模型,覆盖多个癌种,主要来源是 Patient-Derived Models Repository、PDXNet 与 EurOPDX 三个大型资源。这三个名字在今天的库内仍然能看到,说明整合的主体框架从建站起就没变过,变的是规模与命名。

改名发生在之后几年。从 PDX Finder 到 CancerModels.org,域名也换了,原域名现在已不可访问。改名的动机可以从新名字本身读出:去掉 PDX 这个限定,意味着收录范围不再自我限制在一种模型类型上。库里 Model type 字段留有其他取值的可能,正是这一意图的痕迹。

8.2 名字变化背后的定位变化

「Finder」这个词暗示的是一个检索工具,重点是帮人找到东西。「Models.org」这个组合暗示的是一个领域资源,重点是这个领域本身的公共资产。这个措辞变化对应着一个实际的功能扩展:早期版本更多是链接的集合,现在的版本把供者描述、宿主信息、分发状态都纳入了结构化字段。

但扩展是有边界的。它没有向数据仓库方向走,分子数据始终只存链接。这个边界的选择值得留意,因为它把「这个库能长多大」的天花板钉死了——记录数可以继续涨,但只要分子数据不入库,它就永远不能单独支撑一次完整的建模流程。

8.3 它在 PDX 生态里的位置

PDX 领域的信息基础设施大致分三层。最下层是各机构自己的模型管理系统,负责维持小鼠品系、记录传代、管理分发。中间层是联合体级的协调平台,负责统一表征、统一数据格式。最上层是跨联合体的目录,负责让人知道哪里有模型。CancerModels.org 在最上层。

层级 典型主体 主要职责 数据粒度
机构层 单个实验室、CRO 维持品系、分发模型 单模型
联合体层 PDMR、EurOPDX、PDXNet 统一表征、统一格式 联合体内
目录层 CancerModels.org 跨来源检索 全领域

这个分层有一个隐含的层级依赖:上层的质量由下层的质量决定。目录里的每条记录,都是某个机构层或联合体层已经整理过的产物。下游之所以能给出一致的外观,是因为上游已经做过一次整理。这也意味着,凡是上游没整理的,目录层也无从整理。

8.4 与商业模型库的关系

PDX 领域还有一批商业机构在提供模型服务,合同研究组织是其中的主力。库内能看到这类机构的记录,Supplier type 字段会标明 Commercial 或 Academic。商业记录的加入让目录的覆盖更完整,但也带来了一个新问题:商业模型的描述往往更简略,因为详细的表征数据被当作商业资产。

供应商类型 记录特征 数据可得性 分发条件
学术机构 描述较全,联系人明确 分子数据看经费 需签材料转让协议
国家级资源 表征统一,字段规整 数据较全 有公开申请流程
商业机构 描述较简,字段少 部分仅内部 按商业合同
联合体 表征与格式最统一 数据最全 按联合体政策

这四类记录在同一套字段下并排出现,检索时看不出来源类型的差异,除非打开记录看 Supplier type。做候选集筛选时若忽略这一层,很容易出现「筛出十个模型,七个拿不到」的情况。

§9 使用指引

9.1 三种角色的路径

临床前研究者的路径。起点是癌种与取材部位,不是字段名。先用组织学关键词加类型过滤拉一个宽候选,再用 Available for distribution 与 Provider URL 缩到能拿到模型的,最后逐个打开看肿瘤类型与治疗史。三步走完通常会剩下原来的十分之一到五分之一。这个缩减比例不是坏事,它把「看起来可用」和「实际可用」分开了。

生物信息学工程师的路径。起点是数据结构,不是内容。先把全量元数据拉下来,然后枚举关键字段的取值,建一张取值对照表,再据此写筛选规则。这个顺序不能反——先看内容再定规则,会在遇到第三个奇异取值时推翻前面的全部工作。

药物研发团队的路径。起点是药效数据的有无,不是模型数量。库内能直接给出响应数据的是少数,多数要回来源库或论文去找。现实做法是先圈定几个大型来源库,在各自平台内部完成响应数据检索,再用本目录做跨库核对。反过来做会事倍功半。

9.2 一条最短的可用流程

从零到一个可分析的候选集,最短的路径是五步。下面这段脚本把五步串起来,每一步只做一件事。

#-- 五步:取总数、取候选、筛可得、筛有数据、导出
import json
import time
import urllib.parse
import urllib.request

BASE = "https://www.ebi.ac.uk/biostudies/api/v1"

def api(path, **params):
    url = f"{BASE}/{path}"
    if params:
        url += "?" + urllib.parse.urlencode(params)
    with urllib.request.urlopen(url, timeout=60) as r:
        return json.load(r)

#-- 第一步 确认口径
head = api("CancerModelsOrg/search", pageSize=1)
print("总数", head["totalHits"], "精确", head["isTotalHitsExact"])

#-- 第二步 拉候选摘要
q = '"Lung" AND Model type:PDX'
hits = []
for p in range(1, 4):
    r = api("CancerModelsOrg/search", query=q, pageSize=100, page=p)
    hits.extend(r["hits"])
    time.sleep(0.3)
print("候选", len(hits))

#-- 第三步 逐条取属性并筛可得性
def flat(sec, out=None):
    if out is None:
        out = {}
    if isinstance(sec, list):
        for x in sec:
            flat(x, out)
        return out
    for a in sec.get("attributes") or []:
        out.setdefault(a.get("name"), []).append(a.get("value"))
    subs = sec.get("subsections") or []
    if isinstance(subs, dict):
        subs = [subs]
    for s in subs:
        flat(s, out)
    return out

kept = []
for h in hits:
    d = flat(api(f"studies/{h['accession']}")["section"])
    dist = (d.get("Available for distribution") or [""])[0]
    data = (d.get("Dataset Available") or [""])[0]
    if dist == "Yes":
        kept.append({"acc": h["accession"], "id": d.get("Model ID", [""])[0],
                     "data": data, "hist": d.get("Histology", [""])[0]})
    time.sleep(0.2)
print("可分发", len(kept))

#-- 第四步 按数据字段分组,人工判读后再定规则
from collections import Counter
print(Counter(k["data"] for k in kept).most_common())

#-- 第五步 导出
with open("pdx_candidates.json", "w", encoding="utf-8") as f:
    json.dump(kept, f, ensure_ascii=False, indent=1)

这段脚本的关键不在代码,在第四步。第二、三步都可以自动化,第四步必须人工看一眼分布再决定哪些取值算「有数据」。把它放在导出之前,是为了让规则显式化。

9.3 引用方式

学术引用上,通常引两样东西:原始论文,以及具体记录的 accession。论文引用用 2019 年《核酸研究》那篇,DOI 为 10.1093/nar/gky984。记录引用写 BioStudies accession,形如 S-CMO 加五位数字,并注明查询日期,因为记录可能被更新。

引用对象 写法 说明
平台整体 论文 DOI 加官方站点 说明数据来源
单条记录 accession 加查询日期 记录会更新
一批记录 检索式加查询日期 检索式可复现
来源模型 原始机构发布号 学术惯例更认这个

第三行值得强调。引用一批记录时,光写「来自 CancerModels.org」是没法复现的,因为不同筛选条件得到的集合完全不同。把完整的检索式写进去,是让结论可核对的最低成本做法。

§10 AI 使用声明

事项 说明
内容生成 由 AI 助手依据本次查询结果起草,经编辑流程整理
数据获取 通过 BioStudies 公共检索接口与会话式请求获得,未经人工逐条录入
数字核对 所有计数均标注查询日期,口径随文说明
未核实项 别名映射文件的公开性、部分字段取值的完整枚举
事实边界 未做全量下载,未做逐条遍历统计
图像 封面为生成式插画,不含任何真实影像
更新机制 计数类数字带查询日期,日期之后的变化不在本文范围内
责任声明 本文不构成任何临床或研发决策依据

常见误读(避坑清单)

常见说法 实际 证据
编号一样就是同一个模型 编号只在机构内唯一,跨机构同名要视为两个模型 库内 Model ID 直接沿用机构命名,标题第一段的机构缩写才是区分依据
不同论文里名字一样代表同一模型 论文命名习惯按课题组走,同名字常是巧合 库内同名记录分属不同 Organization 子节并不罕见
有一万条记录就有一万个模型 同一模型可能因多次提交得到多条记录 accession 由提交动作分配,与模型本体无关
Dataset Available 为 Yes 才算有分子数据 该字段取值非二值,很多有数据的填的是别的描述 精确过滤 Yes 只得到 13 条,与印象严重不符
它存了分子数据 它只存链接,原始文件在来源库 记录里只有 Provider URL 与外链,无文件下载项
许可统一是 CC0 逐条声明,同一批记录里两种声明并存 单条记录能看到 License 取值为 CC0 或 EMBL-EBI
可以用来做药物响应预测的训练集 标签方案与终点不统一,且多数模型无分子数据 响应数据来自各来源库,字段不在主表
可以直接当临床前结论 是免疫缺陷小鼠上的异种移植,缺免疫微环境 宿主品系字段显示为免疫缺陷背景
它覆盖了全球 PDX 以北美与欧洲机构为主,族裔字段大量缺失 供者族裔多处为 Not Provided
搜到的记录就能拿到模型 可检索、可分发、有数据是三件独立的事 Available for distribution 与 Dataset Available 是两个字段

FAQ

Q1 这和 PDX Finder 是同一个东西吗?
是。PDX Finder 是它早期的名字,后来改名为 CancerModels.org,原域名已不可用。库里记录的建站历史与 2019 年那篇论文能对上。

Q2 为什么记录数只有一万多?PDX 不是应该有更多吗?
全球实际在用的 PDX 数量确实更多,但收录依赖提交。库不做抓取,机构不提交就收不进来。这也是为什么它一直呼吁模型产生方主动参与。

Q3 一个模型在库里会不会有多条记录?
可能。accession 由提交动作分配,同一模型被不同项目各提交一次,就会有两条。做去重时不能只看 accession。

Q4 能不能按患者 ID 把模型和患者端数据连起来?
不能。库内不存患者标识,只有性别、年龄段、族裔这类粗粒度描述。想连患者端数据,只能靠机构加癌种加年龄段的模糊匹配,可靠性有限。

Q5 分子数据到底在哪?
在来源库。记录里的外链指向对方的模型页面或数据门户,具体文件要通过对方的检索系统或申请流程获取。

Q6 给药响应数据是怎么回事?
部分来源库在提交时带了响应数据的链接,格式是各库自己的。库内没有统一的响应本体,字段层面也不以原始测量值入库。

Q7 界面抓不到内容怎么办?
前端是渲染式的,直接抓 HTML 得到的是空壳。改用 BioStudies 的公共接口,路径是 api/v1 加集合名。

Q8 收录类器官或者细胞系吗?
Model type 字段留有扩展可能,但库内主体是 PDX。检索层对 PDX 与 PDOX 做了同义处理,说明类型字段的取值管控不严。

Q9 能不能用在论文里做模型清单?
可以,但要写明 accession 与查询日期,并说明筛选条件。清单的构成完全取决于检索式,不写检索式的清单无法复现。

Q10 这个库会不会某天关掉?
它挂在 EMBL-EBI 与 Jackson Laboratory 的公共基础设施序列里,属于机构级项目而非单一课题组项目,中断风险相对低。但域名已经换过一次,历史链接需要自己维护。

Q11 有没有批量下载的官方文件?
本次查询未找到官方打包下载入口,官方站点以检索为主。全量数据要通过接口自建导出。

Q12 用它做训练集,最大的坑是什么?
把元数据记录数当成样本数,把 Dataset Available 的缺失当成有数据。这两步任何一个出错,后面全部结论都要重做。

术语表

术语 英文 含义
患者来源异种移植 PDX 把患者肿瘤组织移植到免疫缺陷小鼠体内传代形成的模型
PDOX PDOX PDX 的另一种写法,库内检索层视为同义
类器官 organoid 体外三维培养的细胞团,与本库主体不同
模型标识 Model ID 产生机构给模型的命名,机构内唯一
存档编号 accession BioStudies 分配给每次提交的容器编号
生物研究档案 BioStudies EMBL-EBI 的多类型研究数据容器系统
联合目录 union catalog 只记录馆藏位置、不持有实物的目录
别名归一 alias harmonization 把同一实体的不同写法合并到一个标识下
最小信息标准 PDX-MI PDX 领域规定的模型描述最少字段集
受控词表 controlled vocabulary 限定取值的字段字典
本体 ontology 带层级的受控术语体系
组织学 histology 肿瘤的病理学分型
原发部位 primary site 肿瘤最初发生的器官
取材部位 collection site 实际取样所在位置,可能是转移灶
原位移植 orthotopic 把肿瘤移植到对应器官内
皮下移植 subcutaneous 把肿瘤移植到皮下
传代数 passage 模型在小鼠体内传代的次数
宿主品系 host strain 承载移植瘤的小鼠品系
免疫缺陷 immunodeficient 缺少部分免疫功能的品系背景
分发状态 distribution 模型能否对外提供
数据可用性 dataset availability 记录是否关联分子数据
材料转让协议 MTA 获取生物材料的法律文件
患者来源模型库 PDMR 美国 NCI 下属的 PDX 资源
欧洲联合体 EurOPDX 欧洲的 PDX 协作网络
美国联合体 PDXNet 美国的 PDX 协作网络
合同研究组织 CRO 提供外包研发服务的商业机构

附录 A:核心数字核对表

项 值 来源 来源层级
集合记录总数 10348 BioStudies 检索接口 totalHits 官方接口
PDX 类型记录数 6856 同一接口加类型过滤 官方接口
PDOX 写法命中数 6856 同一接口换写法 官方接口
结直肠相关记录 1026 关键词加类型过滤 官方接口
肺相关记录 934 关键词加类型过滤 官方接口
胰腺相关记录 590 关键词加类型过滤 官方接口
乳腺相关记录 551 关键词加类型过滤 官方接口
明确标注有数据 13 可用性字段等值过滤 官方接口
2019 年报告规模 近两千 论文摘要 论文摘要
建站时间 2016 年前后 论文与站点信息互证 二级推断
首发期刊 Nucleic Acids Research 2019 论文元数据 论文摘要
论文 DOI 10.1093/nar/gky984 论文元数据 论文摘要
论文 PMID 30535239 论文元数据 论文摘要
别名映射文件 未找到 本次检索 未核实

附录 B:数字的来源分层

官方页面与接口层。集合总数、类型计数、四个癌种计数、可用性等值过滤计数,全部来自 BioStudies 的 CancerModelsOrg 集合检索接口,查询时间为 2026 年 10 月 26 日。这一层数字可复现,复现时须带上同样的查询字符串。

论文摘要层。2019 年报告近两千个模型、覆盖多个癌种、三个主要来源库,来自《核酸研究》那篇摘要。这一层数字反映的是建站初期状态,与今天的规模差了五倍,引用时必须带上年份。

未核实层。别名映射是否存在公开文件、分子数据覆盖率的真实比例、Collection Site 字段的完整取值分布。这三项本次没有找到可核实的来源,任何引用都应标为未核实,不能凭印象补数字。

附录 C:完整参考文献

  1. PDX Finder: A portal for patient-derived tumor xenograft model discovery. Nucleic Acids Research, 2019. doi:10.1093/nar/gky984. PMID 30535239. PMC6323912.
  2. PDX Finder: A Portal for Patient-Derived tumor Xenograft Model Discovery. bioRxiv 预印本, 2018. doi:10.1101/291443.
  3. CancerModels.org 官方站点,检索日期 2026-10-26。
  4. EMBL-EBI BioStudies CancerModelsOrg 集合检索接口,检索日期 2026-10-26。
  5. EMBL-EBI BioStudies 单条记录接口,记录 S-CMO10361 与 S-CMO5794,检索日期 2026-10-26。

附录 D:任务代码

跑一次全量枚举所需的最小脚本,放在这里以便整段复制。它做三件事:确认口径、枚举可用性字段取值、按癌种分组计数。

#-- 一次性跑完:口径、取值枚举、癌种分组
import json
import time
import urllib.parse
import urllib.request
from collections import Counter

BASE = "https://www.ebi.ac.uk/biostudies/api/v1"

def api(path, **kw):
    url = f"{BASE}/{path}?" + urllib.parse.urlencode(kw)
    with urllib.request.urlopen(url, timeout=60) as r:
        return json.load(r)

head = api("CancerModelsOrg/search", pageSize=1)
print("总数", head["totalHits"], "精确", head["isTotalHitsExact"])

types = {}
for t in ("PDX", "PDOX"):
    types[t] = api("CancerModelsOrg/search", query=f"Model type:{t}", pageSize=1)["totalHits"]
print("类型", types)

cancers = {}
for k in ("Colorectal", "Lung", "Pancreatic", "Breast"):
    q = f'"{k}" AND Model type:PDX'
    cancers[k] = api("CancerModelsOrg/search", query=q, pageSize=1)["totalHits"]
print("癌种", cancers)

q = 'Model type:PDX'
accs = []
for p in range(1, 6):
    r = api("CancerModelsOrg/search", query=q, pageSize=100, page=p)
    accs.extend(h["accession"] for h in r["hits"])
    time.sleep(0.3)

def flat(sec, out=None):
    if out is None:
        out = {}
    if isinstance(sec, list):
        for x in sec:
            flat(x, out)
        return out
    for a in sec.get("attributes") or []:
        out.setdefault(a.get("name"), []).append(a.get("value"))
    subs = sec.get("subsections") or []
    if isinstance(subs, dict):
        subs = [subs]
    for s in subs:
        flat(s, out)
    return out

vals = Counter()
for a in accs:
    d = flat(api(f"studies/{a}")["section"])
    vals[(d.get("Dataset Available") or [None])[0]] += 1
    time.sleep(0.2)
for k, n in vals.most_common(10):
    print(repr(k), n)

附录 E:如果只记五件事

序号 要记住的事
一 它是索引,不是仓库;数据在来源库,它只给指针
二 编号只在机构内唯一,跨库连接要用机构缩写加 Model ID
三 别名归一没有做到跨机构自动合并,依赖提交方申报
四 数据可用性字段不是布尔量,直接按 Yes 过滤会严重漏检
五 可检索、可获取、有数据是三件独立的事,做筛选时分三步

编辑说明

本篇的事实基础是 2026 年 10 月 26 日对 BioStudies 公共检索接口的会话式查询,加上 2019 年《核酸研究》论文摘要。查询覆盖了集合计数、类型计数、四个癌种的计数、可用性字段的等值过滤计数,以及两条完整记录(S-CMO10361、S-CMO5794)的结构。没有做全量下载,也没有对全部记录逐条统计,因此凡涉及「比例」「分布」的表述都限定在已查询的样本范围内。官方站点为前端渲染,直接抓取仅得到容器页面,站点自身的统计数字未能通过 HTML 获取,正文中的计数一律标注为接口来源。别名映射的公开性、Collection Site 字段的完整取值域、给药响应的实际覆盖率三项未能核实,已在附录 B 单列。若后续需要更高精度的统计,正确做法是走 §5.3 的分页拉取,并对 Dataset Available 字段先做取值枚举再定筛选规则。


相关数据集导航

以下为站内 AI-Ready 数据集百科中与本词条共享多个主题标签的相关数据集,按相关度降序排列:

  • ccle — 共享标签:肿瘤学 / 药物发现与化学 / 基因组学与多组学
  • cellminer-cdb — 共享标签:肿瘤学 / 药物发现与化学 / 基因组学与多组学
  • depmap-ccle — 共享标签:肿瘤学 / 药物发现与化学 / 基因组学与多组学
  • genomics-england-100k — 共享标签:肿瘤学 / 基因组学与多组学
  • prism — 共享标签:肿瘤学 / 药物发现与化学
  • target — 共享标签:肿瘤学 / 基因组学与多组学
  • nci-almanac — 共享标签:肿瘤学 / 药物发现与化学
  • aacr-genie — 共享标签:肿瘤学 / 基因组学与多组学
  • intact — 共享标签:药物发现与化学 / 基因组学与多组学
  • msigdb — 共享标签:药物发现与化学 / 基因组学与多组学

导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

返回 AI-Ready 数据集