个体化药物不良事件库 PersADE — 从未有一种药对所有人一样

1123 万患者报告 / 9,828 种药物 / 19,991 个 ADE 术语

来源 https://persade.idrblab.net发布时间: 2026-10-26最后更新: 2026-10-26 阅读 24
个体化药物不良事件库 PersADE — 从未有一种药对所有人一样

信息速览

数据集名称个体化药物不良事件库 PersADE — 从未有一种药对所有人一样
数据类型元数据,本体映射,文献引用,人工标注
规模11,500,749 名患者 / 37,923,032 条患者层记录
接入方式https://persade.idrblab.net
AI 就绪度

药物警戒运转了六十年,主战场一直是人群级信号检测:把某个药与某个事件的报告挑出来计数,看这个数在全部报告里是否偏高,PRR、ROR、IC 算的都是同一件事。它的成就真实——沙利度胺那样的灾难再难重演,很大程度上靠它能及早把异常报告聚起来。

但它回答不了另一个问题。同一个药,多数人没事,少数人出严重事件,人群级指标只能告诉你「有肝损信号」,不能说出事的是哪一类人、在哪种合并用药或什么肾功能水平下。往下推一层需要个体变量:年龄、性别、体重、基础疾病、给药途径、剂量、基因型。

这一层数据极其稀缺。单中心的严重病例一年可能只有几十例,做不了分层;多中心合并又受隐私法规掣肘。PersADE 是少数正面处理这件事的尝试:把两个北美自发上报系统的患者层记录抽出来重排,再叠上药物-蛋白-ADE 的机制链。一屏速览见下表。

§1 它要解决的具体麻烦

1.1 人群级信号检测能发现「有」,发现不了「谁」

药物警戒的标准工具是一条流水线:报告进入数据库、编码成术语、按药-事件配对计数、用 2×2 表做卡方类的检验。这套流水线最擅长的事是「在不该出现的地方发现了一个不该那么高的数」。反过来说,它最不擅长的事,是把一个已经发现的信号再切开。

原因很朴素。做分层就需要每一层有足够的样本,而罕见不良事件本身就是长尾。假设某药每年在全国范围内产生一百多份肝损报告,按年龄段切四层,每层二三十份;再按性别切,每层十几份;再按给药途径切,每层个位数。到这一步,任何统计量都不再有意义。真实世界里,做到「药 × 事件」这一层已经很不容易,再往下一层,样本量就会塌方。

PersADE 的做法不是自己造一个新队列,而是把已经存在的两份报告数据尽量榨出结构。加拿大的报告里本来就带年龄、性别、体重、给药途径与结局标记,FDA 的报告里也有同样的字段。把这些字段从「附注」提升为「维度」,同一批报告就能按不同切法重组。官方页面把它描述为「从原始报告重建一行一药的扁平表」,这个描述是准确的:它没有新增任何病人,只是把原有字段之间的关系显式化了。

这条路的代价同样明显。报告里有什么,它才能分什么。没有肾功能、没有合并用药剂量、没有基因型,这些维度就只能空着,或者从别的库里补一小块。

1.2 三条常见的错误期待

第一次接触这个库的人,往往带着三种预期,它们都会在实际使用中碰壁。

第一种是「这是一个更大的 FAERS」。它的患者层确实包含 FAERS 报告,但同时混入了加拿大警戒计划的报告,还做了术语重映射与评分重算。它既不完整覆盖 FAERS,也不能替代其原始表结构。拿它去复现已发表的信号研究,结果会对不上。

第二种是「个体化等于精准医疗」。这个词在这里窄得多:指把患者属性作为分层变量,不是为每个人算预测风险。库里没有个体层风险模型,也没有「某位 68 岁、70 公斤的女性用这个药的风险是多少」这样的输出,只有某一类人群的报告分布与关联强度。

第三种是「有评分就能排序用药安全性」。0–10 分衡量的是这条关联有多可信,不是这个药有多危险。两者的混淆很常见,后果也严重——后文的常见误读会单独展开。

1.3 它能给到的与给不到的

你的需求 它给的 够不够
查某个药在某个年龄段的不良事件报告构成 按年龄段分箱的计数与占比,附带结局标记 够,且是这个库最扎实的一层
查某个药的脱靶谱与可能的机制通路 药物-蛋白互作(含定量亲和力)、蛋白-ADE 关联、KEGG 与 MSigDB 通路映射 够做机制假设,不够做因果证明
比较口服与静脉给药的 ADE 谱差异 52 类给药途径下的关联与评分 够,但须注意途径字段的缺失率
判断某个药是不是比另一个药更安全 每个药各自的评分与严重度分布 不够,没有共同分母,两药无法直接比
找一个基因型-药物-不良事件的例子 PharmGKB 覆盖的少数 ADE 只够当例子,不够做全基因组筛查
把它喂给模型做药物安全预测 正样本、机制特征、相似度推断 有条件可用,但缺负样本定义与时间切分指引
复现某篇基于 FAERS 的信号论文 两个来源合并后的重算结果 不够,口径不同,数值对不上

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

你的处境 它能帮你什么 下一步该做什么
手上有一份候选药物清单,想在被要求做临床前评估前,先看看哪些组合在真实报告里反复出现 批量查个体化关联,筛出高频且高信度的组合 把筛出的组合拿去和 SIDER、OFFSIDES 的标签来源做交叉,剔除纯标签推测
写论文时要给「老年患者出血风险更高」这句话找个体层面的证据 分年龄段的报告构成,可引用具体药物与具体 ADE 明确写出这是报告构成不是发生率,并补一段自发上报的局限说明
在做药物安全模型,需要一条「药 → 靶点 → 事件」的特征链 互作带亲和力、关联带文献号 用三元组构造图特征,同时保留文献计数作为可信度权重
药理课上要讲清楚为什么同一个药对不同人后果不同 去激励与再激发报告的切片、按性别与体重的分布 优先选高信度关联做案例,避免用低频偶发事件当典型
手上有几家医院的不良事件数据,想先看看公开数据长什么样再设计自己的字段 患者层字段的完整清单与编码方式 照它的字段粒度设计内部表,补上它缺的肾功能与合并用药剂量

1.5 一句话判断

它是一个「把自发报告拆得更细」的工程成果,而不是一个解决了样本量困境的统计方案。想知道某一类人在某种用药方式下被报告了什么,它很好用;想知道这类人真实的发作概率,它给不了。

1.6 不适合谁:四条边界

第一,不适合算任何形式的发生率。 无论你想要的分子分母是「十万分之一」还是「千分之三」,这个库都没有分母。它没有各药的处方量,没有暴露人年,也没有一般人群的基线事件率。任何从它算出来的「率」,本质都是「报告里的占比」。

第二,不适合做药物之间的安全性排名。 排名需要共同分母与共同上报倾向,两个条件它都不具备。「新药被报道得多」和「新药危险」在上报体系里长得一模一样。

第三,不适合做因果判定。 因果评估要时间关系、去激励、再激发与生物学合理性四条同时成立。库里保留了去激励与再激发的报告切片,但没有为每条关联输出因果判断,也没有逐例药师裁定。

第四,不适合当商用产品的底层数据。 官方写明仅限学术用途,商业使用要单独授权。把它打包进商业模型训练集或产品,属于越界。

§2 数据构成与规格

2.1 三层架构:患者层、药物层、表型层

PersADE 在论文里把自己的组织方式称为三层粒度框架,官方页面沿用同一说法。三层不是三个数据库,而是三种切分同一批关联的角度。

患者层回答「谁」,把年龄、性别、体重、疾病背景从报告里提出来,变成可筛选的维度。药物层回答「怎么给的」,把给药途径、剂型、剂量作为变量。表型层回答「为什么用这个药」,把适应症与药物、ADE 串起来,让同一个药在不同适应症下的不良事件谱可以被分开看。

官方页面在表型层举了两个例子。阿司匹林用于心血管预防时,不良事件集中在上消化道紊乱、轻度出血倾向与偶发出血性卒中;用于抗炎镇痛时,谱系变成消化道出血风险升高、伴随耳鸣(官方给出 30%)、外周水肿与长期使用相关的眼部疾病。地塞米松用于黄斑水肿短期治疗时,主要是眼压升高(官方称 25–35% 需降眼压药)与白内障(官方称有晶状体眼三年发生率 67.9%);用于多发性骨髓瘤长期治疗时,变成精神症状(官方称 5–10%)与骨坏死风险升高。

这两个例子说明:没有适应症字段时,两组事件会被混成一条谱;有了表型层,它们能分开。这是「个体化」在这个库里的第二个维度——不只看人,还看这个人为什么吃这个药。值得注意的是,官方给的这几个数字都来自外部文献而非库内统计,它们的作用是说明「分开看之后差异有多大」,不是证明这个库能复算出这些比例。

2.2 字段与记录的完整字典

下载页列出 19 个文件,分核心库文件、关联评分与分析、通路数据三类。下表列出主要文件与其内容,规模以官方页面标注为准。

