Common data models
OMOP, PCORnet, i2b2, Sentinel, and CDISC - schemas for analyzing health data at scale.
Sources last verified
A common data model (CDM) is a fixed schema that many institutions transform their data into, so that one analysis script runs unchanged everywhere. The point is not the schema itself — it is that the analysis becomes portable. Send the code to the data, get aggregate results back, and no patient-level data ever leaves the site.
That distributed-network property is why CDMs exist at all, and it is the thing to hold onto when comparing them.
The one distinction that matters
Every CDM makes a trade between harmonization and fidelity, and the two big US models sit on opposite sides of it:
- OMOP maps everything to standard concepts. An ICD-10-CM diagnosis becomes a SNOMED CT concept. Analyses are portable across countries and coding systems, but the mapping is lossy and you inherit someone else's judgment about equivalence.
- PCORnet keeps the source codes. An ICD-10-CM code stays an ICD-10-CM code. Closer to what the clinician recorded, but you do the harmonization yourself in every study.
OMOP always retains the original value in a _source_value column, so the loss is recoverable —
but the default analysis path uses the mapped concept, and most people never look back.
OMOP CDM
The dominant model for observational research, maintained by the OHDSI community.
- Introduced: OMOP began in 2008 as an FDA-backed public-private partnership; the OHDSI collaborative took over stewardship in 2014
- Purpose: Generate reliable evidence from observational data at scale — originally drug safety surveillance, now everything from characterization to patient-level prediction
- Current version: v5.5.0, released 25 August 2026. v5.4 remains the most widely deployed. v6.0 was published but never gained adoption and is effectively shelved — do not start there
- Encoding: Relational tables, in whatever database you like. DuckDB, PostgreSQL, Snowflake, BigQuery, Databricks and Spark are all common
- Status today: The largest network of its kind — hundreds of databases across dozens of countries, on the order of a billion patient records. EHDEN in Europe and N3C in the US both standardized on it
- Competing formats: PCORnet and Sentinel in the US; OMOP has clearly won internationally
The model is person-centric: every clinical fact points back to a person_id and carries a
date, which makes longitudinal queries natural.
-- Clinical event tables, all keyed to person_id
person(person_id, gender_concept_id, year_of_birth, race_concept_id, ...)
observation_period(person_id, observation_period_start_date, observation_period_end_date)
visit_occurrence(visit_occurrence_id, person_id, visit_concept_id, visit_start_date, ...)
condition_occurrence(condition_occurrence_id, person_id, condition_concept_id, ...)
drug_exposure(drug_exposure_id, person_id, drug_concept_id, drug_exposure_start_date, ...)
measurement(measurement_id, person_id, measurement_concept_id, value_as_number, unit_concept_id, ...)
-- Vocabulary tables: the same in every OMOP instance on earth
concept(concept_id, concept_name, domain_id, vocabulary_id, concept_code, standard_concept)
concept_relationship(concept_id_1, concept_id_2, relationship_id)
concept_ancestor(ancestor_concept_id, descendant_concept_id, min_levels_of_separation)The concept_ancestor table is the reason people put up with the ETL. It is a
pre-computed transitive closure of the SNOMED hierarchy, so "every patient with any form of
diabetes, however specifically coded" is one join rather than a hand-maintained code list:
-- All patients with type 2 diabetes or any descendant concept.
-- 201826 = "Type 2 diabetes mellitus" (SNOMED, via OMOP standard concepts)
SELECT DISTINCT co.person_id
FROM condition_occurrence co
JOIN concept_ancestor ca
ON co.condition_concept_id = ca.descendant_concept_id
WHERE ca.ancestor_concept_id = 201826;R is the first-class language for OMOP by a wide margin, and this is the strongest argument for choosing OMOP in the first place. The gap between the R and Python stacks is larger here than anywhere else in health data.
| Language | Package | Notes |
|---|---|---|
| R | CDMConnector | The modern entry point. Gives you a cdm object of lazy dbplyr tables — start here |
| R | DatabaseConnector, SqlRender | The OHDSI database layer. SqlRender translates one SQL dialect into ten, which is what makes network studies possible |
| R | CohortGenerator, CirceR, Capr | Build and materialize cohort definitions. Capr writes them as R code instead of ATLAS JSON |
| R | CodelistGenerator | Builds concept sets from the vocabulary tables systematically, rather than by hand |
| R | IncidencePrevalence, CohortSurvival, DrugUtilisation | Standard epidemiological analyses, pre-built and network-ready |
| R | CohortMethod, FeatureExtraction | Comparative effectiveness with propensity scores; large-scale covariate construction |
| R | Achilles, DataQualityDashboard, OmopSketch | Database characterization and data quality. Run these before trusting an ETL |
| R | Eunomia, omock | A small pre-built CDM and a mock-data generator. The fastest way to a queryable CDM |
| Python | ohdsi-common, ohdsi-database-connector, ohdsi-sqlrender, ohdsi-circe | Official OHDSI bindings — but they are JPype wrappers around the R/Java packages, not native ports |
| Python | pyomop | Native SQLAlchemy ORM for the CDM. Independent of OHDSI |
| Julia | OMOPCDMCohortCreator, OMOPCDMDatabaseConnector | Real packages from JuliaHealth, in the General registry. Narrow scope, small community |
| SQL | CommonDataModel | The official DDL for every CDM version and dialect |
| CLI | ATLAS / Broadsea | The web UI for cohort definition; Broadsea is the Docker deployment of the whole stack |
PCORnet CDM
The model behind PCORnet, the US National Patient-Centered Clinical Research Network.
- Introduced: 2014, funded by PCORI
- Purpose: Pragmatic clinical trials and observational studies across a US health-system network
- Current version: v6.x
- Encoding: Relational tables, historically SAS datasets
- Status today: Healthy within its network — a large group of US health systems — but essentially US-only and not used outside PCORnet
- Competing formats: OMOP, directly. Several institutions maintain both, and mapping between them is a recurring topic
Structurally it is encounter-centric rather than person-centric, and it preserves source
vocabularies. DIAGNOSIS keeps the ICD-10-CM code, with a DX_TYPE column recording which code
system it came from.
PCORnet tooling is SAS-first and largely unpublished. The Data Curation Query Package is SAS,
distributed to network members rather than released openly, and there is no CRAN or PyPI package
for the model. If you work with PCORnet outside the network's own tooling, you are writing SQL
against a documented schema and reading XPT files with haven or pyreadstat.
i2b2
Informatics for Integrating Biology and the Bedside — the cohort-discovery workhorse of academic medical centers.
- Introduced: ~2004, as an NIH-funded National Center for Biomedical Computing, from what is now Mass General Brigham
- Purpose: Let investigators self-serve feasibility counts — "how many patients here match these criteria?" — without a data request to the analytics team
- Encoding: A star schema in a relational database, built on entity-attribute-value
- Status today: Still very widely deployed, and often the fastest route to a count at a US academic center. Losing ground to OMOP for analysis, but its query UI remains hard to beat for its specific job
- Related: SHRINE federates i2b2 queries across institutions
The whole model is essentially one fact table:
observation_fact(encounter_num, patient_num, concept_cd, start_date, modifier_cd, ...)
patient_dimension(patient_num, birth_date, sex_cd, race_cd, ...)
concept_dimension(concept_path, concept_cd, name_char)
visit_dimension(encounter_num, patient_num, start_date, end_date, location_cd)The flexibility is real — any concept fits without a schema change — but so is the cost:
everything is a string comparison against concept_cd, and there is no type safety anywhere.
There is no maintained CRAN or PyPI client. Query the database directly, or use the web UI and export. The i2b2 core server is the reference implementation.
Sentinel
The FDA's active post-market safety surveillance system.
- Introduced: Mini-Sentinel in 2008, following the FDA Amendments Act of 2007; the full Sentinel System from 2016
- Purpose: Detect and evaluate safety signals for drugs and devices in near-real-time, across a very large distributed claims and EHR population
- Encoding: The Sentinel Common Data Model — relational, claims-oriented, SAS-based
- Status today: Operational and consequential — FDA regulatory decisions cite it — but it is not a model you adopt. You participate in Sentinel, or you do not
- Tooling: SAS, distributed as routine querying packages to data partners
Worth knowing exists, and worth knowing you almost certainly will not build on it directly.
CDISC (SDTM / ADaM)
The clinical trials standards. Categorically different from everything above: these are not optional, they are regulatory requirements.
- Introduced: CDISC founded 1997. SDTM from the mid-2000s; required by the FDA for new drug submissions since December 2016, and by Japan's PMDA
- Purpose: Standardize how trial data is submitted to regulators so reviewers see the same structure from every sponsor
- Status today: Universal in industry pharma, essentially absent from academic research. If you work in trials you will live in it; otherwise you may never touch it
- Competing formats: None. There is no alternative — this is what the regulator accepts
The pipeline is a fixed sequence, and the separation is deliberate: SDTM is what was observed, ADaM is what was analyzed, and a reviewer must be able to trace any analysis result back to a tabulation record.
| Standard | Role |
|---|---|
| CDASH | How data is collected on the case report form |
| SDTM | Tabulation — the observations as collected, one domain per topic (DM, AE, LB, VS) |
| ADaM | Analysis-ready datasets, derived from SDTM, one per analysis (ADSL, ADAE, ADLB) |
| Define-XML | The machine-readable data dictionary that ships with the submission |
| ODM | XML for exchanging CRF and trial data between systems |
| Dataset-JSON | The modern replacement for SAS XPT as a transport format |
STUDYID,DOMAIN,USUBJID,AESEQ,AETERM,AEDECOD,AESTDTC,AESEV,AESER,AEREL
STUDY01,AE,STUDY01-001-0012,1,Headache,Headache,2026-01-15,MILD,N,POSSIBLE
STUDY01,AE,STUDY01-001-0012,2,Nausea,Nausea,2026-01-18,MODERATE,N,PROBABLE
STUDY01,AE,STUDY01-002-0007,1,Rash on forearm,Rash,2026-02-02,MILD,N,UNLIKELYAETERM is what the investigator wrote; AEDECOD is that term mapped to
MedDRA. Keeping both — the verbatim and the coded — is
required, and is a good habit well beyond trials.
The required submission transport format is SAS Transport (XPT) Version 5, a format from 1988 with 8-character variable names and 40-character labels. This constraint shapes every CDISC variable name you will ever see. Dataset-JSON exists to replace it and is progressing through regulatory acceptance, but XPT v5 is still what goes in the submission.
| Language | Package | Notes |
|---|---|---|
| R | admiral | The pharmaverse ADaM toolkit. Now the mainstream open-source alternative to in-house SAS macros |
| R | sdtm.oak | EDC-agnostic SDTM creation. The newer half of the pair |
| R | metacore, metatools | Hold Define-XML metadata in R and build datasets from it |
| R | xportr, haven | Write submission-compliant XPT v5; read SAS files generally |
| R | datasetjson | Read and write CDISC Dataset-JSON |
| R | logrx | Execution logs for validated environments — a regulatory requirement, not a nicety |
| Python | pyreadstat, xport | Read and write XPT and SAS7BDAT |
| Python | odmlib | Work with ODM and Define-XML as Python objects |
| CLI | cdisc-rules-engine | CDISC's own open-source conformance checker. Run it before you submit |
| Julia | ReadStatTables.jl, SASLib.jl | Read SAS files. Nothing CDISC-aware beyond that |
Choosing between them
| OMOP | PCORnet | i2b2 | CDISC | |
|---|---|---|---|---|
| Primary use | Observational research | Pragmatic trials, network studies | Cohort discovery | Regulatory submission |
| Orientation | Person | Encounter | Fact (EAV) | Trial subject |
| Vocabularies | Mapped to standard concepts | Source codes retained | Local concept_cd paths | CDISC controlled terminology |
| Geography | International | US only | Mostly US academic | International |
| Best language | R | SAS | Java / SQL | R |
| Adopt it if | You want portable analyses and a real tool ecosystem | Your network already runs it | You need fast feasibility counts | You are submitting to a regulator |
For a new observational research project with a free choice, OMOP is the default, and the tooling is the reason. For anything heading to a regulator, CDISC is not a choice at all.