Skip to content

OHDSI/MeasurementDiagnostics

Repository files navigation

MeasurementDiagnostics

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

The MeasurementDiagnostics package provides tools to assess how measurements are recorded and used in data mapped to the OMOP Common Data Model.

Diagnostics can be run either on the full dataset or restricted to a specific cohort, helping users better understand data completeness, frequency, and value distributions for measurements of interest.

Installation

The package can be installed from CRAN:

install.packages("MeasurementDiagnostics")

Or you can install the development version of the package from GitHub:

# install.packages("devtools")
devtools::install_github("ohdsi/MeasurementDiagnostics")

Example

Suppose you are conducting a study that relies on measurements of respiratory function. Before using these measurements analytically, you may want to understand how frequently they are recorded, how values are stored, and whether they vary across subgroups. MeasurementDiagnostics can be used to explore these aspects.

For this example we’ll use the GiBleed mock data.

library(omock)
library(MeasurementDiagnostics)
library(OmopViewer)
library(omopgenerics)
cdm <- mockCdmFromDataset(datasetName = "GiBleed")
#> ℹ Reading GiBleed tables.
#> ℹ Adding drug_strength table.
#> ℹ Creating local <cdm_reference> object.
cdm
#> 
#> ── # OMOP CDM reference (local) of GiBleed ─────────────────────────────────────
#> • omop tables: care_site, cdm_source, concept, concept_ancestor, concept_class,
#> concept_relationship, concept_synonym, condition_era, condition_occurrence,
#> cost, death, device_exposure, domain, dose_era, drug_era, drug_exposure,
#> drug_strength, fact_relationship, location, measurement, metadata, note,
#> note_nlp, observation, observation_period, payer_plan_period, person,
#> procedure_occurrence, provider, relationship, source_to_concept_map, specimen,
#> visit_detail, visit_occurrence, vocabulary
#> • cohort tables: -
#> • achilles tables: -
#> • other tables: -

Now we have a cdm reference with our data, we will create a codelist with measurement concepts.

respiratory_function_codes <- list("respiratory_function" = c(4052083L, 4133840L, 3011505L))

And now we can run all measurement diagnostic checks, stratifying results by sex.

respiratory_function_measurements <- summariseMeasurementUse(
  cdm = cdm, codes = respiratory_function_codes, bySex = TRUE
)

The results include three main components:

  1. A summary of measurement use, including the number of subjects with measurements, the number of measurements per subject, and the time between measurements.

  2. A summary of measurement values recorded as numeric.

  3. A summary of measurement values recorded using concepts.

Visualise results: Tables

Tabular summaries can be produced using the corresponding table functions. For example, the following tables display summaries of numeric values and concept-based values:

tableMeasurementValueAsConcept(
  respiratory_function_measurements,
  hide = c("cdm_name", "domain_id", "value_as_concept_id")
)
<style>#noimttsrhs 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; } #noimttsrhs thead, #noimttsrhs tbody, #noimttsrhs tfoot, #noimttsrhs tr, #noimttsrhs td, #noimttsrhs th { border-style: none; } #noimttsrhs p { margin: 0; padding: 0; } #noimttsrhs .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: 3px; border-top-color: #D9D9D9; border-right-style: solid; border-right-width: 3px; border-right-color: #D9D9D9; border-bottom-style: solid; border-bottom-width: 3px; border-bottom-color: #D9D9D9; border-left-style: solid; border-left-width: 3px; border-left-color: #D9D9D9; } #noimttsrhs .gt_caption { padding-top: 4px; padding-bottom: 4px; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .gt_column_spanner_outer:first-child { padding-left: 0; } #noimttsrhs .gt_column_spanner_outer:last-child { padding-right: 0; } #noimttsrhs .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%; } #noimttsrhs .gt_spanner_row { border-bottom-style: hidden; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .gt_from_md > :first-child { margin-top: 0; } #noimttsrhs .gt_from_md > :last-child { margin-bottom: 0; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .gt_row_group_first td { border-top-width: 2px; } #noimttsrhs .gt_row_group_first th { border-top-width: 2px; } #noimttsrhs .gt_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #noimttsrhs .gt_first_summary_row { border-top-style: solid; border-top-color: #D3D3D3; } #noimttsrhs .gt_first_summary_row.thick { border-top-width: 2px; } #noimttsrhs .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; } #noimttsrhs .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #noimttsrhs .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; } #noimttsrhs .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; } #noimttsrhs .gt_striped { background-color: rgba(128, 128, 128, 0.05); } #noimttsrhs .gt_table_body { border-top-style: solid; border-top-width: 3px; border-top-color: #D9D9D9; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #noimttsrhs .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; } #noimttsrhs .gt_footnote { margin: 0px; font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #noimttsrhs .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; } #noimttsrhs .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #noimttsrhs .gt_left { text-align: left; } #noimttsrhs .gt_center { text-align: center; } #noimttsrhs .gt_right { text-align: right; font-variant-numeric: tabular-nums; } #noimttsrhs .gt_font_normal { font-weight: normal; } #noimttsrhs .gt_font_bold { font-weight: bold; } #noimttsrhs .gt_font_italic { font-style: italic; } #noimttsrhs .gt_super { font-size: 65%; } #noimttsrhs .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; } #noimttsrhs .gt_asterisk { font-size: 100%; vertical-align: 0; } #noimttsrhs .gt_indent_1 { text-indent: 5px; } #noimttsrhs .gt_indent_2 { text-indent: 10px; } #noimttsrhs .gt_indent_3 { text-indent: 15px; } #noimttsrhs .gt_indent_4 { text-indent: 20px; } #noimttsrhs .gt_indent_5 { text-indent: 25px; } #noimttsrhs .katex-display { display: inline-flex !important; margin-bottom: 0.75em !important; } #noimttsrhs div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after { height: 0px !important; } </style>
Concept name Concept ID Source concept name Source concept ID Variable name Value as concept name Estimate name
Sex
overall Female Male
respiratory_function
overall overall overall overall measurement_records No matching concept N (%) 8,728 (100.00%) 4,807 (100.00%) 3,921 (100.00%)
FEV1/FVC 3011505 FEV1/FVC 3011505 measurement_records No matching concept N (%) 2,320 (100.00%) 1,374 (100.00%) 946 (100.00%)
Measurement of respiratory function 4052083 Measurement of respiratory function 4052083 measurement_records No matching concept N (%) 4,088 (100.00%) 2,059 (100.00%) 2,029 (100.00%)
Spirometry 4133840 Spirometry 4133840 measurement_records No matching concept N (%) 2,320 (100.00%) 1,374 (100.00%) 946 (100.00%)
respiratory_function_measurements |>
  filterGroup(concept_name == "overall") |>
  tableMeasurementValueAsNumber(
    hide = c(
      "concept_name", "concept_id", "source_concept_name", "source_concept_id", 
      "domain_id", "unit_concept_id"
    )
  )