文件 官方名称 官方标注大小 内容 行结构
ADE_report.txt ADE Report Database 4.22 GB 患者层报告,一行一个「报告 × 药物 × ADE」 制表符分隔,无表头
ADE_Information.txt ADE Information 46.40 MB ADE 术语,含 CUI、MeSH、树号、定义 制表符分隔,无表头
formatted_tree.txt ADE Tree Structure 709 KB ADE 层级树,对应 10 个主类、24 个亚类、79 个具体类型 制表符分隔
MeSH_data.txt MeSH Hierarchical Data 248 KB MeSH 层级数据 制表符分隔
drug_all.txt Complete Drug Database 13.80 MB 药物主表,InChIKey 主键,含名称、SMILES、交叉引用 制表符分隔,无表头
DTI.txt Drug-Protein Associations 14.95 MB 药物-蛋白互作 制表符分隔
Target.tsv Protein Database 13.61 MB 蛋白主表,UniProt 主键,含序列与功能分类 制表符分隔,无表头
AT.csv Adverse Events - Protein associations 879 KB ADE-蛋白关联,带文献号 逗号分隔,三列
INDI_UMLS.txt Disease UMLS Mapping 1.66 MB 适应症与 UMLS 映射 制表符分隔
CCombined_Results_with_scores.txt General Drug-ADE Association Scores 1.06 GB 通用关联评分表 制表符分隔,无表头
CCombined_Results_route_with_scores.txt Route-specific Scores 704.90 MB 按给药途径切的评分表 同上
CCombined_Results_form_with_scores.txt Formulation-specific Scores 495.07 MB 按剂型切的评分表 同上
CCombined_Results_INDI_with_scores.txt Disease-specific Scores 571.65 MB 按适应症切的评分表 同上
uniprot_pathway.txt Protein - Pathway Mapping 32.16 MB 蛋白到通路映射 制表符分隔
Pathway.txt Pathway Database 1.56 MB 通路主表 制表符分隔

患者层文件因为没有表头,字段含义只能从内容与论文描述反推。下表是我从文件前若干行归纳出的列序,属于推断而非官方文档,使用前应当自行校验。

列序 推断含义 示例 备注
1 报告流水号 F4214534 同报告多行共享,前缀疑标识来源
2 药物 InChIKey MUZQPDBAOYKNLO-RKXJKUSZSA-N 对应 drug_all.txt 第一列
3 ADE 的 UMLS CUI C0013221 对应 ADE_Information.txt 第一列
4 给药途径 ORAL / INTRAVENOUS / 空 大小写不统一,部分记录为空
5 疑似的剂型或剂量类别码 D / U 单字母码,语义未在页面说明
6 疑似的剂型或剂量类别码 D / U 与第 5 列成对出现
7 8 事件发生日期 2001-01-01
9 性别 F / M 单字母
10 年龄 45 YR 字符串,带单位后缀
11 体重 200 LB 字符串,磅为单位,缺省为空
12 适应症或用药指征(推断) 空 大量为空
13 结局标记 DE / `HO\ OT`
14 未用列 空 前若干行全空
15 未用列 空 同上
16 商品名或报告用名 ZITHROMAX 原样保留,未做规范
17 ADE 原文 HEPATIC FAILURE 与第 3 列的 CUI 对应

年龄写成 45 YR 这样的字符串,体重写成 200 LB,这两点在使用时最容易踩坑:前者意味着「按年龄段分层」是库作者做好的分箱,你无法拿到精确的连续年龄;后者意味着单位是磅而不是公斤,需要换算。

2.3 一条记录的完整解剖

把患者层文件里的一条记录拆开看,能看清这个库的信息密度到底有多高。下面这一段取自文件开头附近,属于同一报告号的三行。

#-- 截自 ADE_report.txt,同一报告号 F4261678 的前若干行,列间以制表符分隔
F4261678  MQTOSJVFKKJCRP-BICOPXKESA-N  C0009676  ORAL  U  U     2001-01-01  F  52 YR  200 LB    HO|OT      ZITHROMAX  DISORIENTATION
F4261678  MQTOSJVFKKJCRP-BICOPXKESA-N  C0006277  ORAL  U  U     2001-01-01  F  52 YR  200 LB    HO|OT      ZITHROMAX  BRONCHITIS
F4261678  MQTOSJVFKKJCRP-BICOPXKESA-N  C0085605  ORAL  U  U     2001-01-01  F  52 YR  200 LB    HO|OT      ZITHROMAX  HEPATIC FAILURE
F4261678  MQTOSJVFKKJCRP-BICOPXKESA-N  C1443060  ORAL  U  U     2001-01-01  F  52 YR  200 LB    HO|OT      ZITHROMAX  FEELING ABNORMAL
F4261678  MQTOSJVFKKJCRP-BICOPXKESA-N  C0085605  ORAL  U  U     2001-01-01  F  52 YR  200 LB    HO|OT      ZITHROMAX  HEADACHE

这段数据能读出五件事。一,这是 52 岁女性、体重两百磅、口服给药、发生精神紊乱等若干事件的报告。二,结局标记 HO|OT 是住院加其他,说明达到了需住院的严重度,没到死亡。三,肝衰竭(C0085605)在同一报告里出现两次,去重应当按报告号加 CUI 做。四,几个事件之间没有时间戳,先后无从得知。五,商品名写的是 ZITHROMAX,而此处记录的是一个 InChIKey,对应一个活性成分,两个名字被分开保留。

也读不出五件事。这 52 岁是精确年龄还是分箱中值;两百磅落在哪个分箱;她是否还用了别的药(同报告号下若有其他药会以另一批行出现,这段截取里看不到);肝衰竭是入院原因还是住院期间新发;U 这个字母码代表什么,官方页面没有释义。

合起来看,这个库的定位就清楚了:它把一条自发上报里能抽出的结构化字段抽干净了,抽不出来的东西——时间关系、因果判断、合并用药的剂量与起止——仍然空着。§4.4 的案例会回到同一份报告,从评分的角度再看一遍。

2.4 规模分布与三套口径

同一个库在不同页面上的数字并不完全一致,这不是错误,而是三套口径并存。使用前必须搞清楚自己引的是哪一套。

指标 论文摘要口径 官方统计页口径 备注
个体化药物-ADE 关联 406 万级 同一量级 两处一致,具体值见附录 A
通用药物-ADE 关联 220 万级 同一量级 两处一致
被评分的个体化关联 摘要在 406 万之外另给了一个更小的值,差约 15 万条未说明构成 未单列 评分覆盖率约九成六
药物-蛋白-ADE 三元组 约 19.5 万 About 页写约 20 万 存在两个口径,差额官方未说明
蛋白-ADE 关联 三万一千余条 同一量级 一致
药物-蛋白互作 十万八千余条 同一量级 一致
患者数 1150 万级 同一量级 一致
患者层记录 未在摘要给出 三千七百余万条 统计页独有
药物数 一万余个化合物 页面同时出现 9,828 与 10,235 两个口径,前者疑为有 ADE 关联的药物数
ADE 术语数 近两万个 同一量级 一致

这些差异本身就是一个可用信息:官方并没有做严格的单一口径管理,不同页面由不同时期的脚本生成。引用时应当写明「据官方统计页」或「据论文」,而不是笼统写「据官方」。遇到不一致时,正确的写法是「存在两个口径:A 是 X,B 是 Y,差额构成官方未说明」,而不是判定官方出错。

2.5 与同类资源的字段差异

药物安全领域已经有若干成熟的公开资源,PersADE 的位置只有在横向比较里才清楚。

资源 主键与编码 患者层变量 机制层 与 PersADE 的关系
FAERS 报告主键 + MedDRA 术语 有,但需自行从七个关系表重建 无 PersADE 的来源之一,PersADE 是它的加工产物
加拿大警戒计划 报告主键 有 无 PersADE 的另一个来源
SIDER 药物 + 标签术语 无 无 标签推导,被 PersADE 引用作补充
OFFSIDES 药物 + 事件 + 统计量 无 无 统计推断,被 PersADE 引用作补充
CTD 化学物 + 疾病 + 基因 无 有,含毒理通路 被 PersADE 引用作机制来源
ADReCS / ADReCS-Target ADE 层级 + 靶点 无 有,靶点映射 被 PersADE 引用作层级与靶点来源
PharmGKB 基因变异 + 药物 + 表型 部分 有,药物基因组学 被 PersADE 引用作基因层来源,覆盖 190 个 ADE

一句话概括:它的独特性不在于比谁的数据更多,而在于它是少数把「患者层变量」与「机制层」放进同一张表的公开资源。FAERS 有前者没后者,Open Targets 有后者没前者,SIDER 两样都没有但更干净。

§3 身份层:一个药物、一个事件、一个患者,怎么被唯一确定

3.1 三套标识符决定了你能不能把它接进现有流程

整个库的关联能力建立在三个主键上:药物用 InChIKey,事件用 UMLS CUI,蛋白用 UniProt 登录号。三者的取舍各不相同。

