· 6 min read
Why Pharmacovigilance Benefits from Graph Use: Protecting public health with FAERS in AWS Neptune
The FDA's adverse-event database is distributed as relational tables, but its central question — which drug–reaction signals are elevated yet absent from the product label — is a graph anti-join over a terminology hierarchy. This is why that shape belongs in Amazon Neptune rather than a relational warehouse.
Abstract
This work converts the complete public release of the U.S. FDA Adverse Event Reporting System (FAERS) into RDF and loads it into Amazon Neptune for large-scale pharmacovigilance analysis. The design reuses established biomedical and Semantic Web vocabularies rather than minting bespoke terms, models each safety report as a provenance-bearing graph, and materializes drug–reaction disproportionality statistics as first-class observations. The principal analytical objective is the Unlabeled Signal query, which identifies drug–reaction associations that show statistically elevated reporting frequency but are not described in the corresponding FDA product label — a graph anti-join that operationalizes a core regulatory pharmacovigilance task. All data-acquisition and conversion stages have been executed and timed on local infrastructure; the cloud stages are fully specified and scripted. Measured to date: 89 quarterly releases (2004 Q1 – 2026 Q1) acquired and integrity-verified, 18.48 GB of ASCII source decompressed in 11.6 minutes of wall-clock time, and an RDF yield extrapolating to approximately 6.7 × 10⁸ triples after deduplication.
Data
The complete quarterly extract series was retrieved directly from the FDA distribution endpoint, spanning 89 quarters, 2004 Q1 through 2026 Q1. Releases prior to 2012 Q4 use the legacy AERS schema (aers_ascii_*, with a single ISR case key); from 2012 Q4 onward, releases use the modern FAERS schema (faers_ascii_*, with PRIMARYID/CASEID keys).
Acquisition was complete and lossless: 89 of 89 quarters retrieved, 3.24 GB compressed, no missing quarters, no zero-byte files, and zero archive-integrity failures under a full CRC test. All 89 archives were then decompressed in parallel (degree of parallelism P = 16) on a 72-core host:
| Metric | Value |
|---|---|
| Archives decompressed | 89 / 89 (0 failures) |
| Wall-clock time | 693.8 s (11.6 min) |
| Serial-equivalent time | 9,970.9 s (2.77 h) |
| Parallel speedup | 14.37× (≈ 90% efficiency at P = 16) |
| Expansion | 3.24 GB → 18.48 GB (5.7×) |
The slowest archives are recent quarters dominated by large DRUG and DEMO tables; they are NFS-write-bound rather than CPU-bound, so concurrency beyond P = 16 yields diminishing returns.
Model
The model reuses established vocabularies wherever a suitable term exists, and mints terms in a project namespace (fv:) only for concepts with no standard equivalent:
- PROV-O for provenance — each report
prov:wasDerivedFromits quarterly source. - SKOS for the MedDRA hierarchy —
skos:broaderlinks Preferred Term to HLT to HLGT to SOC. - RxNorm (RXCUI) as the normalization target for free-text drug names, and ATC for class-level rollups.
- RDF Data Cube for disproportionality statistics — PRR, ROR, EBGM, and χ² materialized as
qb:Observationvalues. - FHIR RDF for clinical alignment (
fv:SafetyReport ⊑ fhir:AdverseEvent) and OWL-Time for therapy intervals.
Each safety report is typed simultaneously as a project class and a FHIR resource and carries a provenance edge to its source quarter. Drug mentions are reified: role, dose, route, indication, and therapy are properties of the mention within a case, not of the drug in general. This keeps the DRUG, INDI, and THER tables join-free and streamable during conversion, and it suits Neptune’s bulk loader. The ontology validates at 544 triples.
Conversion runs on-premises, on the host that already stores the data, as a single-pass streaming program that uses only the Python standard library and builds no in-memory graph — a requirement at the billion-triple scale. For each CASEID it retains only the latest CASEVERSION, following the AEOLUS deduplication method, and it branches on schema era to handle the legacy ISR-keyed and modern PRIMARYID-keyed releases separately. The measured yield of 49–60 triples per case extrapolates to approximately 6.7 × 10⁸ triples after cross-quarter deduplication. The openFDA label parser streams each JSON part incrementally, emitting fv:labeledReaction edges from the openfda.rxcui array and the labeled adverse-reaction sections.
Neptune
Amazon Neptune is a managed graph database supporting RDF and SPARQL 1.1. Two of its capabilities are load-bearing here. First, it evaluates unbounded property paths (skos:broader*) directly in the query engine. Second, its Bulk Loader ingests N-Triples from Amazon S3 at billion-triple scale. Neptune has no public endpoint and is reached from within its VPC; the recommended topology is a dedicated VPC, a private cluster, a small loader/jump host in the same VPC, an S3 staging bucket colocated in region, and a VPC S3 gateway endpoint so the private cluster can read the bucket during load.
Neptune is memory-bound: query performance depends on retaining the working set in the buffer pool. The design therefore loads on a large writer (db.r6g.8xlarge, 32 vCPU / 256 GiB) and resizes to a smaller steady-state query instance (db.r6g.2xlarge) without reloading, since compute and storage are decoupled. Storage is configured I/O-Optimized for the load phase to eliminate per-request I/O charges.
Because the ETL is performed on-premises and the cluster is torn down after the run, a single end-to-end execution — provision, load, query, teardown — is inexpensive, dominated by instance-hours. The principal risk is a cluster left running, which the scripted stop-and-teardown procedures are designed to prevent. Neptune ML adds graph-neural-network link prediction over the same loaded graph, with no export step.
Method
For each drug–reaction pair, a 2×2 contingency table over all reports yields the Proportional Reporting Ratio (PRR), the Reporting Odds Ratio (ROR), a Yates-corrected χ² statistic, and the Empirical Bayes Geometric Mean (EBGM). These are precomputed and materialized as fv:DrugReactionSignal observations, so analytical queries read stored values rather than recomputing contingency tables at query time.
A candidate Unlabeled Signal meets the conventional screening thresholds (PRR ≥ 2, N ≥ 3 cases, χ² ≥ 4) and has no labeled association to that reaction, where “labeled” is evaluated reflexively-transitively over the MedDRA hierarchy — an on-label association to any ancestor term counts as labeled:
FILTER NOT EXISTS {
?reaction skos:broader* ?labeledTerm .
?drug fv:labeledReaction ?labeledTerm .
}The skos:broader* step is the operative detail. The * makes the ancestor walk reflexive and unbounded, so a label attached at any level of the MedDRA tree — Preferred Term, High-Level Term, High-Level Group Term, or System Organ Class — discharges the signal, and Neptune evaluates that closure within the query engine. The relational equivalent is a recursive CTE over a precomputed transitive-closure table, which must be rebuilt whenever the terminology changes.
Four further analyses support the broader study, each expressed as a traversal or aggregation over the same graph rather than a separate pipeline: disproportionality top-N, drug–drug interaction cliques over co-suspect mentions, System-Organ-Class rollups, and phenotypic drug–drug similarity (Jaccard over shared reaction fingerprints).
The full technical report — data model, ontology, conversion pipeline, Neptune architecture, the complete query suite, and the measured runtimes and cost projections — is available as a PDF:
FAERS as a Semantic Graph in Amazon Neptune (PDF)
RRecktek LLC — July 2026



