拓冰建站拓冰建站
首页 / 资讯中心 / 正文

基于FAERS的维立西呱不良反应信号挖掘与ROR/PRR实战

简介这是一份面向药物警戒研究者、临床药学人员及药品监管方向学习者的专题文档围绕FAERS数据库中的维立西呱不良反应数据展开系统分析与评估。内容涵盖研究背景与目的、数据来源与收集、数据分析方法并依次讨论不良反应的类型分布、严重程度与临床表现进而构建风险评估模型、开展风险因素识别与权重分析最后落脚于加强药品监管、提升医务人员用药水平与患者教育等预防控制策略适合用于论文写作、课题申报或药物安全性方法学参考。资源为单个docx文件压缩包约45KB结构完整、目录层次清晰便于按章节检索与引用。目前已有55人浏览学习。读者可从中获取不良反应特征梳理思路、风险量化评估框架及监管改进方向等具体内容。1. FAERS 加维立西呱这份不良反应分析要解决的是「无分母下怎么判信号」维立西呱是心衰领域里比较新的可溶性鸟苷酸环化酶刺激剂III 期试验的样本量再大也覆盖不了罕见不良反应、迟发不良反应和特殊人群叠加用药的情况。上市之后真正能补这块短板的是自发报告数据而 FAERS 就是 FDA 维护的、公开可下载的不良事件报告库。它的特点是没有分母、没有对照组、报告是自愿提交的所以任何从 FAERS 里算出来的「发生率」都是假的能算的只有「信号」也就是某个药和某个不良反应之间的报告频次是否明显偏离全库的背景分布。这份研究要做的就是把维立西呱相关的报告从 FAERS 全库里拆出来做不均衡分析ROR、PRR 这类四格表方法再对筛出来的阳性信号做时间趋势和亚组验证。适合三类人做药物警戒日常监测的、拿真实世界数据写学位论文的、以及想找一个公开数据库练手数据分析的工程师。难点不在算法而在数据清洗口径——去重规则、药品名归一化、role_cod取舍这三处任一处口径变了最终信号列表就会跟着变。2. FAERS 季度 ASCII 数据的获取与合并维立西呱分析前的字段级清洗2.1 七个表的分工与维立西呱分析真正用得到的字段FDA 按季度发布 FAERS 的 ASCII 压缩包一个包里固定七个文本表用$作分隔符。这不是一张宽表而是围绕primaryid拆开的关系型结构所以第一步永远是先想清楚「哪个字段在哪个表」。表名粒度关键字段在维立西呱分析里的用途DEMO一份报告一行primaryid, caseid, caseversion, fda_dt, age, age_cod, sex, reporter_country去重、亚组分层、时间趋势DRUG报告内一味药一行drugname, prod_ai, role_cod, route, dose_vbm识别维立西呱、判断怀疑角色、提取合并用药REAC报告内一个 PT 一行pt, drug_rec_act不良反应 PT 计数四格表的行方向OUTC报告内一个结局一行outc_cod严重结局分布做信号严重度分层INDI报告内一个适应症一行indi_pt反向确认报告是否用于心衰人群THER报告内一条既往用药一行drug_seq, start_dt判断既往用药辅助因果推断RPSR报告内一个来源一行rpsr_cod区分消费者报告与医务人员报告维立西呱的主分析其实只强依赖 DEMO、DRUG、REAC 三张表OUTC 和 INDI 用来做验证THER、RPSR 属于补充。先把依赖收窄后面内存和调试时间都会省很多。2.2 用 Python 批量解压、合并并统一列名一个季度的压缩包不大但五年的数据合起来如果每个表都全字段读入内存会直接顶到几十 GB。稳妥做法是逐表指定usecols并且在读取阶段就把列名标准化。import pandas as pd, zipfile, os, glob def read_faers_table(zip_path, table, usecolsNone): 从 FAERS 季度压缩包读取单个 ASCII 表 with zipfile.ZipFile(zip_path) as zf: # 包内文件名形如 demo24Q1.txt大小写和缩写不统一 target [n for n in zf.namelist() if os.path.basename(n).lower().startswith(table.lower()) and n.lower().endswith(.txt)][0] with zf.open(target) as f: # FAERS ASCII 固定用 $ 分隔enginepython 在处理多字符分隔符时必须显式指定 df pd.read_csv(f, sep$, dtypestr, encodinglatin-1, usecolsusecols, enginepython, low_memoryFalse) df.columns [c.strip().lower().lstrip(\ufeff) for c in df.columns] return df DEMO_COLS [primaryid,caseid,caseversion,fda_dt,rept_dt, age,age_cod,sex,wt,wt_cod,reporter_country] demo pd.concat( [read_faers_table(z, demo, DEMO_COLS) for z in sorted(glob.glob(faers_ascii_*.zip))], ignore_indexTrue)逻辑上分三步定位压缩包内的目标文件、按固定分隔符读取、统一列名。dtypestr这个参数容易被忽略但必须加——nda_num、caseid这类字段带前导零或者超过整型范围时一旦被自动推断成数值后续做连接就会对不上。latin-1是为了绕开个别季度文件里的非法字节用 utf-8 会直接抛解码错误。low_memoryFalse让 pandas 一次性推断类型避免分块推断时同一列在不同块里得到不同类型。2.3 按 caseid 只保留最新 caseversion 的去重规则FAERS 允许报告者后续补充或修改同一病例每次提交都会生成一个新的caseversion旧版本不会从库里删掉。所以同一份病例在原始数据里可能出现三到四次如果不去重a值会被直接抬高ROR 跟着虚高。demo[fda_dt] pd.to_datetime(demo[fda_dt], format%Y%m%d, errorscoerce) demo[caseversion] pd.to_numeric(demo[caseversion], errorscoerce) # 先按 caseid 版本 接收日期排序同 caseid 只留最后一条 demo (demo.sort_values([caseid, caseversion, fda_dt]) .drop_duplicates(caseid, keeplast))这里排序键用了三个字段而不是只用caseversion因为跨年度数据合并时不同季度包里的版本号并不保证单调fda_dt是更可靠的兜底。DRUG 和 REAC 表本身没有caseversion所以去重必须在 DEMO 上先做再把保留下来的primaryid作为白名单去过滤这两张表——顺序反了就会漏删或误删。注意跨季度合并后必须重跑一次primaryid唯一性检查。不同季度的primaryid存在复用情况直接按primaryid去重会误伤。3. 从 drugname 锁定维立西呱药品归一化与目标队列的口径选择3.1 FAERS 的 drugname 到底有多脏drugname是自由文本字段没有字典约束。同一个维立西呱在全库里能查到的写法至少有下面几类全都得覆盖。写法类型示例处理策略通用名VERICIGUAT直接命中商品名VERQUVO别名词典命中研发代号MK-1242、BAY 1021189正则命中带剂型/盐基VERICIGUAT TABLET、VERICIGUAT SODIUM先规范化再去剂型试验记录VERICIGUAT/PLACEBO标记但不进主分析拼写错误VERICGUAT、VERICIGUAT编辑距离兜底前五类可以靠规则解决第六类必须靠模糊匹配而且模糊匹配扩进来的每一条都要人工抽查否则会把无关药拉进队列。3.2 用正则加别名词典做维立西呱名称归一import re # 通用名 商品名 研发代号统一为小写匹配 VERICIGUAT_PAT re.compile( r(vericiguat|verquvo|mk[\s\-]?1242|bay[\s\-]?1021189), flagsre.IGNORECASE) def norm_drug(name): 去括号内容、去剂型词、压空白用于后续匹配 if not isinstance(name, str): return s re.sub(r\(.*?\), , name) # 去 (TABLET) 类括号 s re.sub(r\b(tablet|capsule|oral|solution|placebo|sodium)\b, , s, flagsre.I) # 去剂型与盐基 s re.sub(r[^a-z0-9\s\-], , s.lower()) return re.sub(r\s, , s).strip() drug[drugname_norm] drug[drugname].map(norm_drug) drug[is_vericiguat] drug[drugname_norm].str.contains(VERICIGUAT_PAT, naFalse)逻辑说明先做规范化再做匹配可以让「VERICIGUAT SODIUM TABLET」和「vericiguat」落到同一个键上。正则里把空格和连字符都设成可选是为了兜住「MK 1242」「MK-1242」「MK1242」三种写法。参数说明flagsre.IGNORECASE必加FAERS 里大小写完全随机剂型词表要包含placebo因为试验记录会把VERICIGUAT/PLACEBO写成一条 drug 行如果不去掉它会被当成有效用药记录。这一步之后要单独把placebo记录打标留到敏感性分析里再决定去留而不是当场删掉。3.3 role_cod 的取舍直接改变信号结果role_cod描述该药在这份报告里的角色PS首要怀疑、SS次要怀疑、C合并用药、I交互作用。这是最容易被做错的一步。常见做法是主分析只取PS把PSSS放进敏感性分析。理由是SS意味着报告者认为主要责任在另一个药把它并进主队列会稀释信号但SS里也可能藏着有价值的信息比如多药联用场景下维立西呱的贡献被低估。至于把C也当作目标药来算是明确的误用——合并用药是背景用药把它算进来相当于把分母和分子同时污染。# 主分析队列首要怀疑 ps_ids set(drug.loc[drug[is_vericiguat] (drug[role_cod] PS), primaryid]) # 敏感性分析队列首要 次要怀疑 ps_ss_ids set(drug.loc[drug[is_vericiguat] (drug[role_cod].isin([PS, SS])), primaryid])两套primaryid集合分别去过滤 REAC得到两套不良反应计数最后对比信号列表的重合度。如果某个 PT 只在PSSS里出现这类信号要单独提出来看原始报告不能直接写进结论。4. 维立西呱不良反应的四格表构建与 ROR/PRR 信号计算4.1 四格表在 FAERS 全库上怎么拼出来不均衡分析的本质是一张 2×2 表四个格子分别对应维立西呱报告中的目标不良反应a、维立西呱报告中的其他不良反应b、其他药物报告中的目标不良反应c、其他药物报告中的其他不良反应d。关键是计数单位。格子定义计数口径a维立西呱 目标 PT去重后的 caseid 数b维立西呱 非目标 PT去重后的 caseid 数c非维立西呱 目标 PT去重后的 caseid 数d非维立西呱 非目标 PT去重后的 caseid 数口径上有两个必须提前定死的地方一是按caseid去重而不是按primaryid同一份病例的不同版本不能重复计数二是一份报告可能同时含多个 PTac与ab的求和基数必须来自同一份去重后的报告集合否则四格表内部就不自洽。4.2 ROR 与 PRR 的 Python 实现与阈值设定import numpy as np def disproportionality(a, b, c, d): 输入四格表计数输出 ROR/PRR/卡方及 95% 置信区间 a, b, c, d map(float, (a, b, c, d)) if min(a, b, c, d) 0: return dict(RORnp.nan, ROR_lownp.nan, ROR_highnp.nan, PRRnp.nan, chi2np.nan) ror (a * d) / (b * c) se np.sqrt(1/a 1/b 1/c 1/d) # ln(ROR) 的标准误 low np.exp(np.log(ror) - 1.96 * se) high np.exp(np.log(ror) 1.96 * se) prr (a / (a c)) / (b / (b d)) chi2 (((a*d - b*c)**2) * (abcd)) / ((ab)*(cd)*(ac)*(bd)) return dict(RORror, ROR_lowlow, ROR_highhigh, PRRprr, chi2chi2)逻辑说明ROR 走的是比值比的思路取对数后近似正态所以置信区间在ln尺度上算完再指数还原PRR 是两个比例相除用卡方检验补一个显著性判断。参数说明1.96对应 95% 置信水平想算 99% 就换成2.576。min(a,b,c,d) 0这个短路不能省——FAERS 里大量 PT 在特定药下只有 0 条直接算除法会得到inf或nan并污染整列。行业里常用的判定阈值是a ≥ 3且 ROR 的 95% CI 下限 1或者PRR ≥ 2且卡方≥ 4。这两个条件我一般要求同时满足才判阳性只要一个满足就标成待观察。阈值不是越松越好a ≥ 3这条是为了挡住单例报告带来的偶然波动。4.3 多重比较与假阳性IC025 与 EBGM 的补位FAERS 的 PT 词表有上万条逐条做检验就是上万次假设检验。即使全部 PT 都是噪声按 5% 的显著性水平也会产生几百个「阳性」。这一步不处理信号列表基本没有参考价值。常见做法有三条路。一是用 Benjamini-Hochberg 方法对卡方对应的 p 值做 FDR 校正控制错误发现率二是换用贝叶斯类方法BCPNN 的IC025 0或 MGPS 的EBGM05 2它们在小样本格子上的表现比频率派方法稳三是限定分析范围只对报告数排名靠前的 PT 做检验把长尾直接排除。我一般会同时跑 ROR 和 IC025两个方法都判阳性的 PT 才进下一轮人工评估重合度低的部分单独看。注意ROR 和 PRR 的数值大小不能跨 PT 直接比大小。一个 ROR 为 30 的信号未必比 ROR 为 8 的信号更值得跟进前者可能只有 3 例报告后者可能有 300 例。5. 维立西呱信号的验证与误判排查5.1 用时间趋势识别报告行为造成的伪信号拿到阳性列表之后第一步不是解读而是按fda_dt把每个 PT 的季度报告数画出来。自发报告系统有一个很典型的模式某个药物上市初期或某篇文献、某条新闻发布之后相关报告会集中出现一波这就是媒体关注带来的报告偏倚。# 把筛选出的阳性 PT 按季度聚合观察是否集中在个别季度 sig reac[reac[primaryid].isin(ps_ids)] sig sig.merge(demo[[primaryid, fda_dt]], onprimaryid, howleft) sig[quarter] sig[fda_dt].dt.to_period(Q) trend sig.groupby([pt, quarter]).size().unstack(fill_value0)如果某个 PT 的报告 80% 落在两个季度内而维立西呱上市已经有几年那这个信号更可能来自外部事件而不是药物本身。反过来报告数在多个季度里稳定出现哪怕总数不大也值得优先看原始病例。5.2 按报告者类型和结局严重度做分层分层维度字段看什么报告者类型RPSR.rpsr_cod消费者报告占比过高时PT 描述往往不规范严重结局OUTC.outc_cod死亡、住院、致残是否集中在某个 PT报告国家DEMO.reporter_country单一国家占比过高说明是区域性报告习惯年龄性别DEMO.age, DEMO.sex与说明书中的特殊人群提示是否一致分层不是为了再算一遍 ROR而是为了判断这个信号有没有可解释性。一个只在消费者报告里出现、没有任何严重结局、且高度集中在单一国家的 PT和另一个在医务人员报告里持续出现、伴随住院结局的 PT处理优先级完全不同。5.3 一套可复现的评估清单把前面的口径全部固化下来下一次换药或者换季度数据时才能直接复用。我一般把这几个参数写进配置文件ROLE_COD_MAIN [PS]、MIN_CASES 3、ROR_CI_LOW 1.0、PRR 2.0、CHI2 4.0、FDR 0.05再加上去重策略和药品匹配正则。任何一次调整只动配置不动代码逻辑。真正要落到评估结论时还有一条边界得守住FAERS 的阳性信号只说明「报告频次不均衡」不说明因果。报告里没有分母、没有用药时长、没有停药后转归的完整随访这些缺口只能靠病历回顾或者别的数据源去补不能在 FAERS 内部解决。把这条边界写清楚比多筛出几个 PT 更重要。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门