InChIKey 是固定长度的结构哈希,带立体化学信息。好处是跨库通用,PubChem、ChEMBL、DrugBank 都能对上;坏处是对盐型、水合物、异构体敏感——同一活性成分的不同盐会得到不同的键,而自发报告里写的往往是商品名或盐型名。论文说药物统一用 InChIKey,但同时保留 InChI 与 CAS 号作交叉引用,drug_all.txt 的第二、三列就是它们。

UMLS CUI 的选择更值得说。药物警戒的主流编码是 MedDRA,因为监管上报体系用它。PersADE 用的是 UMLS,交叉引用 MeSH。这带来一个直接后果:MedDRA 首选术语与这里的 CUI 之间需要一次映射,而 MedDRA 的许可条款对再分发限制很严——避开它可能是有意为之,也可能受许可所限,论文没有说明。

UniProt 登录号最没有争议。人源蛋白用 UniProt 主表,附带结构域、亚细胞定位与基因本体注释,这套体系几乎没有替代品。

3.2 「个体化」在最细的粒度上是什么样

官方说患者层可按年龄区间、性别、体重与给药途径筛选。落到字段层面,最细的粒度是一个报告号、一个年龄分箱、一个性别、一个体重分箱、一个途径、一组结局标记、若干条 ADE。这个粒度对应的统计单元不是「一个人」而是「一次报告」:一个人多次上报会有多个报告号,一份报告涉及多个药会展开成多组行。所以「1123 万患者」严格说指报告号的计数。把报告数当人数,会高估个体层面的样本量。

3.3 与 PharmGKB 的基因层接缝在哪里

PersADE 从 PharmGKB 引入了与 190 个 ADE 相关的遗传变异。这个数字放在近两万个 ADE 的分母上,覆盖率不到百分之一。

这不是疏漏,而是整个药物基因组学的现状:真正有临床证据的基因-药物-事件三联关系只有几百条量级,CPIC 的指南与 PharmGKB 的一级证据加起来远小于药物-ADE 的总量。

接缝很清楚:想用基因分层,只能在那 190 个 ADE 上做;其余两万个 ADE 只能靠年龄、性别、体重、疾病与用药参数。论文把「进一步整合基因型」写成未来方向,等于承认这一层是空的。

3.4 上游系统的身份与它自己的身份

两个上游都是监管级系统:加拿大的由加拿大卫生部运行,美国的由 FDA 运行,前者接收医院、企业、患者与文献来源的报告,后者接收企业强制上报与自愿上报。两者都是自发上报,受同一类偏倚支配。

它与它们的关系是「加工者而非替代者」:没有自己的上报入口、病例裁定流程与独立随访。数据全部产自上游,它做四件事——抽患者字段、统一术语、合并两个来源、叠加机制层。

所以它的局限几乎全部继承自上游:上游漏报它漏报,上游的上报受新闻影响它也受,上游没有分母它也没有。

§4 质量层:评分体系与高信度的那条边界

4.1 评分是怎么算出来的

流程分两步。第一步算关联:对每个药物-ADE 对做 Fisher 精确检验加 Bonferroni 校正判断显著与否,算 ROR 与 PRR 两个报告比值,用病例数代理临床证据强度。第二步把三个维度压成 0–10 分,分五步:原始分计算、关联强度整合、极值归一化、阈值截断、信度分生成。

严重度是另一套分数。五个结局类别赋权:需干预以防损害(RI)、住院(HO)、致残(DS)、危及生命(LT)、死亡(DE),权重 1 到 5,惩罚因子 5 到 1。用条件概率算每个组合在五种结局上的分布,乘以 ROR 的对数变换,切成五级:Minimal 到 Critical。

意图很清楚:把「多常被报告」(信度)与「报告里的结局多坏」(严重度)分开。两者不相关,一个药可以信度极低但一发生就是死亡,另一个可以信度极高但只是恶心。

4.2 高信度那一档为何只有百分之五

官方统计给出了三档的分布。个体化关联里,高信度约 31.6 万条,中信度约 134 万条,低信度约 406 万条。按比例算,高信度一档大约占百分之五出头的量级。

很多人第一次看到会怀疑评分体系坏了:五百万条里只有三十万条能用,剩下的怎么办?答案是用中档。高信度的门槛是三条同时成立——统计显著、报告比值够高、病例数够多。而药物-ADE 对绝大多数是低频的,一个药对一个罕见事件四年被报三次,三条里至少一条撑不住。这是罕见事件的本性,不是评分的缺陷。

论文与 SIDER 做了对比:PersADE 的高信度比例更低(5.1% 对 17.5%),绝对条数却多得多(92,406 对 14,462)。正确解读是比例低因分母大、绝对量多因覆盖广。把比例当质量指标比两个库,会得出反直觉的结论。

4.3 相似度推断:补出来的是「有依据的猜测」

对没有直接评分的药物-ADE 对,官方用 MACCS 指纹与 Tanimoto 系数(阈值不低于 0.7)找结构相似的化合物,按相似度加权平均其评分;ADE 一侧用 UMLS 语义相似度加权。这把「未知」的比例从 34.1% 压到 8.4%。

数字值得盯着看:补全后仍有近一成未知,而补进来的本质是「结构相似的药大概会有类似不良事件」。药理学上站得住——同类药共享靶点与脱靶谱——但它不是原始证据。论文说明两套推断扩展了 1,208 个 ADE 的覆盖。

使用时应当把直接评分与推断评分分开:前者能追到报告号,后者追不到具体病例。混在一起算指标会虚高。§4.4 的案例里那条记录属于前者,它的每一行都能回到同一个报告号上。

4.4 案例:一条记录里的四处断点

用一条记录把质量层的问题讲透,比抽象地讲偏倚有效。患者层文件开头附近有一条记录,52 岁女性、两百磅、口服某一抗生素,同一报告号下出现了精神紊乱、支气管炎、肝衰竭、感觉异常、头痛、腹泻、药物相互作用、肺炎等事件,结局标记为住院加其他。四处值得注意,每一处都对应一个质量问题。

一,肝衰竭与支气管炎、肺炎同现一份报告。后两者很可能是适应症而非不良事件。自发报告里适应症与事件写在同一张表上,上游有专门字段区分,但这个库抽事件列时没保留「这是适应症」的标记。不熟悉该药用途的人,会把这些行当不良事件计数。

二,肝衰竭与「药物相互作用」同现。这类报告通常涉及两个以上药物,但截取里只看到一个药物的 InChIKey。同报告号下是否有第二个药要全表检索,下载表没有提供「同报告药物清单」这个派生列。

三,结局标记是住院加其他,严重度会落在 Severe 或 Critical 档,但它不区分肝衰竭是否住院原因。评分用的是「报告里出现过的结局标签」,不是「该事件导致的结局」,两者差得很远。

四,同一 CUI 在同一报告里出现两次。去重必须按报告号加 CUI 做,否则计数虚高。

四点合起来:这条记录在库里贡献若干条关联,每条都有一个信度分与严重度分。这些分数度量的是「这份报告被如何记录」,不是「这位患者身上发生了什么」。§4.4 的案例用在这里,是提醒拿到评分表的人:分数背后是一条条格式不完全的报告,不是一份份结构化病历。

4.5 三档信度分别在什么场景下可以用

三档分开说,比笼统讲「只有一小部分可用」更实际。

档次 大致规模 适合用来做什么 不该用来做什么
高信度 三十万条量级 机制追溯、案例教学、假设生成的起点 当作临床决策依据
中信度 一百三十余万条 分层分析候选清单、信号优先级排序 单独引用为「已证实」
低信度 四百万条量级 长尾检索、未被系统研究过的线索池 任何计数与排序

这个分档逻辑与药物警戒实践里的「信号 vs 风险」两分法是同构的。信号是「值得进一步查」的意思,风险是「已经查清并需要行动」的意思。库里的高信度档也只是信号,不是风险。

§5 工具层:怎么把数据取下来、接进来、用起来

5.1 下载路径与真实体量

官方下载页把 19 个文件分三组陈列,每个文件都标注大小与发布日期(统一为 2025-06-25)。点按钮会触发前端脚本拼出 ./data/文件名 直接下载,不需要登录,也没有验证码。最大的一项 ADE_report.txt 标注 4.22 GB,是纯文本,下载后无需解压,但单机解析需要相当的磁盘规划。评分表几项加起来超过 2.8 GB,其中 CCombined_Results_with_scores.txt 一项就超过 1 GB。

按「原始数据 6 GB 上下、解析后的 Parquet 2–3 GB、中间态与索引再留 3 GB」估算,普通笔记本要预留二十 GB 以上空闲空间,否则会卡在写盘。

5.2 用 curl 验证文件可访问与体积

在做任何解析之前,先确认链接是否还活着、体积是否与页面标注一致。这一步能省掉很多「下载了一半发现 404」的时间。

#-- 只取响应头,确认状态码与 Content-Length,不下载正文
for f in ADE_report.txt ADE_Information.txt drug_all.txt DTI.txt Target.tsv AT.csv; do
  printf '%-28s' "$f"
  curl -sIL "https://persade.idrblab.net/data/$f" \
    | grep -i -E '^HTTP/|^content-length' \
    | tr -d '\r' \
    | tr '\n' ' '
  echo
