Skip to content

OHDSI/OmopSketch

Repository files navigation

OmopSketch OmopSketch website

Lifecycle: experimental R-CMD-check CRAN status Codecov test coverage

WARNING: this package is under-development and has only been tested using mock data

The goal of OmopSketch is to characterise and visualise an OMOP CDM instance to asses if it meets the necessary criteria to answer a specific clinical question and conduct a certain study.

Installation

You can install the development version of OmopSketch from GitHub with:

# install.packages("remotes")
remotes::install_github("OHDSI/OmopSketch")

Example

Let’s start by creating a cdm object using the Eunomia mock dataset:

library(duckdb)
#> Loading required package: DBI
library(CDMConnector)
library(dplyr, warn.conflicts = FALSE)
library(OmopSketch)
con <- dbConnect(duckdb(), eunomia_dir())
cdm <- cdmFromCon(con = con, cdmSchema = "main", writeSchema = "main")
#> Note: method with signature 'DBIConnection#Id' chosen for function 'dbExistsTable',
#>  target signature 'duckdb_connection#Id'.
#>  "duckdb_connection#ANY" would also be valid
cdm
#> 
#> ── # OMOP CDM reference (duckdb) of Synthea synthetic health database ──────────
#> • omop tables: person, observation_period, visit_occurrence, visit_detail,
#> condition_occurrence, drug_exposure, procedure_occurrence, device_exposure,
#> measurement, observation, death, note, note_nlp, specimen, fact_relationship,
#> location, care_site, provider, payer_plan_period, cost, drug_era, dose_era,
#> condition_era, metadata, cdm_source, concept, vocabulary, domain,
#> concept_class, concept_relationship, relationship, concept_synonym,
#> concept_ancestor, source_to_concept_map, drug_strength
#> • cohort tables: -
#> • achilles tables: -
#> • other tables: -

Snapshot

We first create a snapshot of our database. This will allow us to track when the analysis has been conducted and capture details about the CDM version or the data release.

summariseOmopSnapshot(cdm) |>
  tableOmopSnapshot(type = "flextable")
#> ! Results have not been suppressed.

Characterise the clinical tables

Once we have collected the snapshot information, we can start characteristing the clinical tables of the CDM. By using summariseClinicalRecords() and tableClinicalRecords(), we can easily visualise the main characteristics of specific clinical tables.

summariseClinicalRecords(cdm, c("condition_occurrence", "drug_exposure")) |>
  tableClinicalRecords(type = "flextable")
#> ℹ Summarising table counts
#> ℹ The following estimates will be computed:
#> → Start summary of data, at 2024-09-25 12:14:06.676817
#> 
#> ✔ Summary finished, at 2024-09-25 12:14:06.815944
#> ℹ Summarising records per person
#> ℹ The following estimates will be computed:
#> • records_per_person: mean, sd, median, q25, q75, min, max
#> ! Table is collected to memory as not all requested estimates are supported on
#>   the database side
#> → Start summary of data, at 2024-09-25 12:14:07.908258
#> 
#> ✔ Summary finished, at 2024-09-25 12:14:07.955041
#> ℹ Summarising in_observation, standard, domain_id, and type information
#> ℹ Summarising table counts
#> ℹ The following estimates will be computed:
#> → Start summary of data, at 2024-09-25 12:14:11.725276
#> 
#> ✔ Summary finished, at 2024-09-25 12:14:11.877293
#> ℹ Summarising records per person
#> ℹ The following estimates will be computed:
#> • records_per_person: mean, sd, median, q25, q75, min, max
#> ! Table is collected to memory as not all requested estimates are supported on
#>   the database side
#> → Start summary of data, at 2024-09-25 12:14:12.808874
#> 
#> ✔ Summary finished, at 2024-09-25 12:14:12.850686
#> ℹ Summarising in_observation, standard, domain_id, and type information
#> ! Results have not been suppressed.

We can also explore trends in the clinical table records over time.

summariseRecordCount(cdm, c("condition_occurrence", "drug_exposure")) |>
  plotRecordCount(facet = "omop_table")
#> ! The following column type were changed:
#> • variable_level: from double to character

### Characterise the observation period After visualising the main characteristics of our clinical tables, we can explore the observation period details. OmopSketch provides several functions to have an overwied of the dataset study period.

Using summariseInObservation() and plotInObservation(), we can gather information on the number of records per year.

summariseInObservation(cdm$observation_period, output = "records") |>
  plotInObservation()
#> ! The following column type were changed:
#> • variable_level: from double to character

You can also visualise and explore the characteristics of the observation period per each individual in the database using summariseObservationPeriod().

summariseObservationPeriod(cdm$observation_period) |>
  tableObservationPeriod(type = "flextable")
