A DVC pipeline over every adverse event report the FDA has published since 2004 — 20.6 million distinct cases after deduplication, spanning both the legacy LAERS and current FAERS eras — with disproportionality analysis, confounder adjustment, and a rendered report.
| How much of the database is distinct? | 16.8% of raw records are redundant: 4.1M superseded case versions and 68,088 FDA-retracted cases. 82,313 cases span the 2012 era boundary. |
| What does a report contain? | Mean 3.18 drugs and 2.98 reactions, median 1 drug — with a tail reaching 171 drugs on one report. |
| Who reports? | Consumers (8.9M) now file more than physicians (4.5M). |
| Which drug–event pairs report disproportionately? | 2.01M pairs scored; 688,879 flagged by EB05 ≥ 2, the most conservative rule. |
| Do signals survive confounder adjustment? | 98.5–100% survive Mantel–Haenszel adjustment, but Breslow–Day rejects homogeneity for 50–76% — the confounders modify signals rather than create them. |
| How much of the corpus supports a causal reading? | 0.147% of drug records are a suspect drug with both positive dechallenge and positive rechallenge. |
| How much is vocabulary drift rather than epidemiology? | 32% of tested reaction terms look like MedDRA revisions, touching 16% of reaction mentions. Flagged, not corrected. |
| How many reports are the same incident twice? | 9.5% of linkable records, in 298k clusters — beyond the 16.8% version-based redundancy. Estimated, not removed. |
| Do reaction profiles recover drug classes? | Yes — statins, ACE inhibitors, TNF inhibitors, SSRIs and bisphosphonates average 4.8× more similar within class than to the corpus. |
uv sync
dvc reproNo credentials, no cloud storage, no API key. FDA hosts the source archives; dvc.lock pins every
intermediate by hash. Full run is ~25 minutes on a 10-core laptop, dominated by the 3.5 GB download.
faers verify # check archives against committed SHA-256 checksums
pytest # statistical validation suite
dvc metrics show # corpus, resolution and signal countsdownload → harmonize → dedup ─┐ ┌→ describe ──┐
├→ normalize ─┼→ signals ───┤
brands ───────────────────────┘ ├→ stratify ──┼→ figures → report
├→ duplicates ┤
└→ drift ─────┘
| Stage | What it does |
|---|---|
download |
Fetch 90 quarterly archives, record SHA-256 per file |
harmonize |
Parse both eras into one canonical schema |
dedup |
One record per case; drop retracted and superseded |
brands |
Brand→ingredient index from FDA's NDC directory |
normalize |
Resolve drug names to ingredients via prod_ai, corpus, NDC, RxNav (98.6%) |
describe |
Growth, demographics, reporters, top terms, report structure |
signals |
ROR, PRR, IC/BCPNN, EBGM/MGPS with Fisher + BH-FDR |
stratify |
Mantel–Haenszel by sex, age band, calendar period |
similarity |
Drug–drug similarity from reaction profiles, validated against known drug classes |
duplicates |
Record linkage for independent duplicate reports (estimated, not removed) |
drift |
Flags MedDRA vocabulary change without a licensed dictionary |
figures |
Vega-Lite specs + PNG |
report |
Quarto site |
Every choice that moves a number lives in params.yaml — role filter, minimum
support, stratification variables — so a sensitivity analysis is a params edit, not a code change.
The previous version of this repository was a notebook built on the openFDA web API. That API caps pagination at 25,000 records per query, so its record-level analysis ran on a 10,000-report sample, and it exposes no case-version fields, so deduplication was impossible.
| Source | Coverage | Size |
|---|---|---|
| openFDA API | 20.7M reports | rate-limited, skip capped at 25k |
| openFDA bulk JSON | 20.7M reports | 113 GB, 1,767 partitions |
| FAERS/LAERS ASCII | 90 quarters, 2004Q1–2026Q2 | 3.5 GB |
The ASCII dumps are 34× smaller for the same reports, and carry caseid/caseversion — the only
clean basis for deduplication.
Signals are screened on lower confidence or credible bounds (ROR₀₂₅, PRR₀₂₅, IC₀₂₅, EB05), never point estimates. Four rules are reported side by side because none is authoritative:
- ROR with 95% CI, Haldane–Anscombe corrected
- PRR with the MHRA χ² ≥ 4 criterion
- IC/BCPNN (Norén 2006 shrinkage), WHO-UMC's
IC₀₂₅ > 0 - EBGM/MGPS (DuMouchel 1999) gamma-Poisson shrinkage with EB05/EB95
- Fisher exact (vectorized hypergeometric) with Benjamini–Hochberg FDR
Validated against nine established label warnings — Metformin–Lactic acidosis, Clozapine– Agranulocytosis, Amiodarone–Pulmonary toxicity and others — all nine flagged by all five rules, with implausible pairings correctly suppressed (Aspirin–Alopecia, EB05 = 0.19). Mantel–Haenszel and Breslow–Day are calibrated against simulated data with a known common odds ratio: MH recovers OR = 3.01 against a true 3, and Breslow–Day shows 6.8% type-I error against a nominal 5%.
- PRR variance. The notebook used
1/a − 1/(a+b) + 1/c + 1/(c+d); the final term is a subtraction.tests/test_disproportionality.pypins this. - Dechallenge/rechallenge prevalence. Estimated at ~2% from marginal counts treated as independent; the joint distribution gives 0.147%.
- Reaction trends. Raw yearly counts conflate a term becoming more common with the database growing. Reported here as a share of each year's reports.
- Duplicates. The repetitive clustergram block the notebook attributed to "duplicates or other artifacts" was 4.0M superseded case versions.
- Salt forms.
ATORVASTATINandATORVASTATIN CALCIUMwere counted as two drugs, splitting one substance nearly in half. Resolved to the active moiety via RxNorm.
Reports do not link a drug to a reaction — that field does not exist — and no report is validated. Disproportionality measures reporting associations, not causation or incidence: the number of people exposed without an event is unknown.
The highest-scoring pairs in the whole corpus illustrate this. Technetium–"Radioisotope scan abnormal" and Zuclopenthixol–Anosognosia rank at the top because the association is real and near-deterministic — the tracer is given in order to perform the scan; anosognosia is a feature of the treated psychosis. Strong disproportionality distinguishes association from noise, not harm from indication.
Drug names are resolved to ingredients at 98.6%, via FDA's prod_ai, a dictionary bootstrapped
from the corpus, FDA's NDC directory and NLM's RxNav. The residue — discontinued, foreign and
misspelled products — keeps its reported name and is flagged ingredient_curated = false in the
published table. 28% of distinct drug labels in the signal table are still product names rather
than substances, which splits marginals; treat those signals as provisional.
Further limitations — MedDRA version drift, absent drug-class hierarchy, approximate q-values under dependence — are stated in the report.
A non-commercial academic project.
| Code | MIT (LICENSE) |
| Derived result tables | CC BY 4.0 (DATASET_CARD.md) |
| FAERS source data | public domain (US federal work) |
| Drug identifiers | RxNorm (NLM), public domain |
| Reaction terms | MedDRA — owned by IFPMA/MSSO, not relicensable here |
A licence set here covers this project's own contribution and cannot extend to MedDRA, whose
preferred terms populate the pt column. Those arrive through FDA's public-domain release rather
than the MedDRA distribution, and the pipeline needs no MedDRA licence to run — it performs no
terminology lookup, treating the strings as opaque labels. Everything else in the output is
unencumbered, so anyone who would rather not engage with MedDRA terms can drop that one column and
keep the rest.
FDA Adverse Event Reporting System quarterly extracts, 2004Q1–2026Q2, from fis.fda.gov. Public domain.
FDA republishes quarterly extracts as snapshots and can revise them in place, so
results/source_manifest.json pins a SHA-256 per archive and faers verify reports drift rather
than letting it silently change results.
van Puijenbroek et al. (2002) Pharmacoepidemiol Drug Saf 11:3–10 · DuMouchel (1999) Am Stat 53:177–190 · Norén et al. (2006) Stat Med 25:3740–3757 · Robins, Breslow & Greenland (1986) Biometrics 42:311–323