done

每个文件发一次 HEAD 请求,grep 只留状态码与长度两行,tr -d '\r' 去掉回车符。HEAD 被拒的环境把 -I 换成 -r 0-0 同样有效。

三个键要先对齐再动手:药物用 InChIKey、事件用 UMLS CUI、蛋白用 UniProt 登录号。把这三列分别从 drug_all.txt、ADE_Information.txt、Target.tsv 读出来去重计数,得到的就是你可以连接的实体规模。这一步不涉及任何统计,纯粹是核对「表里到底有多少个不同的键」。

#-- 统计三个主键的去重规模,确认实体数量与官方口径大致吻合
import duckdb

con = duckdb.connect("persade.duckdb")
for label, path, col in [
    ("drug",   "drug_all.txt",        1),
    ("ade",    "ADE_Information.txt", 1),
    ("target", "Target.tsv",          1),
]:
    q = f"SELECT count(DISTINCT c{col}) FROM read_csv('{path}', delim='\t', header=false, all_varchar=true, ignore_errors=true, max_line_size=2000000)"
    n = con.execute(q).fetchone()[0]
    print(f"{label:>7} distinct keys: {n}")

拿到这三个数之后再看附录 A,如果接近官方标注的量级,说明列位推断没错;如果差一个数量级,多半是第一列不是主键,需要往前或往后挪一列再试。

5.3 把 4 GB 的文本按块流式读进数据库

患者层文件太大,直接 pandas.read_csv 会吃光内存。正确做法是按行流式读,边读边写。

#-- 流式把 ADE_report.txt 写成 DuckDB 表,只保留后续分析要用的列
import duckdb

SRC = "ADE_report.txt"
COLS = ["report_id", "inchikey", "ade_cui", "route", "code_a", "code_b",
        "flag", "event_date", "sex", "age", "weight", "indication",
        "outcome", "col14", "col15", "drug_name", "ade_text"]

con = duckdb.connect("persade.duckdb")
con.execute("""
    CREATE OR REPLACE TABLE reports (
        report_id   VARCHAR,
        inchikey    VARCHAR,
        ade_cui     VARCHAR,
        route       VARCHAR,
        code_a      VARCHAR,
        code_b      VARCHAR,
        flag        VARCHAR,
        event_date  DATE,
        sex         VARCHAR,
        age_raw     VARCHAR,
        weight_raw  VARCHAR,
        indication  VARCHAR,
        outcome     VARCHAR,
        col14       VARCHAR,
        col15       VARCHAR,
        drug_name   VARCHAR,
        ade_text    VARCHAR
    )
""")

#-- DuckDB 的 read_csv 支持按列名覆盖表头,这里用 rowid 当列名再重命名更稳
con.execute(f"""
    INSERT INTO reports
    SELECT * FROM read_csv('{SRC}',
        delim = '\t',
        header = false,
        quote = '',
        escape = '',
        columns = {{
            'c1':'VARCHAR','c2':'VARCHAR','c3':'VARCHAR','c4':'VARCHAR',
            'c5':'VARCHAR','c6':'VARCHAR','c7':'VARCHAR','c8':'DATE',
            'c9':'VARCHAR','c10':'VARCHAR','c11':'VARCHAR','c12':'VARCHAR',
            'c13':'VARCHAR','c14':'VARCHAR','c15':'VARCHAR','c16':'VARCHAR',
            'c17':'VARCHAR'
        }},
        ignore_errors = true,
        max_line_size = 1000000
    )
""")
print(con.execute("SELECT count(*), count(DISTINCT report_id) FROM reports").fetchall())

关键的三个参数是 quote=''、escape='' 与 ignore_errors=true。前两个关掉引号解析,避免 ADE 文本里的撇号被当成引号起始;第三个让个别列数不符的行被跳过而不是整批失败。max_line_size 是因为 drug_all.txt 里有的别名串很长,默认行长上限会截断。

5.4 在单条记录上复算 ROR,看评分能不能对上

把一条关联的 ROR 手算一遍,是验证「评分表到底在算什么」的最快办法。

#-- 手工复算某一个药物-ADE 对的 ROR,确认与官方评分表的口径一致
import duckdb

con = duckdb.connect("persade.duckdb")
DRUG = "MQTOSJVFKKJCRP-BICOPXKESA-N"
ADE  = "C0085605"

q = """
WITH base AS (
    SELECT report_id, inchikey, ade_cui FROM reports
),
a AS (SELECT count(DISTINCT report_id) n
      FROM base WHERE inchikey = ? AND ade_cui = ?),
b AS (SELECT count(DISTINCT report_id) n
      FROM base WHERE inchikey = ? AND ade_cui <> ?),
c AS (SELECT count(DISTINCT report_id) n
      FROM base WHERE inchikey <> ? AND ade_cui = ?),
d AS (SELECT count(DISTINCT report_id) n
      FROM base WHERE inchikey <> ? AND ade_cui <> ?)
SELECT a.n, b.n, c.n, d.n,
       round((a.n * 1.0 * d.n) / NULLIF(b.n * 1.0 * c.n, 0), 4) AS ror
FROM a, b, c, d
"""
rows = con.execute(q, [DRUG, ADE, DRUG, ADE, DRUG, ADE, DRUG, ADE]).fetchall()
print(rows)

这段 SQL 用的是报告级去重(count(DISTINCT report_id)),因为一份报告里同一 CUI 可能出现多次。官方评分表里的 ROR 不一定是报告级去重后的值,两者对不上时优先信任官方表,因为它的 2×2 表构造细节(是否按药物-ADE 对计数、是否按行计数)没有公开。

5.5 把评分表与患者层表接起来

评分表与患者层表的连接键是 ADE 的 CUI 与药物的 InChIKey,但评分表的列序没有表头,需要按位置命名。

#-- 给评分表加列名,再与患者层表按药物 InChIKey 与 ADE CUI 连接
import duckdb

con = duckdb.connect("persade.duckdb")
SCORE_COLS = {
    "ade_cui": "VARCHAR", "inchikey": "VARCHAR", "report_ids": "VARCHAR",
    "blank1": "VARCHAR", "case_source": "VARCHAR", "n_reports": "DOUBLE",
    "ror": "DOUBLE", "chi_p": "DOUBLE", "prr": "DOUBLE",
    "fisher_p": "DOUBLE", "score_a": "DOUBLE", "score_b": "DOUBLE",
    "score_c": "DOUBLE", "score_final": "DOUBLE", "score_x": "DOUBLE",
    "score_y": "DOUBLE", "score_z": "DOUBLE", "confidence": "VARCHAR",
    "severity_score": "DOUBLE", "severity_grade": "VARCHAR",
    "sex_dist": "VARCHAR", "age_dist": "VARCHAR", "outcome_dist": "VARCHAR"
}
con.execute("CREATE SCHEMA IF NOT EXISTS score")
con.execute(f"""
    CREATE OR REPLACE TABLE score.general AS
    SELECT * FROM read_csv(
        'CCombined_Results_with_scores.txt',
        delim = '\t', header = false, quote = '', escape = '',
        columns = {SCORE_COLS!r}, ignore_errors = true, max_line_size = 2000000
    )
""")
con.execute("""
    CREATE OR REPLACE TABLE joined AS
    SELECT s.inchikey, s.ade_cui, s.n_reports, s.ror, s.confidence,
           s.severity_grade, r.sex, r.age_raw, r.route
    FROM score.general s
    JOIN reports r
      ON s.inchikey = r.inchikey AND s.ade_cui = r.ade_cui
""")
print(con.execute("SELECT count(*) FROM joined").fetchone())

这一段的意义在于:评分表自己已经带了性别分布、年龄分布与结局分布三个字符串列,形如 Male(75.00%,9);Female(25.00%,3)。这意味着很多分层分析不必回连患者层表就能做——直接解析这三个字符串即可。回连患者层表的价值在于拿到给药途径与具体日期,做时间维度上的分析。

5.6 按年龄分箱与性别做一次交叉计数

最后一组代码演示最典型的分层用法:给定一个药与一个事件,看不同年龄分箱、不同性别下的报告构成。

#-- 输出某个药物-ADE 对在各年龄分箱与性别上的报告计数与占比
import re
import duckdb

con = duckdb.connect("persade.duckdb")
DRUG = "MQTOSJVFKKJCRP-BICOPXKESA-N"
ADE  = "C0085605"

rows = con.execute("""
    SELECT sex, age_raw, count(*) AS n
    FROM reports
    WHERE inchikey = ? AND ade_cui = ?
    GROUP BY sex, age_raw
    ORDER BY n DESC
""", [DRUG, ADE]).fetchall()

def band(age_raw: str) -> str:
    m = re.match(r"^(\d+)", age_raw or "")
    if not m:
        return "unknown"
    a = int(m.group(1))
    for lo, hi, tag in [(0, 17, "<18"), (18, 44, "18-44"),
                        (45, 64, "45-64"), (65, 200, "65+")]:
        if lo <= a <= hi:
            return tag
    return "unknown"