#> ! Results have not been suppressed.

Or if visualisation is prefered, you can easily build a histogram to explore how many participants have more than one observation period.

summariseObservationPeriod(cdm$observation_period) |>
  plotObservationPeriod()

Characterise the concepts

OmopSketch also provides functions to explore some of (or all) the concepts in the dataset.

acetaminophen <- c(1125315,  1127433, 1127078)

summariseConceptCounts(cdm, conceptId = list("acetaminophen" = acetaminophen)) |>
  filter(estimate_name == "record_count") |> 
  plotConceptCounts()
#> ℹ Getting use of codes from acetaminophen
#> ! The following column type were changed:
#> • variable_name: from integer to character

Characterise the population

Finally, OmopSketch can also help us to characterise the population at the start and end of the observation period.

summarisePopulationCharacteristics(cdm) |>
  tablePopulationCharacteristics(type = "flextable")
#> Warning: ! 1 casted column in og_015_1727262876 (cohort_set) as do not match expected
#>   column type:
#> • `cohort_definition_id` from numeric to integer
#> Warning: ! 1 column in og_015_1727262876 do not match expected column type:
#> • `cohort_definition_id` is numeric but expected integer
#> ! cohort columns will be reordered to match the expected order:
#>   cohort_definition_id, subject_id, cohort_start_date, and cohort_end_date.
#> ℹ Building new trimmed cohort
#> Warning: ! 1 column in tmp_011_og_017_1727262877 do not match expected column type:
#> • `cohort_definition_id` is numeric but expected integer
#> Creating initial cohort
#> ! cohort columns will be reordered to match the expected order:
#>   cohort_definition_id, subject_id, cohort_start_date, and cohort_end_date.
#> ! cohort columns will be reordered to match the expected order:
#>   cohort_definition_id, subject_id, cohort_start_date, and cohort_end_date.
#> ✔ Cohort trimmed
#> ℹ adding demographics columns
#> 
#> ℹ summarising data
#> 
#> ✔ summariseCharacteristics finished!
#> 
#> ! The following column type were changed:
#> • variable_name: from integer to character
#> ! Results have not been suppressed.
<style>#yucwhpcvkx table { font-family: system-ui, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } #yucwhpcvkx thead, #yucwhpcvkx tbody, #yucwhpcvkx tfoot, #yucwhpcvkx tr, #yucwhpcvkx td, #yucwhpcvkx th { border-style: none; } #yucwhpcvkx p { margin: 0; padding: 0; } #yucwhpcvkx .gt_table { display: table; border-collapse: collapse; line-height: normal; margin-left: auto; margin-right: auto; color: #333333; font-size: 16px; font-weight: normal; font-style: normal; background-color: #FFFFFF; width: auto; border-top-style: solid; border-top-width: 2px; border-top-color: #A8A8A8; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #A8A8A8; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; } #yucwhpcvkx .gt_caption { padding-top: 4px; padding-bottom: 4px; } #yucwhpcvkx .gt_title { color: #333333; font-size: 125%; font-weight: initial; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; border-bottom-color: #FFFFFF; border-bottom-width: 0; } #yucwhpcvkx .gt_subtitle { color: #333333; font-size: 85%; font-weight: initial; padding-top: 3px; padding-bottom: 5px; padding-left: 5px; padding-right: 5px; border-top-color: #FFFFFF; border-top-width: 0; } #yucwhpcvkx .gt_heading { background-color: #FFFFFF; text-align: center; border-bottom-color: #FFFFFF; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; } #yucwhpcvkx .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #yucwhpcvkx .gt_col_headings { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; } #yucwhpcvkx .gt_col_heading { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 6px; padding-left: 5px; padding-right: 5px; overflow-x: hidden; } #yucwhpcvkx .gt_column_spanner_outer { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: normal; text-transform: inherit; padding-top: 0; padding-bottom: 0; padding-left: 4px; padding-right: 4px; } #yucwhpcvkx .gt_column_spanner_outer:first-child { padding-left: 0; } #yucwhpcvkx .gt_column_spanner_outer:last-child { padding-right: 0; } #yucwhpcvkx .gt_column_spanner { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: bottom; padding-top: 5px; padding-bottom: 5px; overflow-x: hidden; display: inline-block; width: 100%; } #yucwhpcvkx .gt_spanner_row { border-bottom-style: hidden; } #yucwhpcvkx .gt_group_heading { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; text-align: left; } #yucwhpcvkx .gt_empty_group_heading { padding: 0.5px; color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; vertical-align: middle; } #yucwhpcvkx .gt_from_md > :first-child { margin-top: 0; } #yucwhpcvkx .gt_from_md > :last-child { margin-bottom: 0; } #yucwhpcvkx .gt_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; margin: 10px; border-top-style: solid; border-top-width: 1px; border-top-color: #D3D3D3; border-left-style: none; border-left-width: 1px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 1px; border-right-color: #D3D3D3; vertical-align: middle; overflow-x: hidden; } #yucwhpcvkx .gt_stub { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; } #yucwhpcvkx .gt_stub_row_group { color: #333333; background-color: #FFFFFF; font-size: 100%; font-weight: initial; text-transform: inherit; border-right-style: solid; border-right-width: 2px; border-right-color: #D3D3D3; padding-left: 5px; padding-right: 5px; vertical-align: top; } #yucwhpcvkx .gt_row_group_first td { border-top-width: 2px; } #yucwhpcvkx .gt_row_group_first th { border-top-width: 2px; } #yucwhpcvkx .gt_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #yucwhpcvkx .gt_first_summary_row { border-top-style: solid; border-top-color: #D3D3D3; } #yucwhpcvkx .gt_first_summary_row.thick { border-top-width: 2px; } #yucwhpcvkx .gt_last_summary_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #yucwhpcvkx .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #yucwhpcvkx .gt_first_grand_summary_row { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-top-style: double; border-top-width: 6px; border-top-color: #D3D3D3; } #yucwhpcvkx .gt_last_grand_summary_row_top { padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; border-bottom-style: double; border-bottom-width: 6px; border-bottom-color: #D3D3D3; } #yucwhpcvkx .gt_striped { background-color: rgba(128, 128, 128, 0.05); } #yucwhpcvkx .gt_table_body { border-top-style: solid; border-top-width: 2px; border-top-color: #D3D3D3; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #yucwhpcvkx .gt_footnotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; } #yucwhpcvkx .gt_footnote { margin: 0px; font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #yucwhpcvkx .gt_sourcenotes { color: #333333; background-color: #FFFFFF; border-bottom-style: none; border-bottom-width: 2px; border-bottom-color: #D3D3D3; border-left-style: none; border-left-width: 2px; border-left-color: #D3D3D3; border-right-style: none; border-right-width: 2px; border-right-color: #D3D3D3; } #yucwhpcvkx .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #yucwhpcvkx .gt_left { text-align: left; } #yucwhpcvkx .gt_center { text-align: center; } #yucwhpcvkx .gt_right { text-align: right; font-variant-numeric: tabular-nums; } #yucwhpcvkx .gt_font_normal { font-weight: normal; } #yucwhpcvkx .gt_font_bold { font-weight: bold; } #yucwhpcvkx .gt_font_italic { font-style: italic; } #yucwhpcvkx .gt_super { font-size: 65%; } #yucwhpcvkx .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; } #yucwhpcvkx .gt_asterisk { font-size: 100%; vertical-align: 0; } #yucwhpcvkx .gt_indent_1 { text-indent: 5px; } #yucwhpcvkx .gt_indent_2 { text-indent: 10px; } #yucwhpcvkx .gt_indent_3 { text-indent: 15px; } #yucwhpcvkx .gt_indent_4 { text-indent: 20px; } #yucwhpcvkx .gt_indent_5 { text-indent: 25px; } #yucwhpcvkx .katex-display { display: inline-flex !important; margin-bottom: 0.75em !important; } #yucwhpcvkx div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after { height: 0px !important; } </style>
Variable name Variable level Estimate name Database name
Synthea synthetic health database
Number records - N 2,694
Number subjects - N 2,694
Cohort start date - Median [Q25 - Q75] 1961-03-18 [1950-07-13 - 1970-08-29]
Range 1908-09-22 to 1986-11-03
Cohort end date - Median [Q25 - Q75] 2018-12-14 [2018-08-02 - 2019-04-06]
Range 1945-07-20 to 2019-07-03
Age at start - Median [Q25 - Q75] 0 [0 - 0]
Mean (SD) 0.00 (0.00)
Range 0 to 0
Age at end - Median [Q25 - Q75] 57 [47 - 67]
Range 31 to 110
Sex Female N% 1,373 (50.97)
Male N% 1,321 (49.03)
Prior observation - Median [Q25 - Q75] 0 [0 - 0]
Mean (SD) 0.00 (0.00)
Range 0 to 0
Future observation - Median [Q25 - Q75] 20,870 [17,494 - 24,701]
Mean (SD) 21,601.60 (5,460.69)
Range 11,396 to 40,348

As seen, OmopSketch offers multiple functionalities to provide a general overview of a database. Additionally, it includes more tools and arguments that allow for deeper exploration, helping to assess the database’s suitability for specific research studies. For further information, please refer to the vignettes.