| Title: | Validate Patient Cohorts for Real-World Evidence Studies on the OMOP Common Data Model |
|---|---|
| Description: | Validates instantiated patient cohorts on an Observational Medical Outcomes Partnership (OMOP) Common Data Model (CDM) database for real-world-evidence (RWE) studies. From a single function call it produces a structured validation report in Hypertext Markup Language (HTML) and JavaScript Object Notation (JSON) covering concept coverage, cohort attrition, temporal data density, and covariate feasibility against a comparator. The checks are aligned with the United States Food and Drug Administration (FDA) guidance on real-world data and evidence, FDA (2023) <https://www.fda.gov/media/171667/download>, the Harmonized Protocol Template to Enhance Reproducibility (HARPER), Wang and others (2022) <doi:10.1002/pds.5507>, and the Reporting of Studies Conducted Using Observational Routinely-Collected Data for Pharmacoepidemiology (RECORD-PE) statement, Langan and others (2018) <doi:10.1136/bmj.k3532>. A self-contained example database is bundled so the checks can be run without a live database connection. |
| Authors: | Hamid Tavakoli [aut, cre] |
| Maintainer: | Hamid Tavakoli <[email protected]> |
| License: | MIT + file LICENSE |
| Version: | 0.1.2 |
| Built: | 2026-07-21 14:51:34 UTC |
| Source: | https://github.com/cran/rwevalidate |
Collects each module's flags into one row per validation section, mapping to the regulatory framework the section addresses.
build_check_summary(results)build_check_summary(results)
results |
The combined results list (named module outputs, e.g.
|
A data.frame with columns section, status, maps_to, detail.
Opens a PostgreSQL connection via RPostgres and validates that the required
clinical tables (person, observation_period, condition_occurrence,
drug_exposure, measurement, visit_occurrence, death) exist in
cdm_schema and the vocabulary tables (concept, concept_ancestor) exist
in vocab_schema before returning. For split-schema CDMs (clinical and
vocabulary in different schemas) pass vocab_schema; it defaults to
cdm_schema for single-schema builds.
cdm_connect( host = "localhost", port = 5432, dbname, user, password, cdm_schema, vocab_schema = cdm_schema )cdm_connect( host = "localhost", port = 5432, dbname, user, password, cdm_schema, vocab_schema = cdm_schema )
host |
Database host. Default |
port |
Database port. Default |
dbname |
Database name. |
user |
Database user. |
password |
Database password. |
cdm_schema |
Schema holding the clinical CDM tables. |
vocab_schema |
Schema holding the vocabulary tables. Defaults to
|
A live DBI connection to the CDM database.
## Not run: # Requires a live PostgreSQL OMOP CDM server and real credentials, so this # cannot be executed during checks. For a runnable, database-free demo of # the package, see example_cdm() and validate_cohort(). con <- cdm_connect( dbname = "omop", user = "me", password = "secret", cdm_schema = "mimiciv_omop", vocab_schema = "vocab" ) cdm_disconnect(con) ## End(Not run)## Not run: # Requires a live PostgreSQL OMOP CDM server and real credentials, so this # cannot be executed during checks. For a runnable, database-free demo of # the package, see example_cdm() and validate_cohort(). con <- cdm_connect( dbname = "omop", user = "me", password = "secret", cdm_schema = "mimiciv_omop", vocab_schema = "vocab" ) cdm_disconnect(con) ## End(Not run)
Disconnect from a CDM database
cdm_disconnect(con)cdm_disconnect(con)
con |
A live |
Invisibly returns the result of DBI::dbDisconnect().
# Works on any DBI connection, demonstrated here on the bundled in-memory # DuckDB example CDM (no database server needed). if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() cdm_disconnect(con) }# Works on any DBI connection, demonstrated here on the bundled in-memory # DuckDB example CDM (no database server needed). if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() cdm_disconnect(con) }
Flags are strings prefixed "FAIL:" or "WARN:" (see the module functions).
Any FAIL -> "fail" (red); else any WARN -> "warn" (amber); else
"pass" (green).
classify_status(flags)classify_status(flags)
flags |
Character vector of flag messages (may be empty). |
A single string: "pass", "warn", or "fail".
Builds a tiny synthetic OMOP CDM (20 persons) in an in-memory DuckDB database
and returns a live connection. Every table rwevalidate queries for the
concept, attrition, density, and covariate modules is populated, together
with a cohort table that has the standard subject_id,
cohort_definition_id, cohort_start_date, and cohort_end_date columns.
The cohort table carries two arms so all four modules can be demonstrated:
persons 1-10 are the target cohort (cohort_definition_id = 1) and persons
11-20 are a comparator (cohort_definition_id = 2). The clinical tables and
a minimal vocabulary both live in the default main schema.
example_cdm()example_cdm()
This exists so the package can be tried, and its examples can run, without a
live database connection. It is a synthetic demo fixture, not a substitute
for a real CDM. The duckdb package (a soft dependency) must be installed.
The caller owns the returned connection and should close it with
cdm_disconnect().
A live DBI connection to an in-memory DuckDB OMOP CDM.
if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() attrition <- run_attrition(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, vocab_schema = "main") print(attrition$cohort_size) cdm_disconnect(con) }if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() attrition <- run_attrition(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, vocab_schema = "main") print(attrition$cohort_size) cdm_disconnect(con) }
Renders the bundled R Markdown template to a self-contained HTML report and
writes a machine-readable validation_results.json alongside it.
render_validation_report( results, output_dir, cdm_schema, cohort_id, package_version = as.character(utils::packageVersion("rwevalidate")), run_date = Sys.time(), quiet = TRUE )render_validation_report( results, output_dir, cdm_schema, cohort_id, package_version = as.character(utils::packageVersion("rwevalidate")), run_date = Sys.time(), quiet = TRUE )
results |
Combined results list (e.g. |
output_dir |
Directory to write the report into (created if needed). |
cdm_schema |
CDM schema name (for the report header / JSON). |
cohort_id |
Cohort definition id (for the report header / JSON). |
package_version |
Package version string. Defaults to the installed
|
run_date |
Timestamp for the run. Defaults to |
quiet |
Passed to |
Invisibly, a list with html, json, and check_summary.
Profiles an instantiated cohort: size, index-date distribution, prior observation coverage, and demographics at index. Maps to HARPER section 5 and RECORD-PE item 6. All thresholds are arguments.
run_attrition( con, cdm_schema, cohort_table, cohort_id, obs_window = c(-365, 0), vocab_schema = "vocab", obs_warn_pct = 70, obs_fail_pct = 50, attrition_warn_threshold = 0.5 )run_attrition( con, cdm_schema, cohort_table, cohort_id, obs_window = c(-365, 0), vocab_schema = "vocab", obs_warn_pct = 70, obs_fail_pct = 50, attrition_warn_threshold = 0.5 )
con |
A live |
cdm_schema |
Schema holding the clinical CDM tables (e.g. |
cohort_table |
Cohort table, schema-qualified
(e.g. |
cohort_id |
Integer cohort definition id to profile. |
obs_window |
Length-2 numeric |
vocab_schema |
Schema holding the vocabulary tables ( |
obs_warn_pct, obs_fail_pct
|
Prior-observation coverage thresholds (percent
of cohort with sufficient prior observation). Below |
attrition_warn_threshold |
Reserved for stepwise inclusion-criteria attrition (single-criterion drop fraction). Not exercised in v0.1 because criteria lists are not yet a supported input. Default 0.5. |
This profiles the cohort as it was handed in. It reports the cohort-level counts that item 6 asks for, but it does not yet rebuild a step-by-step attrition flowchart (how many subjects each inclusion or exclusion criterion removed), because the cohort table does not carry the criteria that built it. Per-criterion attrition is planned for a later version.
A named list:
integer - distinct subjects in the cohort
data.frame of index_year, n
data.frame: cohort_n, n_sufficient, pct_sufficient, min_prior_days.
cohort_n counts every cohort subject. A subject whose index date falls
outside any observation period counts as insufficient prior observation
rather than being dropped, so cohort_n matches cohort_size.
data.frame: sex, mean_age, min_age, max_age, n
character vector of WARN:/FAIL: messages (empty if all pass)
if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() res <- run_attrition(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, vocab_schema = "main") res$cohort_size res$demographics cdm_disconnect(con) }if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() res <- run_attrition(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, vocab_schema = "main") res$cohort_size res$demographics cdm_disconnect(con) }
Profiles whether the cohort-defining concepts are present, well-mapped, and richly represented in the CDM. Maps to FDA RWE Relevance (are the data fit for the question?). This is the first module to read the vocabulary schema.
run_concepts( con, cdm_schema, cohort_table = NULL, cohort_id = NULL, concept_ids, domain = "condition", vocab_schema = "vocab", prevalence_warn_pct = 10, prevalence_fail_pct = 1, unmapped_warn_pct = 20 )run_concepts( con, cdm_schema, cohort_table = NULL, cohort_id = NULL, concept_ids, domain = "condition", vocab_schema = "vocab", prevalence_warn_pct = 10, prevalence_fail_pct = 1, unmapped_warn_pct = 20 )
con |
A live |
cdm_schema |
Schema holding the clinical CDM tables. |
cohort_table |
Cohort table, schema-qualified. Optional and currently unused; reserved for cohort-restricted prevalence in a later version. |
cohort_id |
Integer cohort definition id. Optional and reserved, see
|
concept_ids |
Numeric vector of cohort-defining seed concept id(s). Their
descendants are expanded via |
domain |
Domain the seed concepts live in; one of the names of the
internal domain map ( |
vocab_schema |
Schema holding the vocabulary tables. Default |
prevalence_warn_pct, prevalence_fail_pct
|
Concept-prevalence thresholds
(percent of CDM persons with at least one descendant record). Below warn
flags |
unmapped_warn_pct |
Per-domain unmapped-record threshold (percent mapped
to |
A named list:
data.frame: n_with, n_total, pct
data.frame: total_descendants,
present_descendants, pct_present
data.frame: domain, n_records, n_unmapped,
pct_mapped
character vector of WARN:/FAIL: messages
if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() # 316139 = "Heart failure" (SNOMED), the seed concept in the demo CDM. res <- run_concepts(con, cdm_schema = "main", concept_ids = 316139, domain = "condition", vocab_schema = "main") res$prevalence res$mapping_by_domain cdm_disconnect(con) }if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() # 316139 = "Heart failure" (SNOMED), the seed concept in the demo CDM. res <- run_concepts(con, cdm_schema = "main", concept_ids = 316139, domain = "condition", vocab_schema = "main") res$prevalence res$mapping_by_domain cdm_disconnect(con) }
Compares a target cohort against a comparator on baseline covariates:
standardized mean differences (SMD love-plot data), a per-arm prevalence table,
and a simple power calculation. Maps to HARPER Sec.4 (comparability). Runs only
when a comparator_id is supplied.
run_covariates( con, cdm_schema, cohort_table, cohort_id, comparator_id, vocab_schema = "vocab", covariate_window = c(-365, 0), top_n = 20, smd_warn_threshold = 0.1, power_baseline_rate = 0.15, power_effect_rate = 0.3, power_sig_level = 0.05, power_warn_threshold = 0.8 )run_covariates( con, cdm_schema, cohort_table, cohort_id, comparator_id, vocab_schema = "vocab", covariate_window = c(-365, 0), top_n = 20, smd_warn_threshold = 0.1, power_baseline_rate = 0.15, power_effect_rate = 0.3, power_sig_level = 0.05, power_warn_threshold = 0.8 )
con |
A live |
cdm_schema |
Schema holding the clinical CDM tables. |
cohort_table |
Cohort table, schema-qualified, containing both arms. |
cohort_id |
Integer cohort definition id of the target arm. |
comparator_id |
Integer cohort definition id of the comparator arm. |
vocab_schema |
Schema holding the vocabulary tables. Default |
covariate_window |
Length-2 numeric |
top_n |
Number of top conditions/drugs to profile. Default 20. |
smd_warn_threshold |
Absolute SMD above which a covariate is flagged imbalanced. Default 0.1 (standard). |
power_baseline_rate, power_effect_rate
|
Assumed comparator and target event rates for the two-proportion power calculation. Defaults 0.15, 0.30. |
power_sig_level |
Significance level for the power calculation. Default 0.05. |
power_warn_threshold |
Power below this value flags a |
A named list: smd_table, prevalence_table, power, flags.
if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() # The demo CDM ships two arms: target = 1, comparator = 2. res <- run_covariates(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, comparator_id = 2, vocab_schema = "main") res$smd_table res$power cdm_disconnect(con) }if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() # The demo CDM ships two arms: target = 1, comparator = 2. res <- run_covariates(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, comparator_id = 2, vocab_schema = "main") res$smd_table res$power cdm_disconnect(con) }
Profiles longitudinal data accrual around the index date: record density per patient per month across clinical domains, and follow-up completeness with a censoring-reason breakdown. Maps to FDA RWE Reliability (data accrual).
run_density( con, cdm_schema, cohort_table, cohort_id, obs_window = c(-365, 365), sparse_warn_min = 1 )run_density( con, cdm_schema, cohort_table, cohort_id, obs_window = c(-365, 365), sparse_warn_min = 1 )
con |
A live |
cdm_schema |
Schema holding the clinical CDM tables (e.g. |
cohort_table |
Cohort table, schema-qualified. |
cohort_id |
Integer cohort definition id. |
obs_window |
Length-2 numeric |
sparse_warn_min |
Minimum total records a domain must have within the
window before it is considered present; domains below this flag |
A named list:
data.frame: domain, months_from_index,
n_patients, n_records, records_per_patient
data.frame: one row per censoring_reason with
n and median_followup_days (median over that group)
per-subject data.frame: subject_id,
effective_end_date, followup_days, censoring_reason
character vector of WARN:/FAIL: messages
if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() res <- run_density(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1) head(res$density_by_domain) res$followup_summary cdm_disconnect(con) }if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() res <- run_density(con, cdm_schema = "main", cohort_table = "cohort", cohort_id = 1) head(res$density_by_domain) res$followup_summary cdm_disconnect(con) }
One-call entry point: connects to the OMOP CDM (or uses a supplied connection), runs the attrition (Module 2) and temporal-density (Module 3) checks, and produces a self-contained HTML report plus a JSON sidecar aligned with FDA RWE and HARPER frameworks.
validate_cohort( cdm_schema, cohort_table, cohort_id = 1, con = NULL, vocab_schema = "vocab", concept_ids = NULL, concept_domain = "condition", comparator_id = NULL, obs_window = c(-365, 0), density_window = c(-365, 365), output_dir = "./validation_report", host = "localhost", port = 5432, dbname = NULL, user = NULL, password = NULL, render_html = TRUE, quiet = TRUE )validate_cohort( cdm_schema, cohort_table, cohort_id = 1, con = NULL, vocab_schema = "vocab", concept_ids = NULL, concept_domain = "condition", comparator_id = NULL, obs_window = c(-365, 0), density_window = c(-365, 365), output_dir = "./validation_report", host = "localhost", port = 5432, dbname = NULL, user = NULL, password = NULL, render_html = TRUE, quiet = TRUE )
cdm_schema |
Schema holding the clinical CDM tables (e.g. |
cohort_table |
Cohort table, schema-qualified
(e.g. |
cohort_id |
Integer cohort definition id. Default 1. |
con |
Optional open |
vocab_schema |
Schema holding the vocabulary tables. Default |
concept_ids |
Optional numeric vector of cohort-defining seed concept
id(s). When supplied, Module 1 (concept coverage) runs and populates report
Section 2. When |
concept_domain |
Domain the seed concepts live in ( |
comparator_id |
Optional integer cohort definition id of a comparator
arm. When supplied, Module 4 (covariate feasibility) runs and populates
report Section 5. When |
obs_window |
Length-2 numeric |
density_window |
Length-2 numeric |
output_dir |
Directory for the report + JSON. Default
|
host, port, dbname, user, password
|
PostgreSQL connection arguments, used
only when |
render_html |
Render the HTML report. Default |
quiet |
Passed to |
Supply either an open con (e.g. for DuckDB testing) or PostgreSQL connection
arguments (dbname, user, password, ...). Connections opened internally
are closed on exit; a connection passed in via con is left open.
Against a live PostgreSQL OMOP CDM, a typical call looks like:
validate_cohort( cdm_schema = "mimic_cdm", cohort_table = "results.rwevalidate_test_cohort", cohort_id = 1, dbname = "FHIR", user = "me", password = "secret", vocab_schema = "vocab", output_dir = "./validation_report" )
Invisibly, a list with results (module outputs + data_source),
report (paths + check summary), and flags (all collected flags).
# Runnable demo on a small in-memory DuckDB CDM (no database needed). # render_html = FALSE writes only the JSON sidecar, so Pandoc is not required. if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() out <- validate_cohort( cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, con = con, vocab_schema = "main", output_dir = tempfile("rwe_demo_"), render_html = FALSE ) print(out$report$check_summary) cdm_disconnect(con) }# Runnable demo on a small in-memory DuckDB CDM (no database needed). # render_html = FALSE writes only the JSON sidecar, so Pandoc is not required. if (requireNamespace("duckdb", quietly = TRUE)) { con <- example_cdm() out <- validate_cohort( cdm_schema = "main", cohort_table = "cohort", cohort_id = 1, con = con, vocab_schema = "main", output_dir = tempfile("rwe_demo_"), render_html = FALSE ) print(out$report$check_summary) cdm_disconnect(con) }
Exposed separately so results can be persisted even when Pandoc is unavailable for HTML rendering.
write_results_json( results, output_dir, cdm_schema, cohort_id, package_version = as.character(utils::packageVersion("rwevalidate")), run_date = Sys.time(), check_summary = NULL )write_results_json( results, output_dir, cdm_schema, cohort_id, package_version = as.character(utils::packageVersion("rwevalidate")), run_date = Sys.time(), check_summary = NULL )
results |
Combined results list (e.g. |
output_dir |
Directory to write the report into (created if needed). |
cdm_schema |
CDM schema name (for the report header / JSON). |
cohort_id |
Cohort definition id (for the report header / JSON). |
package_version |
Package version string. Defaults to the installed
|
run_date |
Timestamp for the run. Defaults to |
check_summary |
Optional pre-built summary; rebuilt if |
The path to the written JSON file (invisibly).