agg = {}
total = sum(n for _, _, n in rows)
for sex, age_raw, n in rows:
    key = (band(age_raw), sex)
    agg[key] = agg.get(key, 0) + n

for (b, s), n in sorted(agg.items(), key=lambda kv: -kv[1]):
    print(f"{b:>8} {s:>2} {n:>6} {n / total:6.2%}")
print("total", total)

两个必须记住的陷阱。一,age_raw 是带 YR 后缀的字符串,正则只取前导数字;空值落到 unknown,而它占比不低。二,百分比的分母是「这个药物-ADE 对的报告总数」,不是用药人群数。结果只能读成「这批报告里 65 岁以上占多少」,不能读成「65 岁以上用药者里多少发生了这个事件」。

5.7 没有 API,但页面查询可以当检索工具

PersADE 没有公开的 REST 接口。站点的检索是服务端渲染的 PHP 页面,基础检索支持按药名、CAS 号、InChIKey、DrugBank 号查药物,按名称、UMLS 号、MeSH 号查 ADE,按基因符号、UniProt 号、蛋白名查靶点;高级检索支持 SMILES 输入与 JSME 结构绘制、ADE 语义检索与患者队列筛选。

对批量需求,正确的做法不是爬页面,而是直接下载全量文件。爬虫既慢又容易被限,而且页面返回的是渲染后的 HTML,解析成本高于直接读制表符文件。

§6 与库内条目的关系

6.1 关系图谱

PersADE 的位置可以按上游、同级、下游三层理解。上游是它依赖的原始数据源:FAERS 条目描述美国这条线,VAERS 描述疫苗这条线,OPENFDA 是接口层,MAUDE 是器械。同级是与它功能重叠却不带患者层的资源:SIDER 与 OFFSIDES 从标签或统计推断关联,PharmGKB 管基因层,CTD 管化学物-基因-疾病。下游是它引用的参照系:MedDRA 与 UMLS 是术语层,KEGG 与 MSigDB 是通路层,Human Protein Atlas 是表达证据。

库内条目 关系类型 具体衔接点
faers 上游来源之一 患者层的美国部分来自 FAERS
vaers 同类不同域 同为自发上报,但疫苗事件的报告倾向与药物不同
openfda 接口层 提供 FDA 侧的结构化查询入口,可用作补充
maude 同类不同域 器械不良事件,方法学同源但对象不同
sider 同级 标签推导的关联,被 PersADE 引用作补充
offsides 同级 统计推断的关联,被 PersADE 引用作补充
ctd 上游机制来源 提供化学物-基因-疾病三元关系
pharmgkb 上游基因层 提供 190 个 ADE 的遗传变异关联
meddra 术语对照 监管侧术语,与 PersADE 的 UMLS 口径需要映射
umls 术语来源 ADE 主键就是 UMLS CUI
kegg 通路来源 348 条通路
msigdb 通路来源 6,191 条基因集
human-protein-atlas 表达证据 器官特异性 ADE 的表达验证
drugbank 药物注释 理化性质与药代参数
chembl 结合数据 17208 条定量亲和力互作
uniprot 蛋白主键 6,980 个蛋白的登录号体系
adrecs 未见独立条目 ADE 层级与靶点映射的上游

这张表最值得注意的一行是 adrecs:它与 PersADE 的关系极近(ADE 层级体系与靶点映射都来自它),但库内没有单独条目。补一条是合理的下一步。

6.2 检索路径

四条检索路径最常用。从药出发:用药名或 CAS 号走基础检索的药物通道,得到详情页(ATC 分类、适应症、理化参数),再跳到 ADE 列表按信度档排序。从事件出发:用 MeSH 名或 UMLS 号走 ADE 通道,得到树号、定义、语义邻居与严重度,再看关联药物。从靶点出发:用基因符号走靶点通道,得到亚细胞定位、家族、结构与双侧关联。从人群出发:走高级检索的患者筛选通道,设定年龄与性别,得到关联子集——这本质是对患者层文件的在线切片,与下载后自己写 SQL 等价。

§7 质量评估与局限性

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

最容易发生的误判,是拿「数据集」的尺子去量它:看有多少行、覆盖多少类、缺值率多高、能不能当训练集。这套尺子在这里会得出完全错误的结论。正确的框架是把它当「一份带机制层的报告汇编」来评。汇编的价值在能否支持推导,不在能否直接训练:三十万条高质量关联有用,因为它回答「这个机制有没有文献支持」;四百万条低质量关联也有用,因为它回答「这个组合有没有人报告过」。

第二个误判是把「自发上报的局限」当成「这个库的缺陷」。漏报、刺激报、分母不可知是上游的固有属性,任何基于自发上报的库都修不了。指责它不能算发生率,等于指责温度计不能测血压。该问的是:在这个前提下,它能做的部分做到什么程度。

第三个误判是把评分当真值。0–10 分是三个维度压缩的产物,中间有阈值截断与归一化,跨药不严格可比。它能排「同一个药的不同事件」,不能排「不同药的同一个事件」。

7.1 五维评分

按 DAIMS 的常规做法,从可用性、完整性、一致性、可追溯性、可复用性五个维度打分。这里的分数是编辑判断,不是官方指标。

维度 分数 判断依据
可用性 4 / 5 免注册、纯文本、无需解码;扣分在无表头与 4 GB 单文件
完整性 4 / 5 三层都有;扣分在缺肾功能、缺合并用药剂量、缺基因型覆盖
一致性 3 / 5 术语统一,但页面间口径不一致,列语义无文档
可追溯性 4 / 5 关联可回追报告号,机制关联带文献号;推断评分不可追
可复用性 3 / 5 学术可用,但商业受限、无 API、无正式 schema

7.2 DAIMS 24 项检查

下表按 24 项逐条给状态,三种:满足、部分满足、不满足。打分口径是「这个库有没有做到」,不是「这件事对该学科有多重要」——第 11 项与第 10 项都不满足,但这不意味着它没用,只意味着它不能承担频率估计的角色。

# 检查项 状态 说明
1 宽格式可用 部分满足 患者层是一行一关联,需要自建宽表
2 唯一标识 满足 报告号、InChIKey、CUI、UniProt 四个主键齐备
3 字段有文档 不满足 下载文件无表头,字段语义靠推断
4 编码体系明确 满足 UMLS、MeSH、InChIKey、UniProt、ATC 均可查
5 值域受控 部分满足 性别与结局受控,年龄与体重为字符串
6 缺失有标识 部分满足 缺失为空串,无显式缺失码
7 时间信息完整 部分满足 只有事件日期,无报告日期、用药起止
8 地理信息 部分满足 有来源系统,无具体地区
9 人群代表性可讨论 部分满足 只有北美两国,全球其他地区不可比
10 样本量足以分层 不满足 单个药单个事件的分层样本常低于百例
11 可计算发生率 不满足 无分母,结构性不可行
12 因果性标注 部分满足 有去激励、再激发切片,无逐例因果裁定
13 机制证据可追 满足 蛋白-ADE 关联带文献号
14 脱靶数据可追 满足 定量亲和力来自 ChEMBL、BindingDB、PDSP、IUPHAR
15 评分方法公开 满足 论文描述了五步流程与公式
16 评分可复算 部分满足 公式公开,但 2×2 表构造细节未公开
17 版本可辨识 满足 文件名带发布日期,官方称按季度更新
18 许可清晰 部分满足 写了学术用途,但无标准许可标识
19 引用格式规范 部分满足 论文可引,数据集本身无 DOI
20 有 API 不满足 只有网页检索
21 文件格式通用 满足 纯文本与制表符、逗号分隔
22 可直接喂模型 部分满足 需要大量清洗与编码
23 负样本可定义 不满足 库外无记录不等于无事件
24 跨库可对齐 满足 四套主键都是国际通用体系

完全满足 9 项,部分满足 10 项,不满足 5 项。工程完成度高、文档与规范度偏低。第 3 项与第 6 项扣分最重,而这两项恰好是 §4.4 的案例里直接暴露出来的问题:没有表头,就没有办法在一行数据上分辨哪一列是适应症、哪一列是结局。

7.3 已知的、可枚举的偏差

偏差分两组:来自上游的与来自加工过程的。前者无解,后者有改进空间。

上游那一组有三条。漏报是根本性的,严重事件、新药事件、被媒体关注的事件更容易被上报,轻微与慢性的则大量沉没。刺激报是漏报的镜像,某药被新闻或监管点名后报告量短期激增再回落;库里没有可用的报告日期,无法做前后对比识别这种冲击。分母不可知这一条已经重复多次,但值得再强调:它让所有「率」在数学上都不成立,不是精度问题而是定义问题。

加工过程那一组有四条。年龄与体重是作者做的分箱,边界未公开,「65 岁以上」指哪个区间要自己校验。两个来源合并后权重不均,美国报告占主导,「北美」这个标签实际接近「美国」。相似度推断把结构相似药的评分摊到了没有直接证据的关联上,会让「高相似药物」看起来比实际更有证据。机制层偏向文献覆盖度高的药,老药大品种三元组多,新药小品种少,容易把「机制解释得通」与「机制研究得多」混为一谈。