<style>#gtapsqzfif 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; } #gtapsqzfif thead, #gtapsqzfif tbody, #gtapsqzfif tfoot, #gtapsqzfif tr, #gtapsqzfif td, #gtapsqzfif th { border-style: none; } #gtapsqzfif p { margin: 0; padding: 0; } #gtapsqzfif .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: 3px; border-top-color: #D9D9D9; border-right-style: solid; border-right-width: 3px; border-right-color: #D9D9D9; border-bottom-style: solid; border-bottom-width: 3px; border-bottom-color: #D9D9D9; border-left-style: solid; border-left-width: 3px; border-left-color: #D9D9D9; } #gtapsqzfif .gt_caption { padding-top: 4px; padding-bottom: 4px; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .gt_bottom_border { border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .gt_column_spanner_outer:first-child { padding-left: 0; } #gtapsqzfif .gt_column_spanner_outer:last-child { padding-right: 0; } #gtapsqzfif .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%; } #gtapsqzfif .gt_spanner_row { border-bottom-style: hidden; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .gt_from_md > :first-child { margin-top: 0; } #gtapsqzfif .gt_from_md > :last-child { margin-bottom: 0; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .gt_row_group_first td { border-top-width: 2px; } #gtapsqzfif .gt_row_group_first th { border-top-width: 2px; } #gtapsqzfif .gt_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #gtapsqzfif .gt_first_summary_row { border-top-style: solid; border-top-color: #D3D3D3; } #gtapsqzfif .gt_first_summary_row.thick { border-top-width: 2px; } #gtapsqzfif .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; } #gtapsqzfif .gt_grand_summary_row { color: #333333; background-color: #FFFFFF; text-transform: inherit; padding-top: 8px; padding-bottom: 8px; padding-left: 5px; padding-right: 5px; } #gtapsqzfif .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; } #gtapsqzfif .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; } #gtapsqzfif .gt_striped { background-color: rgba(128, 128, 128, 0.05); } #gtapsqzfif .gt_table_body { border-top-style: solid; border-top-width: 3px; border-top-color: #D9D9D9; border-bottom-style: solid; border-bottom-width: 2px; border-bottom-color: #D3D3D3; } #gtapsqzfif .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; } #gtapsqzfif .gt_footnote { margin: 0px; font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #gtapsqzfif .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; } #gtapsqzfif .gt_sourcenote { font-size: 90%; padding-top: 4px; padding-bottom: 4px; padding-left: 5px; padding-right: 5px; } #gtapsqzfif .gt_left { text-align: left; } #gtapsqzfif .gt_center { text-align: center; } #gtapsqzfif .gt_right { text-align: right; font-variant-numeric: tabular-nums; } #gtapsqzfif .gt_font_normal { font-weight: normal; } #gtapsqzfif .gt_font_bold { font-weight: bold; } #gtapsqzfif .gt_font_italic { font-style: italic; } #gtapsqzfif .gt_super { font-size: 65%; } #gtapsqzfif .gt_footnote_marks { font-size: 75%; vertical-align: 0.4em; position: initial; } #gtapsqzfif .gt_asterisk { font-size: 100%; vertical-align: 0; } #gtapsqzfif .gt_indent_1 { text-indent: 5px; } #gtapsqzfif .gt_indent_2 { text-indent: 10px; } #gtapsqzfif .gt_indent_3 { text-indent: 15px; } #gtapsqzfif .gt_indent_4 { text-indent: 20px; } #gtapsqzfif .gt_indent_5 { text-indent: 25px; } #gtapsqzfif .katex-display { display: inline-flex !important; margin-bottom: 0.75em !important; } #gtapsqzfif div.Reactable > div.rt-table > div.rt-thead > div.rt-tr.rt-tr-group-header > div.rt-th-group:after { height: 0px !important; } </style>
CDM name Unit concept name Variable name Variable level Estimate name
Sex
overall Female Male
respiratory_function
GiBleed No matching concept Measurement records N 8,728 4,807 3,921
Value as number Median [Q25 – Q75]
Q05 – Q95
Q01 – Q99
Range
Missing value, N (%) 8,728 (100.00%) 4,807 (100.00%) 3,921 (100.00%)

Visualise results: Plots

Each diagnostic result can also be visualised using plotting functions. For instance, the time between measurements can be displayed using boxplots:

respiratory_function_measurements |>
  plotMeasurementSummary( 
    x = "sex", 
    colour = "sex",
    facet = NULL
  ) 

Visualise results: Shiny App

The package OmopViewer supports MeasurementDiagnostics results and provides a user-friendly way of quickly get a shiny app to visualise these results.

exportStaticApp(result = respiratory_function_measurements, directory = tempdir())