信息速览
药物警戒运转了六十年,主战场一直是人群级信号检测:把某个药与某个事件的报告挑出来计数,看这个数在全部报告里是否偏高,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:如果只记五件事
- PersADE 的价值不在规模,在「同一张表里既有患者属性又有机制链」。公开资源里同时满足这两条的几乎没有第二个。
- 它不能算发生率。所有基于自发上报的库都不能,这是定义问题不是精度问题。
- 高信度只占 5%,这是罕见事件的必然,不是评分坏了。中档百万条仍然可用。
- 个体化在这里指分层,不指个体预测。基因型只覆盖少数 ADE,作者自己把补基因数据列为未来工作。
- 仅限学术用途。免注册不等于可以商用。
编辑说明
本文的事实核对时间为 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 — 共享标签:药物安全 / 真实世界数据
导航说明:本章节由全站统一标签体系自动计算生成(标签重合度算法),双向可达;点击链接可跳转至对应数据集词条。