7.4 外部验证矩阵

论文报告了两项验证。第一项与 SIDER 比信度分分布:分层差异显著(论文写 p 小于 0.001,t 检验),SIDER 的高信度比例更高(17.5% 对 5.1%),PersADE 的高信度绝对条数更多(92,406 对 14,462)。这证明的是两个库构造方式不同,不是谁更准。

第二项用恩格列净做对照。论文把它与一项涉及超过 15,000 名 2 型糖尿病患者的临床试验安全性数据并列:生殖道感染(发生率 5.0%,评分 8.65)、高血糖(5.7%,评分 7.29)、背痛(4.0%,3.33)、腹泻(3.6%,3.33)、关节痛(2.9%,3.33)、乳腺癌(0.1%,1.99)、肝损伤(1.5%,1.45)。

这组数字值得细看。乳腺癌 0.1% 得低分合理,但肝损伤 1.5% 在临床试验里不算低频,评分却只有 1.45。论文的解释是「系统正确地把低频严重事件分配了低分」,可 1.5% 与 0.1% 差了十几倍,这一处存在解释空间,不宜当作「低分即罕见」的证明。

验证方式 对照对象 结论 可信度
信度分分布对比 SIDER 分布显著不同,绝对量更多 中,只证明构造不同
单药临床对照 恩格列净试验 常见事件高分、罕见事件低分 中,肝损伤一例存疑
文献引用支撑 十万余篇同行评议论文 机制三元组有出处 高,可逐条追溯
器官特异性验证 Human Protein Atlas 靶点表达器官与 ADE 器官对应 中,属机制合理性

7.5 它为何能持续:机构、上游免费、科研评价三重支撑

它的生存逻辑可以从三个层面看,都不涉及「它有多好用」。

第一层是上游免费。FAERS 每季度免费释放全量数据,加拿大警戒计划也公开提供提取文件。只要这两个系统还在运转,原料就不会断。这与其他需要自建队列的库最大的差别是:它不需要持续投入人力收集数据,只需要投入人力加工。

第二层是机构承载。它挂在浙大药学院 IDRB 实验室名下,同一实验室还维护 TTD、DrugMAP、MetaADEDB、ADENet 等一系列库。「一个实验室维护多个库、共享同一套工程栈」让单个库的边际维护成本很低:共享 SQL 后端、共享前端模板、共享术语表,这是小团队能维持一个 GB 级知识库的原因。

第三层是科研评价。数据库论文在生物信息学与药学信息学是被认可的产出形式,Nucleic Acids Research 的数据库专刊每年接收一批。这意味着「把已有数据整合成新库」本身有学术回报,不依赖用户量或商业收入,也不必像商业产品那样追日活。

三层合起来就是它的可持续性:原料免费、边际成本低、产出被评价体系认可。三条都不依赖于它是不是做得很好。

§8 生态位与历史

8.1 历史沿革:从一个实验室的多库谱系里长出来

它的前身线索在同一实验室的公开页面里:2019 年的 MetaADEDB 是药物-ADE 关联库,2021 年的 MetaADEDB 2.0 与 ADENet 加入预测功能,2024 年的 TTD 整合了定量药物-靶点互作。数据模型上连续:药物主键用 InChIKey、事件用受控术语、关联带统计量、机制层用靶点串起来。

它在这条谱系的末端:把「患者层」这个之前没有的维度加了进来。此前只能回答「这个药与这个事件有关联」,它开始能回答「在什么人群、什么给药方式下有关联」。

论文发表于 2026 年 1 月的 Nucleic Acids Research 数据库专刊,同年有勘误。文件统一标注 2025-06-25 发布,页脚标注 2025-06-01 更新,这两个日期与论文的关系官方未说明。

8.2 生态位:在「监管数据」与「机制知识库」之间的夹层

药物安全的信息生态可以分三个带。上面是监管数据带:FAERS、VigiBase、EudraVigilance,原始、权威、结构复杂、需要专门的处理能力。下面是机制知识带:UniProt、KEGG、ChEMBL、Open Targets,结构化好,但完全不带临床。

PersADE 在中间:把上面的数据做临床层抽取,把下面的数据做机制层拼接,做出来的东西既不是监管数据也不是纯知识库。夹层位置的优势是一站式,劣势是两边都不如原版。

对使用者这意味着一条明确的选择规则:只需要报告本身就去上游,只需要机制就去下游,需要把两者放进一张表做关联分析才轮到它。把它当成上游或下游的替代品,都是位置错配。

8.3 同类夹层资源的对照

做同样夹层工作的资源不多,取舍各不相同。

资源 夹层做法 患者层 机制层 许可
PersADE 患者字段 + 药物-蛋白-ADE 链 有 有 学术限定
OpenVigil 直连 FAERS 的在线统计 有,但为用户自己构造 2×2 表 无 开放
各机构的内部信号库 自行上报 + 内部数据 有,且更深 视机构而定 不公开
商业药物警戒平台 企业上报 + 文献 + 机器学习 有 部分 商业许可
学术单药研究 单药 FAERS 挖掘 有 通常无 论文可引,数据不公开

在「公开、带患者层、带机制层」三条的交集里,它几乎是唯一选项。这个唯一性比任何单个数字都更能解释它为什么值得存在。反过来也要看到风险:唯一选项意味着没有第二个库可以交叉验证同一批结论,使用者只能回到上游报告自己复核,这一条在 §7.4 的外部验证里已经体现为「对照对象只有 SIDER 与单个药的临床试验」。

8.4 一个统计上不够用的数据集为何仍然值得存在

一个样本量不足以做严格分层的库,为什么要做、为什么要用?四个理由。

一,可复现的假设生成。想知道「二甲双胍在老年肾功能不全者身上是否更易乳酸酸中毒」,没有它只能凭印象或读几篇病例报告;有它就能查到该组合在一批报告里的分布,得到一个可检验的假设。假设的质量不取决于样本量,取决于能否被证伪。

二,术语与结构的公共品属性。它把两国的自发报告统一到同一套 UMLS 术语、同一套 InChIKey 主键、同一套评分流程。这个统一工作本身有价值,因为任何想合并多国报告的人都要重做一遍,它把这笔成本一次性付掉了。

三,机制的连接点。把「哪个蛋白被打中」与「该蛋白出问题会导致什么临床事件」放进同一张表,让「为什么是这个事件而不是别的事件」第一次有了可查的路径。这个连接不依赖样本量,依赖文献覆盖与结构数据质量。

四,可被批评的接口。把局限写清楚的库提供了可证伪的基线:后续研究可以说「它给的是报告构成,队列数据算的是发生率,两者差多少」。这种对照本身推动领域前进。

五,拼接成本被它吞掉了一半。想把报告和靶点连起来的研究者,原本要自己写三层映射;它已经把 UMLS CUI、InChIKey、UniProt 三个键对齐,接一份新数据只要对准其中一个键。

五条都不需要「样本量够大」才能成立。这就是它的生存逻辑:不是结论来源,而是起点与基线。

§9 使用指引

9.1 三种角色的路径

药物警戒研究者。 目标是验证或推翻一个已知信号。路径:从 FAERS 原始数据做信号检测,得到候选组合后到 PersADE 查人群分布与机制链,看是否集中在某个亚组。结论会是「信号在这里,且集中在这一类人」,比人群级信号更有信息量。不要反过来用它做信号检测,评分口径与监管实践不同。

药学院与临床药理的研究生。 目标是理解「为什么同一个药对不同人不同」。路径:从高信度档挑一条有机制文献的关联,追到靶点,用 Human Protein Atlas 看靶点表达器官,再回患者层看这些器官的事件是否集中在某年龄段。这条路径把药理学、流行病学与临床串了起来。

做安全建模的工程师。 目标是构造训练数据。路径:评分表作标签并按信度档加权,药物-蛋白互作与蛋白-ADE 关联作图特征的边,用事件日期做时间切分而不是随机切分。先接受一个事实:库里没有负样本,模型只能做关联强度预测,做不了发生率预测。

9.2 一条最短的可用流程

从零到出第一张分层表,最短的路径是四步:确认链接、流式入库、连上评分表、按年龄段切。

#-- 最短流程:下载核心小文件、流式下载大文件、核对行数与体积
set -euo pipefail
BASE="https://persade.idrblab.net/data"
mkdir -p persade_work && cd persade_work

curl -fsSL -o AT.csv              "$BASE/AT.csv"
curl -fsSL -o ADE_Information.txt "$BASE/ADE_Information.txt"

#-- 大文件断点续传,中途断网不用重来
curl -fL -C - -o ADE_report.txt "$BASE/ADE_report.txt"

#-- 核对行数与体积,与官方页面标注的量级对齐
wc -l AT.csv ADE_Information.txt
ls -lh ADE_report.txt

最容易被跳过的是最后一步。如果 ADE_report.txt 下到 3 GB 就断了,后续统计全部失真,等分析做完才发现,返工成本极高。

9.3 引用方式

数据集本身没有独立 DOI,引用要同时给出论文与站点。论文引用见附录 C 第一条,站点写 https://persade.idrblab.net,并注明访问日期。引用具体数据时要写明文件与批次,例如「PersADE ADE Report Database,2025-06-25 发布」。只写「PersADE 数据库」不够,因为多个文件分开发布,版本可能不同步。

§10 AI 使用声明

项目 说明
性质 人工整合的自发报告知识库,不是 AI 生成的合成数据
官方 AI 用途表述 论文把机器学习列为下游应用方向,库本身不含模型
可用于模型训练的部分 关联评分、药物-蛋白互作、蛋白-ADE 关联可作特征与标签
不建议的用途 直接预测个体发病概率、用于临床决策、用于药物间安全排名
伦理要点 全部为已去标识的自发报告,但仅限学术用途
引用要求 引用论文与站点,并注明所用文件与发布批次
版本状态 文件 2025-06-25;论文 2026-01;另有勘误
数据主体权利 上游系统负责;PersADE 不再持有可回溯身份信息

常见误读(避坑清单)

常见说法 实际情况 证据
「某个药在库里的报告多,说明这个药危险」 报告多可能只是用的人多、上市久、被媒体关注过;自发上报没有分母,报告数与风险之间没有单调关系 §7.3 外部偏差:漏报与刺激报;§1.6 第一条边界
「这个库能算发生率,因为有患者数」 患者数是报告数,不是暴露人数;缺处方量与暴露人年,发生率在定义上不成立 §1.5 判断;§7.2 第 11 项
「高信度只占一小部分,说明这个库大部分不能用」 高信度要求三个条件同时成立,罕见事件天然撑不住;中档仍有百万条量级可用 §4.2;官方统计页三档计数
「个体化就是精准医疗,可以给个体算风险」 个体化指分层变量,不是个体预测;库中无个体风险模型 §1.2 第二条错误期待
「它是 FAERS 的镜像,可以复现 FAERS 论文」 它合并了两个来源、重算了评分、换了术语体系,数值对不上 §2.4;§3.4
「基因型数据都在里面,可以做药物基因组学」 只覆盖 PharmGKB 收录的 190 个 ADE,其余两万个 ADE 无基因分层 §3.3;论文事实描述
「评分高的药就是安全的」 评分衡量关联可信度与严重度,不是药物安全性排名;跨药比较无共同分母 §4.1;§7.0 第三条误判
「直接下载就不用管许可」 免注册不等于无限制;官方写明仅限学术用途,商业使用须申请 官方 About 页常见问题

FAQ

Q1:PersADE 和 FAERS 是什么关系?
PersADE 的患者层数据来自两个自发上报系统,其中一个是美国 FDA 的 FAERS。它不是 FAERS 的镜像,而是把两份报告按同一套术语与同一套评分方法重新整理,并叠加了蛋白与通路层。

Q2:可以直接算某种药的不良反应发生率吗?
不能。两个来源都是自发上报,分母(多少人用了这个药)不可知,只能算报告构成与报告比值。要发生率得去队列数据库或有明确处方分母的登记库。

Q3:数据需要申请吗?
下载不需要注册,直接点页面上的下载按钮即可。但官方明确写着仅限学术用途,商业使用要写信给团队单独授权,所以严格意义上是「免注册但有用途限制」。

Q4:为什么高信度关联只占一小部分?
高信度要求统计显著、报告比值高、病例数也够,三条同时满足的组合天然稀少。真实世界的药物-ADE 对绝大多数是低频、小样本,落在中低档是结构性的结果。

Q5:没有基因数据的个体化还算个体化吗?
PersADE 的个体化主要落在年龄、性别、体重、疾病背景与用药途径上,基因型只覆盖 PharmGKB 收录的 190 个 ADE。作者在结论里把「继续整合基因型数据」列为下一步,说明这一层还不完整。

Q6:文件没有表头,怎么确定每一列是什么?
只能按内容与论文描述反推,再用小样本校验。本页 §2.2 给了一份推断列序,使用前应当用已知药物与已知 ADE 抽查几行,确认列位与预期一致。

Q7:可以把它和别的库合并吗?
可以,四个主键都是国际通用体系:药物用 InChIKey、事件用 UMLS CUI、蛋白用 UniProt、适应症可用 MeSH。合并时最大的摩擦是 MedDRA 与 UMLS 之间的术语映射,以及 InChIKey 对盐型敏感导致的重复。

Q8:能不能用它训练药物安全预测模型?
可以用于构造正样本与机制特征,但要自己划时间切分,别用同一批报告做训练与测试。更要注意它没有负样本定义:库里没有记录,不等于临床上没发生过。

Q9:官网页面上有些数字对不上,是出错了吗?
存在多个口径,例如药物-蛋白-ADE 三元组在论文里约十九万五千、在 About 页约二十万。正确写法是并列两个口径并注明出处,而不是判定官方出错。

Q10:为什么下载页的最大文件有 4 GB 多?
患者层文件一行一个「报告 × 药物 × ADE」,覆盖数千万条患者记录,纯文本拉开就是这个体量。解析时按行流式读,不要整表载入内存。

Q11:它是按季度更新的吗?
官方常见问题里写按季度更新、大版本一到两年一次。但下载页所有文件标注的发布日期都是同一天,季度更新在页面上看不到证据。

Q12:如果一个药在库里完全没有记录,说明什么?
只说明在抽取的两个来源中,这段时间内没有对应报告被记录。可能是很少用、事件很轻没上报、当时未上市,或抽取规则把它滤掉了。不能推出「这个药没有不良事件」。

术语表

术语 含义
ADE / ADR 药物不良事件 / 药物不良反应,前者只要求服药期间发生,后者要求可归因于药物
药物警戒 Pharmacovigilance,发现、评估、理解与预防不良反应的学科
自发上报 由医务人员、企业或患者主动提交的报告,没有外部分母
失衡分析 用 2×2 表比较某药某事件的报告占比与背景占比
ROR / PRR / IC 三种常用的失衡指标:报告比值比、报告比例比、信息成分
刺激报 Notoriety bias,某药被点名后报告量短期激增
漏报 Under-reporting,实际发生的事件未被上报,自发体系最主要的偏倚
去激励 / 再激发 Dechallenge / Rechallenge,停药后缓解 / 再用药后复现,两者都支持因果关联
UMLS / CUI 统一医学术语系统 / 其中的概念编号,形如 C0013221
MeSH 美国国立医学图书馆的主题词表
MedDRA 监管用医学术语词典,上报体系的主流编码,使用受许可限制
InChIKey 由结构哈希得到的长字符串,跨库通用的化合物标识
UniProt 蛋白序列与功能主库,登录号是蛋白的通用主键
脱靶 Off-target,药物作用于非治疗目标蛋白引起的非预期效应
KEGG / MSigDB 通路数据库 / 分子特征数据库,用于把蛋白映射到功能单元
Tanimoto 系数 衡量两个分子指纹相似度的指标,取值 0 到 1
MACCS 指纹 一种固定长度的分子结构指纹,常用于相似性检索
DAIMS 数据就绪度与成熟度的评估框架,本文用它做 24 项检查
信度分 PersADE 给每条药物-ADE 关联的 0–10 分,衡量证据强度
严重度分级 PersADE 的五级分级,从 Minimal 到 Critical,衡量报告结局的危害

附录 A:核心数字核对表

项 值 来源 来源层级
个体化药物-ADE 关联 4,061,772 论文正文 论文
通用药物-ADE 关联 2,201,568 论文正文与官方 About 页 论文 / 官方
蛋白-ADE 关联 31,756 论文与官方统计页 论文 / 官方
药物-蛋白互作 108,677 论文与官方统计页 论文 / 官方
定量亲和力互作 17,208 论文正文 论文
蛋白数 6,980 论文与官方页 论文 / 官方
ADE 术语数 19,991 论文与官方页 论文 / 官方
化合物数 10,235 论文与官方页 论文 / 官方
有 ADE 关联的药物数 9,828 官方统计页 官方
患者数 11,500,749 官方统计页 官方
患者层记录 37,923,032 官方统计页 官方
KEGG 通路 348 论文与官方页 论文 / 官方
MSigDB 基因集 6,191 官方统计页 官方
给药途径类别 52 论文正文 论文
途径 / 剂型 / 适应症分层关联 1,649,311 / 1,135,452 / 1,277,009 官方 About 页与统计页 官方
高信度关联 316,144 官方统计页 官方
中信度关联 1,339,808 官方统计页 官方
低信度关联 4,056,728 官方统计页 官方
文献支撑数 115,670 论文正文 论文
下载文件数 19 官方下载页 官方
最大文件 4.22 GB 官方下载页 官方
药物-蛋白-ADE 三元组 194,874(论文)/ 200,457(About 页) 两处口径不同 论文 / 官方

附录 B:数字的来源分层

第一层,官方页面直取。 统计页与 About 页的规模数字、下载页的文件清单与大小、常见问题里的许可与更新节奏,都属这一层,可以放心引用。

第二层,论文与摘要。 五步评分流程、验证设计、1,208 个 ADE 的相似度扩展、恩格列净的对照数字,来自 Nucleic Acids Research 正文与摘要。可靠性取决于论文本身,引用时要连论文一起引。

第三层,未核实。 下载文件的确切列语义(无表头、无 README)、年龄与体重的分箱边界、两个字母码列的含义、文件发布日期与论文的先后、季度更新是否在执行。这几项只能当线索,使用前自行校验。

附录 C:完整参考文献

  • Yu Z, Wang Y, Zhang Y, Zhou Y, Hu X, Shen L, Ni C, Xing P, Huang F, Fu T, Han L, Huang J, Dai H, Zhu F. PersADE: a database of personalized adverse drug events and their underlying molecular mechanisms. Nucleic Acids Research, 2026, 54(D1). doi:10.1093/nar/gkaf1095. PMID: 41217945. PMC: PMC12807753.
  • Correction to the PersADE article. Nucleic Acids Research, 2026. doi:10.1093/nar/gkaf1453. PMID: 41416410.
  • PersADE 官方站点首页、Statistics、About、Resource 下载页与 Tutorial 页。https://persade.idrblab.net ,访问日期 2026-10-26。
  • PersADE 旧地址跳转页。https://idrblab.org/PersADE/index.php 。
  • Caster O, et al. Disproportionality Analysis for Pharmacovigilance Signal Detection in Small Databases or Subsets. Drug Safety, 2020. doi:10.1007/s40264-020-00911-w. PMID: 32008183.
  • Faillie JL. Benefits and strengths of the disproportionality analysis for identification of adverse drug reactions. British Journal of Clinical Pharmacology, 2011. doi:10.1111/j.1365-2125.2011.04037.x. PMID: 21658092.
  • Tregunno PM, et al. Performance of pharmacovigilance signal-detection algorithms for the FDA adverse event reporting system. Clinical Pharmacology & Therapeutics, 2013. doi:10.1038/clpt.2013.115. PMID: 23598483.
  • Khouri C, et al. The REporting of A Disproportionality Analysis for DrUg Safety Signal Detection (READUS-PV). Drug Safety, 2024. doi:10.1007/s40264-024-01423-7.
  • Whirl-Carrillo M, et al. An Evidence-Based Framework for Evaluating Pharmacogenomics Knowledge for Personalized Medicine. Clinical Pharmacology & Therapeutics, 2021. doi:10.1002/cpt.2350. PMC: PMC8457105.

附录 D:任务代码

把前面几节的片段合成一个脚本:确认链接、下载、入库、连评分表、输出一张最粗的分层表。脚本假设在一个空目录里运行,会自动建工作目录,已存在的文件会跳过下载。

#!/usr/bin/env python3
#-- 端到端:确认可下载 -> 下载 -> 入库 -> 连评分表 -> 输出年龄分层
import os
import re
import subprocess
import sys

BASE = "https://persade.idrblab.net/data"
WORK = "persade_work"
FILES = ["ADE_report.txt", "ADE_Information.txt", "drug_all.txt",
         "DTI.txt", "Target.tsv", "AT.csv",
         "CCombined_Results_with_scores.txt"]


def head_ok(name):
    r = subprocess.run(["curl", "-sIL", f"{BASE}/{name}"],
                       capture_output=True, text=True, check=True)
    codes = re.findall(r"^HTTP/[0-9.]+ (\d{3})", r.stdout, re.M)
    lens = re.findall(r"(?i)^content-length: (\d+)", r.stdout, re.M)
    return (codes[-1] if codes else "000"), (lens[-1] if lens else "0")


def fetch(name):
    dst = os.path.join(WORK, name)
    if os.path.exists(dst) and os.path.getsize(dst) > 0:
        print("skip ", name)
        return dst
    print("fetch", name)
    subprocess.run(["curl", "-fL", "-C", "-", "-o", dst, f"{BASE}/{name}"],
                   check=True)
    return dst


def band(age_raw):
    m = re.match(r"^(\d+)", age_raw or "")
    if not m:
        return "unknown"
    a = int(m.group(1))
    if a < 18:
        return "<18"
    if a < 45:
        return "18-44"
    if a < 65:
        return "45-64"
    return "65+"


def main():
    os.makedirs(WORK, exist_ok=True)
    for f in FILES:
        code, length = head_ok(f)
        print(f"HEAD {f:<40} {code} {length}")
        if code != "200":
            sys.exit(f"不可下载:{f}")
    for f in FILES:
        fetch(f)

    import duckdb
    con = duckdb.connect(os.path.join(WORK, "persade.duckdb"))
    con.execute("""
        CREATE OR REPLACE TABLE reports AS
        SELECT * FROM read_csv(?,
            delim = '\t', header = false, quote = '', escape = '',
            columns = {
                'c1':'VARCHAR','c2':'VARCHAR','c3':'VARCHAR','c4':'VARCHAR',
                'c5':'VARCHAR','c6':'VARCHAR','c7':'VARCHAR','c8':'VARCHAR',
                'c9':'VARCHAR','c10':'VARCHAR','c11':'VARCHAR','c12':'VARCHAR',
                'c13':'VARCHAR','c14':'VARCHAR','c15':'VARCHAR','c16':'VARCHAR',
                'c17':'VARCHAR'
            },
            ignore_errors = true, max_line_size = 1000000)
    """, [os.path.join(WORK, "ADE_report.txt")])

    rows = con.execute(
        "SELECT c9, c10, count(*) FROM reports GROUP BY c9, c10"
    ).fetchall()
    agg, total = {}, 0
    for sex, age_raw, n in rows:
        agg[(band(age_raw), sex)] = agg.get((band(age_raw), sex), 0) + n
        total += n
    for (b, sx), n in sorted(agg.items(), key=lambda kv: -kv[1])[:20]:
        print(f"{b:>8} {sx:>2} {n:>10} {n / total:7.2%}")
    print("total", total)


if __name__ == "__main__":
    main()

脚本里有三处需要说明。head_ok 在开始 4 GB 传输前先确认链接有效,避免下到一半才发现 404。fetch 用了 -C -,中断后重跑会续传。最后的分组直接用了推断列序的第 9、10 列,写成 c9、c10 而不是语义名,是为了提醒使用者:这两列的意义是推断出来的,官方如果调整列序,输出就会偏移。

附录 E:如果只记五件事

  1. PersADE 的价值不在规模,在「同一张表里既有患者属性又有机制链」。公开资源里同时满足这两条的几乎没有第二个。
  2. 它不能算发生率。所有基于自发上报的库都不能,这是定义问题不是精度问题。
  3. 高信度只占 5%,这是罕见事件的必然,不是评分坏了。中档百万条仍然可用。
  4. 个体化在这里指分层,不指个体预测。基因型只覆盖少数 ADE,作者自己把补基因数据列为未来工作。
  5. 仅限学术用途。免注册不等于可以商用。

编辑说明

本文的事实核对时间为 2026-10-26,核对范围为:PersADE 官方站点首页、Statistics 页、About 页、Resource 下载页与 Tutorial 页(直接抓取 HTML);Nucleic Acids Research 论文全文(经 Europe PMC 开放获取版本 PMC12807753 获取)与摘要;Europe PMC 两条检索查询,关键词分别为「PersADE」与「personalized adverse drug event pharmacovigilance individual」。

未覆盖的内容:未下载全量数据包做逐条统计,因此本文不含任何分布、分位数、空值率类数字;下载文件的确切列语义为按内容推断,已在 §2.2 与附录 B 标注;年龄与体重分箱边界、code_a 与 code_b 两列的含义、季度更新是否在实际执行,三项未能核实。

口径说明:官方在「药物-蛋白-ADE 三元组」「药物数」「被评分关联数」三处存在两个口径,本文并列呈现并标注差额构成官方未说明,未作对错判断。

AI 参与程度:本文由 AI 协助检索、整理与撰写,数字与结论均回到上述一手来源核对;编辑判断部分(五维评分、DAIMS 24 项打分、常见误读清单)为编辑部的评估意见,不代表官方立场。


相关数据集导航

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

  • offsides — 共享标签:药物发现与化学 / 药物安全 / 知识图谱 / 真实世界数据
  • trinetx — 共享标签:药物发现与化学 / 药物安全 / 真实世界数据
  • fda-sentinel — 共享标签:药物发现与化学 / 药物安全 / 真实世界数据
  • sider — 共享标签:药物发现与化学 / 药物安全 / 知识图谱
  • twosides — 共享标签:药物发现与化学 / 药物安全 / 真实世界数据
  • jader — 共享标签:药物发现与化学 / 药物安全 / 真实世界数据
  • ddinter — 共享标签:药物发现与化学 / 药物安全 / 知识图谱
  • comptox-dashboard — 共享标签:药物发现与化学 / 药物安全 / 知识图谱
  • eudravigilance — 共享标签:药物发现与化学 / 药物安全 / 真实世界数据
  • maude — 共享标签:药物安全 / 真实世界数据

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

返回 AI-Ready 数据集