From 68e11044c911759e490d31613ef692b73303dd13 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sat, 29 Oct 2022 19:59:11 +0200 Subject: [PATCH 01/37] make mockthat optional --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ cran-comments.md | 2 +- tests/testthat/test-callback.R | 4 ++++ tests/testthat/test-concept.R | 2 ++ tests/testthat/test-data-utils.R | 2 ++ tests/testthat/test-setup-attach.R | 2 ++ tests/testthat/test-setup-download.R | 14 +++++++++++++- tests/testthat/test-setup-import.R | 2 ++ tests/testthat/test-utils-cli.R | 2 ++ 10 files changed, 33 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index ab94da3d..45de381a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Focused on (but not exclusive to) data sets hosted on PhysioNet functions for running arbitrary queries against available data sets, a system for defining clinical concepts and encoding their representations in tabular ICU data is presented. -Version: 0.5.3 +Version: 0.5.4 Authors@R: c( person(given = "Nicolas", family = "Bennett", diff --git a/NEWS.md b/NEWS.md index 539f3aab..4b1e28ef 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# ricu 0.5.4 + +* maintenance release: tests using `mockthat` are only run when available + # ricu 0.5.3 * maintenance release for compatibility with an update to `prt` diff --git a/cran-comments.md b/cran-comments.md index 9cbbec36..3ae1996b 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -10,4 +10,4 @@ 0 errors | 0 warnings | 0 notes -* Maintenance release (compatibility with prt v0.1.5) +* Maintenance release (move mockthat off CRAN) diff --git a/tests/testthat/test-callback.R b/tests/testthat/test-callback.R index b144e639..5fabd919 100644 --- a/tests/testthat/test-callback.R +++ b/tests/testthat/test-callback.R @@ -1,6 +1,8 @@ test_that("aumc callbacks", { + skip_if_not_installed("mockthat") + set.seed(11) env <- with_src() @@ -120,6 +122,8 @@ test_that("aumc callbacks", { test_that("hirid callbacks", { + skip_if_not_installed("mockthat") + set.seed(11) env <- with_src() diff --git a/tests/testthat/test-concept.R b/tests/testthat/test-concept.R index 236a45d9..0943f948 100644 --- a/tests/testthat/test-concept.R +++ b/tests/testthat/test-concept.R @@ -1,6 +1,8 @@ test_that("load hirid items", { + skip_if_not_installed("mockthat") + gluc <- mockthat::with_mock( get_hirid_ids = id_tbl(id = c(20005110L, 24000523L, 24000585L), unit = rep("mmol/l", 3L)), diff --git a/tests/testthat/test-data-utils.R b/tests/testthat/test-data-utils.R index 7e698ac6..63864547 100644 --- a/tests/testthat/test-data-utils.R +++ b/tests/testthat/test-data-utils.R @@ -10,6 +10,8 @@ test_that("stay windows", { expect_true("source" %in% id_vars(wins)) expect_setequal(data_vars(wins), c("start", "end")) + skip_if_not_installed("mockthat") + hrd <- mockthat::with_mock( load_src = function(x, src, ...) { if (identical(x, "observations")) { diff --git a/tests/testthat/test-setup-attach.R b/tests/testthat/test-setup-attach.R index db5f28cc..d577d84c 100644 --- a/tests/testthat/test-setup-attach.R +++ b/tests/testthat/test-setup-attach.R @@ -60,6 +60,8 @@ test_that("attach srcs", { cfg_dirs = system.file("testdata", package = "ricu")) ) + skip_if_not_installed("mockthat") + expect_error( expect_message( mockthat::with_mock( diff --git a/tests/testthat/test-setup-download.R b/tests/testthat/test-setup-download.R index a0ecf611..b74c6f8c 100644 --- a/tests/testthat/test-setup-download.R +++ b/tests/testthat/test-setup-download.R @@ -32,7 +32,9 @@ mock_fetch_stream <- function(url, fun, handle, ...) { dat } -wrong_fun <- mockthat::mock(stop("called wrong fun")) +if (requireNamespace("mockthat", quietly = TRUE)) { + wrong_fun <- mockthat::mock(stop("called wrong fun")) +} test_that("credentials", { @@ -46,6 +48,8 @@ test_that("credentials", { withr::local_envvar(FOO_VAR = NA) + skip_if_not_installed("mockthat") + res <- mockthat::with_mock( is_interactive = function() TRUE, read_line = function(...) "baz_val", @@ -57,6 +61,8 @@ test_that("credentials", { test_that("file size", { + skip_if_not_installed("mockthat") + pat_siz <- mockthat::with_mock( `curl::curl_fetch_memory` = mock_fetch_memory, `curl::curl_fetch_disk` = wrong_fun, @@ -70,6 +76,8 @@ test_that("file size", { test_that("hash checking", { + skip_if_not_installed("mockthat") + sha <- mockthat::with_mock( `curl::curl_fetch_memory` = mock_fetch_memory, `curl::curl_fetch_disk` = wrong_fun, @@ -105,6 +113,8 @@ test_that("hash checking", { test_that("file download", { + skip_if_not_installed("mockthat") + tmp <- withr::local_tempdir() dat_mem <- mockthat::with_mock( @@ -170,6 +180,8 @@ test_that("file download", { test_that("src download", { + skip_if_not_installed("mockthat") + mock_dl_check <- function(dest_folder, files, url, user, pass, src) { assert_that( diff --git a/tests/testthat/test-setup-import.R b/tests/testthat/test-setup-import.R index e59dce41..2efc9394 100644 --- a/tests/testthat/test-setup-import.R +++ b/tests/testthat/test-setup-import.R @@ -133,6 +133,8 @@ test_that("import src", { class = "are_in_assert" ) + skip_if_not_installed("mockthat") + mocks <- mockthat::local_mock( src_file_exist = quote({ if (identical(type, "raw")) rep(TRUE, length(x)) diff --git a/tests/testthat/test-utils-cli.R b/tests/testthat/test-utils-cli.R index bd99b1ea..c6977a86 100644 --- a/tests/testthat/test-utils-cli.R +++ b/tests/testthat/test-utils-cli.R @@ -1,6 +1,8 @@ test_that("cli progress", { + skip_if_not_installed("mockthat") + do_stuff <- function(pb = NULL) { for (ntk in split(seq_len(10), (rep(1:7, times = c(rep(1:2, 3), 1))))) { From 70730824f458d7606c256cbc4bcb659b2a19141b Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sat, 29 Oct 2022 20:46:14 +0200 Subject: [PATCH 02/37] update gh actions --- .github/workflows/check.yaml | 2 +- .github/workflows/coverage.yaml | 2 +- .github/workflows/pkgdown.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 787e2e95..78622621 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -49,7 +49,7 @@ jobs: any::rcmdcheck eth-mds/mimic-demo eth-mds/eicu-demo - needs: check + nbenn/mockthat - uses: r-lib/actions/check-r-package@v2 env: diff --git a/.github/workflows/coverage.yaml b/.github/workflows/coverage.yaml index 259b657a..393a7a9a 100644 --- a/.github/workflows/coverage.yaml +++ b/.github/workflows/coverage.yaml @@ -29,7 +29,7 @@ jobs: any::covr eth-mds/mimic-demo eth-mds/eicu-demo - needs: coverage + nbenn/mockthat - name: Test coverage run: covr::codecov(quiet = FALSE) diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 97ffa2f3..a80a385a 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -33,7 +33,7 @@ jobs: local::. eth-mds/mimic-demo eth-mds/eicu-demo - needs: website + nbenn/mockthat - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) From 5340ed06313226fc47beff52400d0306c2a475a7 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 30 Oct 2022 09:20:53 +0100 Subject: [PATCH 03/37] fix test --- tests/testthat/test-setup-download.R | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/testthat/test-setup-download.R b/tests/testthat/test-setup-download.R index b74c6f8c..18c99fce 100644 --- a/tests/testthat/test-setup-download.R +++ b/tests/testthat/test-setup-download.R @@ -209,9 +209,14 @@ test_that("src download", { expect_true(all(lgl_ply(dirs, dir.create))) - mk_dl <- mockthat::local_mock(download_check_data = NULL) + mk_dl <- mockthat::mock(NULL) - expect_invisible(res <- download_src(srcs, dirs)) + expect_invisible( + res <- mockthat::with_mock( + download_check_data = mk_dl, + download_src(srcs, dirs) + ) + ) expect_null(res) expect_true(dir.exists(mockthat::mock_arg(mk_dl, "dest_folder"))) From b4ac31a2cc22da236e56ce9875b2d799ae172d46 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 30 Oct 2022 10:44:08 +0100 Subject: [PATCH 04/37] update roxygen --- DESCRIPTION | 2 +- man/change_id.Rd | 34 ++++++------ man/data_env.Rd | 98 ++++++++++++++++------------------- man/figures/sofa-sep-3-1.png | Bin 38654 -> 38707 bytes man/label_sep3.Rd | 35 +------------ 5 files changed, 63 insertions(+), 106 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 45de381a..54b0b44d 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -69,7 +69,7 @@ Suggests: units, pdftools, magick -RoxygenNote: 7.2.0 +RoxygenNote: 7.2.1 Additional_repositories: https://eth-mds.github.io/physionet-demo VignetteBuilder: knitr Config/testthat/edition: 3 diff --git a/man/change_id.Rd b/man/change_id.Rd index 9c56cfc5..fbc69277 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -68,24 +68,22 @@ example, we request for \code{mimic_demo}, with ICU stay IDs as source and hospital admissions as destination IDs. \if{html}{\out{
}}\preformatted{id_map_helper(mimic_demo, "icustay_id", "hadm_id") -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{## # An `id_tbl`: 136 x 4 -## # Id var: `icustay_id` -## icustay_id hadm_id hadm_id_start hadm_id_end -## -## 1 201006 198503 -3290 mins 9114 mins -## 2 201204 114648 -2 mins 6949 mins -## 3 203766 126949 -1336 mins 8818 mins -## 4 204132 157609 -1 mins 10103 mins -## 5 204201 177678 -368 mins 9445 mins -## ... -## 132 295043 170883 -10413 mins 31258 mins -## 133 295741 176805 -1 mins 3153 mins -## 134 296804 110244 -1294 mins 4599 mins -## 135 297782 167612 -1 mins 207 mins -## 136 298685 151323 -1 mins 19082 mins -## # ... with 126 more rows +#> # An `id_tbl`: 136 x 4 +#> # Id var: `icustay_id` +#> icustay_id hadm_id hadm_id_start hadm_id_end +#> +#> 1 201006 198503 ~17h ~6d +#> 2 201204 114648 ~23h ~4d +#> 3 203766 126949 ~1h ~6d +#> 4 204132 157609 ~23h ~7d +#> 5 204201 177678 ~17h ~6d +#> ... +#> 132 295043 170883 ~18h ~21d +#> 133 295741 176805 ~23h ~2d +#> 134 296804 110244 ~2h ~3d +#> 135 297782 167612 ~23h ~3h +#> 136 298685 151323 ~23h ~13d +#> # ... with 126 more rows }\if{html}{\out{
}} Both start and end columns encode the hospital admission windows relative diff --git a/man/data_env.Rd b/man/data_env.Rd index 20592cec..89860cdd 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -68,47 +68,41 @@ inspected using the \code{$} function. For example for the MIMIC-III demo dataset and the \code{icustays} table, this gives \if{html}{\out{
}}\preformatted{mimic_demo -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{## -## admissions callout caregivers chartevents -## [129 x 19] [77 x 24] [7,567 x 4] [758,355 x 15] -## cptevents d_cpt d_icd_diagnoses d_icd_procedures -## [1,579 x 12] [134 x 9] [14,567 x 4] [3,882 x 4] -## d_items d_labitems datetimeevents diagnoses_icd -## [12,487 x 10] [753 x 6] [15,551 x 14] [1,761 x 5] -## drgcodes icustays inputevents_cv inputevents_mv -## [297 x 8] [136 x 12] [34,799 x 22] [13,224 x 31] -## labevents microbiologyevents outputevents patients -## [76,074 x 9] [2,003 x 16] [11,320 x 13] [100 x 8] -## prescriptions procedureevents_mv procedures_icd services -## [10,398 x 19] [753 x 25] [506 x 5] [163 x 6] -## transfers -## [524 x 13] -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{mimic_demo$icustays -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{## # : [136 x 12] -## # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) -## # Defaults: `intime` (index), `last_careunit` (val) -## # Time vars: `intime`, `outtime` -## row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit -## -## 1 12742 10006 142345 206504 carevue MICU MICU -## 2 12747 10011 105331 232110 carevue MICU MICU -## 3 12749 10013 165520 264446 carevue MICU MICU -## 4 12754 10017 199207 204881 carevue CCU CCU -## 5 12755 10019 177759 228977 carevue MICU MICU -## ... -## 132 42676 44083 198330 286428 metavis~ CCU CCU -## 133 42691 44154 174245 217724 metavis~ MICU MICU -## 134 42709 44212 163189 239396 metavis~ MICU MICU -## 135 42712 44222 192189 238186 metavis~ CCU CCU -## 136 42714 44228 103379 217992 metavis~ SICU SICU -## # ... with 126 more rows, and 5 more variables: first_wardid , -## # last_wardid , intime , outtime , los +#> +#> admissions callout caregivers chartevents +#> [129 x 19] [77 x 24] [7,567 x 4] [758,355 x 15] +#> cptevents d_cpt d_icd_diagnoses d_icd_procedures +#> [1,579 x 12] [134 x 9] [14,567 x 4] [3,882 x 4] +#> d_items d_labitems datetimeevents diagnoses_icd +#> [12,487 x 10] [753 x 6] [15,551 x 14] [1,761 x 5] +#> drgcodes icustays inputevents_cv inputevents_mv +#> [297 x 8] [136 x 12] [34,799 x 22] [13,224 x 31] +#> labevents microbiologyevents outputevents patients +#> [76,074 x 9] [2,003 x 16] [11,320 x 13] [100 x 8] +#> prescriptions procedureevents_mv procedures_icd services +#> [10,398 x 19] [753 x 25] [506 x 5] [163 x 6] +#> transfers +#> [524 x 13] +mimic_demo$icustays +#> # : [136 x 12] +#> # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) +#> # Defaults: `intime` (index), `last_careunit` (val) +#> # Time vars: `intime`, `outtime` +#> row_id subjec~ hadm_id icusta~ dbsour~ first_~ last_c~ first_~ last_w~ +#> +#> 1 12742 10006 142345 206504 carevue MICU MICU 52 52 +#> 2 12747 10011 105331 232110 carevue MICU MICU 15 15 +#> 3 12749 10013 165520 264446 carevue MICU MICU 15 15 +#> 4 12754 10017 199207 204881 carevue CCU CCU 7 7 +#> 5 12755 10019 177759 228977 carevue MICU MICU 15 15 +#> ... +#> 132 42676 44083 198330 286428 metavi~ CCU CCU 7 7 +#> 133 42691 44154 174245 217724 metavi~ MICU MICU 50 50 +#> 134 42709 44212 163189 239396 metavi~ MICU MICU 50 50 +#> 135 42712 44222 192189 238186 metavi~ CCU CCU 7 7 +#> 136 42714 44228 103379 217992 metavi~ SICU SICU 57 57 +#> # ... with 126 more rows, and 3 more variables: intime , outtime , +#> # los }\if{html}{\out{
}} Table subsets can be loaded into memory for example using the @@ -122,18 +116,16 @@ with \code{subject_id == 10124} are of interest, the respective data can be loaded as \if{html}{\out{
}}\preformatted{subset(mimic_demo$icustays, subject_id == 10124) -}\if{html}{\out{
}} - -\if{html}{\out{
}}\preformatted{## row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit -## 1: 12863 10124 182664 261764 carevue MICU MICU -## 2: 12864 10124 170883 222779 carevue MICU MICU -## 3: 12865 10124 170883 295043 carevue CCU CCU -## 4: 12866 10124 170883 237528 carevue MICU MICU -## first_wardid last_wardid intime outtime los -## 1: 23 23 2192-03-29 10:46:51 2192-04-01 06:36:00 2.8258 -## 2: 50 50 2192-04-16 20:58:32 2192-04-20 08:51:28 3.4951 -## 3: 7 7 2192-04-24 02:29:49 2192-04-26 23:59:45 2.8958 -## 4: 23 23 2192-04-30 14:50:44 2192-05-15 23:34:21 15.3636 +#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit +#> 1: 12863 10124 182664 261764 carevue MICU MICU +#> 2: 12864 10124 170883 222779 carevue MICU MICU +#> 3: 12865 10124 170883 295043 carevue CCU CCU +#> 4: 12866 10124 170883 237528 carevue MICU MICU +#> first_wardid last_wardid intime outtime los +#> 1: 23 23 2192-03-29 10:46:51 2192-04-01 06:36:00 2.8258 +#> 2: 50 50 2192-04-16 20:58:32 2192-04-20 08:51:28 3.4951 +#> 3: 7 7 2192-04-24 02:29:49 2192-04-26 23:59:45 2.8958 +#> 4: 23 23 2192-04-30 14:50:44 2192-05-15 23:34:21 15.3636 }\if{html}{\out{
}} Much care has been taken to make \code{ricu} extensible to new datasets. For diff --git a/man/figures/sofa-sep-3-1.png b/man/figures/sofa-sep-3-1.png index 1f947fd8509876db0f62db74ee994500e0de85da..09fcb11b20f3c06973a67c8ace8d50d5b48b0bc7 100644 GIT binary patch literal 38707 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 literal 38654 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\preformatted{\\begin\{tikzpicture\} - \\draw (-6,0) -- (3,0); - \\draw (-6,-0.25) -- (-6,0.25); - \\draw (3,-0.25) -- (3,0.25); - \\draw (0,-0.25) -- (0,0.25); - \\node[align = center] at (0,-0.75) \{SI time\}; - \\node[align = center] at (3,-0.75) \{SI window\\\\ end\}; - \\node[align = center] at (-6,-0.75) \{SI window\\\\ start\}; - \\filldraw (-6, 1) circle (2pt); - \\draw (-6,1) -- (-5.5, 1); - \\filldraw (-5.5, 1) circle (2pt); - \\draw (-5.5,1) -- (-5, 1); - \\node at (-4.25, 1) \{$\\dots$\} ; - \\filldraw (-5, 1) circle (2pt); - \\filldraw (-3.5, 1) circle (2pt); - \\filldraw (-3, 1.5) circle (2pt); - \\filldraw (-2.5, 1.5) circle (2pt); - \\filldraw (-2, 2.5) circle (2pt); - \\draw (-3.5,1) -- (-3, 1.5); - \\draw (-3,1.5) -- (-2.5, 1.5); - \\draw (-2.5,1.5) -- (-2, 2.5); - \\node [black] at (-0.25, 1.75) \{$\\Delta$SOFA $\\geq 2$\}; - \\draw (-6.5, 1) -- (-6.5, 2.5) ; - \\node at (-6.5, 3) \{SOFA\} ; - \\draw (-6.5,1)--(-6.6,1) node[left,font=\\small]\{$0$\}; - \\draw (-6.5,1.5)--(-6.6,1.5) node[left,font=\\small]\{$1$\}; - \\draw (-6.5,2)--(-6.6,2) node[left,font=\\small]\{$2$\}; - \\draw (-6.5,2.5)--(-6.6, 2.5) node[left,font=\\small]\{$3$\}; - \\draw[red] (-2,-0.25) -- (-2,0.25); - \\draw[dashed,red] (-2, 2.35) -- (-2, 0) ; - \\node[red] at (-2, -0.75) \{Sepsis-3 time\}; -\\end\{tikzpicture\} -}\if{html}{\out{}} +\figure{man/figures/sofa-sep-3-1.png} A patient can potentially have multiple SI windows. The argument \code{si_window} is used to control which SI window we focus on (options are From cb62fc19f6af6ec3289d7283ec596c7b964b2033 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 30 Oct 2022 12:57:22 +0100 Subject: [PATCH 05/37] cosmetic fixes --- R/ricu.R | 2 +- man/change_id.Rd | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/R/ricu.R b/R/ricu.R index 4bf86a61..32180338 100644 --- a/R/ricu.R +++ b/R/ricu.R @@ -19,6 +19,6 @@ pkg_env <- function() asNamespace(pkg_name()) release_questions <- function() { c( - "Was `release()` called with \"args = c('--compact-vignettes=gs+qpdf')\"?" + "Was `release()` called with `args = \"--compact-vignettes=gs+qpdf\"`?" ) } diff --git a/man/change_id.Rd b/man/change_id.Rd index fbc69277..27bc399f 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -72,17 +72,17 @@ hospital admissions as destination IDs. #> # Id var: `icustay_id` #> icustay_id hadm_id hadm_id_start hadm_id_end #> -#> 1 201006 198503 ~17h ~6d -#> 2 201204 114648 ~23h ~4d -#> 3 203766 126949 ~1h ~6d -#> 4 204132 157609 ~23h ~7d -#> 5 204201 177678 ~17h ~6d +#> 1 201006 198503 -3290 mins 9114 mins +#> 2 201204 114648 -2 mins 6949 mins +#> 3 203766 126949 -1336 mins 8818 mins +#> 4 204132 157609 -1 mins 10103 mins +#> 5 204201 177678 -368 mins 9445 mins #> ... -#> 132 295043 170883 ~18h ~21d -#> 133 295741 176805 ~23h ~2d -#> 134 296804 110244 ~2h ~3d -#> 135 297782 167612 ~23h ~3h -#> 136 298685 151323 ~23h ~13d +#> 132 295043 170883 -10413 mins 31258 mins +#> 133 295741 176805 -1 mins 3153 mins +#> 134 296804 110244 -1294 mins 4599 mins +#> 135 297782 167612 -1 mins 207 mins +#> 136 298685 151323 -1 mins 19082 mins #> # ... with 126 more rows }\if{html}{\out{}} From d3c4e5dbac35439de0cb9715fafe20766eb38d10 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 30 Oct 2022 16:41:31 +0100 Subject: [PATCH 06/37] fix sep3 man fig --- R/callback-sep3.R | 110 +++++++++++++++++++++++++---------- man/figures/sofa-sep-3-1.png | Bin 38707 -> 39727 bytes man/label_sep3.Rd | 2 +- 3 files changed, 80 insertions(+), 32 deletions(-) diff --git a/R/callback-sep3.R b/R/callback-sep3.R index ef1c6df3..0eb04c8d 100644 --- a/R/callback-sep3.R +++ b/R/callback-sep3.R @@ -19,38 +19,86 @@ #' increase in the SOFA score (see [sofa_score()]) of 2 points or more within #' the suspected infection (SI) window (see [susp_inf()]): #' -#' ```{tikz sofa-sep-3, echo = FALSE} +#' ```{tikz} +#' #| sofa-sep-3, echo = FALSE, +#' #| engine.opts = list( +#' #| extra.preamble = c( +#' #| "\\usepackage{pgfplots}", "\\pgfplotsset{compat=newest}" +#' #| ) +#' #| ) #' \begin{tikzpicture} -#' \draw (-6,0) -- (3,0); -#' \draw (-6,-0.25) -- (-6,0.25); -#' \draw (3,-0.25) -- (3,0.25); -#' \draw (0,-0.25) -- (0,0.25); -#' \node[align = center] at (0,-0.75) {SI time}; -#' \node[align = center] at (3,-0.75) {SI window\\ end}; -#' \node[align = center] at (-6,-0.75) {SI window\\ start}; -#' \filldraw (-6, 1) circle (2pt); -#' \draw (-6,1) -- (-5.5, 1); -#' \filldraw (-5.5, 1) circle (2pt); -#' \draw (-5.5,1) -- (-5, 1); -#' \node at (-4.25, 1) {$\dots$} ; -#' \filldraw (-5, 1) circle (2pt); -#' \filldraw (-3.5, 1) circle (2pt); -#' \filldraw (-3, 1.5) circle (2pt); -#' \filldraw (-2.5, 1.5) circle (2pt); -#' \filldraw (-2, 2.5) circle (2pt); -#' \draw (-3.5,1) -- (-3, 1.5); -#' \draw (-3,1.5) -- (-2.5, 1.5); -#' \draw (-2.5,1.5) -- (-2, 2.5); -#' \node [black] at (-0.25, 1.75) {$\Delta$SOFA $\geq 2$}; -#' \draw (-6.5, 1) -- (-6.5, 2.5) ; -#' \node at (-6.5, 3) {SOFA} ; -#' \draw (-6.5,1)--(-6.6,1) node[left,font=\small]{$0$}; -#' \draw (-6.5,1.5)--(-6.6,1.5) node[left,font=\small]{$1$}; -#' \draw (-6.5,2)--(-6.6,2) node[left,font=\small]{$2$}; -#' \draw (-6.5,2.5)--(-6.6, 2.5) node[left,font=\small]{$3$}; -#' \draw[red] (-2,-0.25) -- (-2,0.25); -#' \draw[dashed,red] (-2, 2.35) -- (-2, 0) ; -#' \node[red] at (-2, -0.75) {Sepsis-3 time}; +#' +#' \draw (-5.5, 0) -- (3.5, 0); +#' \draw (-5.5, -0.2) -- (-5.5, 0.2); +#' \draw (3.5, -0.2) -- (3.5, 0.2); +#' \draw (0.5, -0.2) -- (0.5, 0.2); +#' +#' \node[align = center] at (0.5, 0.5) {SI time}; +#' \node[align = center] at (3.5, 0.5) {SI window end}; +#' \node[align = center] at (-5.5, -0.5) {SI window start}; +#' +#' \draw (-1.5, -0.2) -- (-1.5, 0.2); +#' \draw[dashed] (-1.5, 2) -- (-1.5, 0); +#' \node[align = center] at (-1.5, -0.5) {Sepsis-3 time}; +#' +#' \node[align = center] at (-2.75, 0.5) {-48 hours}; +#' \node[align = center] at (2, -0.5) {24 hours}; +#' +#' \draw[dashed] (0.5, 0) -- (0.5, -1); +#' +#' \draw (0.5, -1) -- (3.5, -1); +#' \draw (3.5, -1.2) -- (3.5, -0.8); +#' \draw (0.5, -1.2) -- (0.5, -0.8); +#' +#' \node at (5.25, -1) {within 24 hours}; +#' +#' \node[align = center] at (0.5, -1.5) {ABX}; +#' \node[align = center] at (3.5, -1.5) {Sampling}; +#' +#' \draw (0.5, -2) -- (9.5, -2); +#' \draw (9.5, -2.2) -- (9.5, -1.8); +#' \draw (0.5, -2.2) -- (0.5, -1.8); +#' +#' \node[align = center] at (5.25, -2.5) {within 72 hours}; +#' +#' \node[align = center] at (0.5, -2.5) {Sampling}; +#' \node[align = center] at (9.5, -2.5) {ABX}; +#' +#' \draw [decorate, decoration = {brace, mirror, amplitude=5pt, raise=4pt}, +#' yshift=0pt] (0, -0.9) -- (0, -2.1); +#' \node at (-3, -1.5) {Either option is permissible}; +#' +#' \filldraw (-6, 1.5) circle (1pt); +#' \draw (-6, 1.5) -- (-5.5, 1.5); +#' \filldraw (-5.5, 1.5) circle (1pt); +#' \draw (-5.5, 1.5) -- (-5, 1); +#' \filldraw (-5, 1) circle (1pt); +#' \draw (-5, 1) -- (-4.5, 1); +#' \filldraw (-4.5, 1) circle (1pt); +#' \draw (-4.5, 1) -- (-4, 1); +#' \filldraw (-4, 1) circle (1pt); +#' \draw (-4, 1) -- (-3.5, 1); +#' \filldraw (-3.5, 1) circle (1pt); +#' \draw (-3.5, 1) -- (-3, 1.5); +#' \filldraw (-3, 1.5) circle (1pt); +#' \draw (-3, 1.5) -- (-2.5, 1.5); +#' \filldraw (-2.5, 1.5) circle (1pt); +#' \draw (-2.5, 1.5) -- (-2, 1.5); +#' \filldraw (-2, 1.5) circle (1pt); +#' \draw (-2, 1.5) -- (-1.5, 2.0); +#' \filldraw (-1.5, 2.0) circle (1pt); +#' +#' \draw [decorate, decoration = {brace, amplitude=5pt, mirror, raise=4pt}, +#' yshift=0pt] (-1.25, 1) -- (-1.25, 2); +#' \node [black] at (0.25, 1.5) {$\Delta$SOFA $\geq 2$}; +#' +#' \draw (-6.5, 1) -- (-6.5, 2.5); +#' \node at (-6.5, 3) {SOFA}; +#' \draw (-6.5, 1) -- (-6.6, 1) node[left]{0}; +#' \draw (-6.5, 1.5) -- (-6.6, 1.5) node[left]{1}; +#' \draw (-6.5, 2) -- (-6.6, 2) node[left]{2}; +#' \draw (-6.5, 2.5) -- (-6.6, 2.5) node[left]{3}; +#' #' \end{tikzpicture} #' ``` #' diff --git a/man/figures/sofa-sep-3-1.png b/man/figures/sofa-sep-3-1.png index 09fcb11b20f3c06973a67c8ace8d50d5b48b0bc7..096d3021c322c18366fdfea03ad7a83d25126239 100644 GIT binary patch literal 39727 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 literal 38707 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 diff --git a/man/label_sep3.Rd b/man/label_sep3.Rd index 68cbf788..9fd07593 100644 --- a/man/label_sep3.Rd +++ b/man/label_sep3.Rd @@ -55,7 +55,7 @@ increase in SOFA score. The Sepsis-3 Consensus (Singer et. al.) defines sepsis as an acute increase in the SOFA score (see \code{\link[=sofa_score]{sofa_score()}}) of 2 points or more within the suspected infection (SI) window (see \code{\link[=susp_inf]{susp_inf()}}): -\figure{man/figures/sofa-sep-3-1.png} +\figure{sofa-sep-3-1.png} A patient can potentially have multiple SI windows. The argument \code{si_window} is used to control which SI window we focus on (options are From 09902bdc57a1f2f720a924d32f5e053ce2ce7f97 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 30 Oct 2022 19:27:38 +0100 Subject: [PATCH 07/37] fix tikz paths --- R/callback-sep3.R | 4 +++- man/roxygen/meta.R | 5 ++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/R/callback-sep3.R b/R/callback-sep3.R index 0eb04c8d..79e4de72 100644 --- a/R/callback-sep3.R +++ b/R/callback-sep3.R @@ -20,12 +20,14 @@ #' the suspected infection (SI) window (see [susp_inf()]): #' #' ```{tikz} -#' #| sofa-sep-3, echo = FALSE, +#' #| sofa-sep-3, +#' #| echo = FALSE, #' #| engine.opts = list( #' #| extra.preamble = c( #' #| "\\usepackage{pgfplots}", "\\pgfplotsset{compat=newest}" #' #| ) #' #| ) +#' #' \begin{tikzpicture} #' #' \draw (-5.5, 0) -- (3.5, 0); diff --git a/man/roxygen/meta.R b/man/roxygen/meta.R index a2a2da69..2d36fe59 100644 --- a/man/roxygen/meta.R +++ b/man/roxygen/meta.R @@ -1,4 +1,7 @@ -options(cli.unicode = FALSE)[["cli.unicode"]] +options( + cli.unicode = FALSE, + knitr.package.upload.fun = basename +) list(markdown = TRUE) From cc9e09c692ed1fbe361329f13ddb47c95ff78a42 Mon Sep 17 00:00:00 2001 From: Drago Plecko Date: Sat, 3 Dec 2022 16:33:07 -0500 Subject: [PATCH 08/37] SNOMED CT codes added to dictionary --- inst/extdata/config/concept-dict.R | 107 ++++++++++++++++++++++++- inst/extdata/config/concept-dict.json | 111 ++++++++++++++++++++++++-- 2 files changed, 210 insertions(+), 8 deletions(-) diff --git a/inst/extdata/config/concept-dict.R b/inst/extdata/config/concept-dict.R index 09a70863..cfec2c50 100644 --- a/inst/extdata/config/concept-dict.R +++ b/inst/extdata/config/concept-dict.R @@ -7,6 +7,7 @@ cfg <- list( min = 0, max = 300, description = "heart rate", + SCTID = 364075005, category = "vitals", sources = list( mimic = list( @@ -33,6 +34,7 @@ cfg <- list( min = 0, max = 300, description = "systolic blood pressure", + SCTID = 271649006, category = "vitals", sources = list( mimic = list( @@ -61,6 +63,7 @@ cfg <- list( min = 0, max = 200, description = "diastolic blood pressure", + SCTID = 271650006, category = "vitals", sources = list( mimic = list( @@ -89,6 +92,7 @@ cfg <- list( min = 0, max = 250, description = "mean arterial pressure", + SCTID = 6797001, category = "vitals", sources = list( mimic = list( @@ -119,6 +123,7 @@ cfg <- list( min = 0, max = 120, description = "respiratory rate", + SCTID = 86290005, category = "respiratory", sources = list( mimic = list( @@ -148,6 +153,7 @@ cfg <- list( min = 50, max = 100, description = "oxygen saturation", + SCTID = 442476006, category = "respiratory", sources = list( mimic = list( @@ -180,6 +186,7 @@ cfg <- list( min = 21, max = 100, description = "fraction of inspired oxygen", + SCTID = 250774007, category = "blood gas", sources = list( mimic = list( @@ -212,6 +219,7 @@ cfg <- list( min = 5, max = 60, description = "totcal CO2", + SCTID = 391396001, category = "blood gas", sources = list( mimic = list( @@ -230,6 +238,7 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "alanine aminotransferase", + SCTID = 34608000, category = "chemistry", sources = list( mimic = list( @@ -255,6 +264,7 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "aspartate aminotransferase", + SCTID = 45896001, category = "chemistry", sources = list( mimic = list( @@ -281,6 +291,7 @@ cfg <- list( min = 0, max = 40, description = "phosphate", + SCTID = 104866001, category = "chemistry", sources = list( mimic = list( @@ -309,6 +320,7 @@ cfg <- list( min = 0, max = 200, description = "blood urea nitrogen", + SCTID = 105011006, category = "chemistry", sources = list( mimic = list( @@ -336,6 +348,7 @@ cfg <- list( min = 6.8, max = 8, description = "pH of blood", + SCTID = 27051004, category = "blood gas", sources = list( mimic = list( @@ -362,6 +375,7 @@ cfg <- list( min = 0, max = 100, description = "total bilirubin", + SCTID = 302787001, category = "chemistry", sources = list( mimic = list( @@ -387,6 +401,7 @@ cfg <- list( ), inr_pt = list( description = "prothrombin time/international normalized ratio", + SCTID = 165581004, category = "hematology", sources = list( mimic = list( @@ -412,6 +427,7 @@ cfg <- list( min = 5, max = 1200, description = "platelet count", + SCTID = 61928009, category = "hematology", sources = list( mimic = list( @@ -438,6 +454,7 @@ cfg <- list( min = 0, max = 50, description = "lactate", + SCTID = 394960005, category = "blood gas", sources = list( mimic = list( @@ -464,6 +481,7 @@ cfg <- list( min = 0, max = 100, description = "lymphocytes", + SCTID = 74765001, category = "hematology", sources = list( mimic = list( @@ -491,6 +509,7 @@ cfg <- list( min = 5, max = 50, description = "bicarbonate", + SCTID = 312471006, category = "chemistry", sources = list( mimic = list( @@ -517,6 +536,7 @@ cfg <- list( min = 0, max = 15, description = "creatinine", + SCTID = 113075003, category = "chemistry", sources = list( mimic = list( @@ -544,6 +564,7 @@ cfg <- list( unit = "sec", min = 0, description = "prothrombine time", + SCTID = 396451008, category = "hematology", sources = list( mimic = list( @@ -565,6 +586,7 @@ cfg <- list( min = 0, max = 100, description = "erythrocyte distribution width", + SCTID = 66842004, category = "hematology", sources = list( mimic = list( @@ -585,6 +607,7 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "alkaline phosphatase", + SCTID = 365771003, category = "chemistry", sources = list( mimic = list( @@ -610,6 +633,7 @@ cfg <- list( unit = c("K/uL", "G/l"), min = 0, description = "white blood cell count", + SCTID = 767002, category = "hematology", sources = list( mimic = list( @@ -636,6 +660,7 @@ cfg <- list( min = 4, max = 18, description = "hemoglobin", + SCTID = 302763003, category = "hematology", sources = list( mimic = list( @@ -665,6 +690,7 @@ cfg <- list( min = 15, max = 60, description = "hematocrit", + SCTID = 28317006, category = "hematology", sources = list( mimic = list( @@ -688,6 +714,7 @@ cfg <- list( min = 10, max = 150, description = "CO2 partial pressure", + SCTID = 250564007, category = "blood gas", sources = list( mimic = list( @@ -714,6 +741,7 @@ cfg <- list( min = 40, max = 600, description = "O2 partial pressure", + SCTID = 250546000, category = "blood gas", sources = list( mimic = list( @@ -738,7 +766,8 @@ cfg <- list( mch = list( unit = "pg", min = 0, - description = "mean cell hemoglobin", + description = "mean corpuscular hemoglobin", + SCTID = 54706004, category = "hematology", sources = list( mimic = list( @@ -765,6 +794,7 @@ cfg <- list( min = 20, max = 50, description = "mean corpuscular hemoglobin concentration", + SCTID = 37254006, category = "hematology", sources = list( mimic = list( @@ -792,6 +822,7 @@ cfg <- list( min = 50, max = 150, description = "mean corpuscular volume", + SCTID = 104133003, category = "hematology", sources = list( mimic = list( @@ -816,6 +847,7 @@ cfg <- list( unit = "sec", min = 0, description = "partial thromboplastin time", + SCTID = 42525009, category = "hematology", sources = list( mimic = list( @@ -841,6 +873,7 @@ cfg <- list( min = 4, max = 20, description = "calcium", + SCTID = 71878006, category = "chemistry", sources = list( mimic = list( @@ -869,6 +902,7 @@ cfg <- list( min = 80, max = 130, description = "chloride", + SCTID = 46511006, category = "chemistry", sources = list( mimic = list( @@ -894,6 +928,7 @@ cfg <- list( min = 0.5, max = 5, description = "magnesium", + SCTID = 38151008, category = "chemistry", sources = list( mimic = list( @@ -923,6 +958,7 @@ cfg <- list( min = 0, max = 10, description = "potassium", + SCTID = 312468003, category = "chemistry", sources = list( mimic = list( @@ -949,6 +985,7 @@ cfg <- list( min = 110, max = 165, description = "sodium", + SCTID = 312469006, category = "chemistry", sources = list( mimic = list( @@ -975,6 +1012,7 @@ cfg <- list( min = 0, max = 50, description = "basophils", + SCTID = 42351005, category = "hematology", sources = list( mimic = list( @@ -998,6 +1036,7 @@ cfg <- list( min = 0, max = 50, description = "eosinophils", + SCTID = 71960002, category = "hematology", sources = list( mimic = list( @@ -1021,6 +1060,7 @@ cfg <- list( min = 0, max = 100, description = "neutrophils", + SCTID = 30630007, category = "hematology", sources = list( mimic = list( @@ -1049,6 +1089,7 @@ cfg <- list( min = 0, max = 1000, description = "glucose", + SCTID = 33747003, category = "chemistry", sources = list( mimic = list( @@ -1079,6 +1120,7 @@ cfg <- list( min = 0.5, max = 2, description = "calcium ionized", + SCTID = 711359007, category = "blood gas", sources = list( mimic = list( @@ -1104,6 +1146,7 @@ cfg <- list( unit = "mg/L", min = 0, description = "C-reactive protein", + SCTID = 55235003, category = "chemistry", sources = list( mimic = list( @@ -1133,6 +1176,7 @@ cfg <- list( min = 0, max = 200, description = "erythrocyte sedimentation rate", + SCTID = 416838001, category = "hematology", sources = list( mimic = list( @@ -1153,6 +1197,7 @@ cfg <- list( ), hbco = list( description = "carboxyhemoglobin", + SCTID = 19821003, category = "blood gas", sources = list( eicu = list( @@ -1172,6 +1217,7 @@ cfg <- list( min = 0, max = 100, description = "methemoglobin", + SCTID = 54937007, category = "blood gas", sources = list( mimic = list( @@ -1196,6 +1242,7 @@ cfg <- list( unit = "ng/mL", min = 0, description = "troponin t", + SCTID = 121871002, category = "chemistry", sources = list( mimic = list( @@ -1222,6 +1269,7 @@ cfg <- list( min = 0, max = 6, description = "albumin", + SCTID = 104485008, category = "chemistry", sources = list( mimic = list( @@ -1250,6 +1298,7 @@ cfg <- list( min = 0, max = 1500, description = "fibrinogen", + SCTID = 250346004, category = "hematology", sources = list( mimic = list( @@ -1278,6 +1327,7 @@ cfg <- list( min = -25, max = 25, description = "base excess", + SCTID = 67487000, category = "blood gas", sources = list( mimic = list( @@ -1304,6 +1354,7 @@ cfg <- list( min = 0, max = 20, description = "red blood cell count", + SCTID = 14089001, category = "hematology", sources = list( mimic = list( @@ -1324,6 +1375,7 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "creatine kinase", + SCTID = 397798009, category = "chemistry", sources = list( mimic = list( @@ -1349,6 +1401,7 @@ cfg <- list( unit = "ng/mL", min = 0, description = "creatine kinase MB", + SCTID = 104613001, category = "chemistry", sources = list( mimic = list( @@ -1373,6 +1426,7 @@ cfg <- list( min = 1, max = 4, description = "GCS eye", + SCTID = 281395000, category = "neurological", sources = list( mimic = list( @@ -1411,6 +1465,7 @@ cfg <- list( min = 1, max = 5, description = "GCS verbal", + SCTID = 281397008, category = "neurological", sources = list( mimic = list( @@ -1452,6 +1507,7 @@ cfg <- list( min = 1, max = 6, description = "GCS motor", + SCTID = 281396004, category = "neurological", sources = list( mimic = list( @@ -1494,6 +1550,7 @@ cfg <- list( min = 3, max = 15, description = "GCS total", + SCTID = 248241002, category = "neurological", sources = list( mimic = list( @@ -1511,6 +1568,7 @@ cfg <- list( max = 2000, aggregate = "sum", description = "urine output", + SCTID = 364202003, category = "output", sources = list( mimic = list( @@ -1546,6 +1604,7 @@ cfg <- list( min = 0, max = 50, description = "dobutamine rate", + SCTID = 387145002, category = "medications", sources = list( mimic = list( @@ -1577,6 +1636,7 @@ cfg <- list( ), dobu_dur = list( description = "dobutamine duration", + SCTID = 387145002, category = "medications", aggregate = "max", sources = list( @@ -1611,6 +1671,7 @@ cfg <- list( dobu60 = list( concepts = c("dobu_rate", "dobu_dur"), description = "dobutamine administration for min 1h", + SCTID = 387145002, category = "medications", interval = "00:01:00", callback = "vaso60", @@ -1621,6 +1682,7 @@ cfg <- list( min = 0, max = 50, description = "dopamine rate", + SCTID = 412383006, category = "medications", sources = list( mimic = list( @@ -1648,6 +1710,7 @@ cfg <- list( ), dopa_dur = list( description = "dopamine duration", + SCTID = 412383006, category = "medications", aggregate = "max", sources = list( @@ -1678,6 +1741,7 @@ cfg <- list( dopa60 = list( concepts = c("dopa_rate", "dopa_dur"), description = "dopamine administration for min 1h", + SCTID = 412383006, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1688,6 +1752,7 @@ cfg <- list( min = 0, max = 3, description = "norepinephrine rate", + SCTID = 50060141000188106, category = "medications", sources = list( mimic = list( @@ -1720,6 +1785,7 @@ cfg <- list( ), norepi_dur = list( description = "norepinephrine duration", + SCTID = 50060141000188106, category = "medications", aggregate = "max", sources = list( @@ -1755,6 +1821,7 @@ cfg <- list( norepi60 = list( concepts = c("norepi_rate", "norepi_dur"), description = "norepinephrine administration for min 1h", + SCTID = 50060141000188106, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1765,6 +1832,7 @@ cfg <- list( min = 0, max = 1.5, description = "epinephrine rate", + SCTID = 387362001, category = "medications", sources = list( mimic = list( @@ -1797,6 +1865,7 @@ cfg <- list( ), epi_dur = list( description = "epinephrine duration", + SCTID = 387362001, category = "medications", aggregate = "max", sources = list( @@ -1832,6 +1901,7 @@ cfg <- list( epi60 = list( concepts = c("epi_rate", "epi_dur"), description = "epinephrine administration for min 1h", + SCTID = 387362001, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1897,6 +1967,7 @@ cfg <- list( target = "win_tbl", levels = c("invasive", "noninvasive"), description = "mechanical ventilation windows", + SCTID = 40617009, category = "respiratory", sources = list( aumc = list( @@ -1930,6 +2001,7 @@ cfg <- list( ett_gcs = list( class = "lgl_cncpt", description = "tracheostomy", + SCTID = 26412008, category = "respiratory", target = "win_tbl", sources = list( @@ -1999,6 +2071,7 @@ cfg <- list( abx = list( class = "lgl_cncpt", description = "antibiotics", + SCTID = 281789004, category = "medications", sources = list( mimic = list( @@ -2104,7 +2177,8 @@ cfg <- list( samp = list( class = "lgl_cncpt", category = "microbiology", - description = "fluid sampling", + description = "body fluid sampling", + SCTID = 127801007, sources = list( mimic = list( list(table = "microbiologyevents", val_var = "org_itemid", @@ -2134,6 +2208,7 @@ cfg <- list( unit = "ng/mL", min = 0, description = "troponin I", + SCTID = 121870001, category = "chemistry", sources = list( mimic = list( @@ -2152,6 +2227,7 @@ cfg <- list( min = 0, max = 50, description = "bilirubin direct", + SCTID = 39748002, category = "chemistry", sources = list( mimic = list( @@ -2179,6 +2255,7 @@ cfg <- list( min = 32, max = 42, description = "temperature", + SCTID = 386725007, category = "vitals", sources = list( mimic = list( @@ -2213,6 +2290,7 @@ cfg <- list( min = 10, max = 60, description = "endtidal CO2", + SCTID = 250790007, category = "vitals", sources = list( mimic = list( @@ -2239,6 +2317,7 @@ cfg <- list( unit = "units/hr", min = 0, description = "insulin", + SCTID = 736101003, category = "medications", sources = list( mimic = list( @@ -2272,6 +2351,7 @@ cfg <- list( levels = c("Female", "Male"), class = "fct_cncpt", description = "patient sex", + SCTID = 734000001, category = "demographics", sources = list( mimic = list( @@ -2306,6 +2386,7 @@ cfg <- list( max = 100, target = "id_tbl", description = "patient age", + SCTID = 424144002, category = "demographics", sources = list( mimic = list( @@ -2340,6 +2421,7 @@ cfg <- list( max = 500, target = "id_tbl", description = "patient weight", + SCTID = 27113001, category = "demographics", sources = list( mimic = list( @@ -2376,6 +2458,7 @@ cfg <- list( max = 230, target = "id_tbl", description = "patient height", + SCTID = 1153637007, category = "demographics", sources = list( mimic = list( @@ -2409,6 +2492,7 @@ cfg <- list( death = list( class = "lgl_cncpt", description = "in hospital mortality", + SCTID = 419620001, category = "outcome", sources = list( mimic = list( @@ -2558,6 +2642,7 @@ cfg <- list( pafi = list( concepts = c("po2", "fio2"), description = "Horowitz index", + SCTID = 438173002, category = "respiratory", aggregate = c("min", "max"), callback = "pafi", @@ -2574,6 +2659,7 @@ cfg <- list( vent_ind = list( concepts = c("vent_start", "vent_end", "mech_vent"), description = "ventilation durations", + SCTID = 40617009, category = "respiratory", interval = "00:01:00", callback = "vent_ind", @@ -2583,6 +2669,7 @@ cfg <- list( vaso_ind = list( concepts = c("dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"), description = "vasopressor indicator", + SCTID = 870386000, category = "medications", callback = "vaso_ind", class = "rec_cncpt" @@ -2590,6 +2677,7 @@ cfg <- list( gcs = list( concepts = c("egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"), description = "Glasgow coma scale (non-sedated)", + SCTID = 248241002, category = "neurological", aggregate = c("min", "min", "min", "min", "any"), callback = "gcs", @@ -2598,6 +2686,7 @@ cfg <- list( urine24 = list( concepts = "urine", description = "urine output per 24h", + SCTID = 395060000, category = "output", callback = "urine24", class = "rec_cncpt" @@ -2652,6 +2741,7 @@ cfg <- list( concepts = c("sofa_resp", "sofa_coag", "sofa_liver", "sofa_cardio", "sofa_cns", "sofa_renal"), description = "sequential organ failure assessment score", + SCTID = 1187491009, category = "outcome", callback = "sofa_score", class = "rec_cncpt" @@ -2666,6 +2756,7 @@ cfg <- list( susp_inf = list( concepts = c("abx", "samp"), description = "suspected infection", + SCTID = 473130003, category = "outcome", aggregate = lapply(list("sum", FALSE), list), callback = "susp_inf", @@ -2674,6 +2765,7 @@ cfg <- list( sep3 = list( concepts = c("sofa", "susp_inf"), description = "sepsis-3 criterion", + SCTID = 91302008, category = "outcome", callback = "sep3", keep_components = c(FALSE, TRUE), @@ -2682,6 +2774,7 @@ cfg <- list( bnd = list( unit = "%", description = "band form neutrophils", + SCTID = 25340006, category = "hematology", sources = list( mimic = list( @@ -2706,6 +2799,7 @@ cfg <- list( sirs = list( concepts = c("temp", "hr", "resp", "pco2", "wbc", "bnd"), description = "systemic inflammatory response syndrome score", + SCTID = 426929000, category = "outcome", callback = "sirs_score", class = "rec_cncpt" @@ -2720,6 +2814,7 @@ cfg <- list( avpu = list( concepts = "gcs", description = "AVPU scale", + SCTID = 449159002, category = "neurological", callback = "avpu", class = "rec_cncpt" @@ -2734,6 +2829,7 @@ cfg <- list( mews = list( concepts = c("sbp", "hr", "resp", "temp","avpu"), description = "modified early warning score", + SCTID = 445551004, category = "outcome", callback = "mews_score", class = "rec_cncpt" @@ -2741,6 +2837,7 @@ cfg <- list( hba1c = list( unit = "%", description = "Hemoglobin A1C", + SCTID = 43396009, category = "hematology", sources = list( mimic = list( @@ -2758,6 +2855,7 @@ cfg <- list( bmi = list( concepts = c("weight", "height"), description = "patient body mass index", + SCTID = 60621009, category = "demographics", callback = "bmi", target = "id_tbl", @@ -2766,6 +2864,7 @@ cfg <- list( adh_rate = list( unit = c("units/min", "U/min"), description = "vasopressin rate", + SCTID = 77671006, category = "medications", sources = list( mimic = list( @@ -2807,6 +2906,7 @@ cfg <- list( phn_rate = list( unit = "mcg/kg/min", description = "phenylephrine rate", + SCTID = 372771005, category = "medications", sources = list( mimic = list( @@ -2841,6 +2941,7 @@ cfg <- list( cort = list( class = "lgl_cncpt", description = "corticosteroids", + SCTID = 304275008, category = "medications", sources = list( hirid = list( @@ -2861,6 +2962,7 @@ cfg <- list( min = 0, unit = "ml/hr", description = "dextrose (as D10)", + SCTID = 313419007, category = "medications", target = "win_tbl", sources = list( @@ -2939,6 +3041,7 @@ cfg <- lapply(cfg, function(x) { x }) + pkg_dir <- rprojroot::find_root(rprojroot::is_r_package) cfg_dir <- file.path(pkg_dir, "inst", "extdata", "config") diff --git a/inst/extdata/config/concept-dict.json b/inst/extdata/config/concept-dict.json index e7a582da..36b415a1 100644 --- a/inst/extdata/config/concept-dict.json +++ b/inst/extdata/config/concept-dict.json @@ -2,6 +2,7 @@ "abx": { "class": "lgl_cncpt", "description": "antibiotics", + "SCTID": 281789004, "category": "medications", "sources": { "aumc": [ @@ -104,6 +105,7 @@ "adh_rate": { "unit": ["units/min", "U/min"], "description": "vasopressin rate", + "SCTID": 77671006, "category": "medications", "sources": { "aumc": [ @@ -249,6 +251,7 @@ "max": 100, "target": "id_tbl", "description": "patient age", + "SCTID": 424144002, "category": "demographics", "sources": { "aumc": [ @@ -312,6 +315,7 @@ "min": 0, "max": 6, "description": "albumin", + "SCTID": 104485008, "category": "chemistry", "sources": { "aumc": [ @@ -372,6 +376,7 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "alkaline phosphatase", + "SCTID": 365771003, "category": "chemistry", "sources": { "aumc": [ @@ -430,6 +435,7 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "alanine aminotransferase", + "SCTID": 34608000, "category": "chemistry", "sources": { "aumc": [ @@ -488,6 +494,7 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "aspartate aminotransferase", + "SCTID": 45896001, "category": "chemistry", "sources": { "aumc": [ @@ -545,6 +552,7 @@ "avpu": { "concepts": "gcs", "description": "AVPU scale", + "SCTID": 449159002, "category": "neurological", "callback": "avpu", "class": "rec_cncpt" @@ -554,6 +562,7 @@ "min": 0, "max": 50, "description": "basophils", + "SCTID": 42351005, "category": "hematology", "sources": { "aumc": [ @@ -611,6 +620,7 @@ "min": -25, "max": 25, "description": "base excess", + "SCTID": 67487000, "category": "blood gas", "sources": { "aumc": [ @@ -672,6 +682,7 @@ "min": 5, "max": 50, "description": "bicarbonate", + "SCTID": 312471006, "category": "chemistry", "sources": { "aumc": [ @@ -731,6 +742,7 @@ "min": 0, "max": 100, "description": "total bilirubin", + "SCTID": 302787001, "category": "chemistry", "sources": { "aumc": [ @@ -792,6 +804,7 @@ "min": 0, "max": 50, "description": "bilirubin direct", + "SCTID": 39748002, "category": "chemistry", "sources": { "aumc": [ @@ -851,6 +864,7 @@ "bmi": { "concepts": ["weight", "height"], "description": "patient body mass index", + "SCTID": 60621009, "category": "demographics", "callback": "bmi", "target": "id_tbl", @@ -859,6 +873,7 @@ "bnd": { "unit": "%", "description": "band form neutrophils", + "SCTID": 25340006, "category": "hematology", "sources": { "aumc": [ @@ -918,6 +933,7 @@ "min": 0, "max": 200, "description": "blood urea nitrogen", + "SCTID": 105011006, "category": "chemistry", "sources": { "aumc": [ @@ -979,6 +995,7 @@ "min": 4, "max": 20, "description": "calcium", + "SCTID": 71878006, "category": "chemistry", "sources": { "aumc": [ @@ -1042,6 +1059,7 @@ "min": 0.5, "max": 2, "description": "calcium ionized", + "SCTID": 711359007, "category": "blood gas", "sources": { "aumc": [ @@ -1100,6 +1118,7 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "creatine kinase", + "SCTID": 397798009, "category": "chemistry", "sources": { "aumc": [ @@ -1158,6 +1177,7 @@ "unit": "ng/mL", "min": 0, "description": "creatine kinase MB", + "SCTID": 104613001, "category": "chemistry", "sources": { "aumc": [ @@ -1217,6 +1237,7 @@ "min": 80, "max": 130, "description": "chloride", + "SCTID": 46511006, "category": "chemistry", "sources": { "aumc": [ @@ -1274,6 +1295,7 @@ "cort": { "class": "lgl_cncpt", "description": "corticosteroids", + "SCTID": 304275008, "category": "medications", "sources": { "aumc": [ @@ -1299,6 +1321,7 @@ "min": 0, "max": 15, "description": "creatinine", + "SCTID": 113075003, "category": "chemistry", "sources": { "aumc": [ @@ -1359,6 +1382,7 @@ "unit": "mg/L", "min": 0, "description": "C-reactive protein", + "SCTID": 55235003, "category": "chemistry", "sources": { "aumc": [ @@ -1423,6 +1447,7 @@ "min": 0, "max": 200, "description": "diastolic blood pressure", + "SCTID": 271650006, "category": "vitals", "sources": { "aumc": [ @@ -1480,6 +1505,7 @@ "death": { "class": "lgl_cncpt", "description": "in hospital mortality", + "SCTID": 419620001, "category": "outcome", "sources": { "aumc": [ @@ -1551,6 +1577,7 @@ "min": 0, "unit": "ml/hr", "description": "dextrose (as D10)", + "SCTID": 313419007, "category": "medications", "target": "win_tbl", "sources": { @@ -1671,6 +1698,7 @@ }, "dobu_dur": { "description": "dobutamine duration", + "SCTID": 387145002, "category": "medications", "aggregate": "max", "sources": { @@ -1762,6 +1790,7 @@ "min": 0, "max": 50, "description": "dobutamine rate", + "SCTID": 387145002, "category": "medications", "sources": { "aumc": [ @@ -1850,6 +1879,7 @@ "dobu60": { "concepts": ["dobu_rate", "dobu_dur"], "description": "dobutamine administration for min 1h", + "SCTID": 387145002, "category": "medications", "interval": "00:01:00", "callback": "vaso60", @@ -1857,6 +1887,7 @@ }, "dopa_dur": { "description": "dopamine duration", + "SCTID": 412383006, "category": "medications", "aggregate": "max", "sources": { @@ -1939,6 +1970,7 @@ "min": 0, "max": 50, "description": "dopamine rate", + "SCTID": 412383006, "category": "medications", "sources": { "aumc": [ @@ -2018,6 +2050,7 @@ "dopa60": { "concepts": ["dopa_rate", "dopa_dur"], "description": "dopamine administration for min 1h", + "SCTID": 412383006, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -2027,6 +2060,7 @@ "min": 1, "max": 4, "description": "GCS eye", + "SCTID": 281395000, "category": "neurological", "sources": { "aumc": [ @@ -2093,6 +2127,7 @@ "min": 0, "max": 50, "description": "eosinophils", + "SCTID": 71960002, "category": "hematology", "sources": { "aumc": [ @@ -2147,6 +2182,7 @@ }, "epi_dur": { "description": "epinephrine duration", + "SCTID": 387362001, "category": "medications", "aggregate": "max", "sources": { @@ -2238,6 +2274,7 @@ "min": 0, "max": 1.5, "description": "epinephrine rate", + "SCTID": 387362001, "category": "medications", "sources": { "aumc": [ @@ -2326,6 +2363,7 @@ "epi60": { "concepts": ["epi_rate", "epi_dur"], "description": "epinephrine administration for min 1h", + "SCTID": 387362001, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -2336,6 +2374,7 @@ "min": 0, "max": 200, "description": "erythrocyte sedimentation rate", + "SCTID": 416838001, "category": "hematology", "sources": { "aumc": [ @@ -2381,6 +2420,7 @@ "min": 10, "max": 60, "description": "endtidal CO2", + "SCTID": 250790007, "category": "vitals", "sources": { "aumc": [ @@ -2425,6 +2465,7 @@ "ett_gcs": { "class": "lgl_cncpt", "description": "tracheostomy", + "SCTID": 26412008, "category": "respiratory", "target": "win_tbl", "sources": { @@ -2489,6 +2530,7 @@ "min": 0, "max": 1500, "description": "fibrinogen", + "SCTID": 250346004, "category": "hematology", "sources": { "aumc": [ @@ -2550,6 +2592,7 @@ "min": 21, "max": 100, "description": "fraction of inspired oxygen", + "SCTID": 250774007, "category": "blood gas", "sources": { "aumc": [ @@ -2636,6 +2679,7 @@ "gcs": { "concepts": ["egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"], "description": "Glasgow coma scale (non-sedated)", + "SCTID": 248241002, "category": "neurological", "aggregate": ["min", "min", "min", "min", "any"], "callback": "gcs", @@ -2646,6 +2690,7 @@ "min": 0, "max": 1000, "description": "glucose", + "SCTID": 33747003, "category": "chemistry", "sources": { "aumc": [ @@ -2715,6 +2760,7 @@ "hba1c": { "unit": "%", "description": "Hemoglobin A1C", + "SCTID": 43396009, "category": "hematology", "sources": { "aumc": [ @@ -2749,6 +2795,7 @@ }, "hbco": { "description": "carboxyhemoglobin", + "SCTID": 19821003, "category": "blood gas", "sources": { "aumc": [ @@ -2787,6 +2834,7 @@ "min": 15, "max": 60, "description": "hematocrit", + "SCTID": 28317006, "category": "hematology", "sources": { "aumc": [ @@ -2840,6 +2888,7 @@ "max": 230, "target": "id_tbl", "description": "patient height", + "SCTID": 1153637007, "category": "demographics", "sources": { "aumc": [ @@ -2903,6 +2952,7 @@ "min": 4, "max": 18, "description": "hemoglobin", + "SCTID": 302763003, "category": "hematology", "sources": { "aumc": [ @@ -2964,6 +3014,7 @@ "min": 0, "max": 300, "description": "heart rate", + "SCTID": 364075005, "category": "vitals", "sources": { "aumc": [ @@ -3020,6 +3071,7 @@ }, "inr_pt": { "description": "prothrombin time/international normalized ratio", + "SCTID": 165581004, "category": "hematology", "sources": { "aumc": [ @@ -3078,6 +3130,7 @@ "unit": "units/hr", "min": 0, "description": "insulin", + "SCTID": 736101003, "category": "medications", "sources": { "aumc": [ @@ -3158,6 +3211,7 @@ "min": 0, "max": 10, "description": "potassium", + "SCTID": 312468003, "category": "chemistry", "sources": { "aumc": [ @@ -3217,6 +3271,7 @@ "min": 0, "max": 50, "description": "lactate", + "SCTID": 394960005, "category": "blood gas", "sources": { "aumc": [ @@ -3378,6 +3433,7 @@ "min": 0, "max": 100, "description": "lymphocytes", + "SCTID": 74765001, "category": "hematology", "sources": { "aumc": [ @@ -3444,6 +3500,7 @@ "min": 0, "max": 250, "description": "mean arterial pressure", + "SCTID": 6797001, "category": "vitals", "sources": { "aumc": [ @@ -3511,7 +3568,8 @@ "mch": { "unit": "pg", "min": 0, - "description": "mean cell hemoglobin", + "description": "mean corpuscular hemoglobin", + "SCTID": 54706004, "category": "hematology", "sources": { "aumc": [ @@ -3572,6 +3630,7 @@ "min": 20, "max": 50, "description": "mean corpuscular hemoglobin concentration", + "SCTID": 37254006, "category": "hematology", "sources": { "aumc": [ @@ -3633,6 +3692,7 @@ "min": 50, "max": 150, "description": "mean corpuscular volume", + "SCTID": 104133003, "category": "hematology", "sources": { "aumc": [ @@ -3692,6 +3752,7 @@ "target": "win_tbl", "levels": ["invasive", "noninvasive"], "description": "mechanical ventilation windows", + "SCTID": 40617009, "category": "respiratory", "sources": { "aumc": [ @@ -3730,6 +3791,7 @@ "min": 0, "max": 100, "description": "methemoglobin", + "SCTID": 54937007, "category": "blood gas", "sources": { "aumc": [ @@ -3787,6 +3849,7 @@ "mews": { "concepts": ["sbp", "hr", "resp", "temp", "avpu"], "description": "modified early warning score", + "SCTID": 445551004, "category": "outcome", "callback": "mews_score", "class": "rec_cncpt" @@ -3796,6 +3859,7 @@ "min": 0.5, "max": 5, "description": "magnesium", + "SCTID": 38151008, "category": "chemistry", "sources": { "aumc": [ @@ -3858,6 +3922,7 @@ "min": 1, "max": 6, "description": "GCS motor", + "SCTID": 281396004, "category": "neurological", "sources": { "aumc": [ @@ -3924,6 +3989,7 @@ "min": 110, "max": 165, "description": "sodium", + "SCTID": 312469006, "category": "chemistry", "sources": { "aumc": [ @@ -3983,6 +4049,7 @@ "min": 0, "max": 100, "description": "neutrophils", + "SCTID": 30630007, "category": "hematology", "sources": { "aumc": [ @@ -4052,6 +4119,7 @@ }, "norepi_dur": { "description": "norepinephrine duration", + "SCTID": 5.00601410001881e+16, "category": "medications", "aggregate": "max", "sources": { @@ -4150,6 +4218,7 @@ "min": 0, "max": 3, "description": "norepinephrine rate", + "SCTID": 5.00601410001881e+16, "category": "medications", "sources": { "aumc": [ @@ -4238,6 +4307,7 @@ "norepi60": { "concepts": ["norepi_rate", "norepi_dur"], "description": "norepinephrine administration for min 1h", + "SCTID": 5.00601410001881e+16, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -4248,6 +4318,7 @@ "min": 50, "max": 100, "description": "oxygen saturation", + "SCTID": 442476006, "category": "respiratory", "sources": { "aumc": [ @@ -4323,6 +4394,7 @@ "pafi": { "concepts": ["po2", "fio2"], "description": "Horowitz index", + "SCTID": 438173002, "category": "respiratory", "aggregate": ["min", "max"], "callback": "pafi", @@ -4333,6 +4405,7 @@ "min": 10, "max": 150, "description": "CO2 partial pressure", + "SCTID": 250564007, "category": "blood gas", "sources": { "aumc": [ @@ -4391,6 +4464,7 @@ "min": 6.8, "max": 8, "description": "pH of blood", + "SCTID": 27051004, "category": "blood gas", "sources": { "aumc": [ @@ -4448,6 +4522,7 @@ "phn_rate": { "unit": "mcg/kg/min", "description": "phenylephrine rate", + "SCTID": 372771005, "category": "medications", "sources": { "eicu": [ @@ -4532,6 +4607,7 @@ "min": 0, "max": 40, "description": "phosphate", + "SCTID": 104866001, "category": "chemistry", "sources": { "aumc": [ @@ -4593,6 +4669,7 @@ "min": 5, "max": 1200, "description": "platelet count", + "SCTID": 61928009, "category": "hematology", "sources": { "aumc": [ @@ -4652,6 +4729,7 @@ "min": 40, "max": 600, "description": "O2 partial pressure", + "SCTID": 250546000, "category": "blood gas", "sources": { "aumc": [ @@ -4710,6 +4788,7 @@ "unit": "sec", "min": 0, "description": "prothrombine time", + "SCTID": 396451008, "category": "hematology", "sources": { "aumc": [ @@ -4760,6 +4839,7 @@ "unit": "sec", "min": 0, "description": "partial thromboplastin time", + "SCTID": 42525009, "category": "hematology", "sources": { "aumc": [ @@ -4886,6 +4966,7 @@ "min": 0, "max": 20, "description": "red blood cell count", + "SCTID": 14089001, "category": "hematology", "sources": { "aumc": [ @@ -4937,6 +5018,7 @@ "min": 0, "max": 100, "description": "erythrocyte distribution width", + "SCTID": 66842004, "category": "hematology", "sources": { "aumc": [ @@ -4988,6 +5070,7 @@ "min": 0, "max": 120, "description": "respiratory rate", + "SCTID": 86290005, "category": "respiratory", "sources": { "aumc": [ @@ -5053,7 +5136,8 @@ "samp": { "class": "lgl_cncpt", "category": "microbiology", - "description": "fluid sampling", + "description": "body fluid sampling", + "SCTID": 127801007, "sources": { "aumc": [ { @@ -5113,6 +5197,7 @@ "min": 0, "max": 300, "description": "systolic blood pressure", + "SCTID": 271649006, "category": "vitals", "sources": { "aumc": [ @@ -5170,12 +5255,10 @@ "sep3": { "concepts": ["sofa", "susp_inf"], "description": "sepsis-3 criterion", + "SCTID": 91302008, "category": "outcome", "callback": "sep3", - "keep_components": [ - false, - true - ], + "keep_components": [false, true], "class": "rec_cncpt" }, "sex": { @@ -5183,6 +5266,7 @@ "levels": ["Female", "Male"], "class": "fct_cncpt", "description": "patient sex", + "SCTID": 734000001, "category": "demographics", "sources": { "aumc": [ @@ -5244,6 +5328,7 @@ "sirs": { "concepts": ["temp", "hr", "resp", "pco2", "wbc", "bnd"], "description": "systemic inflammatory response syndrome score", + "SCTID": 426929000, "category": "outcome", "callback": "sirs_score", "class": "rec_cncpt" @@ -5251,6 +5336,7 @@ "sofa": { "concepts": ["sofa_resp", "sofa_coag", "sofa_liver", "sofa_cardio", "sofa_cns", "sofa_renal"], "description": "sequential organ failure assessment score", + "SCTID": 1187491009, "category": "outcome", "callback": "sofa_score", "class": "rec_cncpt" @@ -5311,6 +5397,7 @@ "susp_inf": { "concepts": ["abx", "samp"], "description": "suspected infection", + "SCTID": 473130003, "category": "outcome", "aggregate": [ [ @@ -5328,6 +5415,7 @@ "min": 5, "max": 60, "description": "totcal CO2", + "SCTID": 391396001, "category": "blood gas", "sources": { "eicu": [ @@ -5374,6 +5462,7 @@ "min": 32, "max": 42, "description": "temperature", + "SCTID": 386725007, "category": "vitals", "sources": { "aumc": [ @@ -5450,6 +5539,7 @@ "min": 3, "max": 15, "description": "GCS total", + "SCTID": 248241002, "category": "neurological", "sources": { "eicu": [ @@ -5486,6 +5576,7 @@ "unit": "ng/mL", "min": 0, "description": "troponin t", + "SCTID": 121871002, "category": "chemistry", "sources": { "aumc": [ @@ -5545,6 +5636,7 @@ "unit": "ng/mL", "min": 0, "description": "troponin I", + "SCTID": 121870001, "category": "chemistry", "sources": { "eicu": [ @@ -5590,6 +5682,7 @@ "max": 2000, "aggregate": "sum", "description": "urine output", + "SCTID": 364202003, "category": "output", "sources": { "aumc": [ @@ -5660,6 +5753,7 @@ "urine24": { "concepts": "urine", "description": "urine output per 24h", + "SCTID": 395060000, "category": "output", "callback": "urine24", "class": "rec_cncpt" @@ -5667,6 +5761,7 @@ "vaso_ind": { "concepts": ["dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"], "description": "vasopressor indicator", + "SCTID": 870386000, "category": "medications", "callback": "vaso_ind", "class": "rec_cncpt" @@ -5751,6 +5846,7 @@ "vent_ind": { "concepts": ["vent_start", "vent_end", "mech_vent"], "description": "ventilation durations", + "SCTID": 40617009, "category": "respiratory", "interval": "00:01:00", "callback": "vent_ind", @@ -5824,6 +5920,7 @@ "min": 1, "max": 5, "description": "GCS verbal", + "SCTID": 281397008, "category": "neurological", "sources": { "aumc": [ @@ -5889,6 +5986,7 @@ "unit": ["K/uL", "G/l"], "min": 0, "description": "white blood cell count", + "SCTID": 767002, "category": "hematology", "sources": { "aumc": [ @@ -5949,6 +6047,7 @@ "max": 500, "target": "id_tbl", "description": "patient weight", + "SCTID": 27113001, "category": "demographics", "sources": { "aumc": [ From 9e76c074250046da93870063c3b7ed57d274b790 Mon Sep 17 00:00:00 2001 From: Drago Plecko Date: Sat, 3 Dec 2022 17:53:58 -0500 Subject: [PATCH 09/37] SCTID argument added to new_cncpt() --- R/concept-utils.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/concept-utils.R b/R/concept-utils.R index e86026ae..200d8beb 100644 --- a/R/concept-utils.R +++ b/R/concept-utils.R @@ -938,7 +938,7 @@ is_target <- function(x, dat) is_type(get_target(x))(dat) #' #' @export #' -new_cncpt <- function(name, items, description = name, +new_cncpt <- function(name, items, description = name, SCTID = NULL, category = NA_character_, aggregate = NULL, ..., target = "ts_tbl", class = "num_cncpt") { From 0d5a525c8464b2fa82048787ce09dbe12d976ab0 Mon Sep 17 00:00:00 2001 From: Drago Date: Sat, 4 Mar 2023 12:44:18 -0500 Subject: [PATCH 10/37] OMOP IDs added --- inst/extdata/config/concept-dict.R | 301 +++++++++++++++++++---------- 1 file changed, 199 insertions(+), 102 deletions(-) diff --git a/inst/extdata/config/concept-dict.R b/inst/extdata/config/concept-dict.R index cfec2c50..a5f29d63 100644 --- a/inst/extdata/config/concept-dict.R +++ b/inst/extdata/config/concept-dict.R @@ -7,7 +7,8 @@ cfg <- list( min = 0, max = 300, description = "heart rate", - SCTID = 364075005, + omopid = 4239408L, + sctid = 364075005, category = "vitals", sources = list( mimic = list( @@ -34,7 +35,8 @@ cfg <- list( min = 0, max = 300, description = "systolic blood pressure", - SCTID = 271649006, + omopid = 4152194L, + sctid = 271649006, category = "vitals", sources = list( mimic = list( @@ -63,7 +65,8 @@ cfg <- list( min = 0, max = 200, description = "diastolic blood pressure", - SCTID = 271650006, + omopid = 4154790L, + sctid = 271650006, category = "vitals", sources = list( mimic = list( @@ -92,7 +95,8 @@ cfg <- list( min = 0, max = 250, description = "mean arterial pressure", - SCTID = 6797001, + omopid = 4239021L, + sctid = 6797001, category = "vitals", sources = list( mimic = list( @@ -123,7 +127,8 @@ cfg <- list( min = 0, max = 120, description = "respiratory rate", - SCTID = 86290005, + omopid = 4313591L, + sctid = 86290005, category = "respiratory", sources = list( mimic = list( @@ -153,7 +158,8 @@ cfg <- list( min = 50, max = 100, description = "oxygen saturation", - SCTID = 442476006, + omopid = 40483579L, + sctid = 442476006, category = "respiratory", sources = list( mimic = list( @@ -186,7 +192,8 @@ cfg <- list( min = 21, max = 100, description = "fraction of inspired oxygen", - SCTID = 250774007, + omopid = 4353936L, + sctid = 250774007, category = "blood gas", sources = list( mimic = list( @@ -219,7 +226,8 @@ cfg <- list( min = 5, max = 60, description = "totcal CO2", - SCTID = 391396001, + omopid = 4193415L, + sctid = 391396001, category = "blood gas", sources = list( mimic = list( @@ -238,7 +246,8 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "alanine aminotransferase", - SCTID = 34608000, + omopid = 4146380L, + sctid = 34608000, category = "chemistry", sources = list( mimic = list( @@ -264,7 +273,8 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "aspartate aminotransferase", - SCTID = 45896001, + omopid = 4263457L, + sctid = 45896001, category = "chemistry", sources = list( mimic = list( @@ -291,7 +301,8 @@ cfg <- list( min = 0, max = 40, description = "phosphate", - SCTID = 104866001, + omopid = 4017907L, + sctid = 104866001, category = "chemistry", sources = list( mimic = list( @@ -320,7 +331,8 @@ cfg <- list( min = 0, max = 200, description = "blood urea nitrogen", - SCTID = 105011006, + omopid = 4017361L, + sctid = 105011006, category = "chemistry", sources = list( mimic = list( @@ -348,7 +360,8 @@ cfg <- list( min = 6.8, max = 8, description = "pH of blood", - SCTID = 27051004, + omopid = 4097822L, + sctid = 27051004, category = "blood gas", sources = list( mimic = list( @@ -375,7 +388,8 @@ cfg <- list( min = 0, max = 100, description = "total bilirubin", - SCTID = 302787001, + omopid = 4118986L, + sctid = 302787001, category = "chemistry", sources = list( mimic = list( @@ -401,7 +415,8 @@ cfg <- list( ), inr_pt = list( description = "prothrombin time/international normalized ratio", - SCTID = 165581004, + omopid = 4306239L, + sctid = 165581004, category = "hematology", sources = list( mimic = list( @@ -427,7 +442,8 @@ cfg <- list( min = 5, max = 1200, description = "platelet count", - SCTID = 61928009, + omopid = 4267147L, + sctid = 61928009, category = "hematology", sources = list( mimic = list( @@ -454,7 +470,8 @@ cfg <- list( min = 0, max = 50, description = "lactate", - SCTID = 394960005, + omopid = 4191725L, + sctid = 394960005, category = "blood gas", sources = list( mimic = list( @@ -481,7 +498,8 @@ cfg <- list( min = 0, max = 100, description = "lymphocytes", - SCTID = 74765001, + omopid = 4254663L, + sctid = 74765001, category = "hematology", sources = list( mimic = list( @@ -509,7 +527,8 @@ cfg <- list( min = 5, max = 50, description = "bicarbonate", - SCTID = 312471006, + omopid = 4194291L, + sctid = 312471006, category = "chemistry", sources = list( mimic = list( @@ -536,7 +555,8 @@ cfg <- list( min = 0, max = 15, description = "creatinine", - SCTID = 113075003, + omopid = 4013964L, + sctid = 113075003, category = "chemistry", sources = list( mimic = list( @@ -564,7 +584,8 @@ cfg <- list( unit = "sec", min = 0, description = "prothrombine time", - SCTID = 396451008, + omopid = 4245261L, + sctid = 396451008, category = "hematology", sources = list( mimic = list( @@ -586,7 +607,8 @@ cfg <- list( min = 0, max = 100, description = "erythrocyte distribution width", - SCTID = 66842004, + omopid = 4281085L, + sctid = 66842004, category = "hematology", sources = list( mimic = list( @@ -607,7 +629,8 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "alkaline phosphatase", - SCTID = 365771003, + omopid = 4275206L, + sctid = 365771003, category = "chemistry", sources = list( mimic = list( @@ -633,7 +656,8 @@ cfg <- list( unit = c("K/uL", "G/l"), min = 0, description = "white blood cell count", - SCTID = 767002, + omopid = 4298431L, + sctid = 767002, category = "hematology", sources = list( mimic = list( @@ -660,7 +684,8 @@ cfg <- list( min = 4, max = 18, description = "hemoglobin", - SCTID = 302763003, + omopid = 4118981L, + sctid = 302763003, category = "hematology", sources = list( mimic = list( @@ -690,7 +715,8 @@ cfg <- list( min = 15, max = 60, description = "hematocrit", - SCTID = 28317006, + omopid = 4151358L, + sctid = 28317006, category = "hematology", sources = list( mimic = list( @@ -714,7 +740,8 @@ cfg <- list( min = 10, max = 150, description = "CO2 partial pressure", - SCTID = 250564007, + omopid = 4097882L, + sctid = 250564007, category = "blood gas", sources = list( mimic = list( @@ -741,7 +768,8 @@ cfg <- list( min = 40, max = 600, description = "O2 partial pressure", - SCTID = 250546000, + omopid = 4094581L, + sctid = 250546000, category = "blood gas", sources = list( mimic = list( @@ -767,7 +795,8 @@ cfg <- list( unit = "pg", min = 0, description = "mean corpuscular hemoglobin", - SCTID = 54706004, + omopid = 4182871L, + sctid = 54706004, category = "hematology", sources = list( mimic = list( @@ -794,7 +823,8 @@ cfg <- list( min = 20, max = 50, description = "mean corpuscular hemoglobin concentration", - SCTID = 37254006, + omopid = 4290193L, + sctid = 37254006, category = "hematology", sources = list( mimic = list( @@ -822,7 +852,8 @@ cfg <- list( min = 50, max = 150, description = "mean corpuscular volume", - SCTID = 104133003, + omopid = 4016239L, + sctid = 104133003, category = "hematology", sources = list( mimic = list( @@ -847,7 +878,8 @@ cfg <- list( unit = "sec", min = 0, description = "partial thromboplastin time", - SCTID = 42525009, + omopid = 4175016L, + sctid = 42525009, category = "hematology", sources = list( mimic = list( @@ -873,7 +905,8 @@ cfg <- list( min = 4, max = 20, description = "calcium", - SCTID = 71878006, + omopid = 4216722L, + sctid = 71878006, category = "chemistry", sources = list( mimic = list( @@ -902,7 +935,8 @@ cfg <- list( min = 80, max = 130, description = "chloride", - SCTID = 46511006, + omopid = 4188066L, + sctid = 46511006, category = "chemistry", sources = list( mimic = list( @@ -928,7 +962,8 @@ cfg <- list( min = 0.5, max = 5, description = "magnesium", - SCTID = 38151008, + omopid = 4243005L, + sctid = 38151008, category = "chemistry", sources = list( mimic = list( @@ -958,7 +993,8 @@ cfg <- list( min = 0, max = 10, description = "potassium", - SCTID = 312468003, + omopid = 4207483L, + sctid = 312468003, category = "chemistry", sources = list( mimic = list( @@ -985,7 +1021,8 @@ cfg <- list( min = 110, max = 165, description = "sodium", - SCTID = 312469006, + omopid = 4208938L, + sctid = 312469006, category = "chemistry", sources = list( mimic = list( @@ -1012,7 +1049,8 @@ cfg <- list( min = 0, max = 50, description = "basophils", - SCTID = 42351005, + omopid = 4172647L, + sctid = 42351005, category = "hematology", sources = list( mimic = list( @@ -1036,7 +1074,8 @@ cfg <- list( min = 0, max = 50, description = "eosinophils", - SCTID = 71960002, + omopid = 4216098L, + sctid = 71960002, category = "hematology", sources = list( mimic = list( @@ -1060,7 +1099,8 @@ cfg <- list( min = 0, max = 100, description = "neutrophils", - SCTID = 30630007, + omopid = 4148615L, + sctid = 30630007, category = "hematology", sources = list( mimic = list( @@ -1089,7 +1129,8 @@ cfg <- list( min = 0, max = 1000, description = "glucose", - SCTID = 33747003, + omopid = 4144235L, + sctid = 33747003, category = "chemistry", sources = list( mimic = list( @@ -1120,7 +1161,8 @@ cfg <- list( min = 0.5, max = 2, description = "calcium ionized", - SCTID = 711359007, + omopid = 46272910L, + sctid = 711359007, category = "blood gas", sources = list( mimic = list( @@ -1146,7 +1188,8 @@ cfg <- list( unit = "mg/L", min = 0, description = "C-reactive protein", - SCTID = 55235003, + omopid = 4208414L, + sctid = 55235003, category = "chemistry", sources = list( mimic = list( @@ -1176,7 +1219,8 @@ cfg <- list( min = 0, max = 200, description = "erythrocyte sedimentation rate", - SCTID = 416838001, + omopid = 4212065L, + sctid = 416838001, category = "hematology", sources = list( mimic = list( @@ -1197,7 +1241,8 @@ cfg <- list( ), hbco = list( description = "carboxyhemoglobin", - SCTID = 19821003, + omopid = 4056787L, + sctid = 19821003, category = "blood gas", sources = list( eicu = list( @@ -1217,7 +1262,8 @@ cfg <- list( min = 0, max = 100, description = "methemoglobin", - SCTID = 54937007, + omopid = 4204561L, + sctid = 54937007, category = "blood gas", sources = list( mimic = list( @@ -1242,7 +1288,8 @@ cfg <- list( unit = "ng/mL", min = 0, description = "troponin t", - SCTID = 121871002, + omopid = 4005525L, + sctid = 121871002, category = "chemistry", sources = list( mimic = list( @@ -1269,7 +1316,8 @@ cfg <- list( min = 0, max = 6, description = "albumin", - SCTID = 104485008, + omopid = 4017497L, + sctid = 104485008, category = "chemistry", sources = list( mimic = list( @@ -1298,7 +1346,8 @@ cfg <- list( min = 0, max = 1500, description = "fibrinogen", - SCTID = 250346004, + omopid = 4094436L, + sctid = 250346004, category = "hematology", sources = list( mimic = list( @@ -1327,7 +1376,8 @@ cfg <- list( min = -25, max = 25, description = "base excess", - SCTID = 67487000, + omopid = 4284393L, + sctid = 67487000, category = "blood gas", sources = list( mimic = list( @@ -1354,7 +1404,8 @@ cfg <- list( min = 0, max = 20, description = "red blood cell count", - SCTID = 14089001, + omopid = 4030871L, + sctid = 14089001, category = "hematology", sources = list( mimic = list( @@ -1375,7 +1426,8 @@ cfg <- list( unit = c("IU/L", "U/l"), min = 0, description = "creatine kinase", - SCTID = 397798009, + omopid = 4265595L, + sctid = 397798009, category = "chemistry", sources = list( mimic = list( @@ -1401,7 +1453,8 @@ cfg <- list( unit = "ng/mL", min = 0, description = "creatine kinase MB", - SCTID = 104613001, + omopid = 4017058L, + sctid = 104613001, category = "chemistry", sources = list( mimic = list( @@ -1426,7 +1479,8 @@ cfg <- list( min = 1, max = 4, description = "GCS eye", - SCTID = 281395000, + omopid = 4084277L, + sctid = 281395000, category = "neurological", sources = list( mimic = list( @@ -1465,7 +1519,8 @@ cfg <- list( min = 1, max = 5, description = "GCS verbal", - SCTID = 281397008, + omopid = 4084912L, + sctid = 281397008, category = "neurological", sources = list( mimic = list( @@ -1507,7 +1562,8 @@ cfg <- list( min = 1, max = 6, description = "GCS motor", - SCTID = 281396004, + omopid = 4083352L, + sctid = 281396004, category = "neurological", sources = list( mimic = list( @@ -1550,7 +1606,8 @@ cfg <- list( min = 3, max = 15, description = "GCS total", - SCTID = 248241002, + omopid = 4093836L, + sctid = 248241002, category = "neurological", sources = list( mimic = list( @@ -1568,7 +1625,8 @@ cfg <- list( max = 2000, aggregate = "sum", description = "urine output", - SCTID = 364202003, + omopid = 4264378L, + sctid = 364202003, category = "output", sources = list( mimic = list( @@ -1604,7 +1662,8 @@ cfg <- list( min = 0, max = 50, description = "dobutamine rate", - SCTID = 387145002, + omopid = 1337720L, + sctid = 387145002, category = "medications", sources = list( mimic = list( @@ -1636,7 +1695,6 @@ cfg <- list( ), dobu_dur = list( description = "dobutamine duration", - SCTID = 387145002, category = "medications", aggregate = "max", sources = list( @@ -1671,7 +1729,6 @@ cfg <- list( dobu60 = list( concepts = c("dobu_rate", "dobu_dur"), description = "dobutamine administration for min 1h", - SCTID = 387145002, category = "medications", interval = "00:01:00", callback = "vaso60", @@ -1682,7 +1739,8 @@ cfg <- list( min = 0, max = 50, description = "dopamine rate", - SCTID = 412383006, + omopid = 1337860L, + sctid = 412383006, category = "medications", sources = list( mimic = list( @@ -1710,7 +1768,6 @@ cfg <- list( ), dopa_dur = list( description = "dopamine duration", - SCTID = 412383006, category = "medications", aggregate = "max", sources = list( @@ -1741,7 +1798,6 @@ cfg <- list( dopa60 = list( concepts = c("dopa_rate", "dopa_dur"), description = "dopamine administration for min 1h", - SCTID = 412383006, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1752,7 +1808,8 @@ cfg <- list( min = 0, max = 3, description = "norepinephrine rate", - SCTID = 50060141000188106, + omopid = 1321341, + sctid = 50060141000188106, category = "medications", sources = list( mimic = list( @@ -1785,7 +1842,6 @@ cfg <- list( ), norepi_dur = list( description = "norepinephrine duration", - SCTID = 50060141000188106, category = "medications", aggregate = "max", sources = list( @@ -1821,7 +1877,6 @@ cfg <- list( norepi60 = list( concepts = c("norepi_rate", "norepi_dur"), description = "norepinephrine administration for min 1h", - SCTID = 50060141000188106, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1832,7 +1887,8 @@ cfg <- list( min = 0, max = 1.5, description = "epinephrine rate", - SCTID = 387362001, + omopid = 1343916L, + sctid = 387362001, category = "medications", sources = list( mimic = list( @@ -1865,7 +1921,6 @@ cfg <- list( ), epi_dur = list( description = "epinephrine duration", - SCTID = 387362001, category = "medications", aggregate = "max", sources = list( @@ -1901,7 +1956,6 @@ cfg <- list( epi60 = list( concepts = c("epi_rate", "epi_dur"), description = "epinephrine administration for min 1h", - SCTID = 387362001, category = "outcome", interval = "00:01:00", callback = "vaso60", @@ -1967,7 +2021,8 @@ cfg <- list( target = "win_tbl", levels = c("invasive", "noninvasive"), description = "mechanical ventilation windows", - SCTID = 40617009, + omopid = 4230167L, + sctid = 40617009, category = "respiratory", sources = list( aumc = list( @@ -2001,7 +2056,8 @@ cfg <- list( ett_gcs = list( class = "lgl_cncpt", description = "tracheostomy", - SCTID = 26412008, + omopid = 4097216L, + sctid = 26412008, category = "respiratory", target = "win_tbl", sources = list( @@ -2046,6 +2102,7 @@ cfg <- list( min = -5, max = 4, description = "Richmond agitation sedation scale", + omopid = 36684829L, category = "neurological", sources = list( mimic = list( @@ -2071,7 +2128,8 @@ cfg <- list( abx = list( class = "lgl_cncpt", description = "antibiotics", - SCTID = 281789004, + omopid = 4085730L, + sctid = 281789004, category = "medications", sources = list( mimic = list( @@ -2178,7 +2236,8 @@ cfg <- list( class = "lgl_cncpt", category = "microbiology", description = "body fluid sampling", - SCTID = 127801007, + omopid = 4133843L, + sctid = 127801007, sources = list( mimic = list( list(table = "microbiologyevents", val_var = "org_itemid", @@ -2208,7 +2267,8 @@ cfg <- list( unit = "ng/mL", min = 0, description = "troponin I", - SCTID = 121870001, + omopid = 4007805L, + sctid = 121870001, category = "chemistry", sources = list( mimic = list( @@ -2227,7 +2287,8 @@ cfg <- list( min = 0, max = 50, description = "bilirubin direct", - SCTID = 39748002, + omopid = 4216632L, + sctid = 39748002, category = "chemistry", sources = list( mimic = list( @@ -2255,7 +2316,8 @@ cfg <- list( min = 32, max = 42, description = "temperature", - SCTID = 386725007, + omopid = 4302666L, + sctid = 386725007, category = "vitals", sources = list( mimic = list( @@ -2290,7 +2352,8 @@ cfg <- list( min = 10, max = 60, description = "endtidal CO2", - SCTID = 250790007, + omopid = 4353940L, + sctid = 250790007, category = "vitals", sources = list( mimic = list( @@ -2317,7 +2380,8 @@ cfg <- list( unit = "units/hr", min = 0, description = "insulin", - SCTID = 736101003, + omopid = 42537007L, + sctid = 736101003, category = "medications", sources = list( mimic = list( @@ -2351,7 +2415,8 @@ cfg <- list( levels = c("Female", "Male"), class = "fct_cncpt", description = "patient sex", - SCTID = 734000001, + omopid = 37116947L, + sctid = 734000001, category = "demographics", sources = list( mimic = list( @@ -2386,7 +2451,8 @@ cfg <- list( max = 100, target = "id_tbl", description = "patient age", - SCTID = 424144002, + omopid = 4314456L, + sctid = 424144002, category = "demographics", sources = list( mimic = list( @@ -2421,7 +2487,8 @@ cfg <- list( max = 500, target = "id_tbl", description = "patient weight", - SCTID = 27113001, + omopid = 4099154L, + sctid = 27113001, category = "demographics", sources = list( mimic = list( @@ -2458,7 +2525,8 @@ cfg <- list( max = 230, target = "id_tbl", description = "patient height", - SCTID = 1153637007, + omopid = 607590L, + sctid = 1153637007, category = "demographics", sources = list( mimic = list( @@ -2492,7 +2560,8 @@ cfg <- list( death = list( class = "lgl_cncpt", description = "in hospital mortality", - SCTID = 419620001, + omopid = 4306655L, + sctid = 419620001, category = "outcome", sources = list( mimic = list( @@ -2626,6 +2695,7 @@ cfg <- list( min = 0, target = "id_tbl", description = "hospital length of stay", + omopid = 462369952L, category = "outcome", sources = list( mimic = list( @@ -2642,7 +2712,8 @@ cfg <- list( pafi = list( concepts = c("po2", "fio2"), description = "Horowitz index", - SCTID = 438173002, + omopid = 4233883L, + sctid = 438173002, category = "respiratory", aggregate = c("min", "max"), callback = "pafi", @@ -2659,7 +2730,8 @@ cfg <- list( vent_ind = list( concepts = c("vent_start", "vent_end", "mech_vent"), description = "ventilation durations", - SCTID = 40617009, + omopid = 4230167L, + sctid = 40617009, category = "respiratory", interval = "00:01:00", callback = "vent_ind", @@ -2669,7 +2741,8 @@ cfg <- list( vaso_ind = list( concepts = c("dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"), description = "vasopressor indicator", - SCTID = 870386000, + omopid = 3655896L, + sctid = 870386000, category = "medications", callback = "vaso_ind", class = "rec_cncpt" @@ -2677,7 +2750,8 @@ cfg <- list( gcs = list( concepts = c("egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"), description = "Glasgow coma scale (non-sedated)", - SCTID = 248241002, + omopid = 4093836L, + sctid = 248241002, category = "neurological", aggregate = c("min", "min", "min", "min", "any"), callback = "gcs", @@ -2686,7 +2760,8 @@ cfg <- list( urine24 = list( concepts = "urine", description = "urine output per 24h", - SCTID = 395060000, + omopid = 4191836L, + sctid = 395060000, category = "output", callback = "urine24", class = "rec_cncpt" @@ -2694,6 +2769,7 @@ cfg <- list( sofa_resp = list( concepts = c("pafi", "vent_ind"), description = "SOFA respiratory component", + omopid = 1616907L, category = "outcome", callback = "sofa_resp", class = "rec_cncpt" @@ -2701,6 +2777,7 @@ cfg <- list( sofa_coag = list( concepts = "plt", description = "SOFA coagulation component", + omopid = 1616896L, category = "outcome", aggregate = "min", callback = "sofa_coag", @@ -2709,6 +2786,7 @@ cfg <- list( sofa_liver = list( concepts = "bili", description = "SOFA liver component", + omopid = 1617043L, category = "outcome", aggregate = "max", callback = "sofa_liver", @@ -2717,6 +2795,7 @@ cfg <- list( sofa_cardio = list( concepts = c("map", "dopa60", "norepi60", "dobu60", "epi60"), description = "SOFA cardiovascular component", + omopid = 1617534L, category = "outcome", aggregate = c("min", "max", "max", "max", "max"), callback = "sofa_cardio", @@ -2725,6 +2804,7 @@ cfg <- list( sofa_cns = list( concepts = "gcs", description = "SOFA central nervous system component", + omopid = 1616439L, category = "outcome", callback = "sofa_cns", class = "rec_cncpt" @@ -2732,6 +2812,7 @@ cfg <- list( sofa_renal = list( concepts = c("crea", "urine24"), description = "SOFA renal component", + omopid = 1616355L, category = "outcome", aggregate = c("max", NA), callback = "sofa_renal", @@ -2741,7 +2822,8 @@ cfg <- list( concepts = c("sofa_resp", "sofa_coag", "sofa_liver", "sofa_cardio", "sofa_cns", "sofa_renal"), description = "sequential organ failure assessment score", - SCTID = 1187491009, + omopid = 1616852L, + sctid = 1187491009, category = "outcome", callback = "sofa_score", class = "rec_cncpt" @@ -2749,6 +2831,7 @@ cfg <- list( qsofa = list( concepts = c("gcs", "sbp", "resp"), description = "quick SOFA score", + omopid = 1616732L, category = "outcome", callback = "qsofa_score", class = "rec_cncpt" @@ -2756,7 +2839,8 @@ cfg <- list( susp_inf = list( concepts = c("abx", "samp"), description = "suspected infection", - SCTID = 473130003, + omopid = 43021378L, + sctid = 473130003, category = "outcome", aggregate = lapply(list("sum", FALSE), list), callback = "susp_inf", @@ -2765,7 +2849,8 @@ cfg <- list( sep3 = list( concepts = c("sofa", "susp_inf"), description = "sepsis-3 criterion", - SCTID = 91302008, + omopid = 132797L, + sctid = 91302008, category = "outcome", callback = "sep3", keep_components = c(FALSE, TRUE), @@ -2774,7 +2859,8 @@ cfg <- list( bnd = list( unit = "%", description = "band form neutrophils", - SCTID = 25340006, + omopid = 4100147L, + sctid = 25340006, category = "hematology", sources = list( mimic = list( @@ -2799,7 +2885,8 @@ cfg <- list( sirs = list( concepts = c("temp", "hr", "resp", "pco2", "wbc", "bnd"), description = "systemic inflammatory response syndrome score", - SCTID = 426929000, + omopid = 4140444L, + sctid = 426929000, category = "outcome", callback = "sirs_score", class = "rec_cncpt" @@ -2807,6 +2894,7 @@ cfg <- list( supp_o2 = list( concepts = c("vent_ind", "fio2"), description = "supplemental oxygen", + omopid = 36304401L, category = "respiratory", callback = "supp_o2", class = "rec_cncpt" @@ -2814,7 +2902,8 @@ cfg <- list( avpu = list( concepts = "gcs", description = "AVPU scale", - SCTID = 449159002, + omopid = 40493498L, + sctid = 449159002, category = "neurological", callback = "avpu", class = "rec_cncpt" @@ -2822,6 +2911,7 @@ cfg <- list( news = list( concepts = c("resp", "o2sat", "supp_o2", "temp", "sbp", "hr", "avpu"), description = "national early warning score", + omopid = 44808550, category = "outcome", callback = "news_score", class = "rec_cncpt" @@ -2829,7 +2919,8 @@ cfg <- list( mews = list( concepts = c("sbp", "hr", "resp", "temp","avpu"), description = "modified early warning score", - SCTID = 445551004, + omopid = 40484211L, + sctid = 445551004, category = "outcome", callback = "mews_score", class = "rec_cncpt" @@ -2837,7 +2928,8 @@ cfg <- list( hba1c = list( unit = "%", description = "Hemoglobin A1C", - SCTID = 43396009, + omopid = 4184637L, + sctid = 43396009, category = "hematology", sources = list( mimic = list( @@ -2855,7 +2947,8 @@ cfg <- list( bmi = list( concepts = c("weight", "height"), description = "patient body mass index", - SCTID = 60621009, + omopid = 4245997L, + sctid = 60621009, category = "demographics", callback = "bmi", target = "id_tbl", @@ -2864,7 +2957,8 @@ cfg <- list( adh_rate = list( unit = c("units/min", "U/min"), description = "vasopressin rate", - SCTID = 77671006, + omopid = 1507835L, + sctid = 77671006, category = "medications", sources = list( mimic = list( @@ -2906,7 +3000,8 @@ cfg <- list( phn_rate = list( unit = "mcg/kg/min", description = "phenylephrine rate", - SCTID = 372771005, + omopid = 1135766L, + sctid = 372771005, category = "medications", sources = list( mimic = list( @@ -2941,7 +3036,8 @@ cfg <- list( cort = list( class = "lgl_cncpt", description = "corticosteroids", - SCTID = 304275008, + omopid = 304275008L, + sctid = 304275008, category = "medications", sources = list( hirid = list( @@ -2962,7 +3058,8 @@ cfg <- list( min = 0, unit = "ml/hr", description = "dextrose (as D10)", - SCTID = 313419007, + omopid = 4197597L, + sctid = 313419007, category = "medications", target = "win_tbl", sources = list( From da7a8928e909abe158591062376697a2d270e59b Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:44:02 +0100 Subject: [PATCH 11/37] update json concepts --- inst/extdata/config/concept-dict.json | 301 +++++++++++++++++--------- 1 file changed, 199 insertions(+), 102 deletions(-) diff --git a/inst/extdata/config/concept-dict.json b/inst/extdata/config/concept-dict.json index 36b415a1..5f761313 100644 --- a/inst/extdata/config/concept-dict.json +++ b/inst/extdata/config/concept-dict.json @@ -2,7 +2,8 @@ "abx": { "class": "lgl_cncpt", "description": "antibiotics", - "SCTID": 281789004, + "omopid": 4085730, + "sctid": 281789004, "category": "medications", "sources": { "aumc": [ @@ -105,7 +106,8 @@ "adh_rate": { "unit": ["units/min", "U/min"], "description": "vasopressin rate", - "SCTID": 77671006, + "omopid": 1507835, + "sctid": 77671006, "category": "medications", "sources": { "aumc": [ @@ -251,7 +253,8 @@ "max": 100, "target": "id_tbl", "description": "patient age", - "SCTID": 424144002, + "omopid": 4314456, + "sctid": 424144002, "category": "demographics", "sources": { "aumc": [ @@ -315,7 +318,8 @@ "min": 0, "max": 6, "description": "albumin", - "SCTID": 104485008, + "omopid": 4017497, + "sctid": 104485008, "category": "chemistry", "sources": { "aumc": [ @@ -376,7 +380,8 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "alkaline phosphatase", - "SCTID": 365771003, + "omopid": 4275206, + "sctid": 365771003, "category": "chemistry", "sources": { "aumc": [ @@ -435,7 +440,8 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "alanine aminotransferase", - "SCTID": 34608000, + "omopid": 4146380, + "sctid": 34608000, "category": "chemistry", "sources": { "aumc": [ @@ -494,7 +500,8 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "aspartate aminotransferase", - "SCTID": 45896001, + "omopid": 4263457, + "sctid": 45896001, "category": "chemistry", "sources": { "aumc": [ @@ -552,7 +559,8 @@ "avpu": { "concepts": "gcs", "description": "AVPU scale", - "SCTID": 449159002, + "omopid": 40493498, + "sctid": 449159002, "category": "neurological", "callback": "avpu", "class": "rec_cncpt" @@ -562,7 +570,8 @@ "min": 0, "max": 50, "description": "basophils", - "SCTID": 42351005, + "omopid": 4172647, + "sctid": 42351005, "category": "hematology", "sources": { "aumc": [ @@ -620,7 +629,8 @@ "min": -25, "max": 25, "description": "base excess", - "SCTID": 67487000, + "omopid": 4284393, + "sctid": 67487000, "category": "blood gas", "sources": { "aumc": [ @@ -682,7 +692,8 @@ "min": 5, "max": 50, "description": "bicarbonate", - "SCTID": 312471006, + "omopid": 4194291, + "sctid": 312471006, "category": "chemistry", "sources": { "aumc": [ @@ -742,7 +753,8 @@ "min": 0, "max": 100, "description": "total bilirubin", - "SCTID": 302787001, + "omopid": 4118986, + "sctid": 302787001, "category": "chemistry", "sources": { "aumc": [ @@ -804,7 +816,8 @@ "min": 0, "max": 50, "description": "bilirubin direct", - "SCTID": 39748002, + "omopid": 4216632, + "sctid": 39748002, "category": "chemistry", "sources": { "aumc": [ @@ -864,7 +877,8 @@ "bmi": { "concepts": ["weight", "height"], "description": "patient body mass index", - "SCTID": 60621009, + "omopid": 4245997, + "sctid": 60621009, "category": "demographics", "callback": "bmi", "target": "id_tbl", @@ -873,7 +887,8 @@ "bnd": { "unit": "%", "description": "band form neutrophils", - "SCTID": 25340006, + "omopid": 4100147, + "sctid": 25340006, "category": "hematology", "sources": { "aumc": [ @@ -933,7 +948,8 @@ "min": 0, "max": 200, "description": "blood urea nitrogen", - "SCTID": 105011006, + "omopid": 4017361, + "sctid": 105011006, "category": "chemistry", "sources": { "aumc": [ @@ -995,7 +1011,8 @@ "min": 4, "max": 20, "description": "calcium", - "SCTID": 71878006, + "omopid": 4216722, + "sctid": 71878006, "category": "chemistry", "sources": { "aumc": [ @@ -1059,7 +1076,8 @@ "min": 0.5, "max": 2, "description": "calcium ionized", - "SCTID": 711359007, + "omopid": 46272910, + "sctid": 711359007, "category": "blood gas", "sources": { "aumc": [ @@ -1118,7 +1136,8 @@ "unit": ["IU/L", "U/l"], "min": 0, "description": "creatine kinase", - "SCTID": 397798009, + "omopid": 4265595, + "sctid": 397798009, "category": "chemistry", "sources": { "aumc": [ @@ -1177,7 +1196,8 @@ "unit": "ng/mL", "min": 0, "description": "creatine kinase MB", - "SCTID": 104613001, + "omopid": 4017058, + "sctid": 104613001, "category": "chemistry", "sources": { "aumc": [ @@ -1237,7 +1257,8 @@ "min": 80, "max": 130, "description": "chloride", - "SCTID": 46511006, + "omopid": 4188066, + "sctid": 46511006, "category": "chemistry", "sources": { "aumc": [ @@ -1295,7 +1316,8 @@ "cort": { "class": "lgl_cncpt", "description": "corticosteroids", - "SCTID": 304275008, + "omopid": 304275008, + "sctid": 304275008, "category": "medications", "sources": { "aumc": [ @@ -1321,7 +1343,8 @@ "min": 0, "max": 15, "description": "creatinine", - "SCTID": 113075003, + "omopid": 4013964, + "sctid": 113075003, "category": "chemistry", "sources": { "aumc": [ @@ -1382,7 +1405,8 @@ "unit": "mg/L", "min": 0, "description": "C-reactive protein", - "SCTID": 55235003, + "omopid": 4208414, + "sctid": 55235003, "category": "chemistry", "sources": { "aumc": [ @@ -1447,7 +1471,8 @@ "min": 0, "max": 200, "description": "diastolic blood pressure", - "SCTID": 271650006, + "omopid": 4154790, + "sctid": 271650006, "category": "vitals", "sources": { "aumc": [ @@ -1505,7 +1530,8 @@ "death": { "class": "lgl_cncpt", "description": "in hospital mortality", - "SCTID": 419620001, + "omopid": 4306655, + "sctid": 419620001, "category": "outcome", "sources": { "aumc": [ @@ -1577,7 +1603,8 @@ "min": 0, "unit": "ml/hr", "description": "dextrose (as D10)", - "SCTID": 313419007, + "omopid": 4197597, + "sctid": 313419007, "category": "medications", "target": "win_tbl", "sources": { @@ -1698,7 +1725,6 @@ }, "dobu_dur": { "description": "dobutamine duration", - "SCTID": 387145002, "category": "medications", "aggregate": "max", "sources": { @@ -1790,7 +1816,8 @@ "min": 0, "max": 50, "description": "dobutamine rate", - "SCTID": 387145002, + "omopid": 1337720, + "sctid": 387145002, "category": "medications", "sources": { "aumc": [ @@ -1879,7 +1906,6 @@ "dobu60": { "concepts": ["dobu_rate", "dobu_dur"], "description": "dobutamine administration for min 1h", - "SCTID": 387145002, "category": "medications", "interval": "00:01:00", "callback": "vaso60", @@ -1887,7 +1913,6 @@ }, "dopa_dur": { "description": "dopamine duration", - "SCTID": 412383006, "category": "medications", "aggregate": "max", "sources": { @@ -1970,7 +1995,8 @@ "min": 0, "max": 50, "description": "dopamine rate", - "SCTID": 412383006, + "omopid": 1337860, + "sctid": 412383006, "category": "medications", "sources": { "aumc": [ @@ -2050,7 +2076,6 @@ "dopa60": { "concepts": ["dopa_rate", "dopa_dur"], "description": "dopamine administration for min 1h", - "SCTID": 412383006, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -2060,7 +2085,8 @@ "min": 1, "max": 4, "description": "GCS eye", - "SCTID": 281395000, + "omopid": 4084277, + "sctid": 281395000, "category": "neurological", "sources": { "aumc": [ @@ -2127,7 +2153,8 @@ "min": 0, "max": 50, "description": "eosinophils", - "SCTID": 71960002, + "omopid": 4216098, + "sctid": 71960002, "category": "hematology", "sources": { "aumc": [ @@ -2182,7 +2209,6 @@ }, "epi_dur": { "description": "epinephrine duration", - "SCTID": 387362001, "category": "medications", "aggregate": "max", "sources": { @@ -2274,7 +2300,8 @@ "min": 0, "max": 1.5, "description": "epinephrine rate", - "SCTID": 387362001, + "omopid": 1343916, + "sctid": 387362001, "category": "medications", "sources": { "aumc": [ @@ -2363,7 +2390,6 @@ "epi60": { "concepts": ["epi_rate", "epi_dur"], "description": "epinephrine administration for min 1h", - "SCTID": 387362001, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -2374,7 +2400,8 @@ "min": 0, "max": 200, "description": "erythrocyte sedimentation rate", - "SCTID": 416838001, + "omopid": 4212065, + "sctid": 416838001, "category": "hematology", "sources": { "aumc": [ @@ -2420,7 +2447,8 @@ "min": 10, "max": 60, "description": "endtidal CO2", - "SCTID": 250790007, + "omopid": 4353940, + "sctid": 250790007, "category": "vitals", "sources": { "aumc": [ @@ -2465,7 +2493,8 @@ "ett_gcs": { "class": "lgl_cncpt", "description": "tracheostomy", - "SCTID": 26412008, + "omopid": 4097216, + "sctid": 26412008, "category": "respiratory", "target": "win_tbl", "sources": { @@ -2530,7 +2559,8 @@ "min": 0, "max": 1500, "description": "fibrinogen", - "SCTID": 250346004, + "omopid": 4094436, + "sctid": 250346004, "category": "hematology", "sources": { "aumc": [ @@ -2592,7 +2622,8 @@ "min": 21, "max": 100, "description": "fraction of inspired oxygen", - "SCTID": 250774007, + "omopid": 4353936, + "sctid": 250774007, "category": "blood gas", "sources": { "aumc": [ @@ -2679,7 +2710,8 @@ "gcs": { "concepts": ["egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"], "description": "Glasgow coma scale (non-sedated)", - "SCTID": 248241002, + "omopid": 4093836, + "sctid": 248241002, "category": "neurological", "aggregate": ["min", "min", "min", "min", "any"], "callback": "gcs", @@ -2690,7 +2722,8 @@ "min": 0, "max": 1000, "description": "glucose", - "SCTID": 33747003, + "omopid": 4144235, + "sctid": 33747003, "category": "chemistry", "sources": { "aumc": [ @@ -2760,7 +2793,8 @@ "hba1c": { "unit": "%", "description": "Hemoglobin A1C", - "SCTID": 43396009, + "omopid": 4184637, + "sctid": 43396009, "category": "hematology", "sources": { "aumc": [ @@ -2795,7 +2829,8 @@ }, "hbco": { "description": "carboxyhemoglobin", - "SCTID": 19821003, + "omopid": 4056787, + "sctid": 19821003, "category": "blood gas", "sources": { "aumc": [ @@ -2834,7 +2869,8 @@ "min": 15, "max": 60, "description": "hematocrit", - "SCTID": 28317006, + "omopid": 4151358, + "sctid": 28317006, "category": "hematology", "sources": { "aumc": [ @@ -2888,7 +2924,8 @@ "max": 230, "target": "id_tbl", "description": "patient height", - "SCTID": 1153637007, + "omopid": 607590, + "sctid": 1153637007, "category": "demographics", "sources": { "aumc": [ @@ -2952,7 +2989,8 @@ "min": 4, "max": 18, "description": "hemoglobin", - "SCTID": 302763003, + "omopid": 4118981, + "sctid": 302763003, "category": "hematology", "sources": { "aumc": [ @@ -3014,7 +3052,8 @@ "min": 0, "max": 300, "description": "heart rate", - "SCTID": 364075005, + "omopid": 4239408, + "sctid": 364075005, "category": "vitals", "sources": { "aumc": [ @@ -3071,7 +3110,8 @@ }, "inr_pt": { "description": "prothrombin time/international normalized ratio", - "SCTID": 165581004, + "omopid": 4306239, + "sctid": 165581004, "category": "hematology", "sources": { "aumc": [ @@ -3130,7 +3170,8 @@ "unit": "units/hr", "min": 0, "description": "insulin", - "SCTID": 736101003, + "omopid": 42537007, + "sctid": 736101003, "category": "medications", "sources": { "aumc": [ @@ -3211,7 +3252,8 @@ "min": 0, "max": 10, "description": "potassium", - "SCTID": 312468003, + "omopid": 4207483, + "sctid": 312468003, "category": "chemistry", "sources": { "aumc": [ @@ -3271,7 +3313,8 @@ "min": 0, "max": 50, "description": "lactate", - "SCTID": 394960005, + "omopid": 4191725, + "sctid": 394960005, "category": "blood gas", "sources": { "aumc": [ @@ -3331,6 +3374,7 @@ "min": 0, "target": "id_tbl", "description": "hospital length of stay", + "omopid": 462369952, "category": "outcome", "sources": { "eicu": [ @@ -3433,7 +3477,8 @@ "min": 0, "max": 100, "description": "lymphocytes", - "SCTID": 74765001, + "omopid": 4254663, + "sctid": 74765001, "category": "hematology", "sources": { "aumc": [ @@ -3500,7 +3545,8 @@ "min": 0, "max": 250, "description": "mean arterial pressure", - "SCTID": 6797001, + "omopid": 4239021, + "sctid": 6797001, "category": "vitals", "sources": { "aumc": [ @@ -3569,7 +3615,8 @@ "unit": "pg", "min": 0, "description": "mean corpuscular hemoglobin", - "SCTID": 54706004, + "omopid": 4182871, + "sctid": 54706004, "category": "hematology", "sources": { "aumc": [ @@ -3630,7 +3677,8 @@ "min": 20, "max": 50, "description": "mean corpuscular hemoglobin concentration", - "SCTID": 37254006, + "omopid": 4290193, + "sctid": 37254006, "category": "hematology", "sources": { "aumc": [ @@ -3692,7 +3740,8 @@ "min": 50, "max": 150, "description": "mean corpuscular volume", - "SCTID": 104133003, + "omopid": 4016239, + "sctid": 104133003, "category": "hematology", "sources": { "aumc": [ @@ -3752,7 +3801,8 @@ "target": "win_tbl", "levels": ["invasive", "noninvasive"], "description": "mechanical ventilation windows", - "SCTID": 40617009, + "omopid": 4230167, + "sctid": 40617009, "category": "respiratory", "sources": { "aumc": [ @@ -3791,7 +3841,8 @@ "min": 0, "max": 100, "description": "methemoglobin", - "SCTID": 54937007, + "omopid": 4204561, + "sctid": 54937007, "category": "blood gas", "sources": { "aumc": [ @@ -3849,7 +3900,8 @@ "mews": { "concepts": ["sbp", "hr", "resp", "temp", "avpu"], "description": "modified early warning score", - "SCTID": 445551004, + "omopid": 40484211, + "sctid": 445551004, "category": "outcome", "callback": "mews_score", "class": "rec_cncpt" @@ -3859,7 +3911,8 @@ "min": 0.5, "max": 5, "description": "magnesium", - "SCTID": 38151008, + "omopid": 4243005, + "sctid": 38151008, "category": "chemistry", "sources": { "aumc": [ @@ -3922,7 +3975,8 @@ "min": 1, "max": 6, "description": "GCS motor", - "SCTID": 281396004, + "omopid": 4083352, + "sctid": 281396004, "category": "neurological", "sources": { "aumc": [ @@ -3989,7 +4043,8 @@ "min": 110, "max": 165, "description": "sodium", - "SCTID": 312469006, + "omopid": 4208938, + "sctid": 312469006, "category": "chemistry", "sources": { "aumc": [ @@ -4049,7 +4104,8 @@ "min": 0, "max": 100, "description": "neutrophils", - "SCTID": 30630007, + "omopid": 4148615, + "sctid": 30630007, "category": "hematology", "sources": { "aumc": [ @@ -4113,13 +4169,13 @@ "news": { "concepts": ["resp", "o2sat", "supp_o2", "temp", "sbp", "hr", "avpu"], "description": "national early warning score", + "omopid": 44808550, "category": "outcome", "callback": "news_score", "class": "rec_cncpt" }, "norepi_dur": { "description": "norepinephrine duration", - "SCTID": 5.00601410001881e+16, "category": "medications", "aggregate": "max", "sources": { @@ -4218,7 +4274,8 @@ "min": 0, "max": 3, "description": "norepinephrine rate", - "SCTID": 5.00601410001881e+16, + "omopid": 1321341, + "sctid": 5.00601410001881e+16, "category": "medications", "sources": { "aumc": [ @@ -4307,7 +4364,6 @@ "norepi60": { "concepts": ["norepi_rate", "norepi_dur"], "description": "norepinephrine administration for min 1h", - "SCTID": 5.00601410001881e+16, "category": "outcome", "interval": "00:01:00", "callback": "vaso60", @@ -4318,7 +4374,8 @@ "min": 50, "max": 100, "description": "oxygen saturation", - "SCTID": 442476006, + "omopid": 40483579, + "sctid": 442476006, "category": "respiratory", "sources": { "aumc": [ @@ -4394,7 +4451,8 @@ "pafi": { "concepts": ["po2", "fio2"], "description": "Horowitz index", - "SCTID": 438173002, + "omopid": 4233883, + "sctid": 438173002, "category": "respiratory", "aggregate": ["min", "max"], "callback": "pafi", @@ -4405,7 +4463,8 @@ "min": 10, "max": 150, "description": "CO2 partial pressure", - "SCTID": 250564007, + "omopid": 4097882, + "sctid": 250564007, "category": "blood gas", "sources": { "aumc": [ @@ -4464,7 +4523,8 @@ "min": 6.8, "max": 8, "description": "pH of blood", - "SCTID": 27051004, + "omopid": 4097822, + "sctid": 27051004, "category": "blood gas", "sources": { "aumc": [ @@ -4522,7 +4582,8 @@ "phn_rate": { "unit": "mcg/kg/min", "description": "phenylephrine rate", - "SCTID": 372771005, + "omopid": 1135766, + "sctid": 372771005, "category": "medications", "sources": { "eicu": [ @@ -4607,7 +4668,8 @@ "min": 0, "max": 40, "description": "phosphate", - "SCTID": 104866001, + "omopid": 4017907, + "sctid": 104866001, "category": "chemistry", "sources": { "aumc": [ @@ -4669,7 +4731,8 @@ "min": 5, "max": 1200, "description": "platelet count", - "SCTID": 61928009, + "omopid": 4267147, + "sctid": 61928009, "category": "hematology", "sources": { "aumc": [ @@ -4729,7 +4792,8 @@ "min": 40, "max": 600, "description": "O2 partial pressure", - "SCTID": 250546000, + "omopid": 4094581, + "sctid": 250546000, "category": "blood gas", "sources": { "aumc": [ @@ -4788,7 +4852,8 @@ "unit": "sec", "min": 0, "description": "prothrombine time", - "SCTID": 396451008, + "omopid": 4245261, + "sctid": 396451008, "category": "hematology", "sources": { "aumc": [ @@ -4839,7 +4904,8 @@ "unit": "sec", "min": 0, "description": "partial thromboplastin time", - "SCTID": 42525009, + "omopid": 4175016, + "sctid": 42525009, "category": "hematology", "sources": { "aumc": [ @@ -4897,6 +4963,7 @@ "qsofa": { "concepts": ["gcs", "sbp", "resp"], "description": "quick SOFA score", + "omopid": 1616732, "category": "outcome", "callback": "qsofa_score", "class": "rec_cncpt" @@ -4905,6 +4972,7 @@ "min": -5, "max": 4, "description": "Richmond agitation sedation scale", + "omopid": 36684829, "category": "neurological", "sources": { "aumc": [ @@ -4966,7 +5034,8 @@ "min": 0, "max": 20, "description": "red blood cell count", - "SCTID": 14089001, + "omopid": 4030871, + "sctid": 14089001, "category": "hematology", "sources": { "aumc": [ @@ -5018,7 +5087,8 @@ "min": 0, "max": 100, "description": "erythrocyte distribution width", - "SCTID": 66842004, + "omopid": 4281085, + "sctid": 66842004, "category": "hematology", "sources": { "aumc": [ @@ -5070,7 +5140,8 @@ "min": 0, "max": 120, "description": "respiratory rate", - "SCTID": 86290005, + "omopid": 4313591, + "sctid": 86290005, "category": "respiratory", "sources": { "aumc": [ @@ -5137,7 +5208,8 @@ "class": "lgl_cncpt", "category": "microbiology", "description": "body fluid sampling", - "SCTID": 127801007, + "omopid": 4133843, + "sctid": 127801007, "sources": { "aumc": [ { @@ -5197,7 +5269,8 @@ "min": 0, "max": 300, "description": "systolic blood pressure", - "SCTID": 271649006, + "omopid": 4152194, + "sctid": 271649006, "category": "vitals", "sources": { "aumc": [ @@ -5255,7 +5328,8 @@ "sep3": { "concepts": ["sofa", "susp_inf"], "description": "sepsis-3 criterion", - "SCTID": 91302008, + "omopid": 132797, + "sctid": 91302008, "category": "outcome", "callback": "sep3", "keep_components": [false, true], @@ -5266,7 +5340,8 @@ "levels": ["Female", "Male"], "class": "fct_cncpt", "description": "patient sex", - "SCTID": 734000001, + "omopid": 37116947, + "sctid": 734000001, "category": "demographics", "sources": { "aumc": [ @@ -5328,7 +5403,8 @@ "sirs": { "concepts": ["temp", "hr", "resp", "pco2", "wbc", "bnd"], "description": "systemic inflammatory response syndrome score", - "SCTID": 426929000, + "omopid": 4140444, + "sctid": 426929000, "category": "outcome", "callback": "sirs_score", "class": "rec_cncpt" @@ -5336,7 +5412,8 @@ "sofa": { "concepts": ["sofa_resp", "sofa_coag", "sofa_liver", "sofa_cardio", "sofa_cns", "sofa_renal"], "description": "sequential organ failure assessment score", - "SCTID": 1187491009, + "omopid": 1616852, + "sctid": 1187491009, "category": "outcome", "callback": "sofa_score", "class": "rec_cncpt" @@ -5344,6 +5421,7 @@ "sofa_cardio": { "concepts": ["map", "dopa60", "norepi60", "dobu60", "epi60"], "description": "SOFA cardiovascular component", + "omopid": 1617534, "category": "outcome", "aggregate": ["min", "max", "max", "max", "max"], "callback": "sofa_cardio", @@ -5352,6 +5430,7 @@ "sofa_cns": { "concepts": "gcs", "description": "SOFA central nervous system component", + "omopid": 1616439, "category": "outcome", "callback": "sofa_cns", "class": "rec_cncpt" @@ -5359,6 +5438,7 @@ "sofa_coag": { "concepts": "plt", "description": "SOFA coagulation component", + "omopid": 1616896, "category": "outcome", "aggregate": "min", "callback": "sofa_coag", @@ -5367,6 +5447,7 @@ "sofa_liver": { "concepts": "bili", "description": "SOFA liver component", + "omopid": 1617043, "category": "outcome", "aggregate": "max", "callback": "sofa_liver", @@ -5375,6 +5456,7 @@ "sofa_renal": { "concepts": ["crea", "urine24"], "description": "SOFA renal component", + "omopid": 1616355, "category": "outcome", "aggregate": ["max", null], "callback": "sofa_renal", @@ -5383,6 +5465,7 @@ "sofa_resp": { "concepts": ["pafi", "vent_ind"], "description": "SOFA respiratory component", + "omopid": 1616907, "category": "outcome", "callback": "sofa_resp", "class": "rec_cncpt" @@ -5390,6 +5473,7 @@ "supp_o2": { "concepts": ["vent_ind", "fio2"], "description": "supplemental oxygen", + "omopid": 36304401, "category": "respiratory", "callback": "supp_o2", "class": "rec_cncpt" @@ -5397,7 +5481,8 @@ "susp_inf": { "concepts": ["abx", "samp"], "description": "suspected infection", - "SCTID": 473130003, + "omopid": 43021378, + "sctid": 473130003, "category": "outcome", "aggregate": [ [ @@ -5415,7 +5500,8 @@ "min": 5, "max": 60, "description": "totcal CO2", - "SCTID": 391396001, + "omopid": 4193415, + "sctid": 391396001, "category": "blood gas", "sources": { "eicu": [ @@ -5462,7 +5548,8 @@ "min": 32, "max": 42, "description": "temperature", - "SCTID": 386725007, + "omopid": 4302666, + "sctid": 386725007, "category": "vitals", "sources": { "aumc": [ @@ -5539,7 +5626,8 @@ "min": 3, "max": 15, "description": "GCS total", - "SCTID": 248241002, + "omopid": 4093836, + "sctid": 248241002, "category": "neurological", "sources": { "eicu": [ @@ -5576,7 +5664,8 @@ "unit": "ng/mL", "min": 0, "description": "troponin t", - "SCTID": 121871002, + "omopid": 4005525, + "sctid": 121871002, "category": "chemistry", "sources": { "aumc": [ @@ -5636,7 +5725,8 @@ "unit": "ng/mL", "min": 0, "description": "troponin I", - "SCTID": 121870001, + "omopid": 4007805, + "sctid": 121870001, "category": "chemistry", "sources": { "eicu": [ @@ -5682,7 +5772,8 @@ "max": 2000, "aggregate": "sum", "description": "urine output", - "SCTID": 364202003, + "omopid": 4264378, + "sctid": 364202003, "category": "output", "sources": { "aumc": [ @@ -5753,7 +5844,8 @@ "urine24": { "concepts": "urine", "description": "urine output per 24h", - "SCTID": 395060000, + "omopid": 4191836, + "sctid": 395060000, "category": "output", "callback": "urine24", "class": "rec_cncpt" @@ -5761,7 +5853,8 @@ "vaso_ind": { "concepts": ["dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"], "description": "vasopressor indicator", - "SCTID": 870386000, + "omopid": 3655896, + "sctid": 870386000, "category": "medications", "callback": "vaso_ind", "class": "rec_cncpt" @@ -5846,7 +5939,8 @@ "vent_ind": { "concepts": ["vent_start", "vent_end", "mech_vent"], "description": "ventilation durations", - "SCTID": 40617009, + "omopid": 4230167, + "sctid": 40617009, "category": "respiratory", "interval": "00:01:00", "callback": "vent_ind", @@ -5920,7 +6014,8 @@ "min": 1, "max": 5, "description": "GCS verbal", - "SCTID": 281397008, + "omopid": 4084912, + "sctid": 281397008, "category": "neurological", "sources": { "aumc": [ @@ -5986,7 +6081,8 @@ "unit": ["K/uL", "G/l"], "min": 0, "description": "white blood cell count", - "SCTID": 767002, + "omopid": 4298431, + "sctid": 767002, "category": "hematology", "sources": { "aumc": [ @@ -6047,7 +6143,8 @@ "max": 500, "target": "id_tbl", "description": "patient weight", - "SCTID": 27113001, + "omopid": 4099154, + "sctid": 27113001, "category": "demographics", "sources": { "aumc": [ From 70c243eba662651ef062cc6da46171dda673fdd7 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:59:47 +0100 Subject: [PATCH 12/37] rm sctids for now --- inst/extdata/config/concept-dict.R | 94 --------------------------- inst/extdata/config/concept-dict.json | 94 --------------------------- 2 files changed, 188 deletions(-) diff --git a/inst/extdata/config/concept-dict.R b/inst/extdata/config/concept-dict.R index a5f29d63..0b79cb25 100644 --- a/inst/extdata/config/concept-dict.R +++ b/inst/extdata/config/concept-dict.R @@ -8,7 +8,6 @@ cfg <- list( max = 300, description = "heart rate", omopid = 4239408L, - sctid = 364075005, category = "vitals", sources = list( mimic = list( @@ -36,7 +35,6 @@ cfg <- list( max = 300, description = "systolic blood pressure", omopid = 4152194L, - sctid = 271649006, category = "vitals", sources = list( mimic = list( @@ -66,7 +64,6 @@ cfg <- list( max = 200, description = "diastolic blood pressure", omopid = 4154790L, - sctid = 271650006, category = "vitals", sources = list( mimic = list( @@ -96,7 +93,6 @@ cfg <- list( max = 250, description = "mean arterial pressure", omopid = 4239021L, - sctid = 6797001, category = "vitals", sources = list( mimic = list( @@ -128,7 +124,6 @@ cfg <- list( max = 120, description = "respiratory rate", omopid = 4313591L, - sctid = 86290005, category = "respiratory", sources = list( mimic = list( @@ -159,7 +154,6 @@ cfg <- list( max = 100, description = "oxygen saturation", omopid = 40483579L, - sctid = 442476006, category = "respiratory", sources = list( mimic = list( @@ -193,7 +187,6 @@ cfg <- list( max = 100, description = "fraction of inspired oxygen", omopid = 4353936L, - sctid = 250774007, category = "blood gas", sources = list( mimic = list( @@ -227,7 +220,6 @@ cfg <- list( max = 60, description = "totcal CO2", omopid = 4193415L, - sctid = 391396001, category = "blood gas", sources = list( mimic = list( @@ -247,7 +239,6 @@ cfg <- list( min = 0, description = "alanine aminotransferase", omopid = 4146380L, - sctid = 34608000, category = "chemistry", sources = list( mimic = list( @@ -274,7 +265,6 @@ cfg <- list( min = 0, description = "aspartate aminotransferase", omopid = 4263457L, - sctid = 45896001, category = "chemistry", sources = list( mimic = list( @@ -302,7 +292,6 @@ cfg <- list( max = 40, description = "phosphate", omopid = 4017907L, - sctid = 104866001, category = "chemistry", sources = list( mimic = list( @@ -332,7 +321,6 @@ cfg <- list( max = 200, description = "blood urea nitrogen", omopid = 4017361L, - sctid = 105011006, category = "chemistry", sources = list( mimic = list( @@ -361,7 +349,6 @@ cfg <- list( max = 8, description = "pH of blood", omopid = 4097822L, - sctid = 27051004, category = "blood gas", sources = list( mimic = list( @@ -389,7 +376,6 @@ cfg <- list( max = 100, description = "total bilirubin", omopid = 4118986L, - sctid = 302787001, category = "chemistry", sources = list( mimic = list( @@ -416,7 +402,6 @@ cfg <- list( inr_pt = list( description = "prothrombin time/international normalized ratio", omopid = 4306239L, - sctid = 165581004, category = "hematology", sources = list( mimic = list( @@ -443,7 +428,6 @@ cfg <- list( max = 1200, description = "platelet count", omopid = 4267147L, - sctid = 61928009, category = "hematology", sources = list( mimic = list( @@ -471,7 +455,6 @@ cfg <- list( max = 50, description = "lactate", omopid = 4191725L, - sctid = 394960005, category = "blood gas", sources = list( mimic = list( @@ -499,7 +482,6 @@ cfg <- list( max = 100, description = "lymphocytes", omopid = 4254663L, - sctid = 74765001, category = "hematology", sources = list( mimic = list( @@ -528,7 +510,6 @@ cfg <- list( max = 50, description = "bicarbonate", omopid = 4194291L, - sctid = 312471006, category = "chemistry", sources = list( mimic = list( @@ -556,7 +537,6 @@ cfg <- list( max = 15, description = "creatinine", omopid = 4013964L, - sctid = 113075003, category = "chemistry", sources = list( mimic = list( @@ -585,7 +565,6 @@ cfg <- list( min = 0, description = "prothrombine time", omopid = 4245261L, - sctid = 396451008, category = "hematology", sources = list( mimic = list( @@ -608,7 +587,6 @@ cfg <- list( max = 100, description = "erythrocyte distribution width", omopid = 4281085L, - sctid = 66842004, category = "hematology", sources = list( mimic = list( @@ -630,7 +608,6 @@ cfg <- list( min = 0, description = "alkaline phosphatase", omopid = 4275206L, - sctid = 365771003, category = "chemistry", sources = list( mimic = list( @@ -657,7 +634,6 @@ cfg <- list( min = 0, description = "white blood cell count", omopid = 4298431L, - sctid = 767002, category = "hematology", sources = list( mimic = list( @@ -685,7 +661,6 @@ cfg <- list( max = 18, description = "hemoglobin", omopid = 4118981L, - sctid = 302763003, category = "hematology", sources = list( mimic = list( @@ -716,7 +691,6 @@ cfg <- list( max = 60, description = "hematocrit", omopid = 4151358L, - sctid = 28317006, category = "hematology", sources = list( mimic = list( @@ -741,7 +715,6 @@ cfg <- list( max = 150, description = "CO2 partial pressure", omopid = 4097882L, - sctid = 250564007, category = "blood gas", sources = list( mimic = list( @@ -769,7 +742,6 @@ cfg <- list( max = 600, description = "O2 partial pressure", omopid = 4094581L, - sctid = 250546000, category = "blood gas", sources = list( mimic = list( @@ -796,7 +768,6 @@ cfg <- list( min = 0, description = "mean corpuscular hemoglobin", omopid = 4182871L, - sctid = 54706004, category = "hematology", sources = list( mimic = list( @@ -824,7 +795,6 @@ cfg <- list( max = 50, description = "mean corpuscular hemoglobin concentration", omopid = 4290193L, - sctid = 37254006, category = "hematology", sources = list( mimic = list( @@ -853,7 +823,6 @@ cfg <- list( max = 150, description = "mean corpuscular volume", omopid = 4016239L, - sctid = 104133003, category = "hematology", sources = list( mimic = list( @@ -879,7 +848,6 @@ cfg <- list( min = 0, description = "partial thromboplastin time", omopid = 4175016L, - sctid = 42525009, category = "hematology", sources = list( mimic = list( @@ -906,7 +874,6 @@ cfg <- list( max = 20, description = "calcium", omopid = 4216722L, - sctid = 71878006, category = "chemistry", sources = list( mimic = list( @@ -936,7 +903,6 @@ cfg <- list( max = 130, description = "chloride", omopid = 4188066L, - sctid = 46511006, category = "chemistry", sources = list( mimic = list( @@ -963,7 +929,6 @@ cfg <- list( max = 5, description = "magnesium", omopid = 4243005L, - sctid = 38151008, category = "chemistry", sources = list( mimic = list( @@ -994,7 +959,6 @@ cfg <- list( max = 10, description = "potassium", omopid = 4207483L, - sctid = 312468003, category = "chemistry", sources = list( mimic = list( @@ -1022,7 +986,6 @@ cfg <- list( max = 165, description = "sodium", omopid = 4208938L, - sctid = 312469006, category = "chemistry", sources = list( mimic = list( @@ -1050,7 +1013,6 @@ cfg <- list( max = 50, description = "basophils", omopid = 4172647L, - sctid = 42351005, category = "hematology", sources = list( mimic = list( @@ -1075,7 +1037,6 @@ cfg <- list( max = 50, description = "eosinophils", omopid = 4216098L, - sctid = 71960002, category = "hematology", sources = list( mimic = list( @@ -1100,7 +1061,6 @@ cfg <- list( max = 100, description = "neutrophils", omopid = 4148615L, - sctid = 30630007, category = "hematology", sources = list( mimic = list( @@ -1130,7 +1090,6 @@ cfg <- list( max = 1000, description = "glucose", omopid = 4144235L, - sctid = 33747003, category = "chemistry", sources = list( mimic = list( @@ -1162,7 +1121,6 @@ cfg <- list( max = 2, description = "calcium ionized", omopid = 46272910L, - sctid = 711359007, category = "blood gas", sources = list( mimic = list( @@ -1189,7 +1147,6 @@ cfg <- list( min = 0, description = "C-reactive protein", omopid = 4208414L, - sctid = 55235003, category = "chemistry", sources = list( mimic = list( @@ -1220,7 +1177,6 @@ cfg <- list( max = 200, description = "erythrocyte sedimentation rate", omopid = 4212065L, - sctid = 416838001, category = "hematology", sources = list( mimic = list( @@ -1242,7 +1198,6 @@ cfg <- list( hbco = list( description = "carboxyhemoglobin", omopid = 4056787L, - sctid = 19821003, category = "blood gas", sources = list( eicu = list( @@ -1263,7 +1218,6 @@ cfg <- list( max = 100, description = "methemoglobin", omopid = 4204561L, - sctid = 54937007, category = "blood gas", sources = list( mimic = list( @@ -1289,7 +1243,6 @@ cfg <- list( min = 0, description = "troponin t", omopid = 4005525L, - sctid = 121871002, category = "chemistry", sources = list( mimic = list( @@ -1317,7 +1270,6 @@ cfg <- list( max = 6, description = "albumin", omopid = 4017497L, - sctid = 104485008, category = "chemistry", sources = list( mimic = list( @@ -1347,7 +1299,6 @@ cfg <- list( max = 1500, description = "fibrinogen", omopid = 4094436L, - sctid = 250346004, category = "hematology", sources = list( mimic = list( @@ -1377,7 +1328,6 @@ cfg <- list( max = 25, description = "base excess", omopid = 4284393L, - sctid = 67487000, category = "blood gas", sources = list( mimic = list( @@ -1405,7 +1355,6 @@ cfg <- list( max = 20, description = "red blood cell count", omopid = 4030871L, - sctid = 14089001, category = "hematology", sources = list( mimic = list( @@ -1427,7 +1376,6 @@ cfg <- list( min = 0, description = "creatine kinase", omopid = 4265595L, - sctid = 397798009, category = "chemistry", sources = list( mimic = list( @@ -1454,7 +1402,6 @@ cfg <- list( min = 0, description = "creatine kinase MB", omopid = 4017058L, - sctid = 104613001, category = "chemistry", sources = list( mimic = list( @@ -1480,7 +1427,6 @@ cfg <- list( max = 4, description = "GCS eye", omopid = 4084277L, - sctid = 281395000, category = "neurological", sources = list( mimic = list( @@ -1520,7 +1466,6 @@ cfg <- list( max = 5, description = "GCS verbal", omopid = 4084912L, - sctid = 281397008, category = "neurological", sources = list( mimic = list( @@ -1563,7 +1508,6 @@ cfg <- list( max = 6, description = "GCS motor", omopid = 4083352L, - sctid = 281396004, category = "neurological", sources = list( mimic = list( @@ -1607,7 +1551,6 @@ cfg <- list( max = 15, description = "GCS total", omopid = 4093836L, - sctid = 248241002, category = "neurological", sources = list( mimic = list( @@ -1626,7 +1569,6 @@ cfg <- list( aggregate = "sum", description = "urine output", omopid = 4264378L, - sctid = 364202003, category = "output", sources = list( mimic = list( @@ -1663,7 +1605,6 @@ cfg <- list( max = 50, description = "dobutamine rate", omopid = 1337720L, - sctid = 387145002, category = "medications", sources = list( mimic = list( @@ -1740,7 +1681,6 @@ cfg <- list( max = 50, description = "dopamine rate", omopid = 1337860L, - sctid = 412383006, category = "medications", sources = list( mimic = list( @@ -1809,7 +1749,6 @@ cfg <- list( max = 3, description = "norepinephrine rate", omopid = 1321341, - sctid = 50060141000188106, category = "medications", sources = list( mimic = list( @@ -1888,7 +1827,6 @@ cfg <- list( max = 1.5, description = "epinephrine rate", omopid = 1343916L, - sctid = 387362001, category = "medications", sources = list( mimic = list( @@ -2022,7 +1960,6 @@ cfg <- list( levels = c("invasive", "noninvasive"), description = "mechanical ventilation windows", omopid = 4230167L, - sctid = 40617009, category = "respiratory", sources = list( aumc = list( @@ -2057,7 +1994,6 @@ cfg <- list( class = "lgl_cncpt", description = "tracheostomy", omopid = 4097216L, - sctid = 26412008, category = "respiratory", target = "win_tbl", sources = list( @@ -2129,7 +2065,6 @@ cfg <- list( class = "lgl_cncpt", description = "antibiotics", omopid = 4085730L, - sctid = 281789004, category = "medications", sources = list( mimic = list( @@ -2237,7 +2172,6 @@ cfg <- list( category = "microbiology", description = "body fluid sampling", omopid = 4133843L, - sctid = 127801007, sources = list( mimic = list( list(table = "microbiologyevents", val_var = "org_itemid", @@ -2268,7 +2202,6 @@ cfg <- list( min = 0, description = "troponin I", omopid = 4007805L, - sctid = 121870001, category = "chemistry", sources = list( mimic = list( @@ -2288,7 +2221,6 @@ cfg <- list( max = 50, description = "bilirubin direct", omopid = 4216632L, - sctid = 39748002, category = "chemistry", sources = list( mimic = list( @@ -2317,7 +2249,6 @@ cfg <- list( max = 42, description = "temperature", omopid = 4302666L, - sctid = 386725007, category = "vitals", sources = list( mimic = list( @@ -2353,7 +2284,6 @@ cfg <- list( max = 60, description = "endtidal CO2", omopid = 4353940L, - sctid = 250790007, category = "vitals", sources = list( mimic = list( @@ -2381,7 +2311,6 @@ cfg <- list( min = 0, description = "insulin", omopid = 42537007L, - sctid = 736101003, category = "medications", sources = list( mimic = list( @@ -2416,7 +2345,6 @@ cfg <- list( class = "fct_cncpt", description = "patient sex", omopid = 37116947L, - sctid = 734000001, category = "demographics", sources = list( mimic = list( @@ -2452,7 +2380,6 @@ cfg <- list( target = "id_tbl", description = "patient age", omopid = 4314456L, - sctid = 424144002, category = "demographics", sources = list( mimic = list( @@ -2488,7 +2415,6 @@ cfg <- list( target = "id_tbl", description = "patient weight", omopid = 4099154L, - sctid = 27113001, category = "demographics", sources = list( mimic = list( @@ -2526,7 +2452,6 @@ cfg <- list( target = "id_tbl", description = "patient height", omopid = 607590L, - sctid = 1153637007, category = "demographics", sources = list( mimic = list( @@ -2561,7 +2486,6 @@ cfg <- list( class = "lgl_cncpt", description = "in hospital mortality", omopid = 4306655L, - sctid = 419620001, category = "outcome", sources = list( mimic = list( @@ -2713,7 +2637,6 @@ cfg <- list( concepts = c("po2", "fio2"), description = "Horowitz index", omopid = 4233883L, - sctid = 438173002, category = "respiratory", aggregate = c("min", "max"), callback = "pafi", @@ -2731,7 +2654,6 @@ cfg <- list( concepts = c("vent_start", "vent_end", "mech_vent"), description = "ventilation durations", omopid = 4230167L, - sctid = 40617009, category = "respiratory", interval = "00:01:00", callback = "vent_ind", @@ -2742,7 +2664,6 @@ cfg <- list( concepts = c("dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"), description = "vasopressor indicator", omopid = 3655896L, - sctid = 870386000, category = "medications", callback = "vaso_ind", class = "rec_cncpt" @@ -2751,7 +2672,6 @@ cfg <- list( concepts = c("egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"), description = "Glasgow coma scale (non-sedated)", omopid = 4093836L, - sctid = 248241002, category = "neurological", aggregate = c("min", "min", "min", "min", "any"), callback = "gcs", @@ -2761,7 +2681,6 @@ cfg <- list( concepts = "urine", description = "urine output per 24h", omopid = 4191836L, - sctid = 395060000, category = "output", callback = "urine24", class = "rec_cncpt" @@ -2823,7 +2742,6 @@ cfg <- list( "sofa_cns", "sofa_renal"), description = "sequential organ failure assessment score", omopid = 1616852L, - sctid = 1187491009, category = "outcome", callback = "sofa_score", class = "rec_cncpt" @@ -2840,7 +2758,6 @@ cfg <- list( concepts = c("abx", "samp"), description = "suspected infection", omopid = 43021378L, - sctid = 473130003, category = "outcome", aggregate = lapply(list("sum", FALSE), list), callback = "susp_inf", @@ -2850,7 +2767,6 @@ cfg <- list( concepts = c("sofa", "susp_inf"), description = "sepsis-3 criterion", omopid = 132797L, - sctid = 91302008, category = "outcome", callback = "sep3", keep_components = c(FALSE, TRUE), @@ -2860,7 +2776,6 @@ cfg <- list( unit = "%", description = "band form neutrophils", omopid = 4100147L, - sctid = 25340006, category = "hematology", sources = list( mimic = list( @@ -2886,7 +2801,6 @@ cfg <- list( concepts = c("temp", "hr", "resp", "pco2", "wbc", "bnd"), description = "systemic inflammatory response syndrome score", omopid = 4140444L, - sctid = 426929000, category = "outcome", callback = "sirs_score", class = "rec_cncpt" @@ -2903,7 +2817,6 @@ cfg <- list( concepts = "gcs", description = "AVPU scale", omopid = 40493498L, - sctid = 449159002, category = "neurological", callback = "avpu", class = "rec_cncpt" @@ -2920,7 +2833,6 @@ cfg <- list( concepts = c("sbp", "hr", "resp", "temp","avpu"), description = "modified early warning score", omopid = 40484211L, - sctid = 445551004, category = "outcome", callback = "mews_score", class = "rec_cncpt" @@ -2929,7 +2841,6 @@ cfg <- list( unit = "%", description = "Hemoglobin A1C", omopid = 4184637L, - sctid = 43396009, category = "hematology", sources = list( mimic = list( @@ -2948,7 +2859,6 @@ cfg <- list( concepts = c("weight", "height"), description = "patient body mass index", omopid = 4245997L, - sctid = 60621009, category = "demographics", callback = "bmi", target = "id_tbl", @@ -2958,7 +2868,6 @@ cfg <- list( unit = c("units/min", "U/min"), description = "vasopressin rate", omopid = 1507835L, - sctid = 77671006, category = "medications", sources = list( mimic = list( @@ -3001,7 +2910,6 @@ cfg <- list( unit = "mcg/kg/min", description = "phenylephrine rate", omopid = 1135766L, - sctid = 372771005, category = "medications", sources = list( mimic = list( @@ -3037,7 +2945,6 @@ cfg <- list( class = "lgl_cncpt", description = "corticosteroids", omopid = 304275008L, - sctid = 304275008, category = "medications", sources = list( hirid = list( @@ -3059,7 +2966,6 @@ cfg <- list( unit = "ml/hr", description = "dextrose (as D10)", omopid = 4197597L, - sctid = 313419007, category = "medications", target = "win_tbl", sources = list( diff --git a/inst/extdata/config/concept-dict.json b/inst/extdata/config/concept-dict.json index 5f761313..10fd7f3c 100644 --- a/inst/extdata/config/concept-dict.json +++ b/inst/extdata/config/concept-dict.json @@ -3,7 +3,6 @@ "class": "lgl_cncpt", "description": "antibiotics", "omopid": 4085730, - "sctid": 281789004, "category": "medications", "sources": { "aumc": [ @@ -107,7 +106,6 @@ "unit": ["units/min", "U/min"], "description": "vasopressin rate", "omopid": 1507835, - "sctid": 77671006, "category": "medications", "sources": { "aumc": [ @@ -254,7 +252,6 @@ "target": "id_tbl", "description": "patient age", "omopid": 4314456, - "sctid": 424144002, "category": "demographics", "sources": { "aumc": [ @@ -319,7 +316,6 @@ "max": 6, "description": "albumin", "omopid": 4017497, - "sctid": 104485008, "category": "chemistry", "sources": { "aumc": [ @@ -381,7 +377,6 @@ "min": 0, "description": "alkaline phosphatase", "omopid": 4275206, - "sctid": 365771003, "category": "chemistry", "sources": { "aumc": [ @@ -441,7 +436,6 @@ "min": 0, "description": "alanine aminotransferase", "omopid": 4146380, - "sctid": 34608000, "category": "chemistry", "sources": { "aumc": [ @@ -501,7 +495,6 @@ "min": 0, "description": "aspartate aminotransferase", "omopid": 4263457, - "sctid": 45896001, "category": "chemistry", "sources": { "aumc": [ @@ -560,7 +553,6 @@ "concepts": "gcs", "description": "AVPU scale", "omopid": 40493498, - "sctid": 449159002, "category": "neurological", "callback": "avpu", "class": "rec_cncpt" @@ -571,7 +563,6 @@ "max": 50, "description": "basophils", "omopid": 4172647, - "sctid": 42351005, "category": "hematology", "sources": { "aumc": [ @@ -630,7 +621,6 @@ "max": 25, "description": "base excess", "omopid": 4284393, - "sctid": 67487000, "category": "blood gas", "sources": { "aumc": [ @@ -693,7 +683,6 @@ "max": 50, "description": "bicarbonate", "omopid": 4194291, - "sctid": 312471006, "category": "chemistry", "sources": { "aumc": [ @@ -754,7 +743,6 @@ "max": 100, "description": "total bilirubin", "omopid": 4118986, - "sctid": 302787001, "category": "chemistry", "sources": { "aumc": [ @@ -817,7 +805,6 @@ "max": 50, "description": "bilirubin direct", "omopid": 4216632, - "sctid": 39748002, "category": "chemistry", "sources": { "aumc": [ @@ -878,7 +865,6 @@ "concepts": ["weight", "height"], "description": "patient body mass index", "omopid": 4245997, - "sctid": 60621009, "category": "demographics", "callback": "bmi", "target": "id_tbl", @@ -888,7 +874,6 @@ "unit": "%", "description": "band form neutrophils", "omopid": 4100147, - "sctid": 25340006, "category": "hematology", "sources": { "aumc": [ @@ -949,7 +934,6 @@ "max": 200, "description": "blood urea nitrogen", "omopid": 4017361, - "sctid": 105011006, "category": "chemistry", "sources": { "aumc": [ @@ -1012,7 +996,6 @@ "max": 20, "description": "calcium", "omopid": 4216722, - "sctid": 71878006, "category": "chemistry", "sources": { "aumc": [ @@ -1077,7 +1060,6 @@ "max": 2, "description": "calcium ionized", "omopid": 46272910, - "sctid": 711359007, "category": "blood gas", "sources": { "aumc": [ @@ -1137,7 +1119,6 @@ "min": 0, "description": "creatine kinase", "omopid": 4265595, - "sctid": 397798009, "category": "chemistry", "sources": { "aumc": [ @@ -1197,7 +1178,6 @@ "min": 0, "description": "creatine kinase MB", "omopid": 4017058, - "sctid": 104613001, "category": "chemistry", "sources": { "aumc": [ @@ -1258,7 +1238,6 @@ "max": 130, "description": "chloride", "omopid": 4188066, - "sctid": 46511006, "category": "chemistry", "sources": { "aumc": [ @@ -1317,7 +1296,6 @@ "class": "lgl_cncpt", "description": "corticosteroids", "omopid": 304275008, - "sctid": 304275008, "category": "medications", "sources": { "aumc": [ @@ -1344,7 +1322,6 @@ "max": 15, "description": "creatinine", "omopid": 4013964, - "sctid": 113075003, "category": "chemistry", "sources": { "aumc": [ @@ -1406,7 +1383,6 @@ "min": 0, "description": "C-reactive protein", "omopid": 4208414, - "sctid": 55235003, "category": "chemistry", "sources": { "aumc": [ @@ -1472,7 +1448,6 @@ "max": 200, "description": "diastolic blood pressure", "omopid": 4154790, - "sctid": 271650006, "category": "vitals", "sources": { "aumc": [ @@ -1531,7 +1506,6 @@ "class": "lgl_cncpt", "description": "in hospital mortality", "omopid": 4306655, - "sctid": 419620001, "category": "outcome", "sources": { "aumc": [ @@ -1604,7 +1578,6 @@ "unit": "ml/hr", "description": "dextrose (as D10)", "omopid": 4197597, - "sctid": 313419007, "category": "medications", "target": "win_tbl", "sources": { @@ -1817,7 +1790,6 @@ "max": 50, "description": "dobutamine rate", "omopid": 1337720, - "sctid": 387145002, "category": "medications", "sources": { "aumc": [ @@ -1996,7 +1968,6 @@ "max": 50, "description": "dopamine rate", "omopid": 1337860, - "sctid": 412383006, "category": "medications", "sources": { "aumc": [ @@ -2086,7 +2057,6 @@ "max": 4, "description": "GCS eye", "omopid": 4084277, - "sctid": 281395000, "category": "neurological", "sources": { "aumc": [ @@ -2154,7 +2124,6 @@ "max": 50, "description": "eosinophils", "omopid": 4216098, - "sctid": 71960002, "category": "hematology", "sources": { "aumc": [ @@ -2301,7 +2270,6 @@ "max": 1.5, "description": "epinephrine rate", "omopid": 1343916, - "sctid": 387362001, "category": "medications", "sources": { "aumc": [ @@ -2401,7 +2369,6 @@ "max": 200, "description": "erythrocyte sedimentation rate", "omopid": 4212065, - "sctid": 416838001, "category": "hematology", "sources": { "aumc": [ @@ -2448,7 +2415,6 @@ "max": 60, "description": "endtidal CO2", "omopid": 4353940, - "sctid": 250790007, "category": "vitals", "sources": { "aumc": [ @@ -2494,7 +2460,6 @@ "class": "lgl_cncpt", "description": "tracheostomy", "omopid": 4097216, - "sctid": 26412008, "category": "respiratory", "target": "win_tbl", "sources": { @@ -2560,7 +2525,6 @@ "max": 1500, "description": "fibrinogen", "omopid": 4094436, - "sctid": 250346004, "category": "hematology", "sources": { "aumc": [ @@ -2623,7 +2587,6 @@ "max": 100, "description": "fraction of inspired oxygen", "omopid": 4353936, - "sctid": 250774007, "category": "blood gas", "sources": { "aumc": [ @@ -2711,7 +2674,6 @@ "concepts": ["egcs", "mgcs", "vgcs", "tgcs", "ett_gcs"], "description": "Glasgow coma scale (non-sedated)", "omopid": 4093836, - "sctid": 248241002, "category": "neurological", "aggregate": ["min", "min", "min", "min", "any"], "callback": "gcs", @@ -2723,7 +2685,6 @@ "max": 1000, "description": "glucose", "omopid": 4144235, - "sctid": 33747003, "category": "chemistry", "sources": { "aumc": [ @@ -2794,7 +2755,6 @@ "unit": "%", "description": "Hemoglobin A1C", "omopid": 4184637, - "sctid": 43396009, "category": "hematology", "sources": { "aumc": [ @@ -2830,7 +2790,6 @@ "hbco": { "description": "carboxyhemoglobin", "omopid": 4056787, - "sctid": 19821003, "category": "blood gas", "sources": { "aumc": [ @@ -2870,7 +2829,6 @@ "max": 60, "description": "hematocrit", "omopid": 4151358, - "sctid": 28317006, "category": "hematology", "sources": { "aumc": [ @@ -2925,7 +2883,6 @@ "target": "id_tbl", "description": "patient height", "omopid": 607590, - "sctid": 1153637007, "category": "demographics", "sources": { "aumc": [ @@ -2990,7 +2947,6 @@ "max": 18, "description": "hemoglobin", "omopid": 4118981, - "sctid": 302763003, "category": "hematology", "sources": { "aumc": [ @@ -3053,7 +3009,6 @@ "max": 300, "description": "heart rate", "omopid": 4239408, - "sctid": 364075005, "category": "vitals", "sources": { "aumc": [ @@ -3111,7 +3066,6 @@ "inr_pt": { "description": "prothrombin time/international normalized ratio", "omopid": 4306239, - "sctid": 165581004, "category": "hematology", "sources": { "aumc": [ @@ -3171,7 +3125,6 @@ "min": 0, "description": "insulin", "omopid": 42537007, - "sctid": 736101003, "category": "medications", "sources": { "aumc": [ @@ -3253,7 +3206,6 @@ "max": 10, "description": "potassium", "omopid": 4207483, - "sctid": 312468003, "category": "chemistry", "sources": { "aumc": [ @@ -3314,7 +3266,6 @@ "max": 50, "description": "lactate", "omopid": 4191725, - "sctid": 394960005, "category": "blood gas", "sources": { "aumc": [ @@ -3478,7 +3429,6 @@ "max": 100, "description": "lymphocytes", "omopid": 4254663, - "sctid": 74765001, "category": "hematology", "sources": { "aumc": [ @@ -3546,7 +3496,6 @@ "max": 250, "description": "mean arterial pressure", "omopid": 4239021, - "sctid": 6797001, "category": "vitals", "sources": { "aumc": [ @@ -3616,7 +3565,6 @@ "min": 0, "description": "mean corpuscular hemoglobin", "omopid": 4182871, - "sctid": 54706004, "category": "hematology", "sources": { "aumc": [ @@ -3678,7 +3626,6 @@ "max": 50, "description": "mean corpuscular hemoglobin concentration", "omopid": 4290193, - "sctid": 37254006, "category": "hematology", "sources": { "aumc": [ @@ -3741,7 +3688,6 @@ "max": 150, "description": "mean corpuscular volume", "omopid": 4016239, - "sctid": 104133003, "category": "hematology", "sources": { "aumc": [ @@ -3802,7 +3748,6 @@ "levels": ["invasive", "noninvasive"], "description": "mechanical ventilation windows", "omopid": 4230167, - "sctid": 40617009, "category": "respiratory", "sources": { "aumc": [ @@ -3842,7 +3787,6 @@ "max": 100, "description": "methemoglobin", "omopid": 4204561, - "sctid": 54937007, "category": "blood gas", "sources": { "aumc": [ @@ -3901,7 +3845,6 @@ "concepts": ["sbp", "hr", "resp", "temp", "avpu"], "description": "modified early warning score", "omopid": 40484211, - "sctid": 445551004, "category": "outcome", "callback": "mews_score", "class": "rec_cncpt" @@ -3912,7 +3855,6 @@ "max": 5, "description": "magnesium", "omopid": 4243005, - "sctid": 38151008, "category": "chemistry", "sources": { "aumc": [ @@ -3976,7 +3918,6 @@ "max": 6, "description": "GCS motor", "omopid": 4083352, - "sctid": 281396004, "category": "neurological", "sources": { "aumc": [ @@ -4044,7 +3985,6 @@ "max": 165, "description": "sodium", "omopid": 4208938, - "sctid": 312469006, "category": "chemistry", "sources": { "aumc": [ @@ -4105,7 +4045,6 @@ "max": 100, "description": "neutrophils", "omopid": 4148615, - "sctid": 30630007, "category": "hematology", "sources": { "aumc": [ @@ -4275,7 +4214,6 @@ "max": 3, "description": "norepinephrine rate", "omopid": 1321341, - "sctid": 5.00601410001881e+16, "category": "medications", "sources": { "aumc": [ @@ -4375,7 +4313,6 @@ "max": 100, "description": "oxygen saturation", "omopid": 40483579, - "sctid": 442476006, "category": "respiratory", "sources": { "aumc": [ @@ -4452,7 +4389,6 @@ "concepts": ["po2", "fio2"], "description": "Horowitz index", "omopid": 4233883, - "sctid": 438173002, "category": "respiratory", "aggregate": ["min", "max"], "callback": "pafi", @@ -4464,7 +4400,6 @@ "max": 150, "description": "CO2 partial pressure", "omopid": 4097882, - "sctid": 250564007, "category": "blood gas", "sources": { "aumc": [ @@ -4524,7 +4459,6 @@ "max": 8, "description": "pH of blood", "omopid": 4097822, - "sctid": 27051004, "category": "blood gas", "sources": { "aumc": [ @@ -4583,7 +4517,6 @@ "unit": "mcg/kg/min", "description": "phenylephrine rate", "omopid": 1135766, - "sctid": 372771005, "category": "medications", "sources": { "eicu": [ @@ -4669,7 +4602,6 @@ "max": 40, "description": "phosphate", "omopid": 4017907, - "sctid": 104866001, "category": "chemistry", "sources": { "aumc": [ @@ -4732,7 +4664,6 @@ "max": 1200, "description": "platelet count", "omopid": 4267147, - "sctid": 61928009, "category": "hematology", "sources": { "aumc": [ @@ -4793,7 +4724,6 @@ "max": 600, "description": "O2 partial pressure", "omopid": 4094581, - "sctid": 250546000, "category": "blood gas", "sources": { "aumc": [ @@ -4853,7 +4783,6 @@ "min": 0, "description": "prothrombine time", "omopid": 4245261, - "sctid": 396451008, "category": "hematology", "sources": { "aumc": [ @@ -4905,7 +4834,6 @@ "min": 0, "description": "partial thromboplastin time", "omopid": 4175016, - "sctid": 42525009, "category": "hematology", "sources": { "aumc": [ @@ -5035,7 +4963,6 @@ "max": 20, "description": "red blood cell count", "omopid": 4030871, - "sctid": 14089001, "category": "hematology", "sources": { "aumc": [ @@ -5088,7 +5015,6 @@ "max": 100, "description": "erythrocyte distribution width", "omopid": 4281085, - "sctid": 66842004, "category": "hematology", "sources": { "aumc": [ @@ -5141,7 +5067,6 @@ "max": 120, "description": "respiratory rate", "omopid": 4313591, - "sctid": 86290005, "category": "respiratory", "sources": { "aumc": [ @@ -5209,7 +5134,6 @@ "category": "microbiology", "description": "body fluid sampling", "omopid": 4133843, - "sctid": 127801007, "sources": { "aumc": [ { @@ -5270,7 +5194,6 @@ "max": 300, "description": "systolic blood pressure", "omopid": 4152194, - "sctid": 271649006, "category": "vitals", "sources": { "aumc": [ @@ -5329,7 +5252,6 @@ "concepts": ["sofa", "susp_inf"], "description": "sepsis-3 criterion", "omopid": 132797, - "sctid": 91302008, "category": "outcome", "callback": "sep3", "keep_components": [false, true], @@ -5341,7 +5263,6 @@ "class": "fct_cncpt", "description": "patient sex", "omopid": 37116947, - "sctid": 734000001, "category": "demographics", "sources": { "aumc": [ @@ -5404,7 +5325,6 @@ "concepts": ["temp", "hr", "resp", "pco2", "wbc", "bnd"], "description": "systemic inflammatory response syndrome score", "omopid": 4140444, - "sctid": 426929000, "category": "outcome", "callback": "sirs_score", "class": "rec_cncpt" @@ -5413,7 +5333,6 @@ "concepts": ["sofa_resp", "sofa_coag", "sofa_liver", "sofa_cardio", "sofa_cns", "sofa_renal"], "description": "sequential organ failure assessment score", "omopid": 1616852, - "sctid": 1187491009, "category": "outcome", "callback": "sofa_score", "class": "rec_cncpt" @@ -5482,7 +5401,6 @@ "concepts": ["abx", "samp"], "description": "suspected infection", "omopid": 43021378, - "sctid": 473130003, "category": "outcome", "aggregate": [ [ @@ -5501,7 +5419,6 @@ "max": 60, "description": "totcal CO2", "omopid": 4193415, - "sctid": 391396001, "category": "blood gas", "sources": { "eicu": [ @@ -5549,7 +5466,6 @@ "max": 42, "description": "temperature", "omopid": 4302666, - "sctid": 386725007, "category": "vitals", "sources": { "aumc": [ @@ -5627,7 +5543,6 @@ "max": 15, "description": "GCS total", "omopid": 4093836, - "sctid": 248241002, "category": "neurological", "sources": { "eicu": [ @@ -5665,7 +5580,6 @@ "min": 0, "description": "troponin t", "omopid": 4005525, - "sctid": 121871002, "category": "chemistry", "sources": { "aumc": [ @@ -5726,7 +5640,6 @@ "min": 0, "description": "troponin I", "omopid": 4007805, - "sctid": 121870001, "category": "chemistry", "sources": { "eicu": [ @@ -5773,7 +5686,6 @@ "aggregate": "sum", "description": "urine output", "omopid": 4264378, - "sctid": 364202003, "category": "output", "sources": { "aumc": [ @@ -5845,7 +5757,6 @@ "concepts": "urine", "description": "urine output per 24h", "omopid": 4191836, - "sctid": 395060000, "category": "output", "callback": "urine24", "class": "rec_cncpt" @@ -5854,7 +5765,6 @@ "concepts": ["dopa_dur", "norepi_dur", "dobu_dur", "epi_dur"], "description": "vasopressor indicator", "omopid": 3655896, - "sctid": 870386000, "category": "medications", "callback": "vaso_ind", "class": "rec_cncpt" @@ -5940,7 +5850,6 @@ "concepts": ["vent_start", "vent_end", "mech_vent"], "description": "ventilation durations", "omopid": 4230167, - "sctid": 40617009, "category": "respiratory", "interval": "00:01:00", "callback": "vent_ind", @@ -6015,7 +5924,6 @@ "max": 5, "description": "GCS verbal", "omopid": 4084912, - "sctid": 281397008, "category": "neurological", "sources": { "aumc": [ @@ -6082,7 +5990,6 @@ "min": 0, "description": "white blood cell count", "omopid": 4298431, - "sctid": 767002, "category": "hematology", "sources": { "aumc": [ @@ -6144,7 +6051,6 @@ "target": "id_tbl", "description": "patient weight", "omopid": 4099154, - "sctid": 27113001, "category": "demographics", "sources": { "aumc": [ From d54188d612f2128b338882e6bcc6f35a491ddb13 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:00:18 +0100 Subject: [PATCH 13/37] add omopid fileds in cncpt ctor --- DESCRIPTION | 2 +- R/concept-utils.R | 9 ++++++--- man/data_concepts.Rd | 3 +++ man/data_env.Rd | 28 ++++++++++++++-------------- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 54b0b44d..6ea1446a 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -69,7 +69,7 @@ Suggests: units, pdftools, magick -RoxygenNote: 7.2.1 +RoxygenNote: 7.2.3 Additional_repositories: https://eth-mds.github.io/physionet-demo VignetteBuilder: knitr Config/testthat/edition: 3 diff --git a/R/concept-utils.R b/R/concept-utils.R index 200d8beb..9a45fccf 100644 --- a/R/concept-utils.R +++ b/R/concept-utils.R @@ -889,6 +889,7 @@ is_target <- function(x, dat) is_type(get_target(x))(dat) #' @param name The name of the concept #' @param items Zero or more `itm` objects #' @param description String-valued concept description +#' @param omopid OMOP identifier #' @param category String-valued category #' @param aggregate NULL or a string denoting a function used to aggregate per #' id and if applicable per time step @@ -938,19 +939,21 @@ is_target <- function(x, dat) is_type(get_target(x))(dat) #' #' @export #' -new_cncpt <- function(name, items, description = name, SCTID = NULL, +new_cncpt <- function(name, items, description = name, omopid = NA_integer_, category = NA_character_, aggregate = NULL, ..., target = "ts_tbl", class = "num_cncpt") { assert_that(is.string(name), null_or(class, is.character), is.string(target), - is.string(description), is.string(category)) + is.string(description), is.string(category), is_scalar(omopid), + is_intish(omopid)) if (!is_concept(items)) { items <- set_target(as_item(items), target) } res <- list(name = name, items = items, description = description, - category = category, aggregate = aggregate, target = target) + omopid = omopid, category = category, aggregate = aggregate, + target = target) init_cncpt(structure(res, class = c(class, "cncpt")), ...) } diff --git a/man/data_concepts.Rd b/man/data_concepts.Rd index dd4b22a3..5004d363 100644 --- a/man/data_concepts.Rd +++ b/man/data_concepts.Rd @@ -19,6 +19,7 @@ new_cncpt( name, items, description = name, + omopid = NA_integer_, category = NA_character_, aggregate = NULL, ..., @@ -60,6 +61,8 @@ as_concept(x) \item{description}{String-valued concept description} +\item{omopid}{OMOP identifier} + \item{category}{String-valued category} \item{aggregate}{NULL or a string denoting a function used to aggregate per diff --git a/man/data_env.Rd b/man/data_env.Rd index 89860cdd..70cec38b 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -88,21 +88,21 @@ mimic_demo$icustays #> # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) #> # Defaults: `intime` (index), `last_careunit` (val) #> # Time vars: `intime`, `outtime` -#> row_id subjec~ hadm_id icusta~ dbsour~ first_~ last_c~ first_~ last_w~ -#> -#> 1 12742 10006 142345 206504 carevue MICU MICU 52 52 -#> 2 12747 10011 105331 232110 carevue MICU MICU 15 15 -#> 3 12749 10013 165520 264446 carevue MICU MICU 15 15 -#> 4 12754 10017 199207 204881 carevue CCU CCU 7 7 -#> 5 12755 10019 177759 228977 carevue MICU MICU 15 15 +#> row_id subject_id hadm_id icustay_id dbsource first_careu~ last_careun~ +#> +#> 1 12742 10006 142345 206504 carevue MICU MICU +#> 2 12747 10011 105331 232110 carevue MICU MICU +#> 3 12749 10013 165520 264446 carevue MICU MICU +#> 4 12754 10017 199207 204881 carevue CCU CCU +#> 5 12755 10019 177759 228977 carevue MICU MICU #> ... -#> 132 42676 44083 198330 286428 metavi~ CCU CCU 7 7 -#> 133 42691 44154 174245 217724 metavi~ MICU MICU 50 50 -#> 134 42709 44212 163189 239396 metavi~ MICU MICU 50 50 -#> 135 42712 44222 192189 238186 metavi~ CCU CCU 7 7 -#> 136 42714 44228 103379 217992 metavi~ SICU SICU 57 57 -#> # ... with 126 more rows, and 3 more variables: intime , outtime , -#> # los +#> 132 42676 44083 198330 286428 metavis~ CCU CCU +#> 133 42691 44154 174245 217724 metavis~ MICU MICU +#> 134 42709 44212 163189 239396 metavis~ MICU MICU +#> 135 42712 44222 192189 238186 metavis~ CCU CCU +#> 136 42714 44228 103379 217992 metavis~ SICU SICU +#> # ... with 126 more rows, and 5 more variables: first_wardid , +#> # last_wardid , intime , outtime , los }\if{html}{\out{}} Table subsets can be loaded into memory for example using the From fc8729790b68b5a50dbfbbf594b67843fa57ba1d Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:32:11 +0100 Subject: [PATCH 14/37] support omop loading --- NAMESPACE | 2 ++ R/assertions.R | 10 +++++- R/concept-load.R | 54 ++++++++++++++++++++++++++++++++ R/concept-utils.R | 4 +-- R/utils-misc.R | 5 +++ man/load_concepts.Rd | 13 ++++++++ tests/testthat/_snaps/concept.md | 48 ++++++++++++++++++++++++++++ tests/testthat/test-concept.R | 30 ++++++++++++++++++ 8 files changed, 163 insertions(+), 3 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index c524e727..15748ab3 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -170,10 +170,12 @@ S3method(load_concepts,cncpt) S3method(load_concepts,concept) S3method(load_concepts,default) S3method(load_concepts,fct_cncpt) +S3method(load_concepts,integer) S3method(load_concepts,item) S3method(load_concepts,itm) S3method(load_concepts,lgl_cncpt) S3method(load_concepts,num_cncpt) +S3method(load_concepts,numeric) S3method(load_concepts,rec_cncpt) S3method(load_concepts,unt_cncpt) S3method(load_difftime,aumc_tbl) diff --git a/R/assertions.R b/R/assertions.R index f0654975..14e40701 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -62,7 +62,15 @@ is_intish <- function(x) { } on_failure(is_intish) <- function(call, env) { - format_assert("{as_label(call$x)} integer-values", "is_intish_assert") + format_assert("{as_label(call$x)} contains non integer values", + "is_intish_assert") +} + +no_na <- function(x) !anyNA(x) + +on_failure(no_na) <- function(call, env) { + format_assert("{as_label(call$x)} contains NA values", + "no_na_assert") } has_length <- function(x, length = NA) { diff --git a/R/concept-load.R b/R/concept-load.R index 2f2c6d7e..a0387bb8 100644 --- a/R/concept-load.R +++ b/R/concept-load.R @@ -190,6 +190,60 @@ load_concepts.character <- function(x, src = NULL, concepts = NULL, ..., } } +#' @rdname load_concepts +#' @export +load_concepts.integer <- function(x, src = NULL, concepts = NULL, ..., + dict_name = "concept-dict", + dict_dirs = NULL) { + + assert_that(no_na(x)) + + if (is.null(concepts)) { + + assert_that(not_null(src)) + + concepts <- load_dictionary(src, name = dict_name, cfg_dirs = dict_dirs) + + } else if (not_null(src)) { + + concepts <- subset_src(concepts, src) + } + + mapping <- set_names( + int_ply(concepts, `[[`, "omopid"), + chr_ply(concepts, `[[`, "name") + ) + + hits <- match(x, mapping) + + if (any(is.na(hits))) { + + warn_ricu(" + The following {qty(sum(is.na(hits)))} concept{?s} could not be matched: + {concat(x[is.na(hits)])}", + "omop_miss_id" + ) + + hits <- hits[!is.na(hits)] + } + + res <- load_concepts(concepts[hits], src = NULL, ...) + + res <- rename_cols(res, paste0("omop_", mapping[data_vars(res)]), + data_vars(res), by_ref = TRUE) + + res +} + +#' @rdname load_concepts +#' @export +load_concepts.numeric <- function(x, ...) { + + assert_that(all_fun(x, is_intish)) + + load_concepts(as.integer(x), ...) +} + #' @param aggregate Controls how data within concepts is aggregated #' @param merge_data Logical flag, specifying whether to merge concepts into #' wide format or return a list, each entry corresponding to a concept diff --git a/R/concept-utils.R b/R/concept-utils.R index 9a45fccf..114bd1ea 100644 --- a/R/concept-utils.R +++ b/R/concept-utils.R @@ -952,8 +952,8 @@ new_cncpt <- function(name, items, description = name, omopid = NA_integer_, } res <- list(name = name, items = items, description = description, - omopid = omopid, category = category, aggregate = aggregate, - target = target) + omopid = as.integer(omopid), category = category, + aggregate = aggregate, target = target) init_cncpt(structure(res, class = c(class, "cncpt")), ...) } diff --git a/R/utils-misc.R b/R/utils-misc.R index 0f14d29c..d3a1de5f 100644 --- a/R/utils-misc.R +++ b/R/utils-misc.R @@ -256,3 +256,8 @@ sys_name <- function() Sys.info()[["sysname"]] sys_env <- function(...) Sys.getenv(...) set_units <- function(x, value) units::set_units(x, value, mode = "standard") + +set_names <- function(object = nm, nm) { + names(object) <- nm + object +} diff --git a/man/load_concepts.Rd b/man/load_concepts.Rd index 396fb89d..f47d8cdd 100644 --- a/man/load_concepts.Rd +++ b/man/load_concepts.Rd @@ -3,6 +3,8 @@ \name{load_concepts} \alias{load_concepts} \alias{load_concepts.character} +\alias{load_concepts.integer} +\alias{load_concepts.numeric} \alias{load_concepts.concept} \alias{load_concepts.cncpt} \alias{load_concepts.num_cncpt} @@ -25,6 +27,17 @@ load_concepts(x, ...) dict_dirs = NULL ) +\method{load_concepts}{integer}( + x, + src = NULL, + concepts = NULL, + ..., + dict_name = "concept-dict", + dict_dirs = NULL +) + +\method{load_concepts}{numeric}(x, ...) + \method{load_concepts}{concept}( x, src = NULL, diff --git a/tests/testthat/_snaps/concept.md b/tests/testthat/_snaps/concept.md index e5850e1a..1e33e9c1 100644 --- a/tests/testthat/_snaps/concept.md +++ b/tests/testthat/_snaps/concept.md @@ -43,6 +43,54 @@ gcs_raw gcs_raw +--- + + Code + print(dat1) + Output + # A `ts_tbl`: 1,914 x 3 + # Id var: `icustay_id` + # Units: `omop_4144235` [mg/dL] + # Index var: `charttime` (1 hours) + icustay_id charttime omop_4144235 + + 1 201006 -58 hours 116 + 2 201006 -45 hours 83 + 3 201006 -21 hours 91 + 4 201006 0 hours 175 + 5 201006 11 hours 129 + ... + 1,910 298685 260 hours 159 + 1,911 298685 272 hours 153 + 1,912 298685 290 hours 182 + 1,913 298685 293 hours 122 + 1,914 298685 299 hours 121 + # ... with 1,904 more rows + +--- + + Code + print(dat2) + Output + # A `ts_tbl`: 1,920 x 4 + # Id var: `icustay_id` + # Units: `omop_4144235` [mg/dL], `omop_4017497` [g/dL] + # Index var: `charttime` (1 hours) + icustay_id charttime omop_4144235 omop_4017497 + + 1 201006 -58 hours 116 NA + 2 201006 -45 hours 83 NA + 3 201006 -21 hours 91 NA + 4 201006 0 hours 175 2.4 + 5 201006 11 hours 129 NA + ... + 1,916 298685 260 hours 159 NA + 1,917 298685 272 hours 153 2.2 + 1,918 298685 290 hours 182 NA + 1,919 298685 293 hours 122 NA + 1,920 298685 299 hours 121 2.5 + # ... with 1,910 more rows + # load external dictionary Code diff --git a/tests/testthat/test-concept.R b/tests/testthat/test-concept.R index 0943f948..874c8406 100644 --- a/tests/testthat/test-concept.R +++ b/tests/testthat/test-concept.R @@ -145,6 +145,36 @@ test_that("load concepts", { expect_snapshot(print(gcs_raw)) }) +test_that("load concepts", { + + dat1 <- load_concepts(4144235L, "mimic_demo", verbose = FALSE) + + expect_s3_class(dat1, "ts_tbl") + expect_true(is_ts_tbl(dat1)) + expect_identical(colnames(dat1), + c("icustay_id", "charttime", "omop_4144235")) + expect_equal(interval(dat1), hours(1L)) + + expect_snapshot(print(dat1)) + + dat2 <- load_concepts(c(4144235, 4017497), "mimic_demo", verbose = FALSE) + + expect_s3_class(dat2, "ts_tbl") + expect_true(is_ts_tbl(dat2)) + expect_identical( + colnames(dat2), + c("icustay_id", "charttime", "omop_4144235", "omop_4017497") + ) + expect_equal(interval(dat2), hours(1L)) + + expect_snapshot(print(dat2)) + + expect_warning( + load_concepts(c(4144235, 123), "mimic_demo", verbose = FALSE), + class = "omop_miss_id" + ) +}) + skip_if_srcs_missing("eicu_demo") test_that("load concepts multi src", { From c0fcf7a61a26488e89f6e11bd51076d7741204ab Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:41:43 +0100 Subject: [PATCH 15/37] fix tests --- R/config-utils.R | 4 ++-- tests/testthat/_snaps/config.md | 6 +++--- tests/testthat/test-setup-download.R | 1 + 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/R/config-utils.R b/R/config-utils.R index fe6d81f6..faf09d75 100644 --- a/R/config-utils.R +++ b/R/config-utils.R @@ -17,8 +17,8 @@ as_src_cfg.src_cfg <- function(x) x as_src_cfg.src_env <- function(x) { args <- list(name = src_name(x), id_cfg = as_id_cfg(x), - col_cfg = vec_unchop(lapply(x, as_col_cfg), name_spec = "{inner}"), - tbl_cfg = vec_unchop(lapply(x, as_tbl_cfg), name_spec = "{inner}") + col_cfg = list_unchop(lapply(x, as_col_cfg), name_spec = "{inner}"), + tbl_cfg = list_unchop(lapply(x, as_tbl_cfg), name_spec = "{inner}") ) do.call(new_src_cfg, diff --git a/tests/testthat/_snaps/config.md b/tests/testthat/_snaps/config.md index 187ec0e6..e63dcbe2 100644 --- a/tests/testthat/_snaps/config.md +++ b/tests/testthat/_snaps/config.md @@ -3,7 +3,7 @@ Code print(id) Output - [3]> + patient hadm icustay `subject_id` `hadm_id` `icustay_id` @@ -12,7 +12,7 @@ Code print(co) Output - [25]> + admissions callout caregivers chartevents [0, 0, 5, 0, 1] [0, 1, 6, 0, 1] [1, 0, 0, 0, 1] [0, 1, 2, 1, 1] cptevents d_cpt d_icd_diagnoses d_icd_procedures @@ -33,7 +33,7 @@ Code print(tb) Output - [25]> + admissions callout caregivers chartevents [?? x 19; 1] [?? x 24; 1] [?? x 4; 1] [?? x 15; 2] cptevents d_cpt d_icd_diagnoses d_icd_procedures diff --git a/tests/testthat/test-setup-download.R b/tests/testthat/test-setup-download.R index 18c99fce..cfd4789c 100644 --- a/tests/testthat/test-setup-download.R +++ b/tests/testthat/test-setup-download.R @@ -259,6 +259,7 @@ test_that("src download", { expect_invisible( res <- mockthat::with_mock( download_file = dl_file, + get_cred = "foo", download_src(src, dir, verbose = FALSE) ) ) From 517d6ede2a6a5e1d4565af48c39ea22397b4985c Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 08:52:31 +0100 Subject: [PATCH 16/37] cast to dt before calling foverlaps; fixes #26 --- R/tbl-class.R | 5 +++-- R/utils-ts.R | 5 ++++- man/id_tbl.Rd | 5 +++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/R/tbl-class.R b/R/tbl-class.R index 218f1a7c..a19707c5 100644 --- a/R/tbl-class.R +++ b/R/tbl-class.R @@ -73,8 +73,9 @@ #' 1. for `ts_tbl` that the string-valued `index_var` column is available and #' does not intersect with `id_vars` and that the index column obeys the #' specified interval. -#' 1. for `win_tbl` that the string-valued `dur_var` corresponds to a `difftime` -#' vector and is not among the columns marked as index or ID variables +#' 1. for `win_tbl` that the string-valued `dur_var` corresponds to a +#' `difftime` vector and is not among the columns marked as index or ID +#' variables #' #' Finally, inheritance can be checked by calling `is_id_tbl()` and #' `is_ts_tbl()`. Note that due to `ts_tbl` inheriting from `id_tbl`, diff --git a/R/utils-ts.R b/R/utils-ts.R index e7538c6a..9bfcf103 100644 --- a/R/utils-ts.R +++ b/R/utils-ts.R @@ -573,8 +573,11 @@ merge_ranges <- function(x, lwr_var = index_var(x), upr_var = data_vars(x), x <- x[, c(upr_var) := get(upr_var) + max_gap] } + ptype <- as_ptype(x) + x <- sort(x, by = c(id_vars(x), lwr_var, upr_var), by_ref = TRUE) - x <- reclass_tbl(data.table::foverlaps(x, x, mult = "first"), as_ptype(x)) + x <- as.data.table(x, by_ref = TRUE) + x <- reclass_tbl(data.table::foverlaps(x, x, mult = "first"), ptype) expr <- quote(list(max(get(tmp_var)))) names(expr) <- c("", upr_var) diff --git a/man/id_tbl.Rd b/man/id_tbl.Rd index 35e680d6..b9869a91 100644 --- a/man/id_tbl.Rd +++ b/man/id_tbl.Rd @@ -158,8 +158,9 @@ vector holding onto the \code{id_vars} specification are available \item for \code{ts_tbl} that the string-valued \code{index_var} column is available and does not intersect with \code{id_vars} and that the index column obeys the specified interval. -\item for \code{win_tbl} that the string-valued \code{dur_var} corresponds to a \code{difftime} -vector and is not among the columns marked as index or ID variables +\item for \code{win_tbl} that the string-valued \code{dur_var} corresponds to a +\code{difftime} vector and is not among the columns marked as index or ID +variables } Finally, inheritance can be checked by calling \code{is_id_tbl()} and From 484286d3f7ff14d8744541aa141168780b264c29 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:44:48 +0100 Subject: [PATCH 17/37] change sign in id downgrade; fixes #24 --- R/data-utils.R | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/R/data-utils.R b/R/data-utils.R index 3e1020d5..5dcac99a 100644 --- a/R/data-utils.R +++ b/R/data-utils.R @@ -743,14 +743,16 @@ change_id_helper <- function(x, targ, src, cols, dir = c("down", "up"), ...) { if (identical(dir, "down")) { map <- id_map(src, targ, idx, sft, NULL) + fun <- `+` } else { map <- id_map(src, idx, targ, sft, NULL) + fun <- `-` } res <- merge(x, map, by = idx, ...) if (length(cols)) { - res <- res[, c(cols) := lapply(.SD, `-`, get(sft)), .SDcols = cols] + res <- res[, c(cols) := lapply(.SD, fun, get(sft)), .SDcols = cols] } res <- set_id_vars(res, targ) From ad37a3873470d1b54f6384e9b0841de310730a76 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 10:20:11 +0100 Subject: [PATCH 18/37] fix check failure --- .github/workflows/check.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/check.yaml b/.github/workflows/check.yaml index 78622621..a3ea0c3d 100644 --- a/.github/workflows/check.yaml +++ b/.github/workflows/check.yaml @@ -55,5 +55,6 @@ jobs: env: _R_CHECK_CRAN_INCOMING_: false _R_CHECK_FORCE_SUGGESTS_: false + _R_CHECK_DOC_SIZES_: false with: upload-snapshots: true From bbc1d9120472a1587abc32c9922543e1b6d9b68c Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:54:29 +0100 Subject: [PATCH 19/37] use rd macros --- R/callback-cncpt.R | 37 +++++++++++-------------------------- R/callback-sofa.R | 30 ++++++++---------------------- man/callback_cncpt.Rd | 37 +++++++++++-------------------------- man/callback_sofa.Rd | 30 ++++++++---------------------- man/macros/macros.Rd | 11 +++++++++++ 5 files changed, 49 insertions(+), 96 deletions(-) create mode 100644 man/macros/macros.Rd diff --git a/R/callback-cncpt.R b/R/callback-cncpt.R index fd2bdc5d..4d416ae4 100644 --- a/R/callback-cncpt.R +++ b/R/callback-cncpt.R @@ -113,32 +113,21 @@ rename_data_var <- function(new_name, old_name = NULL) { #' values might not be ideal for some datasets and/or analysis tasks. #' #' ## `pafi` -#' In order to calculate the PaO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}}/FiO\ifelse{ -#' latex}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} (or -#' Horowitz index), for a given time point, both a PaO\ifelse{ -#' latex}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and a -#' FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} measurement is required. As the two are often -#' not measured at the same time, some form of imputation or matching -#' procedure is required. Several options are available: +#' In order to calculate the PaO\subs{2}/FiO\subs{2} (or Horowitz index), for +#' a given time point, both a PaO\subs{2} and an FiO\subs{2} measurement is +#' required. As the two are often not measured at the same time, some form of +#' imputation or matching procedure is required. Several options are available: #' #' * `match_vals` allows for a time difference of maximally `match_win` #' between two measurements for calculating their ratio -#' * `extreme_vals` uses the worst PaO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and a -#' FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} values within the time window spanned by -#' `match_win` +#' * `extreme_vals` uses the worst PaO\subs{2} and FiO\subs{2} values within +#' the time window spanned by `match_win` #' * `fill_gaps` represents a variation of `extreme_vals`, where ratios are #' evaluated at every time-point as specified by `interval`as opposed to -#' only the time points where either a PaO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} or a -#' FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} measurement is available +#' only the time points where either a PaO\subs{2} or an FiO\subs{2} +#' measurement is available #' -#' Finally, `fix_na_fio2` imputes all remaining missing FiO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} with 21, +#' Finally, `fix_na_fio2` imputes all remaining missing FiO\subs{2} with 21, #' the percentage (by volume) of oxygen in (tropospheric) air. #' #' ## `vent_ind` @@ -189,13 +178,9 @@ rename_data_var <- function(new_name, old_name = NULL) { #' #' @param ... Data input used for concept calculation #' @param match_win Time-span during which matching of values is allowed -#' @param mode Method for matching PaO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and -#' FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} values +#' @param mode Method for matching PaO\subs{2} and FiO\subs{2} values #' @param fix_na_fio2 Logical flag indicating whether to impute missing -#' FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} values with 21 +#' FiO\subs{2} values with 21 #' @param interval Expected time series step size (determined from data if #' `NULL`) #' diff --git a/R/callback-sofa.R b/R/callback-sofa.R index 57db1883..07a1fa27 100644 --- a/R/callback-sofa.R +++ b/R/callback-sofa.R @@ -38,40 +38,26 @@ #' | **SOFA score** | 1 | 2 | 3 | 4 | #' | --------------------------- | --------- | --------- | -------- | ------ | #' | **Respiration** | | | | | -#' | PaO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}}/FiO\ifelse{latex -#' }{\out{\textsubscript{2}}}{\ifelse{html -#' }{\out{2}}{2}} \[mmHg\] | < 400 | < 300 | < 200 | < 100 | +#' | PaO\subs{2}/FiO\subs{2} \[mmHg\] | < 400 | < 300 | < 200 | < 100 | #' | and mechanical ventilation | | | yes | yes | #' | **Coagulation** | | | | | -#' | Platelets \[\ifelse{latex}{\out{$\times$}}{\ifelse{html -#' }{\out{×}}{x}}10\ifelse{latex}{\out{\textsuperscript{3}}}{\ifelse{ -#' html}{\out{3}}{3}}/mm\ifelse{latex -#' }{\out{\textsuperscript{3}}}{\ifelse{html -#' }{\out{3}}{3}}\] | < 150 | < 100 | < 50 | < 20 | +#' | Platelets \[\tims{}10\sups{3}/mm\sups{3}\] | < 150 | < 100 | < 50 | < 20 | #' | **Liver** | | | | | #' | Bilirubin \[mg/dl\] | 1.2-1.9 | 2.0-5.9 | 6.0-11.9 | > 12.0 | -#' | **Cardiovascular**\ifelse{latex}{\out{\textsuperscript{a}}}{\ifelse{html -#' }{\out{a}}{a}} | | | | | +#' | **Cardiovascular**\sups{a} | | | | | #' | MAP | < 70 mmHg | | | | -#' | or dopamine | | \ifelse{latex}{\out{$\le$}}{\ifelse{html -#' }{\out{≤}}{<=}} 5 | > 5 | > 15 | +#' | or dopamine | | \leq{}5 | > 5 | > 15 | #' | or dobutamine | | any dose | | | -#' | or epinephrine | | | \ifelse{latex}{\out{$\le$}}{\ifelse{html -#' }{\out{≤}}{<=}} 0.1 | > 0.1 | -#' | or norepinephrine | | | \ifelse{latex}{\out{$\le$}}{\ifelse{html -#' }{\out{≤}}{<=}} 0.1 | > 0.1 | +#' | or epinephrine | | | \leq{}0.1 | > 0.1 | +#' | or norepinephrine | | | \leq{}0.1 | > 0.1 | #' | **Central nervous system** | | | | | #' | Glasgow Coma Score | 13-14 | 10-12 | 6-9 | < 6 | #' | **Renal** | | | | | #' | Creatinine \[mg/dl\] | 1.2-1.9 | 2.0-3.4 | 3.5-4.9 | > 5.0 | #' | or urine output \[ml/day\] | | | < 500 | < 200 | #' -#' \ifelse{latex}{\out{\textsuperscript{a}}}{\ifelse{html -#' }{\out{a}}{a}}Adrenergic agents administered for at least 1h -#' (doses given are in \[\ifelse{latex}{\out{$\mu$}}{\ifelse{html -#' }{\out{μ}}{u}}g/kg \ifelse{latex}{\out{$\cdot$}}{\ifelse{html -#' }{\out{·}}{.}} min\] +#' Adrenergic\sups{a} agents administered for at least 1h +#' (doses given are in \[\smu{}g/kg \dotm{} min\] #' #' At default, for each patient, a score is calculated for every time step, #' from the first available measurement to the last. In instead of a regularly diff --git a/man/callback_cncpt.Rd b/man/callback_cncpt.Rd index 95d350e5..c7602659 100644 --- a/man/callback_cncpt.Rd +++ b/man/callback_cncpt.Rd @@ -67,14 +67,10 @@ norepi_equiv(..., interval = NULL) \item{match_win}{Time-span during which matching of values is allowed} -\item{mode}{Method for matching PaO\ifelse{latex -}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and -FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -}{\out{2}}{2}} values} +\item{mode}{Method for matching PaO\subs{2} and FiO\subs{2} values} \item{fix_na_fio2}{Logical flag indicating whether to impute missing -FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -}{\out{2}}{2}} values with 21} +FiO\subs{2} values with 21} \item{interval}{Expected time series step size (determined from data if \code{NULL})} @@ -118,33 +114,22 @@ investigating stability with respect to such choices. Furthermore, default values might not be ideal for some datasets and/or analysis tasks. \subsection{\code{pafi}}{ -In order to calculate the PaO\ifelse{latex -}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}}/FiO\ifelse{ -latex}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} (or -Horowitz index), for a given time point, both a PaO\ifelse{ -latex}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and a -FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html -}{\out{2}}{2}} measurement is required. As the two are often -not measured at the same time, some form of imputation or matching -procedure is required. Several options are available: +In order to calculate the PaO\subs{2}/FiO\subs{2} (or Horowitz index), for +a given time point, both a PaO\subs{2} and an FiO\subs{2} measurement is +required. As the two are often not measured at the same time, some form of +imputation or matching procedure is required. Several options are available: \itemize{ \item \code{match_vals} allows for a time difference of maximally \code{match_win} between two measurements for calculating their ratio -\item \code{extreme_vals} uses the worst PaO\ifelse{latex - }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} and a -FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html - }{\out{2}}{2}} values within the time window spanned by -\code{match_win} +\item \code{extreme_vals} uses the worst PaO\subs{2} and FiO\subs{2} values within +the time window spanned by \code{match_win} \item \code{fill_gaps} represents a variation of \code{extreme_vals}, where ratios are evaluated at every time-point as specified by \code{interval}as opposed to -only the time points where either a PaO\ifelse{latex - }{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} or a -FiO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html - }{\out{2}}{2}} measurement is available +only the time points where either a PaO\subs{2} or an FiO\subs{2} +measurement is available } -Finally, \code{fix_na_fio2} imputes all remaining missing FiO\ifelse{latex -}{\out{\textsubscript{2}}}{\ifelse{html}{\out{2}}{2}} with 21, +Finally, \code{fix_na_fio2} imputes all remaining missing FiO\subs{2} with 21, the percentage (by volume) of oxygen in (tropospheric) air. } diff --git a/man/callback_sofa.Rd b/man/callback_sofa.Rd index 4dc8d511..b1dd158b 100644 --- a/man/callback_sofa.Rd +++ b/man/callback_sofa.Rd @@ -77,29 +77,18 @@ Building on separate concepts, measurements for each component are converted to a component score using the definition by Vincent et. al.:\tabular{lllll}{ \strong{SOFA score} \tab 1 \tab 2 \tab 3 \tab 4 \cr \strong{Respiration} \tab \tab \tab \tab \cr - PaO\ifelse{latex}{\out{\textsubscript{2}}}{\ifelse{html - }{\out{2}}{2}}/FiO\ifelse{latex - }{\out{\textsubscript{2}}}{\ifelse{html - }{\out{2}}{2}} [mmHg] \tab < 400 \tab < 300 \tab < 200 \tab < 100 \cr + PaO\subs{2}/FiO\subs{2} [mmHg] \tab < 400 \tab < 300 \tab < 200 \tab < 100 \cr and mechanical ventilation \tab \tab \tab yes \tab yes \cr \strong{Coagulation} \tab \tab \tab \tab \cr - Platelets [\ifelse{latex}{\out{$\times$}}{\ifelse{html - }{\out{×}}{x}}10\ifelse{latex}{\out{\textsuperscript{3}}}{\ifelse{ - html}{\out{3}}{3}}/mm\ifelse{latex - }{\out{\textsuperscript{3}}}{\ifelse{html - }{\out{3}}{3}}] \tab < 150 \tab < 100 \tab < 50 \tab < 20 \cr + Platelets [\tims{}10\sups{3}/mm\sups{3}] \tab < 150 \tab < 100 \tab < 50 \tab < 20 \cr \strong{Liver} \tab \tab \tab \tab \cr Bilirubin [mg/dl] \tab 1.2-1.9 \tab 2.0-5.9 \tab 6.0-11.9 \tab > 12.0 \cr - \strong{Cardiovascular}\ifelse{latex}{\out{\textsuperscript{a}}}{\ifelse{html - }{\out{a}}{a}} \tab \tab \tab \tab \cr + \strong{Cardiovascular}\sups{a} \tab \tab \tab \tab \cr MAP \tab < 70 mmHg \tab \tab \tab \cr - or dopamine \tab \tab \ifelse{latex}{\out{$\le$}}{\ifelse{html - }{\out{≤}}{<=}} 5 \tab > 5 \tab > 15 \cr + or dopamine \tab \tab \leq{}5 \tab > 5 \tab > 15 \cr or dobutamine \tab \tab any dose \tab \tab \cr - or epinephrine \tab \tab \tab \ifelse{latex}{\out{$\le$}}{\ifelse{html - }{\out{≤}}{<=}} 0.1 \tab > 0.1 \cr - or norepinephrine \tab \tab \tab \ifelse{latex}{\out{$\le$}}{\ifelse{html - }{\out{≤}}{<=}} 0.1 \tab > 0.1 \cr + or epinephrine \tab \tab \tab \leq{}0.1 \tab > 0.1 \cr + or norepinephrine \tab \tab \tab \leq{}0.1 \tab > 0.1 \cr \strong{Central nervous system} \tab \tab \tab \tab \cr Glasgow Coma Score \tab 13-14 \tab 10-12 \tab 6-9 \tab < 6 \cr \strong{Renal} \tab \tab \tab \tab \cr @@ -108,11 +97,8 @@ converted to a component score using the definition by Vincent et. al.:\tabular{ } -\ifelse{latex}{\out{\textsuperscript{a}}}{\ifelse{html -}{\out{a}}{a}}Adrenergic agents administered for at least 1h -(doses given are in [\ifelse{latex}{\out{$\mu$}}{\ifelse{html -}{\out{μ}}{u}}g/kg \ifelse{latex}{\out{$\cdot$}}{\ifelse{html -}{\out{·}}{.}} min] +Adrenergic\sups{a} agents administered for at least 1h +(doses given are in [\smu{}g/kg \dotm{} min] At default, for each patient, a score is calculated for every time step, from the first available measurement to the last. In instead of a regularly diff --git a/man/macros/macros.Rd b/man/macros/macros.Rd new file mode 100644 index 00000000..977de5f9 --- /dev/null +++ b/man/macros/macros.Rd @@ -0,0 +1,11 @@ +\newcommand{\subs}{\ifelse{latex}{\out{\textsubscript{#1}}}{\ifelse{html}{\out{#1}}{#1}}} + +\newcommand{\sups}{\ifelse{latex}{\out{\textsuperscript{#1}}}{\ifelse{html}{\out{#1}}{#1}}} + +\newcommand{\tims}{\ifelse{latex}{\out{$\times$}}{\ifelse{html}{\out{×}}{x}}} + +\newcommand{\leq}{\ifelse{latex}{\out{$\le$}}{\ifelse{html}{\out{≤}}{<=}}} + +\newcommand{\smu}{\ifelse{latex}{\out{$\mu$}}{\ifelse{html}{\out{μ}}{u}}} + +\newcommand{\dotm}{\ifelse{latex}{\out{$\cdot$}}{\ifelse{html}{\out{·}}{.}}} From 3f4431f8667e0730fa1345a342e648a993d69906 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 14:56:05 +0100 Subject: [PATCH 20/37] rename vignettes --- vignettes/jss.Rmd | 1197 ---------------------- vignettes/jss.bst | 1653 ------------------------------- vignettes/jss.cls | 488 --------- vignettes/jsslogo.jpg | Bin 22731 -> 0 bytes vignettes/ricu.Rmd | 1216 +++++++++++++++++++++-- vignettes/{jss.bib => ricu.bib} | 0 vignettes/start.Rmd | 151 +++ 7 files changed, 1282 insertions(+), 3423 deletions(-) delete mode 100644 vignettes/jss.Rmd delete mode 100644 vignettes/jss.bst delete mode 100644 vignettes/jss.cls delete mode 100644 vignettes/jsslogo.jpg rename vignettes/{jss.bib => ricu.bib} (100%) create mode 100644 vignettes/start.Rmd diff --git a/vignettes/jss.Rmd b/vignettes/jss.Rmd deleted file mode 100644 index 5caea3eb..00000000 --- a/vignettes/jss.Rmd +++ /dev/null @@ -1,1197 +0,0 @@ ---- -title: - plain: "ricu: R's Interface to Intensive Care Data" - formatted: "\\pkg{ricu}: \\proglang{R}'s Interface to Intensive Care Data" - short: "\\pkg{ricu}: \\proglang{R} meets ICU data" -author: - - name: "Nicolas Bennett\\footnotemark[1]" - affiliation: ETH Zürich - address: | - | Seminar for Statistics - | Rämistrasse 101 - | CH-8092 Zurich - email: \email{nicolas.bennett@stat.math.ethz.ch} - - name: "Drago Plečko\\footnotemark[1]\\footnotetext[1]{These authors contributed equally.}" - affiliation: ETH Zürich - address: | - | Seminar for Statistics - | Rämistrasse 101 - | CH-8092 Zürich - email: \email{drago.plecko@stat.math.ethz.ch} - - name: Ida-Fong Ukor - affiliation: "Monash Health \\AND" - affiliation2: Monash Health - address: | - | Department of Anaesthesiology and Perioperative Medicine - | 246 Clayton Road - | Clayton VIC 3168 - email: \email{ida-fong.ukor@monashhealth.org} - - name: Nicolai Meinshausen - affiliation: ETH Zürich - address: | - | Seminar for Statistics - | Rämistrasse 101 - | CH-8092 Zürich - email: \email{meinshausen@stat.math.ethz.ch} - - name: Peter Bühlmann - affiliation: ETH Zürich - address: | - | Seminar for Statistics - | Rämistrasse 101 - | CH-8092 Zürich - email: \email{peter.buehlmann@stat.math.ethz.ch} -abstract: > - Providing computational infrastructure for handling diverse intensive care unit (ICU) datasets, the \proglang{R} package \pkg{ricu} enables writing dataset-agnostic analysis code, thereby facilitating multi-center training and validation of machine learning models. The package is designed with an emphasis on extensibility both to new datasets as well as clinical data concepts, and currently supports the loading of around 100 patient variables corresponding to a total of 395,941 ICU admissions from five data sources collected in Europe and the United States. By allowing for the addition of user-specified medical concepts and data sources, the aim of \pkg{ricu} is to foster robust, data-based intensive care research, allowing the user to externally validate their method or conclusion with relative ease, and in turn facilitating reproducible and therefore transparent work in this field. -keywords: - formatted: [electronic health records, computational physiology, critical care medicine] - plain: [electronic health records, computational physiology, critical care medicine] -preamble: > - \usepackage{amsmath} - \usepackage{booktabs} - \usepackage{makecell} - \usepackage{threeparttablex} - \usepackage{pdflscape} -vignette: > - %\VignetteIndexEntry{Accessing ICU data with R (Bennett & Plečko, JSS 2021)} - %\VignetteEngine{knitr::rmarkdown} - %\VignetteEncoding{UTF-8} -output: > - if (packageVersion("rticles") < 0.5 || rmarkdown::pandoc_version() >= 2) - rticles::jss_article else rmarkdown::html_vignette -documentclass: jss -classoption: - - notitle - - nojss - - noheadings -bibliography: jss.bib -pkgdown: - as_is: true - extension: pdf ---- - -```{r setup, include = FALSE} -source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) - -options(width = 76) - -library(ricu) -library(data.table) -library(forestmodel) -library(survival) -library(ggplot2) - -options(kableExtra.latex.load_packages = FALSE) -library(kableExtra) - -srcs <- c("mimic", "eicu", "aumc", "hirid", "miiv") -``` - -```{r, assign-src, echo = FALSE} -src <- "mimic_demo" -``` - -```{r, assign-demo, echo = FALSE} -demo <- c(src, "eicu_demo") -``` - -```{tikz, tikz-setup, eval = FALSE, echo = FALSE} -\usetikzlibrary{ - positioning, shadows, arrows, shapes, shapes.arrows, shapes.geometric, - arrows.meta, trees, shapes.misc -} -\tikzset{ - every node/.style = { - draw = none, align = center, fill = none, text centered, - anchor = center, font = \it - }, - every label/.style={circle, draw, fill = yellow}, - f1/.style = { - draw = , fill = gray!15, thick, inner sep = 3pt, minimum width = 10em, - minimum height = 4em, align = center, text centered}, - f2/.style = { - draw = none, fill = red!15, thick, inner sep = 3pt, minimum width = 5em, - align = center, text centered - } -} -``` - -\maketitle - -\renewcommand*{\thefootnote}{\fnsymbol{footnote}} -\footnotetext{$^{*}$These authors contributed equally.} -\renewcommand*{\thefootnote}{\arabic{footnote}} - -```{r, demo-miss, echo = FALSE, eval = !srcs_avail(demo), results = "asis"} -demo_missing_msg(demo, "jss.pdf") -knitr::opts_chunk$set(eval = FALSE) -``` - -# Introduction - -Collection of electronic health records has seen a significant rise in recent years \citep{evans2016}, opening up opportunities and providing the grounds for a large body of data-driven research oriented towards helping clinicians in decision-making and therefore improving patient care and health outcomes \citep{jiang2017}. While growing amounts of collected patient data might contribute to an increasingly hard task for intensivists to focus on relevant subsets thereof \citep{pickering2013}, this poses an opportunity for the application of machine learning (ML) methods. - -One example of a problem that has received much attention from the ML community is early prediction of sepsis in the intensive care unit \citep[ICU;][]{desautels2016, nemati2018, futoma2017, kam2017}. Interestingly, there is evidence that a large proportion of the publications are based on the same dataset \citep{fleuren2019}, the Medical Information Mart for Intensive Care III \citep[MIMIC-III;][]{johnson2016}, which shows a systematic lack of external validation. This issue has recently again been highlighted by a study demonstrating poor performance in external validation of a widely adopted proprietary sepsis prediction model \citep{wong2021}. - -Contributing to this problem might well be the lack of computational infrastructure handling multiple datasets. The MIMIC-III dataset consists of 26 different tables containing about 20GB of data. While much work and care has gone into data preprocessing in order to provide a self-contained ready-to-use data resource with MIMIC-III, seemingly simple tasks such as computing a Sepsis-3 label \citep{singer2016} remain non-trivial efforts^[There is considerable heterogeneity in number of patients satisfying the Sepsis-3 criterion \citep{singer2016} among studies investigating MIMIC-III. Reported Sepsis-3 prevalence ranges from 11.3% \citep{desautels2016}, over 23.9% \citep{nemati2018} and 25.4% \citep{wang2018}, up to 49.1% \citep{johnson2018}. While some of this variation may be explained by differing patient inclusion criteria, diversity in label implementation must also contribute significantly.]. This is only exacerbated when aiming to co-integrate multiple different datasets of this form, spanning hospitals and even countries, in order to capture effects of differing practice and demographics. - -The aim of \pkg{ricu} is to provide computational infrastructure allowing users to investigate complex research questions in the context of critical care medicine as easily as possible by providing a unified interface to a heterogeneous set of data sources. The package enables users to write dataset-agnostic code which can simplify implementation and shorten the time necessary for prototyping code querying different datasets. In its current form, the package handles five large-scale, publicly available intensive care databases out of the box: MIMIC-III from the Beth Israel Deaconess Medical Center in Boston, Massachusetts \citep[BIDMC;][]{johnson2016}, the eICU Collaborative Research Database \citep{pollard2018}, containing data collected from 208 hospitals across the United States, the High Time Resolution ICU Dataset (HiRID) from the Department of Intensive Care Medicine of the Bern University Hospital, Switzerland \citep{faltys2021}, the Amsterdam University Medical Center Database (AmsterdamUMCdb) from the Amsterdam University Medical Center \citep{thoral2021} and MIMIC-IV, again using data from BIDMC \citep{johnson2021}. Furthermore, \pkg{ricu} was designed with extensibility in mind such that adding new public and/or private user-provided datasets is possible. Being implemented in \proglang{R}, a programming language popular among statisticians and data analysts, it is our hope to contribute to accessible and reproducible research by using a familiar environment and requiring only few system dependencies, thereby simplifying setup considerably. - -To our knowledge, infrastructure that provides a common interface to multiple such datasets is a novel contribution. While there have been efforts \citep{adibuzzaman2016, wang2020} attempting to abstract away some specifics of a dataset, these have so far exclusively focused on MIMIC-III, the most popular of public ICU datsets, and have not been designed with dataset interoperability in mind. - -Given the somewhat narrow focus of the targeted datasets, it may come as a surprise as to how heterogeneous the resulting datasets are. In MIMIC-III and HiRID, for example, time-stamps are reported as absolute times (albeit randomly shifted due to data privacy concerns), whereas eICU and AmsterdamUMCdb use relative times (with origins being admission times). Another example involves different types of patient identifiers and their use among datasets. Common to all is the notion of an ICU admission identifier (ID), but apart from that, the amount of available information varies: While ICU (and hospital) readmissions for a given patient can be identified in some, this is not possible in other datasets. Furthermore, use of identifier systems might not be consistent over tables. In MIMIC-III, for example, some tables refer to ICU stay IDs while others use hospital stay IDs, which slightly complicates data retrieval for a fixed ID system. Additionally, table layouts vary (*long* versus *wide* data arrangement) and data organization in general is far from consistent over datasets. - -# Quick start guide - -The following list gives a quick outline of the steps required for setting up and starting to use \pkg{ricu}, alongside some section references on where to find further details. A more comprehensive version of this overview is available as a [separate vignette](https://CRAN.R-project.org/package=ricu/vignettes/ricu.html). - -1. Package installation: - - * Installation from [CRAN](https://CRAN.R-project.org) as `install.packages("ricu")` provides the most recently released version of \pkg{ricu}. - - * Alternatively, the latest development version is available from [GitHub](https://github.com/eth-mds/ricu) by running `remotes::install_github("eth-mds/ricu")`. - -1. Requesting access to datasets and data source setup: - - * Demo datasets can be set up by installing the data packages `mimic.demo` and/or `eicu.demo` from [GitHub]("https://eth-mds.github.io/physionet-demo") using `install.packages()` as shown in Section \ref{ready-to-use-datasets}. - - * The complete MIMIC-III, eICU, HiRID and MIMIC-IV datasets can be accessed by [registering](https://physionet.org/register) and setting up a [credentialed account](https://physionet.org/settings/credentialing) at [PhysioNet](https://physionet.org). - - * Access to AmsterdamUMCdb can be requested via the [Amsterdam Medical Data Science Website](https://amsterdammedicaldatascience.nl/#amsterdamumcdb). - - * The obtained credentials can be configured for PhysioNet datasets by setting environment variables `RICU_PHYSIONET_USER` and `RICU_PHYSIONET_PASS`, while the download token for AmsterdamUMCdb can be set as `RICU_AUMC_TOKEN`. - - * Datasets are downloaded and set up either automatically upon the first access attempt or manually by running `setup_data_src()`; the environment variable `RICU_DATA_PATH` can be set to control data location. - - * Dataset availability can be queried by calling `src_data_avail()`. - - A more detailed description of the supported datasets is given in Section \ref{ready-to-use-datasets}, summarized in Table \ref{tab:datasets}, while Section \ref{data-sources} provides implementation details, elaborating on how datasets are represented in code. - -1. Loading of data corresponding to clinical concepts using `load_concepts()`: - - * Currently, over 100 data concepts are available for the 4 supported datasets (see `concept_availability()`/`explain_dictionary()` for names, availability etc.). - - * For example, glucose and age data can be loaded by passing `c("age", "glu")` to `load_concepts()`. - - Section \ref{data-concepts} goes into more detail on how data concepts are represented within \pkg{ricu} and an overview of the preconfigured concepts is available from Section \ref{ready-to-use-concepts}. - -1. Extending the concept dictionary: - - * Data concepts can be specified in code using the constructors `concept()`/`item()` or `new_concept()`/`new_item()`. - - * For session persistence, data concepts can also be specified as JSON formatted objects. - - * JSON-based concept dictionaries can either extend or replace others and they can be pointed to by setting the environment variable `RICU_CONFIG_PATH`. - - The JSON format used to encode data concepts is discussed in more detail in Section \ref{concept-specification}. - -1. Adding new datasets: - - * A JSON-based dataset configuration file is required, from which the configuration objects described in Section \ref{data-source-configuration} are created. - - * In order for concepts to be available from the new dataset, the dictionary requires extension by adding new data items. - - Further information about adding a new dataset is available from Section \ref{adding-external-datasets}. Some code used when AmsterdamUMCdb was not yet fully integrated with \pkg{ricu} is available from [GitHub](https://github.com/eth-mds/aumc) and is used for demonstration purposes to set up AmsterdamUMCdb as an external dataset `aumc_ext`. - -Finally, Section \ref{examples} shows briefly how \pkg{ricu} could be used in practice to address clinical questions by presenting two small examples. - -# Ready-to-use datasets - -Several large-scale ICU datasets collected from multiple hospitals in the US and Europe can be set up for access using \pkg{ricu} with minimal user effort. Provisions in terms of required configuration information alongside functions for download and setup are part of \pkg{ricu}, opening up easy access to these datasets. Data itself, however, is not part of \pkg{ricu} and while the supported datasets are publicly available, access has to be granted by the dataset creators individually. Four datasets, MIMIC-III, MIMIC-IV, eICU and HiRID are hosted on PhysioNet \citep{goldberger2000}, access to which requires an [account](https://physionet.org/register/), while the fifth, AmsterdamUMCdb is currently distributed via a separate platform, requiring a [download link](https://amsterdammedicaldatascience.nl/#amsterdamumcdb). - -For both MIMIC-III and eICU, small subsets of data are available as demo datasets that do not require credentialed access to PhysioNet. As the terms for distribution of these demo datasets are less restrictive, they can be made available as data packages \pkg{mimic.demo} and \pkg{eicu.demo}. Due to size constraints, however they are not available via CRAN, but can be installed from GitHub as - -```{r demo-data, eval = FALSE} -install.packages( - c("mimic.demo", "eicu.demo"), - repos = "https://eth-mds.github.io/physionet-demo" -) -``` - -Provisions for datasets configured to be attached during package loading are made irrespective of whether data is actually available. Upon access of an incomplete dataset, the user is asked for permission to download in interactive sessions and an error is thrown otherwise. Credentials can either be provided as environment variables (`RICU_PHYSIONET_USER` and `RICU_PHYSIONET_PASS` for access to PhysioNet data, as well as `RICU_AUMC_TOKEN` for AmsterdamUMCdb) and if the corresponding variables are unset, user input is again required in interactive sessions. For non-interactive sessions, functionality is exported such that data can be downloaded and set up ahead of first access (see `?setup_src_data`). - -Contingent on being granted access by the data owners, download requires a stable Internet connection, as well as 50 to 100 GB of temporary disk storage for unpacking and preparing the data for efficient access. In terms of permanent storage, 5 to 10 GB per dataset are required (see Table \ref{tab:datasets}), while memory requirements are kept reasonably low by iterating over row-chunks for setup operations. Laptop class hardware (8-16 GB of memory) should suffice for setup and many analysis tasks which focus only on subsets of rows (and columns). Initial data source setup (depending on available download speeds and CPU/disk type) may take upwards of an hour per dataset. - -The following paragraphs serve to give quick introductions to the included datasets, outlining some strengths and weaknesses of each of the datasets. Especially the PhysioNet datasets [MIMIC-III and MIMIC-IV](https://mimic.mit.edu/docs/), as well as [eICU](https://eicu-crd.mit.edu/about/eicu/) offer good documentation on the respective websites. Datasets are listed in order of being added to \pkg{ricu} and the section is concluded with a table summarizing similarities and differences among the datasets (see Table \ref{tab:datasets}). - -## MIMIC-III - -The [Medical Information Mart for Intensive Care III (MIMIC-III)](https://physionet.org/content/mimiciii/1.4/) represents the third iteration of the arguably most influential initiative for collecting and providing large-scale ICU data to the public^[The initial MIMIC (at the time short for Multi-parameter Intelligent Monitoring for Intensive Care) data release dates back 20 years and initially contained data on roughly 100 patients recorded from patient monitors in the medical, surgical, and cardiac intensive care units of Boston's Beth Israel Hospital during the years 1992-1999 \citep{moody1996}. Significantly broadened in scope, MIMIC-II was released 10 years after, now including data on almost 27,000 adult hospital admissions collected from ICUs of Beth Israel Deaconess Medical Center from 2001 to 2008 \citep{lee2011}.]. The dataset comprises de-identified health related data of roughly 46,000 patients admitted to critical care units of BIDMC during the years 2001-2012. Amounting to just over 61,000 individual ICU admission, data is available on demographics, routine vital sign measurements (at approximately 1 hour resolution), laboratory tests, medication, as well as critical care procedures, organized as a 26-table relational structure. - -```{r mimic-tbls, eval = TRUE} -mimic -``` - -One thing of note from a data-organizational perspective is that a change in electronic health care systems occurred in 2008. Owing to this, roughly 38,000 ICU admissions spanning the years 2001 though 2008 are documented using the CareVue system, while for 2008 and onwards, data was extracted from the MetaVision clinical information system. Item identifiers differ between the two systems, requiring queries to consider both ID mappings (heart rate for example being available both as `itemid` number `211` for CareVue and `220045` for MetaVision) as does documentation of infusions and other procedures that are considered as input events (cf., `inputevents_cv` and `inputevents_mv` tables). Especially with respect to such input event data, MetaVision generally provides data of superior quality. - -In terms of patient identifiers, MIMIC-III allows for identifying both individual patients (`subject_id`) across hospital admissions (`hadm_id`) and for connecting ICU (re)admissions (`icustay_id`) to hospital admissions. Using the respective one-to-many relationships, \pkg{ricu} can retrieve patient data using any of the above IDs, irrespective of how the raw data is organized. - -## eICU - -Unlike the single-center focus of other datasets, the [eICU Collaborative Research Database](https://physionet.org/content/eicu-crd/2.0/) constitutes an amalgamation of data from critical care units of over 200 hospitals throughout the continental United States. Large-scale data collected via the Philips eICU program, which provides telehealth infrastructure for intensive care units, is available from the Philips eICU Research Institute (eRI), albeit neither publicly nor freely. Only data corresponding to roughly 200,000 ICU admissions, sampled from a larger population of over 3 million ICU admissions and stratified by hospital, is being made available via PhysioNet. Patients with discharge dates in 2014 or 2015 were considered, with stays in low acuity units being removed. - -```{r width-inc, include = FALSE} -old_width <- options(width = 78)[["width"]] -``` - -```{r eicu-tbls, eval = TRUE} -eicu -``` - -```{r width-dec, include = FALSE} -options(width = old_width) -``` - -The data is organized into 31 tables and includes patient demographics, routine vital signs, laboratory measurements, medication administrations, admission diagnoses, as well as treatment information. Owing to the wide range of hospitals participating in this data collection initiative, spanning small, rural, non-teaching health centers with fewer than 100 beds to large teaching hospitals with an excess of 500 beds, data availability varies. Even if data was being recorded at the bedside it might end up missing from the eICU dataset due to technical limitations of the collection process. As for patient identifiers, while it is possible to link ICU admissions corresponding to the same hospital stay, it is not possible to identify patients across hospital stays. - -Data resolution again varies considerably over included variables. The `vitalperiodic` table stands out as one of the few examples of a *wide* table organization (laying out variables as columns), as opposed to the *long* presentation (following an entity–attribute–value model) of most other tables containing patient measurement data. The average time step in `vitalperiodic` is around 5 minutes, but data missingness ranges from around 1% for heart rate and pulse oximetry to roughly 10% for respiration rate and up to 80% for systemic and 90% for pulmonary artery blood pressure measurements, therefore giving approximately hourly resolution for such variables. - -## HiRID - -Developed for early prediction of circulatory failure \citep{hyland2020}, the [High Time Resolution ICU Dataset (HiRID)](https://physionet.org/content/hirid/1.0/) contains data on almost 34,000 admissions to the Department of Intensive Care Medicine of the Bern University Hospital, Switzerland, an interdisciplinary 60-bed unit. Given the clear focus on a concrete application during data collection, this dataset is the most limited in terms of data breadth, which is also reflected in a comparatively simple data layout comprising only 5 tables^[The data is available in three states: as raw data and in two intermediary preprocessing stages explained in \cite{hyland2020}. While \pkg{ricu} focuses exclusively on raw data, the *merged* stage represents a selection of variables that were deemed most predictive for determining circulatory failure, which are then merged into 18 meta-variables, representing different clinical concepts. Time stamps in *merged* data are left unchanged, yielding irregular time series, whereas for the *imputed* stage, data is down-sampled to a 5 minute grid and missing values are imputed using a scheme discussed in \cite{hyland2020}.]. - -```{r hirid-tbls, eval = TRUE} -hirid -``` - -Collected during the period of January 2008 through June 2016, roughly 700 distinct variables covering routine vital signs, diagnostic test results and treatment parameters are available with variables monitored at the bedside being recorded with two minute time resolution. In terms of demographic information and patient identifier systems however, the data is limited. It is not possible to identify subsequent ICU admissions corresponding to the same patient and apart from patient age, sex, weight and height, very little information is available to characterize patients. There is no medical history, no admission diagnoses, only in-ICU mortality information, no unstructured patient data and no information on patient discharge. Furthermore, data on body fluid sampling has been omitted, complicating for example the construction of a Sepsis-3 label \citep{singer2016}. - -## AmsterdamUMCdb - -As a second European dataset, also focusing on increased time-resolution over the US datasets, [AmsterdamUMCdb](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) has been made available in late 2019, containing data on over 23,000 intensive care unit and high dependency unit admissions of adult patients during the years 2003 through 2016. The department of Intensive Care at Amsterdam University Medical Center is a mixed medical-surgical ICU with 32 bed intensive care and 12 bed high dependency units with an average of 1000-2000 yearly admissions. Covering middle ground between the US datasets and HiRID in terms of breadth of included data, while providing a maximal time-resolution of 1 minute, AmsterdamUMCdb constitutes a well organized high quality ICU data resource organized succinctly as a 7-table relational structure. - -```{r aumc-tbls, eval = TRUE} -aumc -``` - -For data anonymization purposes, demographic information such as patient weight, height and age only available as binned variables instead of raw numeric values. Apart from this, there is information on patient origin, mortality, admission diagnoses, as well as numerical measurements including vital parameters, lab results, outputs from drains and catheters, information on administered medication, and other medical procedures. In terms of patient identifiers, it is possible to link ICU admissions corresponding to the same individual, but it is not possible to identify separate hospital admissions. - -## MIMIC-IV - -The most recently released dataset and next iteration in the MIMIC line of datasets, MIMIC-IV, has recently been released as first stable version \citep{johnson2021} and support in \pkg{ricu} is available as dataset `miiv`. Compared to MIMIC-III, this release shifts focus to newer data, dropping all CareVue-documented patients and with that, patients who were admitted before 2008, while adding patients admitted up to and including 2019. The resulting dataset contains data on over 256,000 patients of which, 53,000 were admitted to ICUs, resulting in 76,000 unique ICU and almost 70,000 related hospital admissions. - -```{r miiv-tbls, eval = TRUE} -miiv -``` - -```{r src-overview, echo = FALSE, results = "asis", cache = TRUE} -as_quant <- function(x) { - - if (is_id_tbl(x)) { - x <- data_col(x) - } - - if (identical(length(x), 0L) || isTRUE(is.na(x))) { - return("-") - } - - res <- format_2(quantile(x, probs = seq(0.25, 0.75, 0.25), na.rm = TRUE)) - - paste0(res[2L], " (", res[1L], "--", res[3L], ")") -} - -big_mark <- function(x) { - - if (identical(length(x), 0L) || isTRUE(is.na(x))) { - return("-") - } - - formatC(x, big.mark = ",", format = "d") -} - -format_2 <- function(x) { - formatC(x, digits = 2L, format = "f") -} - -n_patient <- function(x, type) { - if (type %in% names(as_id_cfg(x))) nrow(stay_windows(x, type)) else NA -} - -feat_freq <- function(src, concept, time_span = "hours") { - res <- load_concepts(concept, src, interval = mins(1L), verbose = FALSE) - res <- res[, 1 / diff(as.double(get(index_var(res)), units = time_span)), - by = c(id_var(res))] - res -} - -years <- function(src) { - switch(src, - mimic = "2001--2012", - eicu = "2014--2015", - hirid = "2008--2016", - aumc = "2003--2016", - miiv = "2008--2019", - NA - ) -} - -country <- function(src) { - switch(src, - mimic = "United States", - eicu = "United States", - hirid = "Switzerland", - aumc = "Netherlands", - miiv = "United States", - NA - ) -} - -summarize <- function(src, avail) { - - ids <- as_id_cfg(src) - cnc <- avail[, src] - - nrow(stay_windows(src, "icustay")) - - los_icu <- load_concepts("los_icu", src, verbose = FALSE) - - hosp_len <- if ("hadm" %in% names(ids)) { - load_concepts("los_hosp", src, id_type = "hadm", verbose = FALSE) - } - - fil <- list.files(src_data_dir(src), recursive = TRUE, full.names = TRUE) - siz <- sum(vapply(fil, file.size, numeric(1L))) * 1e-9 - row <- vapply(as_src_env(src), nrow, integer(1L)) - - c(`Number of tables` = big_mark(length(as_src_env(src))), - `Disk storage [GB]` = format_2(siz), - `Largest table [rows]` = big_mark(max(row)), - `Available concepts` = sum(cnc), - `Time span` = years(src), - `Country of origin` = country(src), - `ICU` = big_mark(n_patient(src, "icustay")), - `Hospital` = big_mark(n_patient(src, "hadm")), - `Unique patients` = big_mark(n_patient(src, "patient")), - `ICU stays` = as_quant(los_icu), - `Hospital stays` = as_quant(hosp_len), - `Heart rate` = as_quant(feat_freq(src, "hr")), - `Mean arterial pressure` = as_quant(feat_freq(src, "map")), - `Bilirubin` = as_quant(feat_freq(src, "bili", "days")), - `Lactate` = as_quant(feat_freq(src, "lact", "days")) - ) -} - -if (srcs_avail(demo) && !srcs_avail(srcs)) { - srcs <- demo -} - -src_names <- c( - mimic = "MIMIC-III", eicu = "eICU", hirid = "HiRID", aumc = "AmsterdamUMCdb", - miiv = "MIMIC-IV", mimic_demo = "MIMIC (demo)", eicu_demo = "eICU (demo)" -)[srcs] - -src_names[is.na(src_names)] <- srcs[is.na(src_names)] - -dict <- load_dictionary(srcs) -avai <- concept_availability(dict, include_rec = FALSE) -summ <- vapply(srcs, summarize, character(15L), avai) - -colnames(summ) <- src_names -rownames(summ) <- rownames(summ) -rownames(summ)[4L] <- paste0(rownames(summ)[4L], - footnote_marker_symbol(1, "latex")) - -n_rec_cpt <- nrow(concept_availability(dict, include_rec = TRUE)) - - nrow(avai) - -capt <- paste( - "Comparison of datasets supported by \\pkg{ricu}, highlighting some of", - "the major similarities and distinguishing features among the five data", - "sources described in the preceding sections. Values followed by", - "parenthesized ranges represent medians and are accompanied by", - "interquartile ranges." -) - -tbl <- kable(summ, format = "latex", escape = FALSE, booktabs = TRUE, - caption = capt, label = "datasets") -tbl <- pack_rows(tbl, "Data collection", 5, 6) -tbl <- pack_rows(tbl, "Admission counts", 7, 9) -tbl <- pack_rows(tbl, "Stay lengths [day]", 10, 11) -tbl <- pack_rows(tbl, "Vital signs [1/hour]", 12, 13) -tbl <- pack_rows(tbl, "Lab tests [1/day]", 14, 15) -tbl <- footnote(tbl, symbol = paste( - "These values represent the number of atomic concepts per data source.", - "Additionally,", n_rec_cpt, "recursive concepts are available, which", - "build on data source specific atomic concepts in a source agnostic manner", - "(see Section \\\\ref{concept-specification} for details)."), - threeparttable = TRUE, escape = FALSE -) - -if (identical(srcs, demo)) { - tbl -} else { - landscape(tbl) -} -``` - -```{r, full-miss, echo = FALSE, eval = identical(srcs, demo), results = "asis"} -demo_instead_full_msg(demo, srcs, "jss.pdf") -``` - -In addition to including newer ICU data, this MIMIC release puts both more emphasis on data collected outside the ICU, newly making emergency department (ED) data available. In a similar vein, the set of considered data types is also expanded by including chest X-ray (CXR) imagery directly with MIMIC data, using the same patient identifiers, while expanding the amount of unstructured text data (still to be made publicly available). Despite these promising developments, the focus of \pkg{ricu} remains on data that lies in the intersection of the supported datasets and therefore both ED and CXR data cannot be accessed by the current `miiv` implementation. Finally, documentation of medication administration has been much improved by not only reporting prescriptions, but, using an electronic Medicine Administration Record (eMAR) system, including time-stamped data on administration of individual formulary units. - -# Data concepts - -One of the key components of \pkg{ricu} is a scheme for specifying how to retrieve data corresponding to predefined clinical concepts from a given data source. This abstraction provides a mechanism for hiding away the data source specific implementation of a concept, in turn enabling dataset agnostic code for analysis. Heart rate, for example can be loaded from datasets `r paste1(demo)` using the `hr` concept as - -```{r, load-conc, eval = srcs_avail(demo)} -<> -<> - -load_concepts("hr", demo, verbose = FALSE) -``` - -This requires infrastructure for specifying how to retrieve data subsets (Section \ref{concept-specification}) that is both extensible (to new concepts and new datasets) and flexible enough to handle concept-specific preprocessing. Furthermore, allowing for code re-use for common data transformation tasks is important for simplifying both code development and maintenance. Building on this framework, \pkg{ricu} has included a dictionary with over 100 concepts implemented for all five supported datasets (where possible; see also Section \ref{ready-to-use-concepts} for further details). - -## Data classes - -In order to represent tabular ICU data, \pkg{ricu} provides several classes, all inheriting from `data.table`. The most basic of which, `id_tbl`, marks one (or several) columns as `id_vars` which serve to define a grouping (i.e., identify patients or unit stays). Inheriting from `id_tbl`, `ts_tbl` is capable of representing grouped time series data. In addition to `id_var` column(s), a single column is marked as `index_var` and is required to hold a base \proglang{R} `difftime` vector. Furthermore, `ts_tbl` contains a scalar-valued `difftime` object as `interval` attribute, specifying the time series step size. More recently, a further class, `win_tbl`, inheriting from `ts_tbl` has been added. Objects of this class can be used for time-stamped measurements associated with a validity period. A set of drug infusions, consisting of both rates and intervals can as such be conveniently represented by a `win_tbl` object. - -Metadata for classes inheriting from `id_tbl` is transiently added to `data.table` objects and for S3 generic functions which allow for object modifications, down-casting is implicit: - -```{r id-tbl, eval = TRUE} -(dat <- ts_tbl(a = 1:5, b = hours(1:5), c = rnorm(5))) -dat[["b"]] <- dat[["b"]] + mins(30) -dat -``` - -Due to time series step size of `dat` being specified as 1 hour, an internal inconsistency is encountered when shifting time stamps by 30 minutes, as time steps are no longer multiples of the time series interval, in turn causing down-casting to `id_tbl`. Furthermore, if column `a` were to be removed, direct down-casting to `data.table` would be required in order to resolve resulting inconsistencies^[Updating an object inheriting from `id_tbl` using `data.table::set()` bypasses consistency checks as this is not an S3 generic function and therefore its behavior cannot be tailored to requirements of `id_tbl` objects. It therefore is up to the user to avoid creating invalid `id_tbl` objects in such a way.]. - -Coercion to base classes `data.frame` and `data.table`, by stripping away the extra attributes, is easily possible using functions `as.data.frame()` and `as.data.table()`. Coercion is also available as `data.table`-style by-reference operation by passing `by_ref = TRUE` to any of the above coercion functions. User caution is advised, as this does break with base \proglang{R} by-value (or copy-on-modify) semantics and may lead to unexpected behavior. - -In its current form, `win_tbl` objects can both be used to represent for example drug rates or drug amounts, administered over a specified time-period. When calling the utility function `expand()` however, which creates a `ts_tbl` from a `win_tbl` by assigning values to the corresponding time steps, values are assumed to be *valid* for the given interval. - -```{r win-tbl, eval = TRUE} -(dat <- win_tbl(a = 1:5, b = hours(1:5), c = mins(rep(90, 5)), - d = runif(5))) -expand(dat) -``` - -In a case where `d` represented drug amounts instead of drug rates, the current implementation of `expand()` would produce incorrect results. One would expect the overall amount in such a scenario to be evenly divided by -- and the resulting fractions assigned to -- the corresponding time steps. Allowing for this distinction is being considered, but, as of yet, has not been implemented. - -Utilizing the attached metadata of objects inheriting from `id_tbl`, several utility functions can be called with concise semantics (as seen in the above example, where `expand()` is able to determine the required column names from the `win_tbl` object by default). Utilities include functions for sorting, checking for duplicates, aggregating data per combination of `id_vars` (and time step/time duration), checking time series data for gaps, verifying time series regularity and converting between irregular and regular time series, as well as functions for several types of moving window operations. Adding to those class-specific implementations, `id_tbl` objects inherit from `data.table` (and therefore from `data.frame`), ensuring compatibility with a wide range of functionality targeted at these base-classes. - -## Ready-to-use concepts - -The current selection of clinical concepts that is included with \pkg{ricu} covers many physiological variables that are available throughout the included datasets. Treatment-related information on the other hand, being more heterogeneous in nature and therefore harder to harmonize across datasets, has been added on an as-needed basis and therefore is more limited in breadth. A quick note on loading from multiple sources simultaneously: In the introductory example, heart rate was loaded from multiple data sources, resulting in a column `source` being added. This allows for identifying patient IDs corresponding to the respective data sources and the extra column is added to the set of `id_vars`. In the following calls to `load_concepts()`, only data from a single source is requested and therefore no corresponding `source` column is added. - -Available concepts can be enumerated using `load_dictionary()` and the utility function `explain_dictionary()` can be used to display some concept metadata. - -```{r, load-dict, eval = srcs_avail(demo)} -dict <- load_dictionary(demo) -head(dict) -explain_dictionary(head(dict)) -``` - -The following subsections serve to introduce some of the included concepts as well as highlight limitations that come with current implementations. Grouping the available concepts by category yields the following counts - -```{r, dict-cat, eval = srcs_avail(demo)} -table(vapply(dict, `[[`, character(1L), "category")) -``` - -### Physiological data - -The largest and most well established group of concepts (covering more than half of all currently included concepts) includes physiological patient measurements such as routine vital signs, respiratory variables, fluid discharge amounts, as well as many kinds of laboratory tests including blood gas measurements, chemical analysis of body fluids and hematology assays. - -```{r, load-phys, eval = srcs_avail(src)} -load_concepts(c("alb", "glu"), src, interval = mins(15L), - verbose = FALSE) -``` - -Most concepts of this kind are represented by `num_cncpt` objects (see Section \ref{concept-specification}) with an associated unit of measurement and a range of permissible values. Data is mainly returned as `ts_tbl` objects, representing time-dependent observations. Apart from conversion to a common unit (using functionality offered by the \pkg{units} package \citep{pebesma2016} or possibly using the `convert_unit()` callback function), little has to be done in terms of preprocessing: values are simply reported at time-points rounded to the requested interval. - -### Patient demographics - -Moving on from dynamic, time-varying patient data, this group of concepts focuses on static patient information. While the assumption of remaining constant throughout a stay is likely to hold for variables including patient sex or height this is only approximately true for others such as weight. Nevertheless, such effects are ignored and concepts of this group will be mainly returned as `id_tbl` objects with no corresponding time-stamps included. - -Whenever requesting concepts which are returned with associated time-stamps (e.g., glucose) alongside time-constant data (e.g., age), merging will duplicate static data over all time-points. - -```{r, load-demog, eval = srcs_avail(src)} -load_concepts(c("age", "glu"), src, verbose = FALSE) -``` - -Despite a best-effort approach, data availability can be a limiting factor. While for physiological variables, there is good agreement even across countries, data-privacy considerations, as well as lack of a common standard for data encoding, may cause issues that are hard to resolve. In some cases, this can be somewhat mitigated while in others, this is a limitation to be kept in mind. In AmsterdamUMCdb, for example, patient age, height and weight are not available as continuous variables, but as factor variables with patients binned into groups. Such variables are then approximated by returning the respective mid-points of groups for `aumc` data^[Prioritizing consistency over accuracy, one could apply the same binning to datasets which report numeric values, but the concepts included with \pkg{ricu} attempt to strike a balance between consistency and amount of applied preprocessing. With the extensible architecture of data concepts, however, such categorical variants of patient demographic concepts could easily be added.]. Other concepts, such as `adm` (categorizing admission types) or a potential `icd` concept (diagnoses as ICD-9 codes) can only return data if available from the data source in question. Unfortunately, neither `aumc` nor `hirid` contain ICD-9 encoded diagnoses, and in the case of `hirid`, no diagnosis information is available at all. - -### Treatment-related information - -The largest group of concepts dealing with treatment-related information is described by the `medications` category. In addition to drug administrations, only basic ventilation information is currently provided as ready-to-use concept. Just like availability of common ICU procedures, patient medication is also underdeveloped, covering mainly vasopressor administrations, as well as corticosteroids, antibiotics and dextrose infusions. The current concepts retrieving treatment-related information are mostly focused on providing data required for constructing clinical scores described in Section \ref{outcomes}. While this group of concepts lends itself to use of `win_tbl` objects, a call to `load_concepts()`, requesting multiple concepts which do not all return data as `win_tbl` (while leaving the `merge` argument at default value `TRUE`), all `win_tbl` objects are converted to `ts_tbl` in order to be merged with the non-`win_tbl` objects. - -Ventilation is represented by several concepts: a ventilation indicator variable (`vent_ind`), represented by a `win_tbl` object is constructed from start and end events (concepts `vent_start` and `vent_end`). This includes any kind of mechanical ventilation (invasive via an endotracheal or tracheostomy tube), as well as non-invasive ventilation via face or nasal masks. In line with other concepts belonging to this group, the current state is far from being comprehensive and expansion to further ventilation parameters is desirable. - -The singular concept addressing antibiotics (`abx`) returns an indicator signaling whenever an antibiotic was administered. This includes any route of administration (intravenous, oral, topical, etc.) and does neither report dosage, nor active ingredient. Finally, vasopressor administration is reported by several concepts representing different vasoactive drugs (including dopamine, dobutamine, epinephrine, norepinephrine and vasopressin), as well as different administration aspects such as rate, duration and rate administered for at least 60 minutes, which is used in Sepsis-Related Organ Failure Assessment (SOFA) scoring \citep{vincent1996}. - - - -```{r, load-treat, eval = srcs_avail(src)} -load_concepts(c("abx", "vent_ind", "norepi_rate", "norepi_dur"), src, - verbose = FALSE) -``` - -As cautioned in Section \ref{patient-demographics}, variability in data reporting across datasets can lead to issues: the `prescriptions` table included with MIMIC-III, for example, reports time-stamps as dates only, yielding a discrepancy of up to 24 hours when merged with data where time-accuracy is on the order of minutes. Another problem exists with concepts that attempt to report administration windows, as some datasets do not describe infusions with clear cut start/endpoints but rather report infusion parameters at (somewhat) regular time intervals. This can cause artifacts when the requested time step-size deviates from the dataset inherent time grid and introduces uncertainty when attempting to determine start/endpoints for creating a `win_tbl` object. - -```{r, load-dex, eval = srcs_avail("mimic_demo")} -load_concepts("dex", "mimic_demo", verbose = FALSE) -``` - -Furthermore for a concept like dextrose administration as implemented in `dex`, where infusions are returned alongside bolus administrations, this can yield large rate values, as the returned unit is ml/hr and in this particular case, values are harmonized such that they correspond to 10% dextrose solutions. A bolus administration of 50 ml dextrose 50% will therefore be reported as 15000 ml/hr administered within 1 minute. - -### Outcomes - -A group of more loosely associated concepts can be used to describe patient state. This includes common clinical endpoints, such as death or length of ICU stay, as well as scoring systems such as SOFA, the systemic inflammatory response syndrome \citep[SIRS;][]{bone1992} criterion, the National Early Warning Score \citep[NEWS;][]{jones2012} and the Modified Early Warning Score \citep[MEWS;][]{subbe2001}. - -While the more straightforward outcomes can be retrieved directly from data, clinical scores often incorporate multiple variables, based upon which a numeric score is constructed. This can typically be achieved by using concepts of type `rec_cncpt` (see Section \ref{concept-specification}), specifying the needed components and supplying a callback function that applies rules for score construction. - -```{r, load-out, eval = srcs_avail(src)} -load_concepts(c("sirs", "death"), src, verbose = FALSE, - keep_components = TRUE) -``` - -Callback functions can become rather involved (especially for more complex concepts such as SOFA) and may offer arbitrary arguments to tune their behavior. As callback functions to `rec_cncpt` objects are typically called internally from `load_concepts()`, arguments not used by `load_concepts()`, such as `keep_components` in the above example (causing not only the score column, but also individual score components to be retained) are forwarded. Therefore, some care has to be taken as when requesting multiple concepts within the same call to `load_concepts()`, while passing arguments intended for concept-level callback functions, as all involved callback functions will be called with the same forwarded arguments. When for example requesting multiple scores (such as SOFA or SIRS), it is currently not possible to enable `keep_components` for only a subset thereof. This setup consequently also requires that all involved callback functions are allowed to be called with the given set of extra arguments. - -## Concept specification - -Just like data source configuration (as discussed in Section \ref{data-source-configuration}), concept specification relies on JSON-formatted text files, parsed by \pkg{jsonlite} \citep{ooms2014}. A default dictionary of concepts is included with \pkg{ricu}, containing a selection of commonly used clinical concepts. Several types of concepts exist within \pkg{ricu} and with extensibility in mind, new types can easily be added. A quick remark on terminology before diving into more details on how to specify data concepts: A *concept* corresponds to a clinical variable such as a bilirubin measurement or the ventilation status of a patient, and an *item* encodes how to retrieve data corresponding to a given concept from a data source. A *concept* therefore contains several *items* (zero, one or multiple are possible per data source). - -All concepts consist of minimal metadata including a name, target class (defaults to `ts_tbl`; see Section \ref{data-classes}), an aggregation specification^[Every concept needs a default aggregation method which can be used during data loading to return data that is unique per key (either per `id_vars` group or per combination of `ìd_vars` and `index_var`) otherwise down-stream merging of multiple concepts is ill-defined. The aggregation default can be manually overridden during loading or automatically, by specification as part of a `rec_cncpt` object. If no aggregation method is explicitly indicated the global default is `first()` for character, `median()` for numeric and `any()` for logical vectors.] and class information (`num_concept` if not otherwise specified), as well as optional `description` and `category` information. Adding to that, depending on concept class, further fields can be supplied. In the case of the most widespread concept type (`num_cncpt`; used to represent numeric data) this is `unit` which encodes one (or several synonymous) unit(s) of measurement, as well as a minimal and maximal plausible values (specified as `min` and `max`). The concept for heart rate data (`hr`) for example can be specified as - -``` -{ - "hr": { - "unit": ["bpm", "/min"], - "min": 0, - "max": 300, - "description": "heart rate", - "category": "routine vital signs", - "sources": { - ... - } - } -} -``` - -Metadata is used during concept loading for data-preprocessing. For numeric concepts, the specified measurement unit is compared to that of the data (if available), with messages being displayed in case of mismatches, while the range of plausible values is used to filter out measurements that fall outside the specified interval. Other types of concepts include categorical concepts (`fct_cncpt`), concepts representing binary data (`lgl_cncpt`), as well as recursive concepts (`rec_cncpt`), which build on other *atomic* concepts^[An example for a recursive concept is the PaO~2~/FiO~2~ ratio, used for instance to assess patients with acute respiratory distress syndrome (ARDS) or for Sepsis-Related Organ Failure Assessment (SOFA) \citep{villar2013, vincent1996}. Given both PaO~2~ and FiO~2~ as individual concepts, the PaO~2~/FiO~2~ ratio is provided by \pkg{ricu} as a recursive concept (`pafi`), requesting the two atomic concepts `pao2` and `fio2` and performing some form of imputation for when at a given time step one or both values are missing.]. - -Finally, the most recently added concept class, `unt_cncpt`, inheriting from `num_cncpt`, aims to simplify manual conversion to target units, leveraging capabilities provided by the \pkg{units} package. For this to work, both source and target units have to be recognized and convertible (as reported by `units::ud_are_convertible()`). Measurement units that are not available by default can be registered using `units::install_unit()`. - -Specification of how data can be retrieved from a data source is encoded by data *items*. Lists of data items (associated with data source names) are provided as `sources` element. For the demo datasets corresponding to eICU and MIMIC-III, heart rate data retrieval is specified as - -``` -{ - "eicu_demo": [ - { - "table": "vitalperiodic", - "val_var": "heartrate", - "class": "col_itm" - } - ], - "mimic_demo": [ - { - "ids": [211, 220045], - "table": "chartevents", - "sub_var": "itemid" - } - ] -} -``` - -Analogously to how different concept classes are used to represent different types of data, different item classes handle different data loading requirements. The most common scenario is selecting a subset of rows from a table by matching a set of ID values (`sub_itm`). In the above example, heart rate data in MIMIC-III can be located by searching for ID values 211 and 220045 in column `itemid` of table `chartevents` (heart rate data is stored in *long* format). Conversely, heart rate data in eICU is stored in *wide* format, requiring no row-subsetting. Column `heartrate` of table `vitalperiodic` contains all corresponding data and such data situations are handled by the `col_itm` class. Other item classes include `rgx_itm` where a regular expression is used for selecting rows and `fun_itm` where an arbitrary function can be used for data loading. If a data loading scenario is not covered by these classes, adding further `itm` subclasses is encouraged. - -In order to extend the current concept library both to new datasets and new concepts, further JSON files can be incorporated by adding paths to their enclosing directories to `RICU_CONFIG_PATH`. Concepts with names that exist in files of the same name but with higher precedence are only used for their `sources` entries, such that `hr` for `new_dataset` can be specified as follows, while concepts with non-existing names are treated as new concepts. - -``` -"hr": { - "sources": { - "new_dataset": [ - { - "ids": 6640, - "table": "numericitems", - "sub_var": "itemid" - } - ] - } -} -``` - -Central to providing the required flexibility for loading of certain data concepts that require some specific preprocessing are callback functions that can be specified for several *item* types. Functions (with appropriate signatures), designated as callback functions, are invoked on individual data items, before concept-related preprocessing is applied. A common scenario for this is unit of measurement conversion: In MIMIC-III data for example, several `itemid` values correspond to temperature measurements, some of which refer to temperatures measured in degrees Celsius whereas others are used for measurements in degrees Fahrenheit. As the information encoding which measurement corresponds to which `itemid` values is no longer available during concept-related preprocessing, this is best resolved at the level of individual data items. Several function factories are available for generating callback functions and `convert_unit()` is intended for covering unit conversions^[The presented implementation of this concept predates the addition of automatic unit conversion using the \pkg{units} package. While the concept definition as used by \pkg{ricu} will be updated to reflect these new capabilities, this example remains for illustration purposes.]. Data *items* corresponding to the `temp` concept for MIMIC-III are specified as - -``` -{ - "mimic_demo": [ - { - "ids": [676, 677, 223762], - "table": "chartevents", - "sub_var": "itemid" - }, - { - "ids": [678, 679, 223761, 224027], - "table": "chartevents", - "sub_var": "itemid", - "callback": "convert_unit(fahr_to_cels, 'C', 'f')" - } - ] -} -``` - -indicating that for ID values 676, 677 and 223762 no preprocessing is required and for the remaining ID values the function `fahr_to_cels()` is applied to entries of the `val_var` column where the regular expression `"f"` is `TRUE` for the `unit_var` column (the values of which being ultimately replaced with `"C"`). - -# Data sources - -Every dataset is represented by an environment with class attributes and associated metadata objects stored as object attributes to that environment. Dataset environments all inherit from `src_env` and from any number of class names constructed from data source name(s) with a suffix `_env` attached. The environment representing MIMIC-III, for example inherits from `src_env` and `mimic_env`, while the corresponding demo dataset inherits from `src_env`, `mimic_env` and `mimic_demo_env`. These sub-classes are later used for tailoring the process of data loading to particularities of individual datasets. - -A `src_env` contains an active binding per associated table, which returns a `src_tbl` object representing the requested table. As is the case for `src_env` objects, `src_tbl` objects inherit from additional classes for reasons explained above. The `admissions` table of the MIMIC-III demo dataset for example, inherits from `mimic_demo_tbl` and `mimic_tbl` (alongside classes `src_tbl` and `prt`). - -```{r mimic-adm, eval = srcs_avail("mimic_demo")} -mimic_demo$admissions -``` - -Powered by the \pkg{prt} \citep{bennett2021} package, `src_tbl` objects represent row-partitioned tabular data stored as multiple binary files created by the \pkg{fst} \citep{klik2020} package. In addition to standard subsetting, `prt` objects can be subsetted via the base \proglang{R} S3 generic function `subset()` and using non-standard evaluation (NSE): - -```{r mimic-sub, eval = srcs_avail("mimic_demo")} -subset(mimic_demo$admissions, subject_id > 44000, language:ethnicity) -``` - -This syntax makes it possible to read row-subsets of *long* tables into memory with little memory overhead. While terseness of such an API does introduce potential ambiguity, this is mostly overcome by using the tidy eval framework provided by \pkg{rlang} \citep{wickham2020}: - -```{r mimic-tidy, eval = srcs_avail("mimic_demo")} -subject_id <- 44000:45000 -subset(mimic_demo$admissions, .data$subject_id %in% .env$subject_id, - subject_id:dischtime) -``` - -By using \pkg{rlang} pronouns (`.data` and `.env`), the distinction can readily be made between a name referring to an object within the context of the data and an object within the context of the calling environment. - -## Data source setup - -In order to make a dataset accessible to \pkg{ricu}, three steps are necessary, each handled by an exported S3 generic function: `download_scr()`, `import_src()` and `attach_src()`. The first two steps, data download and import, are one-time procedures, whereas attaching is carried out every time the package namespace is loaded. By default, all data sources known to \pkg{ricu} are configured to be attached and in case some data is missing for a given data source, the missing data is downloaded and imported on first access. An outline of the steps involved for data source setup is shown in Figure \ref{fig:src-setup}. - -```{tikz, src-setup, fig.cap = "Making a dataset available to \\pkg{ricu} involves several steps, starting with data download, followed by preparation for efficient access and finalized by instantiation of data structures containing relevant metadata. The functions which are used for each step are displayed above arrows and below (in red) are indicated specific configuration settings or environment variables which are need for (or can be used to customize) the specific step.", fig.ext = "png", cache = TRUE, echo = FALSE, eval = TRUE} - -<> - -\begin{tikzpicture} - - \node [f1, label={above left:{a}}] (ricu) at (0, 19) { - \texttt{ricu} installed\\ no data (apart from\\ demo datasets) - }; - \node [f1, label={above left:{b}}] (csv) at (10, 19) { - raw tables\\ (.csv files) - }; - \node [f1, label={above left:{c}}] (fst) at (0, 12) { - (partitioned) \texttt{fst}\\ tables (\texttt{prt} objects) - }; - \node [f1, label={above left:{d}}] (env) at (10, 12) { - queryable \texttt{src\_env}\\ containing \texttt{src\_tbl}\\ objects - }; - - \draw [-Stealth] (ricu) to [bend right = 0] node[above, rotate=0]{ - \texttt{download\_src()} - } node[f2, below, rotate=0]{ - \texttt{RICU\_PHYSIONET\_USER}\\ \texttt{RICU\_PHYSIONET\_PASS}\\ - \texttt{RICU\_AUMC\_TOKEN} - } (csv); - \draw [-Stealth] (csv) to [bend right = 0] node[above, rotate=35]{ - \texttt{import\_src()} - } node[f2, below, rotate=35]{ - \texttt{RICU\_DATA\_PATH}\\ \texttt{RICU\_CONFIG\_PATH}\\ - \texttt{tbl\_cfg} - } (fst); - \draw [-Stealth] (fst) to [bend right = 0] node[above, rotate=0]{ - \texttt{attach\_src()} - } node[f2, below, rotate=0]{ - \texttt{RICU\_SRC\_LOAD}\\ \texttt{id\_cfg}, \texttt{col\_cfg} - } (env); - -\end{tikzpicture} -``` - -### Data download - -The first step towards accessing data is data download, taken care of by the S3 generic function `download_src()`. For the datasets included with \pkg{ricu}, prior to calling `download_src()`, the following environment variables can be set (indicated in red in the $a \to b$ edge in Figure \ref{fig:src-setup}): - -* `RICU_PHYSIONET_USER`/`RICU_PHYSIONET_PASS`: PhysioNet login credentials with access to the requested dataset(s). -* `RICU_AUMC_TOKEN`: Download token, extracted from the download URL received after being granted data access. - -If any of the required access credentials are not available as environment variables, they can be supplied as function arguments to `download_src()` or the user is queried in interactive sessions and an error is thrown otherwise. - -As a quick reminder on system requirements for initial data setup operations: Each of the supported datasets requires 5-10 GB disk space for permanent storage and 50-100 GB of temporary disk storage during download and import. Memory requirements are kept low (8-16 GB) by performing all setup operations only on subsets of rows at the time. Initial data source setup can be expected to take upwards of an hour per dataset. - -### Data import - -After successful data download, importing prepares tables for efficient random row- and column-access, for which the raw data format (.csv) is not well suited (see edge $b \to c$ in Figure \ref{fig:src-setup}). Tables are read in using \pkg{readr} \citep{hester2020}, potentially (re-)partitioned row-wise, and re-saved using \pkg{fst}. Environment variables that can be set to customize \pkg{ricu} data handling, relevant for import and attaching include: - -* `RICU_DATA_PATH`: Optional data storage location (if unset, this defaults to a system-specific, user-specific directory). The current value used for this setting can be queried by calling `data_dir()`. -* `RICU_CONFIG_PATH`: A comma-separated set of paths to directories containing configuration files. The current set of paths is retrievable by calling `config_paths()` and the ordering of paths determines precedence of how configuration files are combined (if multiple files of the same name are available). - -For importing, the information contained in `tbl_cfg` configuration objects is most relevant. This determines column data types, table partitioning and sanity checks like number of rows per table. Please refer to Section \ref{table-configuration} for more information on the construction of `tbl_cfg` objects. - -### Data attaching - -Finally, attaching a dataset creates a corresponding `src_env` object, containing a corresponding `src_tbl` object for each table, which together with associated metadata are used by \pkg{ricu} to run queries against the data (edge $c \to d$ in Figure \ref{fig:src-setup}). The environment variable `RICU_SRC_LOAD` may contain a comma-separated list of data source names that are set up for being automatically attached on namespace loading. This defaults to all currently supported datasets and the active set of source names is available as `auto_attach_srcs()`. Apart from this automatism, the process of attaching a dataset can be manually invoked by calling `attach_src()`, which can be convenient when for example updating the data source configuration after it has been modified. - -Two configuration objects which are important for data loading (see the following Section \ref{data-loading}) are `id_cfg` and `col_cfg` (described in Sections \ref{id-configuration} and \ref{default-column-configuration}, respectively), providing default values for certain types of columns, including time-stamp, measurement value and measurement unit column names, as well as defining relationships between patient identifiers (such as hospital stay ID and ICU stay ID). - -## Data loading - -The lowest level of data access is direct subsetting of `src_tbl` objects as shown at the start of Section \ref{data-sources}. As `src_tbl` inherits from `prt`, the `subset()` implementation provided by \pkg{prt} can be used for NSE of data-expressions against on-disk, tabular data. Building on that, several S3 generic functions successively homogenize data representations as visualized in Figure \ref{fig:data-loading}. - -```{tikz, data-loading, fig.cap = "Data loading proceeds through several layers, each contributing a step towards harmonizing discrepancies among raw data representations provided by the different data sources. Raw data tables are represented by \\pkg{ricu} as \\code{src\\_tbl} objects which can be queried using \\code{load\\_src()}. Absolute time-stamps in the returned \\code{data.table} are converted to times relative to admission (in minutes) by \\code{load\\_difftime()} and finally, \\code{load\\_id()}/\\allowbreak\\code{load\\_ts()}/\\allowbreak\\code{load\\_win()} ensure a given ID system and time interval.", fig.ext = "png", cache = TRUE, echo = FALSE, eval = TRUE} - -<> - -\begin{tikzpicture} - - \node [f1, label={above left:{a}}] (fst) at (0, 19) { - \texttt{src\_tbl} object\\ on-disk table - }; - \node [f1, label={above left:{b}}] (dt) at (10, 19) { - \texttt{data.table object}\\ in-memory table - }; - \node [f1, label={above left:{c}}] (dat) at (0, 12) { - \texttt{data.table object}\\ minute resolution\\ in-data ID - }; - \node [f1, label={above left:{d}}] (tbl) at (10, 12) { - \texttt{id\_tbl} object\\ requested resolution\\ requested ID - }; - - \draw [-Stealth] (dt) to [bend right = 0] node[above, rotate=0]{ - \texttt{load\_src()} - } node[f2, below, rotate=0]{ - \texttt{subset()} - } (fst); - \draw [-Stealth] (dat) to [bend right = 0] node[above, rotate=35]{ - \texttt{load\_difftime()} - } node[f2, below, rotate=35]{ - column config\\ \texttt{id\_origin()} - } (dt); - \draw [-Stealth] (tbl) to [bend right = 0] node[above, rotate=0]{ - \texttt{load\_id()}/\texttt{load\_ts()}/\texttt{load\_win()} - } node[f2, below, rotate=0]{ - ID config\\ \texttt{id\_windows()} - } (dat); - -\end{tikzpicture} -``` - -The most basic layer in data loading is provided by the S3 generic function `load_src()`, which provides a string-based interface to the `cols` argument of `subset()` while forwarding the unevaluated expression passed as `rows` (see edge $a \to b$ in Figure \ref{fig:data-loading}). - -```{r load-src, eval = srcs_avail("mimic_demo")} -load_src(mimic_demo$admissions, subject_id > 44000, - cols = c("hadm_id", "admittime", "dischtime")) -``` - -As data sources differ in their representation of time-stamps, a next step in data homogenization is to converge to a common format: the time difference to the origin time-point of a given ID system (for example ICU admission). - -```{r load-dt, eval = FALSE} -load_difftime(mimic_demo$admissions, subject_id > 44000, - cols = c("hadm_id", "admittime", "dischtime")) -``` - -```{r load-dt-print, eval = srcs_avail("mimic_demo"), echo = FALSE} -load_difftime(mimic_demo$admissions, subject_id > 44000, - cols = c("hadm_id", "admittime", "dischtime"))[] -``` - -The function `load_difftime()` is expected to return timestamps as base \proglang{R} `difftime` vectors (in minutes; edge $b \to c$ in Figure \ref{fig:data-loading}). The argument `id_hint` can be used to specify a preferred ID system, but if not available in raw data, `load_difftime()` will return data using the ID system with highest cardinality (i.e., ICU stay ID is preferred over hospital stay ID). In the above example, if `icustay_id` were requested, data would be returned using `hadm_id`, whereas a `subject_id` request would be honored, as the corresponding ID column is available in the `admissions` table. - -Building on `load_difftime()` functionality, functions `load_id()`/\allowbreak`load_ts()`/\allowbreak`load_win()` return `id_tbl`/\allowbreak`ts_tbl`/\allowbreak`win_tbl` objects with the requested ID system (passed as `id_var` argument). This uses raw data IDs if available or calls `change_id()` in order to convert to the desired ID system (edge $c \to d$ in Figure \ref{fig:data-loading}). Similarly, where `load_difftime()` returns data with fixed time interval of one minute, `load_id()` allows for arbitrary time intervals (using `change_interval()`; defaults to 1 hour). - -```{r load-id, eval = FALSE} -load_id(mimic_demo$admissions, subject_id > 44000, - cols = c("admittime", "dischtime"), id_var = "hadm_id") -``` - -```{r load-id-print, eval = srcs_avail("mimic_demo"), echo = FALSE} -load_id(mimic_demo$admissions, subject_id > 44000, - cols = c("admittime", "dischtime"), id_var = "hadm_id")[] -``` - -Throughout several of theses functions, `col_cfg` objects are used to provide sensible defaults. In order to convert to relative times, `load_difftime()`, for example, requires names of columns for which this applies (provided by the `time_vars` entry), and `load_ts()` needs to know which of the `time_vars` to use as `index_var`. For more information on the construction of `col_cfg` objects, please refer to Section \ref{default-column-configuration}. - -A call to `change_id()` requires the construction of a table which contains the mapping between different ID systems, together with information about how to convert timestamps between these ID systems (edge $c \to d$ in Figure \ref{fig:data-loading}). The function responsible for providing the necessary information is `id_windows()` and the associated S3 generic function `id_win_helper()`. The entry point `id_windows()` wraps `id_win_helper()`, providing memoization, as the resulting structure is expensive to compute relative to the frequency of being required. - -```{r id-win, eval = srcs_avail("mimic_demo")} -id_windows(mimic_demo) -``` - -Analogously, the function pair `id_origin()` and `id_orig_helper()`, with the former wrapping the latter and again providing memoization, is used for datasets where time-stamps are represented by absolute times, returning the origin time-points for a given ID system which then can be used to calculate relative times (edge $b \to c$ in Figure \ref{fig:data-loading}). - -```{r id-orig, eval = srcs_avail("mimic_demo")} -id_origin(mimic_demo, "icustay_id") -``` - -For the included datasets, the implementations of `id_win_helper()` and `id_orig_helper()`, use information contained in `id_cfg` objects (see Section \ref{id-configuration}) to determine which columns in which tables are required for constructing the corresponding lookup tables. Doing so, however, is not necessary: an `id_win_helper()` implementation for a new dataset could forego this by hard-coding table/column names as part of the function logic, in-turn simplifying the corresponding `id_cfg` object to merely providing naming and ordering information. - -## Data source configuration - -Data source environments (and corresponding `src_tbl` objects) are constructed using source configuration objects: list-based structures, inheriting from `src_cfg` and from any number of data source specific class names with suffix `_cfg` appended (as discussed at the beginning of Section \ref{data-sources}). The exported function `load_src_cfg()` reads a JSON formatted file and creates a `src_cfg` object per data source and further therein contained objects. - -```{r mimic-cfg, eval = TRUE} -cfg <- load_src_cfg("mimic_demo") -str(cfg, max.level = 3L, width = 70L) -mi_cfg <- cfg[["mimic_demo"]] -``` - -In addition to required fields `name` and `prefix` (used as class prefix), as well as further arbitrary fields contained in `extra` (`url` in this case), several configuration objects are part of `src_cfg`: `id_cfg`, `col_cfg` and `tbl_cfg`. - -### ID configuration - -An `id_cfg` object contains an ordered set of key-value pairs representing patient identifiers in a dataset. An implicit assumption currently is that a given patient ID system is used consistently throughout a dataset, meaning that for example an ICU stay ID is always referred to by the same name throughout all tables containing a corresponding column. Owing to the relational origins of these datasets this has been fulfilled in all instances encountered so far. In MIMIC-III, ID systems - -```{r mimic-ids, eval = TRUE} -as_id_cfg(mi_cfg) -``` - -are available, allowing for identification of individual patients, their (potentially multiple) hospital admissions over the course of the years and their corresponding ICU admissions (as well as potential re-admissions). Ordering corresponds to cardinality: moving to larger values implies moving along a one-to-many relationship. This information is used in data-loading, whenever the target ID system is not contained in the raw data. - -### Default column configuration - -Again used in data loading, this per-table set of key-value pairs specifies column defaults as `col_cfg` object. Each key describes a type of column with special meaning and the corresponding value specifies said column for a given table. The print method for `col_cfg` reports all keys alongside the per-table counts of accordingly registered values (i.e., columns). - -```{r mimic-col, eval = TRUE} -as_col_cfg(mi_cfg) -``` - -The following column defaults are currently in use throughout \pkg{ricu} but the set of keys can be extended to arbitrary new values: - -* `id_var`: In case a table does not contain at least one ID column corresponding to one of the ID systems specified as `id_cfg`, the default ID column can be set on a per-table basis as `id_var`^[This for example is the case for the `d_items` table in MIMIC-III, which does not contain any patient related data, but holds information on items encoding types of measurements, procedures, etc., used throughout other tables holding actual patient data.]. -* `index_var`: A column that is used to define an ordering in time over rows, thereby providing a time series index^[For the MIMIC-III table `inputevents_mv`, of the four available time variables (`starttime`, `endtime`, `storetime`, `comments_date`), `starttime` lends itself to be used as index variable more than the other candidates and therefore is set as default.]. -* `time_vars`: Columns which will be treated as time variables (important for converting between ID systems for example), but not as time series indices^[In case of the `admissions` table in MIMIC-III for example, a total of five columns are considered to be time variables, none of which stands out as potential `index_var`.]. -* `unit_var`: Used in concept loading (more specifically for `num_cncpt` concepts, see Section \ref{concept-specification}) to identify columns that represent unit of measurement information. -* `val_var`: Again used when loading data concepts, this identified a default value variable in a table, representing the column of interest to be used as returned data column. - -While `id_var`, `index_var` and `time_vars` are used to provide sensible defaults to functions used for general data loading (Section \ref{data-loading}), `unit_var`, `val_var`, as well as potential user-defined defaults are only used in concept loading (see Section \ref{ready-to-use-concepts}) and therefore need not be prioritized when integrating new data sources until data concepts have been mapped. - -### Table configuration - -Finally, `tbl_cfg` objects are used during the initial setup of a data source. In order to create a representation of a table that is accessible by \pkg{ricu} from raw data, several key pieces of information are required: - -* File name(s): In the simplest case, a single file corresponds to a single table. Other scenarios that have been encountered (and are therefore handled) include tables partitioned into multiple files and .tar archives containing multiple tables. - -* Column specification: For each column, the expected data type has to be known, as well as a pair of names, one corresponding to the raw data column name and one corresponding to the column name to be used within \pkg{ricu}. - -* (Optional) number of rows: Used as sanity check whenever available. - -* (Optional) partitioning information: For very *long* tables it can be useful to specify a row-partitioning. This currently is only possible by applying a vector of breakpoints to a single numeric column, thereby defining a grouping. - -Table configuration objects are only used within the context of the functions `download_src()` and `import_src()` and are therefore not required if download and import are carried out manually. - -```{r mimic-tbl, eval = TRUE} -as_tbl_cfg(mi_cfg) -``` - -For the `chartevents` table of the MIMIC-III demo dataset, rows are partitioned into two groups, while all other tables are represented by a single partition. Furthermore, the expected number of rows is unknown (`??`) as this is missing from the corresponding `tbl_cfg` object. - -## Adding external datasets - -In order to add a new dataset to \pkg{ricu}, several aspects outlined in the previous subsections require consideration. For illustration purposes, code for integrating AmsterdamUMCdb as external dataset is available from [GitHub](https://github.com/eth-mds/aumc). While this is no longer needed for using the `aumc` data source, the repository will remain as it might serve as template to integration of new datasets. Throughout this repository (and the following paragraphs), the AmsterdamUMCdb data treated as an \pkg{ricu}-external dataset is referred to as `aumc_ext`. - -### Adding configuration information - -Central to adding a new dataset to \pkg{ricu} is providing some configuration information in a `data-sources.json` file pointed to by the environment variable `RICU_CONFIG_PATH`. Depending on particularities of the dataset in question, corresponding implementations of some of the S3 generic functions mentioned throughout Sections \ref{data-source-setup} and \ref{data-loading} might have to be provided. The amount of confirmation information required to get started also depends on the desired level of integration. As data download and import are one-time procedures, these steps can be carried out manually, negating the need for specifying column data types in `data-sources.json` and providing data source specific methods for the `download_src()` and `import_src()` generics. - -The basic organization of a data source configuration entry, as it could be used for `aumc_ext`, specified as JSON is as follows: - -``` -{ - "name": "aumc_ext", - "id_cfg": { - "patient": { - "id": "patientid", - "position": 1 - }, - "icustay": { - "id": "admissionid", - "position": 2 - } - }, - "tables": { - ... - } -} -``` - -The shown `id_cfg` entry represents the minimally required set of entries, where for each ID specification, `start`, `end` and `table` are omitted (when compared to the `aumc` configuration provided by \pkg{ricu}). The `tables` entry expands to something like the following: - -``` -"tables": { - "freetextitems": { - }, - "drugitems": { - "defaults": { - "index_var": "start", - "val_var": "dose", - "unit_var": "doseunit", - "time_vars": ["start", "stop"] - } - }, - "numericitems": { - "defaults": { - "index_var": "measuredat", - "val_var": "value", - "unit_var": "unit", - "time_vars": ["measuredat", "registeredat", "updatedat"] - }, - "partitioning": { - "col": "", - "breaks": [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 - ] - } - }, - ... -} -``` - -Minimally required is simply an entry indicating the data source membership of a table (if not partitioned; cf., `freetextitems`). This does slightly complicate data exploration, as if no `defaults` are available, no default values can be provided to calls to `load_ts()` and related functions and therefore repeatedly have to be specified in corresponding function calls. Also, when specifying data items in such a setup, the per-table column names for special columns such as `index_var`, `val_var`, etc., have to be repeated for each individual item entry. - -For partitioned tables, the basic structure of a `partitioning` entry is required, but the content itself is irrelevant, as this is only used for setup (cf., `numericitems`). The length of `breaks`, however, is required to match the number of partitions (i.e., a length 23 `breaks` specification corresponds to a partitioning into 24 row-groups.)^[Originally it was intended to use partitioning information during data loading in order to narrow down the set of partitions that have to be accessed. So far, this optimization has not been implemented.]. The directory containing such a `data-sources.json` can then be pointed to by the environment variable `RICU_CONFIG_PATH`, making it available to \pkg{ricu}. - -### Enabling data loading - -As for functions that are required, currently there is no default method available for the loading step provided by `load_difftime()` and most likely an implementation of the generic function `id_win_helper()` will be required as well. For `aumc_ext`, `load_difftime()` could be implemented as - -```{r, ext-difftime} -ms_as_min <- function(x) { - as.difftime(as.integer(x / 6e4), units = "mins") -} - -aumc_difftime <- function(x, rows, cols = colnames(x), - id_hint = id_vars(x), - time_vars = ricu::time_vars(x), ...) { - - if (id_hint %in% colnames(x)) { - id_sel <- id_hint - } else { - id_opt <- id_var_opts(sort(as_id_cfg(x), decreasing = TRUE)) - id_sel <- intersect(id_opt, colnames(x))[1L] - } - - stopifnot(is.character(id_sel), length(id_sel) == 1L) - - if (!id_sel %in% cols) { - cols <- c(id_sel, cols) - } - - time_vars <- intersect(time_vars, cols) - - dat <- load_src(x, {{ rows }}, cols) - dat <- dat[, c(time_vars) := lapply(.SD, ms_as_min), - .SDcols = time_vars] - - as_id_tbl(dat, id_vars = id_sel, by_ref = TRUE) -} -``` - -Such a function attempts to use the ID as requested as `id_hint`, but falls back to the best possible alternative (using the ordering as previously specified in the `id_cfg` JSON configuration) if not provided by the data. The helper function `id_var_opts()` returns the dataset-specific column names of an `id_cfg` object (as opposed to the dataset-agnostic ID names; cf., `subject_id` and `patient`). Both the row-subsetting expression and column selection are passed on to `load_src()` and all columns specified as `time_vars` are converted to `difftime` vectors in minutes. Operations can safely be carried out using by-reference semantics, as intermediate objects are not exposed to the user. - -For a possible implementation of the `id_win_helper()` generic, column and table names to assemble the desired lookup table are hard coded instead of provided by the corresponding `id_cfg` object (as is the case in the \pkg{ricu}-internal implementation). - -```{r, ext-win} -aumc_windows <- function(x) { - - ids <- c("admissionid", "patientid") - sta <- c("admittedat", "firstadmittedat") - end <- c("dischargedat", "dateofdeath") - - tbl <- as_src_tbl(x, "admissions") - - res <- tbl[, c(ids, sta[1L], end)] - res <- res[, c(sta[2L]) := 0L] - res <- res[, c(sta, end) := lapply(.SD, ms_as_min), - .SDcols = c(sta, end)] - - res <- data.table::setcolorder(res, c(ids, sta, end)) - res <- rename_cols(res, c(ids, paste0(ids, "_start"), - paste0(ids, "_end")), by_ref = TRUE) - - as_id_tbl(res, ids[2L], by_ref = TRUE) -} -``` - -As all the required information is available form the `admissions` table, `aumc_windows()` simply loads the corresponding columns, converts them to minute resolution, followed by some renaming. ICU admissions and discharges in this table are relative to initial hospital admissions and therefore an all-zero column `firstadmittedat` is added and the `id_var` of the resulting `id_tbl` is marked as `patientid`^[The patient ID created in this way is different to that available for MIMIC-III, where patient date of birth is provided. An approximate date of birth could be constructed if ages were reported more precisely, but given the rough binning available here, this might be considered an acceptable limitation of resulting patient IDs. Nevertheless awareness of such differences in data presentation is important.]. - -A final step in making a new dataset accessible to \pkg{ricu} lies in specifying concept items. To this end, a file `concept-dict.json` can be added to the directory pointed to by the environment variable `RICU_CONFIG_PATH`, containing entries like the following, which will make it possible to use the `hr` concept across all datasets included with \pkg{ricu}, alongside the newly added dataset. - -``` -{ - "hr": { - "sources": { - "aumc_ext": [ - { - "ids": 6640, - "table": "numericitems", - "sub_var": "itemid" - } - ] - } - } -} -``` - -The above outline serves as an example on how to proceed when adding new data to \pkg{ricu}. Aspects like having multiple patient IDs, for example, could be further simplified^[An example for such a reduced setup is available from the [AUMC GitHub repository](https://github.com/eth-mds/aumc) as `aumc_min`. Moving to only a single patient identifier also does away with the need for a `id_win_helper()` implementation, as `change_id()` will not be called in such a scenario.]. Owing to the extensive use of S3 generic functions, \pkg{ricu} offers considerable flexibility for customizing certain behavior to specifics of a given data source, while providing fallback procedures whenever more general treatment can be applied. - -### Summary of required steps - -Summarizing aspects explained in more detail in the previous sections, the following points list the required steps for adding new data in the order they should be considered in. The approach taken here being is to start simple and expand. - -1. Tables saved as `.fst` files should be moved to the folder returned by `src_data_dir()` when passed the dataset name (alternatively, methods implementing `src_download()` and `src_import()` are required). - -1. A minimal data source configuration file `data-sources.json` is required in the directory pointed to by `RICU_CONFIG_PATH`. For AmsterdamUMCdb, this could be as minimal as (assuming no partitioning): - - { - "name": "aumc_min", - "id_cfg": { - "icustay": "admissionid" - }, - "tables": { - "admissions": {}, - "drugitems": {}, - "freetextitems": {}, - "listitems": {}, - "numericitems": {}, - "procedureorderitems": {}, - "processitems": {} - } - } - - File names have to match table names, i.e., the admissions table should be named `admissions.fst`. Upon a call to `attach_src()` (or next loading of the package and having added the data source name to `RICU_SRC_LOAD`) the new data source can be explored using `load_src()`. - -1. A `load_difftime()` method is required, which: - - - passes a row-subsetting expression to `load_src()` using the \pkg{rlang} curly-curly operator, - - converts columns passed as `time_vars` to minute-resolution `difftime` vectors, - - returns an `id_tbl` object where patient identifiers are chosen such that time-stamps are relative to corresponding admission, - - (optionally) uses the column passed as `id_hint` for patient identifiers, if multiple identifiers are available from data. - - Upon registering this method with S3 dispatch, higher-level data loading functions such as `load_ts()` become available (given that no changes in patient identifiers are requested). - -1. (Optional) if the source configuration specifies multiple patient identifiers which are not all available from all tables directly, an implementation of `id_win_helper()` most likely will be required (see Section \ref{data-loading}). - -1. Now, the source configuration can be expanded with per-table column defaults and data items can be added to the concepts included with \pkg{ricu} by creating a `concept-dict.json` under the path pointed to by `RICU_CONFIG_PATH`. For more information on readily available concepts, refer to Section \ref{ready-to-use-concepts} and for specifying new concepts altogether, pointers are available in section \ref{concept-specification}. - -# Examples - -In order to briefly illustrate how \pkg{ricu} could be applied to real-world clinical questions, two examples are provided in the following sections. The first example fully relies on data concepts that are included with \pkg{ricu}. Whereas the second one explores both how some data preprocessing can be added to an existing concept, by creating a recursive concept (or `rec_cncpt`), as well as how to create an entirely new data concept in code (instead of JSON specification as outlined in Section \ref{concept-specification}), using constructors `item()` and `concept()`. - -## Lactate and mortality - -First, the association of lactate levels and mortality is investigated. This problem has been studied before and it is widely accepted that both static and dynamic lactate indices are associated with increased mortality \citep{haas2016, nichol2011, van2013}. In order to model this relationship, a time-varying proportional hazards Cox model \citep{therneau2000, therneau2015} is fitted, which includes the SOFA score as a general predictor of illness severity, using MIMIC-III demo data. Furthermore, for the sake of this example, the patient cohort is defined to be patients admitted from 2008 onwards (corresponding to the MetaVision database) of ages 20 to 90 years old. - -```{r cox-surv, eval = srcs_avail("mimic_demo")} -src <- "mimic_demo" - -cohort <- load_id("icustays", src, dbsource == "metavision", - cols = NULL) -cohort <- load_concepts("age", src, patient_ids = cohort, - verbose = FALSE) - -dat <- load_concepts(c("lact", "death", "sofa"), src, - patient_ids = cohort[age > 20 & age < 90, ], - verbose = FALSE) - -dat <- dat[, - head(.SD, n = match(TRUE, death, .N)), by = c(id_vars(dat)) -] - -dat <- fill_gaps(dat) - -dat <- replace_na(dat, c(NA, FALSE), type = c("locf", "const"), - by_ref = TRUE, vars = c("lact", "death"), - by = id_vars(dat)) - -cox_mod <- coxph( - Surv(charttime - 1L, charttime, death) ~ lact + sofa, - data = dat -) -``` - -After loading the data, some minor preprocessing is still required before modeling: first, data is filtered such that only data up to (and including) the hour in which the `death` flag switches to `TRUE` is used. Following that, missing values for `lact` are imputed using a last observation carry forward (LOCF) scheme (observing the patient grouping) and missing `death` values are set to `FALSE`. The resulting model fit can be visualized as: - -```{r cox-plot, eval = srcs_avail(src), echo = FALSE, warning = FALSE, message = FALSE, fig.width = 8} -theme_fp <- function(...) { - theme_bw(...) + - theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), - axis.title.y = element_blank(), axis.title.x = element_blank(), - axis.text.y = element_blank(), axis.ticks.y = element_blank()) -} - -forest_model(cox_mod, theme = theme_fp(16)) -``` - -A simple exploration already shows that the increased values of lactate are associated with mortality, even after adjusting for the SOFA score. Using abstractions provided by \pkg{ricu}, this analysis could now also be applied to other datasets with minimal effort. - -## Diabetes and insulin treatment - -For the next example, again using MIMIC-III demo data, comorbidities and treatment related information are used: the amount of insulin administered to patients in the first 24 hours from their ICU admission is analyzed, in connection with diabetic status, in order to determine whether diabetic patients receive more insulin over that time-span, when compared to non-diabetic patients. For this, two concepts are introduced: `ins24`, a binned variable representing the cumulative amount of insulin administered within the first 24 hours of an ICU admission, and `diab`, a logical variable encoding diabetes comorbidity. - -As there already is an insulin concept available, `ins24` can be implemented as `rec_cncpt`, requesting data from the `ins` concept. In order to be able to calculate the total amount of insulin administered, it is required to change the default aggregation method from `median()` to `sum()`. Failing to do so would yield under-reported values whenever several insulin administrations fall within a given time-step. The callback function `ins_cb()` is then inserted into the loading process, performing of the preprocessing steps outlined above: first data is subsetted to fall into the first 24 hours of ICU admissions, followed by binning of summed values. - -```{r ins24, eval = srcs_avail(src)} -ins_breaks <- c(0, 1, 10, 20, 40, Inf) - -ins_cb <- function(ins, ...) { - - day_one <- function(x) x >= hours(0L) & x <= hours(24L) - - idx_var <- index_var(ins) - ids_var <- id_vars(ins) - - ins <- ins[ - day_one(get(idx_var)), list(ins24 = sum(ins)), by = c(ids_var) - ] - - ins <- ins[, - ins24 := list(cut(ins24, breaks = ins_breaks, right = FALSE)) - ] - - ins -} - -ins24 <- load_dictionary(src, "ins") -ins24 <- concept("ins24", ins24, "insulin in first 24h", - aggregate = "sum", callback = ins_cb, - target = "id_tbl", class = "rec_cncpt") -``` - -The binary diabetes concept can be implemented as `lgl_cncpt`, for which ICD-9 codes are matched using a regular expression. As not only the subset of diabetic patients is of interest, a `col_itm` is more suited for diabetes status retrieval over a `rgx_itm`. For creating the required callback function, which produces a logical vector, the exported function factory `transform_fun()` can be employed, coupled with a function like `grep_diab()`, performing the desired transformation. The two concepts are then combined using `c()` and loaded via `load_concepts()`. - -```{r diab, eval = srcs_avail(src)} -grep_diab <- function(x) { - grepl("^250\\.?[0-9]{2}$", x) -} - -diab <- item(src, table = "diagnoses_icd", - callback = transform_fun(grep_diab), - class = "col_itm") - -diab <- concept("diab", diab, "diabetes", target = "id_tbl", - class = "lgl_cncpt") - -dat <- load_concepts(c(ins24, diab), id_type = "icustay", - verbose = FALSE) -dat <- replace_na(dat, "[0,1)", vars = "ins24") - -dat -``` - -Following this, the difference between the two groups can be visualized with a histogram over the binned insulin administration values: - -```{r diabetes-visualize, echo = FALSE, eval = srcs_avail(src), fig.height = 3} -dat <- dat[, weight := 1 / .N, by = diab] -ggplot(dat, aes(x = ins24, fill = diab)) + - stat_count(aes(weight = weight), alpha = 0.75, position = "dodge") + - labs(x = "Amount of administered insulin in first 24h of ICU stay [units]", - y = "Proportion of patients", - fill = "Diabetic") + - theme_bw(10) -``` - -The plot suggests that for the MetaVision cohort defined in the previous example (without age subsetting) and during the first day of ICU stay, perhaps unsurprisingly, with increasing insulin dosage, diabetic patients receive more insulin compared to non-diabetic patients. This effect is more pronounced when looking at the full MIMIC-III data instead of the demo subset which includes only data corresponding to roughly 130 ICU stays. - -# Acknowledgments - -Nicolas Bennett, Drago Plečko, Nicolai Meinshausen and Peter Bühlmann were supported by grant #2017-110 of the Strategic Focal Area "Personalized Health and Related Technologies (PHRT)" of the ETH Domain for the SPHN/PHRT Driver Project "Personalized Swiss Sepsis Study". - -```{r session-info, include = FALSE} -sessionInfo() -``` diff --git a/vignettes/jss.bst b/vignettes/jss.bst deleted file mode 100644 index ae754154..00000000 --- a/vignettes/jss.bst +++ /dev/null @@ -1,1653 +0,0 @@ -%% -%% This is file `jss.bst', -%% generated with the docstrip utility. -%% -%% The original source files were: -%% -%% merlin.mbs (with options: `ay,nat,nm-rvx,keyxyr,dt-beg,yr-par,note-yr,tit-qq,atit-u,trnum-it,vol-bf,volp-com,num-xser,pre-edn,isbn,issn,edpar,pp,ed,xedn,xand,etal-it,revdata,eprint,url,url-blk,doi,nfss') -%% -%% ** BibTeX style file for JSS publications (http://www.jstatsoft.org/) -%% -%% License: GPL-2 | GPL-3 - % =============================================================== - % IMPORTANT NOTICE: - % This bibliographic style (bst) file has been generated from one or - % more master bibliographic style (mbs) files, listed above, provided - % with kind permission of Patrick W Daly. - % - % This generated file can be redistributed and/or modified under the terms - % of the General Public License (Version 2 or 3). - % =============================================================== - % Name and version information of the main mbs file: - % \ProvidesFile{merlin.mbs}[2011/11/18 4.33 (PWD, AO, DPC)] - % For use with BibTeX version 0.99a or later - %------------------------------------------------------------------- - % This bibliography style file is intended for texts in ENGLISH - % This is an author-year citation style bibliography. As such, it is - % non-standard LaTeX, and requires a special package file to function properly. - % Such a package is natbib.sty by Patrick W. Daly - % The form of the \bibitem entries is - % \bibitem[Jones et al.(1990)]{key}... - % \bibitem[Jones et al.(1990)Jones, Baker, and Smith]{key}... - % The essential feature is that the label (the part in brackets) consists - % of the author names, as they should appear in the citation, with the year - % in parentheses following. There must be no space before the opening - % parenthesis! - % With natbib v5.3, a full list of authors may also follow the year. - % In natbib.sty, it is possible to define the type of enclosures that is - % really wanted (brackets or parentheses), but in either case, there must - % be parentheses in the label. - % The \cite command functions as follows: - % \citet{key} ==>> Jones et al. (1990) - % \citet*{key} ==>> Jones, Baker, and Smith (1990) - % \citep{key} ==>> (Jones et al., 1990) - % \citep*{key} ==>> (Jones, Baker, and Smith, 1990) - % \citep[chap. 2]{key} ==>> (Jones et al., 1990, chap. 2) - % \citep[e.g.][]{key} ==>> (e.g. Jones et al., 1990) - % \citep[e.g.][p. 32]{key} ==>> (e.g. Jones et al., 1990, p. 32) - % \citeauthor{key} ==>> Jones et al. - % \citeauthor*{key} ==>> Jones, Baker, and Smith - % \citeyear{key} ==>> 1990 - %--------------------------------------------------------------------- - -ENTRY - { address - archive - author - booktitle - chapter - collaboration - doi - edition - editor - eid - eprint - howpublished - institution - isbn - issn - journal - key - month - note - number - numpages - organization - pages - publisher - school - series - title - type - url - volume - year - } - {} - { label extra.label sort.label short.list } -INTEGERS { output.state before.all mid.sentence after.sentence after.block } -FUNCTION {init.state.consts} -{ #0 'before.all := - #1 'mid.sentence := - #2 'after.sentence := - #3 'after.block := -} -STRINGS { s t} -FUNCTION {output.nonnull} -{ 's := - output.state mid.sentence = - { ", " * write$ } - { output.state after.block = - { add.period$ write$ - newline$ - "\newblock " write$ - } - { output.state before.all = - 'write$ - { add.period$ " " * write$ } - if$ - } - if$ - mid.sentence 'output.state := - } - if$ - s -} -FUNCTION {output} -{ duplicate$ empty$ - 'pop$ - 'output.nonnull - if$ -} -FUNCTION {output.check} -{ 't := - duplicate$ empty$ - { pop$ "empty " t * " in " * cite$ * warning$ } - 'output.nonnull - if$ -} -FUNCTION {fin.entry} -{ add.period$ - write$ - newline$ -} - -FUNCTION {new.block} -{ output.state before.all = - 'skip$ - { after.block 'output.state := } - if$ -} -FUNCTION {new.sentence} -{ output.state after.block = - 'skip$ - { output.state before.all = - 'skip$ - { after.sentence 'output.state := } - if$ - } - if$ -} -FUNCTION {add.blank} -{ " " * before.all 'output.state := -} - -FUNCTION {date.block} -{ - new.block -} - -FUNCTION {not} -{ { #0 } - { #1 } - if$ -} -FUNCTION {and} -{ 'skip$ - { pop$ #0 } - if$ -} -FUNCTION {or} -{ { pop$ #1 } - 'skip$ - if$ -} -FUNCTION {non.stop} -{ duplicate$ - "}" * add.period$ - #-1 #1 substring$ "." = -} - -STRINGS {z} - -FUNCTION {remove.dots} -{ 'z := - "" - { z empty$ not } - { z #1 #2 substring$ - duplicate$ "\." = - { z #3 global.max$ substring$ 'z := * } - { pop$ - z #1 #1 substring$ - z #2 global.max$ substring$ 'z := - duplicate$ "." = 'pop$ - { * } - if$ - } - if$ - } - while$ -} -FUNCTION {new.block.checkb} -{ empty$ - swap$ empty$ - and - 'skip$ - 'new.block - if$ -} -FUNCTION {field.or.null} -{ duplicate$ empty$ - { pop$ "" } - 'skip$ - if$ -} -FUNCTION {emphasize} -{ duplicate$ empty$ - { pop$ "" } - { "\emph{" swap$ * "}" * } - if$ -} -FUNCTION {bolden} -{ duplicate$ empty$ - { pop$ "" } - { "\textbf{" swap$ * "}" * } - if$ -} -FUNCTION {tie.or.space.prefix} -{ duplicate$ text.length$ #3 < - { "~" } - { " " } - if$ - swap$ -} - -FUNCTION {capitalize} -{ "u" change.case$ "t" change.case$ } - -FUNCTION {space.word} -{ " " swap$ * " " * } - % Here are the language-specific definitions for explicit words. - % Each function has a name bbl.xxx where xxx is the English word. - % The language selected here is ENGLISH -FUNCTION {bbl.and} -{ "and"} - -FUNCTION {bbl.etal} -{ "et~al." } - -FUNCTION {bbl.editors} -{ "eds." } - -FUNCTION {bbl.editor} -{ "ed." } - -FUNCTION {bbl.edby} -{ "edited by" } - -FUNCTION {bbl.edition} -{ "edition" } - -FUNCTION {bbl.volume} -{ "volume" } - -FUNCTION {bbl.of} -{ "of" } - -FUNCTION {bbl.number} -{ "number" } - -FUNCTION {bbl.nr} -{ "no." } - -FUNCTION {bbl.in} -{ "in" } - -FUNCTION {bbl.pages} -{ "pp." } - -FUNCTION {bbl.page} -{ "p." } - -FUNCTION {bbl.eidpp} -{ "pages" } - -FUNCTION {bbl.chapter} -{ "chapter" } - -FUNCTION {bbl.techrep} -{ "Technical Report" } - -FUNCTION {bbl.mthesis} -{ "Master's thesis" } - -FUNCTION {bbl.phdthesis} -{ "Ph.D. thesis" } - -MACRO {jan} {"January"} - -MACRO {feb} {"February"} - -MACRO {mar} {"March"} - -MACRO {apr} {"April"} - -MACRO {may} {"May"} - -MACRO {jun} {"June"} - -MACRO {jul} {"July"} - -MACRO {aug} {"August"} - -MACRO {sep} {"September"} - -MACRO {oct} {"October"} - -MACRO {nov} {"November"} - -MACRO {dec} {"December"} - -MACRO {acmcs} {"ACM Computing Surveys"} - -MACRO {acta} {"Acta Informatica"} - -MACRO {cacm} {"Communications of the ACM"} - -MACRO {ibmjrd} {"IBM Journal of Research and Development"} - -MACRO {ibmsj} {"IBM Systems Journal"} - -MACRO {ieeese} {"IEEE Transactions on Software Engineering"} - -MACRO {ieeetc} {"IEEE Transactions on Computers"} - -MACRO {ieeetcad} - {"IEEE Transactions on Computer-Aided Design of Integrated Circuits"} - -MACRO {ipl} {"Information Processing Letters"} - -MACRO {jacm} {"Journal of the ACM"} - -MACRO {jcss} {"Journal of Computer and System Sciences"} - -MACRO {scp} {"Science of Computer Programming"} - -MACRO {sicomp} {"SIAM Journal on Computing"} - -MACRO {tocs} {"ACM Transactions on Computer Systems"} - -MACRO {tods} {"ACM Transactions on Database Systems"} - -MACRO {tog} {"ACM Transactions on Graphics"} - -MACRO {toms} {"ACM Transactions on Mathematical Software"} - -MACRO {toois} {"ACM Transactions on Office Information Systems"} - -MACRO {toplas} {"ACM Transactions on Programming Languages and Systems"} - -MACRO {tcs} {"Theoretical Computer Science"} -FUNCTION {bibinfo.check} -{ swap$ - duplicate$ missing$ - { - pop$ pop$ - "" - } - { duplicate$ empty$ - { - swap$ pop$ - } - { swap$ - pop$ - } - if$ - } - if$ -} -FUNCTION {bibinfo.warn} -{ swap$ - duplicate$ missing$ - { - swap$ "missing " swap$ * " in " * cite$ * warning$ pop$ - "" - } - { duplicate$ empty$ - { - swap$ "empty " swap$ * " in " * cite$ * warning$ - } - { swap$ - pop$ - } - if$ - } - if$ -} -FUNCTION {format.eprint} -{ eprint duplicate$ empty$ - 'skip$ - { "\eprint" - archive empty$ - 'skip$ - { "[" * archive * "]" * } - if$ - "{" * swap$ * "}" * - } - if$ -} -FUNCTION {format.url} -{ - url - duplicate$ empty$ - { pop$ "" } - { "\urlprefix\url{" swap$ * "}" * } - if$ -} - -INTEGERS { nameptr namesleft numnames } - - -STRINGS { bibinfo} - -FUNCTION {format.names} -{ 'bibinfo := - duplicate$ empty$ 'skip$ { - 's := - "" 't := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr - "{vv~}{ll}{ jj}{ f{}}" - format.name$ - remove.dots - bibinfo bibinfo.check - 't := - nameptr #1 > - { - namesleft #1 > - { ", " * t * } - { - s nameptr "{ll}" format.name$ duplicate$ "others" = - { 't := } - { pop$ } - if$ - "," * - t "others" = - { - " " * bbl.etal emphasize * - } - { " " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ - } if$ -} -FUNCTION {format.names.ed} -{ - 'bibinfo := - duplicate$ empty$ 'skip$ { - 's := - "" 't := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr - "{f{}~}{vv~}{ll}{ jj}" - format.name$ - remove.dots - bibinfo bibinfo.check - 't := - nameptr #1 > - { - namesleft #1 > - { ", " * t * } - { - s nameptr "{ll}" format.name$ duplicate$ "others" = - { 't := } - { pop$ } - if$ - "," * - t "others" = - { - - " " * bbl.etal emphasize * - } - { " " * t * } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ - } if$ -} -FUNCTION {format.key} -{ empty$ - { key field.or.null } - { "" } - if$ -} - -FUNCTION {format.authors} -{ author "author" format.names - duplicate$ empty$ 'skip$ - { collaboration "collaboration" bibinfo.check - duplicate$ empty$ 'skip$ - { " (" swap$ * ")" * } - if$ - * - } - if$ -} -FUNCTION {get.bbl.editor} -{ editor num.names$ #1 > 'bbl.editors 'bbl.editor if$ } - -FUNCTION {format.editors} -{ editor "editor" format.names duplicate$ empty$ 'skip$ - { - " " * - get.bbl.editor - "(" swap$ * ")" * - * - } - if$ -} -FUNCTION {format.isbn} -{ isbn "isbn" bibinfo.check - duplicate$ empty$ 'skip$ - { - new.block - "ISBN " swap$ * - } - if$ -} - -FUNCTION {format.issn} -{ issn "issn" bibinfo.check - duplicate$ empty$ 'skip$ - { - new.block - "ISSN " swap$ * - } - if$ -} - -FUNCTION {format.doi} -{ doi empty$ - { "" } - { - new.block - "\doi{" doi * "}" * - } - if$ -} -FUNCTION {format.note} -{ - note empty$ - { "" } - { note #1 #1 substring$ - duplicate$ "{" = - 'skip$ - { output.state mid.sentence = - { "l" } - { "u" } - if$ - change.case$ - } - if$ - note #2 global.max$ substring$ * "note" bibinfo.check - } - if$ -} - -FUNCTION {format.title} -{ title - "title" bibinfo.check - duplicate$ empty$ 'skip$ - { - "\enquote{" swap$ * - add.period$ "}" * - } - if$ -} -FUNCTION {format.full.names} -{'s := - "" 't := - #1 'nameptr := - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr - "{vv~}{ll}" format.name$ - 't := - nameptr #1 > - { - namesleft #1 > - { ", " * t * } - { - s nameptr "{ll}" format.name$ duplicate$ "others" = - { 't := } - { pop$ } - if$ - t "others" = - { - " " * bbl.etal emphasize * - } - { - numnames #2 > - { "," * } - 'skip$ - if$ - bbl.and - space.word * t * - } - if$ - } - if$ - } - 't - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -FUNCTION {author.editor.key.full} -{ author empty$ - { editor empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { editor format.full.names } - if$ - } - { author format.full.names } - if$ -} - -FUNCTION {author.key.full} -{ author empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { author format.full.names } - if$ -} - -FUNCTION {editor.key.full} -{ editor empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { editor format.full.names } - if$ -} - -FUNCTION {make.full.names} -{ type$ "book" = - type$ "inbook" = - or - 'author.editor.key.full - { type$ "proceedings" = - 'editor.key.full - 'author.key.full - if$ - } - if$ -} - -FUNCTION {output.bibitem} -{ newline$ - "\bibitem[{" write$ - label write$ - ")" make.full.names duplicate$ short.list = - { pop$ } - { * } - if$ - "}]{" * write$ - cite$ write$ - "}" write$ - newline$ - "" - before.all 'output.state := -} - -FUNCTION {n.dashify} -{ - 't := - "" - { t empty$ not } - { t #1 #1 substring$ "-" = - { t #1 #2 substring$ "--" = not - { "--" * - t #2 global.max$ substring$ 't := - } - { { t #1 #1 substring$ "-" = } - { "-" * - t #2 global.max$ substring$ 't := - } - while$ - } - if$ - } - { t #1 #1 substring$ * - t #2 global.max$ substring$ 't := - } - if$ - } - while$ -} - -FUNCTION {word.in} -{ bbl.in capitalize - " " * } - -FUNCTION {format.date} -{ year "year" bibinfo.check duplicate$ empty$ - { - "empty year in " cite$ * "; set to ????" * warning$ - pop$ "????" - } - 'skip$ - if$ - extra.label * - before.all 'output.state := - " (" swap$ * ")" * -} -FUNCTION {format.btitle} -{ title "title" bibinfo.check - duplicate$ empty$ 'skip$ - { - emphasize - } - if$ -} -FUNCTION {either.or.check} -{ empty$ - 'pop$ - { "can't use both " swap$ * " fields in " * cite$ * warning$ } - if$ -} -FUNCTION {format.bvolume} -{ volume empty$ - { "" } - { bbl.volume volume tie.or.space.prefix - "volume" bibinfo.check * * - series "series" bibinfo.check - duplicate$ empty$ 'pop$ - { swap$ bbl.of space.word * swap$ - emphasize * } - if$ - "volume and number" number either.or.check - } - if$ -} -FUNCTION {format.number.series} -{ volume empty$ - { number empty$ - { series field.or.null } - { series empty$ - { number "number" bibinfo.check } - { output.state mid.sentence = - { bbl.number } - { bbl.number capitalize } - if$ - number tie.or.space.prefix "number" bibinfo.check * * - bbl.in space.word * - series "series" bibinfo.check * - } - if$ - } - if$ - } - { "" } - if$ -} - -FUNCTION {format.edition} -{ edition duplicate$ empty$ 'skip$ - { - output.state mid.sentence = - { "l" } - { "t" } - if$ change.case$ - "edition" bibinfo.check - " " * bbl.edition * - } - if$ -} -INTEGERS { multiresult } -FUNCTION {multi.page.check} -{ 't := - #0 'multiresult := - { multiresult not - t empty$ not - and - } - { t #1 #1 substring$ - duplicate$ "-" = - swap$ duplicate$ "," = - swap$ "+" = - or or - { #1 'multiresult := } - { t #2 global.max$ substring$ 't := } - if$ - } - while$ - multiresult -} -FUNCTION {format.pages} -{ pages duplicate$ empty$ 'skip$ - { duplicate$ multi.page.check - { - bbl.pages swap$ - n.dashify - } - { - bbl.page swap$ - } - if$ - tie.or.space.prefix - "pages" bibinfo.check - * * - } - if$ -} -FUNCTION {format.journal.pages} -{ pages duplicate$ empty$ 'pop$ - { swap$ duplicate$ empty$ - { pop$ pop$ format.pages } - { - ", " * - swap$ - n.dashify - "pages" bibinfo.check - * - } - if$ - } - if$ -} -FUNCTION {format.journal.eid} -{ eid "eid" bibinfo.check - duplicate$ empty$ 'pop$ - { swap$ duplicate$ empty$ 'skip$ - { - ", " * - } - if$ - swap$ * - numpages empty$ 'skip$ - { bbl.eidpp numpages tie.or.space.prefix - "numpages" bibinfo.check * * - " (" swap$ * ")" * * - } - if$ - } - if$ -} -FUNCTION {format.vol.num.pages} -{ volume field.or.null - duplicate$ empty$ 'skip$ - { - "volume" bibinfo.check - } - if$ - bolden - number "number" bibinfo.check duplicate$ empty$ 'skip$ - { - swap$ duplicate$ empty$ - { "there's a number but no volume in " cite$ * warning$ } - 'skip$ - if$ - swap$ - "(" swap$ * ")" * - } - if$ * - eid empty$ - { format.journal.pages } - { format.journal.eid } - if$ -} - -FUNCTION {format.chapter.pages} -{ chapter empty$ - 'format.pages - { type empty$ - { bbl.chapter } - { type "l" change.case$ - "type" bibinfo.check - } - if$ - chapter tie.or.space.prefix - "chapter" bibinfo.check - * * - pages empty$ - 'skip$ - { ", " * format.pages * } - if$ - } - if$ -} - -FUNCTION {format.booktitle} -{ - booktitle "booktitle" bibinfo.check - emphasize -} -FUNCTION {format.in.ed.booktitle} -{ format.booktitle duplicate$ empty$ 'skip$ - { - editor "editor" format.names.ed duplicate$ empty$ 'pop$ - { - " " * - get.bbl.editor - "(" swap$ * "), " * - * swap$ - * } - if$ - word.in swap$ * - } - if$ -} -FUNCTION {format.thesis.type} -{ type duplicate$ empty$ - 'pop$ - { swap$ pop$ - "t" change.case$ "type" bibinfo.check - } - if$ -} -FUNCTION {format.tr.number} -{ number "number" bibinfo.check - type duplicate$ empty$ - { pop$ bbl.techrep } - 'skip$ - if$ - "type" bibinfo.check - swap$ duplicate$ empty$ - { pop$ "t" change.case$ } - { tie.or.space.prefix * * } - if$ -} -FUNCTION {format.article.crossref} -{ - word.in - " \cite{" * crossref * "}" * -} -FUNCTION {format.book.crossref} -{ volume duplicate$ empty$ - { "empty volume in " cite$ * "'s crossref of " * crossref * warning$ - pop$ word.in - } - { bbl.volume - capitalize - swap$ tie.or.space.prefix "volume" bibinfo.check * * bbl.of space.word * - } - if$ - " \cite{" * crossref * "}" * -} -FUNCTION {format.incoll.inproc.crossref} -{ - word.in - " \cite{" * crossref * "}" * -} -FUNCTION {format.org.or.pub} -{ 't := - "" - address empty$ t empty$ and - 'skip$ - { - t empty$ - { address "address" bibinfo.check * - } - { t * - address empty$ - 'skip$ - { ", " * address "address" bibinfo.check * } - if$ - } - if$ - } - if$ -} -FUNCTION {format.publisher.address} -{ publisher "publisher" bibinfo.warn format.org.or.pub -} - -FUNCTION {format.organization.address} -{ organization "organization" bibinfo.check format.org.or.pub -} - -FUNCTION {article} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.title "title" output.check - new.block - crossref missing$ - { - journal - "journal" bibinfo.check - emphasize - "journal" output.check - format.vol.num.pages output - } - { format.article.crossref output.nonnull - format.pages output - } - if$ - format.issn output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} -FUNCTION {book} -{ output.bibitem - author empty$ - { format.editors "author and editor" output.check - editor format.key output - } - { format.authors output.nonnull - crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - format.date "year" output.check - date.block - format.btitle "title" output.check - crossref missing$ - { format.bvolume output - new.block - format.number.series output - format.edition output - new.sentence - format.publisher.address output - } - { - new.block - format.book.crossref output.nonnull - } - if$ - format.isbn output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} -FUNCTION {booklet} -{ output.bibitem - format.authors output - author format.key output - format.date "year" output.check - date.block - format.title "title" output.check - new.block - howpublished "howpublished" bibinfo.check output - address "address" bibinfo.check output - format.isbn output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {inbook} -{ output.bibitem - author empty$ - { format.editors "author and editor" output.check - editor format.key output - } - { format.authors output.nonnull - crossref missing$ - { "author and editor" editor either.or.check } - 'skip$ - if$ - } - if$ - format.date "year" output.check - date.block - format.btitle "title" output.check - crossref missing$ - { - format.bvolume output - format.chapter.pages "chapter and pages" output.check - new.block - format.number.series output - format.edition output - new.sentence - format.publisher.address output - } - { - format.chapter.pages "chapter and pages" output.check - new.block - format.book.crossref output.nonnull - } - if$ - crossref missing$ - { format.isbn output } - 'skip$ - if$ - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {incollection} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.title "title" output.check - new.block - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.bvolume output - format.number.series output - format.edition output - format.chapter.pages output - new.sentence - format.publisher.address output - format.isbn output - } - { format.incoll.inproc.crossref output.nonnull - format.chapter.pages output - } - if$ - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} -FUNCTION {inproceedings} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.title "title" output.check - new.block - crossref missing$ - { format.in.ed.booktitle "booktitle" output.check - format.bvolume output - format.number.series output - format.pages output - new.sentence - publisher empty$ - { format.organization.address output } - { organization "organization" bibinfo.check output - format.publisher.address output - } - if$ - format.isbn output - format.issn output - } - { format.incoll.inproc.crossref output.nonnull - format.pages output - } - if$ - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} -FUNCTION {conference} { inproceedings } -FUNCTION {manual} -{ output.bibitem - format.authors output - author format.key output - format.date "year" output.check - date.block - format.btitle "title" output.check - organization address new.block.checkb - organization "organization" bibinfo.check output - address "address" bibinfo.check output - format.edition output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {mastersthesis} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.btitle - "title" output.check - new.block - bbl.mthesis format.thesis.type output.nonnull - school "school" bibinfo.warn output - address "address" bibinfo.check output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {misc} -{ output.bibitem - format.authors output - author format.key output - format.date "year" output.check - date.block - format.title output - new.block - howpublished "howpublished" bibinfo.check output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} -FUNCTION {phdthesis} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.btitle - "title" output.check - new.block - bbl.phdthesis format.thesis.type output.nonnull - school "school" bibinfo.warn output - address "address" bibinfo.check output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {proceedings} -{ output.bibitem - format.editors output - editor format.key output - format.date "year" output.check - date.block - format.btitle "title" output.check - format.bvolume output - format.number.series output - new.sentence - publisher empty$ - { format.organization.address output } - { organization "organization" bibinfo.check output - format.publisher.address output - } - if$ - format.isbn output - format.issn output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {techreport} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.title - "title" output.check - new.block - format.tr.number emphasize output.nonnull - institution "institution" bibinfo.warn output - address "address" bibinfo.check output - format.doi output - new.block - format.note output - format.eprint output - format.url output - fin.entry -} - -FUNCTION {unpublished} -{ output.bibitem - format.authors "author" output.check - author format.key output - format.date "year" output.check - date.block - format.title "title" output.check - format.doi output - new.block - format.note "note" output.check - format.eprint output - format.url output - fin.entry -} - -FUNCTION {default.type} { misc } -READ -FUNCTION {sortify} -{ purify$ - "l" change.case$ -} -INTEGERS { len } -FUNCTION {chop.word} -{ 's := - 'len := - s #1 len substring$ = - { s len #1 + global.max$ substring$ } - 's - if$ -} -FUNCTION {format.lab.names} -{ 's := - "" 't := - s #1 "{vv~}{ll}" format.name$ - s num.names$ duplicate$ - #2 > - { pop$ - " " * bbl.etal emphasize * - } - { #2 < - 'skip$ - { s #2 "{ff }{vv }{ll}{ jj}" format.name$ "others" = - { - " " * bbl.etal emphasize * - } - { bbl.and space.word * s #2 "{vv~}{ll}" format.name$ - * } - if$ - } - if$ - } - if$ -} - -FUNCTION {author.key.label} -{ author empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { author format.lab.names } - if$ -} - -FUNCTION {author.editor.key.label} -{ author empty$ - { editor empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { editor format.lab.names } - if$ - } - { author format.lab.names } - if$ -} - -FUNCTION {editor.key.label} -{ editor empty$ - { key empty$ - { cite$ #1 #3 substring$ } - 'key - if$ - } - { editor format.lab.names } - if$ -} - -FUNCTION {calc.short.authors} -{ type$ "book" = - type$ "inbook" = - or - 'author.editor.key.label - { type$ "proceedings" = - 'editor.key.label - 'author.key.label - if$ - } - if$ - 'short.list := -} - -FUNCTION {calc.label} -{ calc.short.authors - short.list - "(" - * - year duplicate$ empty$ - short.list key field.or.null = or - { pop$ "" } - 'skip$ - if$ - * - 'label := -} - -FUNCTION {sort.format.names} -{ 's := - #1 'nameptr := - "" - s num.names$ 'numnames := - numnames 'namesleft := - { namesleft #0 > } - { s nameptr - "{vv{ } }{ll{ }}{ f{ }}{ jj{ }}" - format.name$ 't := - nameptr #1 > - { - " " * - namesleft #1 = t "others" = and - { "zzzzz" 't := } - 'skip$ - if$ - t sortify * - } - { t sortify * } - if$ - nameptr #1 + 'nameptr := - namesleft #1 - 'namesleft := - } - while$ -} - -FUNCTION {sort.format.title} -{ 't := - "A " #2 - "An " #3 - "The " #4 t chop.word - chop.word - chop.word - sortify - #1 global.max$ substring$ -} -FUNCTION {author.sort} -{ author empty$ - { key empty$ - { "to sort, need author or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { author sort.format.names } - if$ -} -FUNCTION {author.editor.sort} -{ author empty$ - { editor empty$ - { key empty$ - { "to sort, need author, editor, or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { editor sort.format.names } - if$ - } - { author sort.format.names } - if$ -} -FUNCTION {editor.sort} -{ editor empty$ - { key empty$ - { "to sort, need editor or key in " cite$ * warning$ - "" - } - { key sortify } - if$ - } - { editor sort.format.names } - if$ -} -FUNCTION {presort} -{ calc.label - label sortify - " " - * - type$ "book" = - type$ "inbook" = - or - 'author.editor.sort - { type$ "proceedings" = - 'editor.sort - 'author.sort - if$ - } - if$ - #1 entry.max$ substring$ - 'sort.label := - sort.label - * - " " - * - title field.or.null - sort.format.title - * - #1 entry.max$ substring$ - 'sort.key$ := -} - -ITERATE {presort} -SORT -STRINGS { last.label next.extra } -INTEGERS { last.extra.num last.extra.num.extended last.extra.num.blank number.label } -FUNCTION {initialize.extra.label.stuff} -{ #0 int.to.chr$ 'last.label := - "" 'next.extra := - #0 'last.extra.num := - "a" chr.to.int$ #1 - 'last.extra.num.blank := - last.extra.num.blank 'last.extra.num.extended := - #0 'number.label := -} -FUNCTION {forward.pass} -{ last.label label = - { last.extra.num #1 + 'last.extra.num := - last.extra.num "z" chr.to.int$ > - { "a" chr.to.int$ 'last.extra.num := - last.extra.num.extended #1 + 'last.extra.num.extended := - } - 'skip$ - if$ - last.extra.num.extended last.extra.num.blank > - { last.extra.num.extended int.to.chr$ - last.extra.num int.to.chr$ - * 'extra.label := } - { last.extra.num int.to.chr$ 'extra.label := } - if$ - } - { "a" chr.to.int$ 'last.extra.num := - "" 'extra.label := - label 'last.label := - } - if$ - number.label #1 + 'number.label := -} -FUNCTION {reverse.pass} -{ next.extra "b" = - { "a" 'extra.label := } - 'skip$ - if$ - extra.label 'next.extra := - extra.label - duplicate$ empty$ - 'skip$ - { "{\natexlab{" swap$ * "}}" * } - if$ - 'extra.label := - label extra.label * 'label := -} -EXECUTE {initialize.extra.label.stuff} -ITERATE {forward.pass} -REVERSE {reverse.pass} -FUNCTION {bib.sort.order} -{ sort.label - " " - * - year field.or.null sortify - * - " " - * - title field.or.null - sort.format.title - * - #1 entry.max$ substring$ - 'sort.key$ := -} -ITERATE {bib.sort.order} -SORT -FUNCTION {begin.bib} -{ preamble$ empty$ - 'skip$ - { preamble$ write$ newline$ } - if$ - "\begin{thebibliography}{" number.label int.to.str$ * "}" * - write$ newline$ - "\newcommand{\enquote}[1]{``#1''}" - write$ newline$ - "\providecommand{\natexlab}[1]{#1}" - write$ newline$ - "\providecommand{\url}[1]{\texttt{#1}}" - write$ newline$ - "\providecommand{\urlprefix}{URL }" - write$ newline$ - "\expandafter\ifx\csname urlstyle\endcsname\relax" - write$ newline$ - " \providecommand{\doi}[1]{doi:\discretionary{}{}{}#1}\else" - write$ newline$ - " \providecommand{\doi}{doi:\discretionary{}{}{}\begingroup \urlstyle{rm}\Url}\fi" - write$ newline$ - "\providecommand{\eprint}[2][]{\url{#2}}" - write$ newline$ -} -EXECUTE {begin.bib} -EXECUTE {init.state.consts} -ITERATE {call.type$} -FUNCTION {end.bib} -{ newline$ - "\end{thebibliography}" write$ newline$ -} -EXECUTE {end.bib} -%% End of customized bst file -%% -%% End of file `jss.bst'. diff --git a/vignettes/jss.cls b/vignettes/jss.cls deleted file mode 100644 index 0a33291c..00000000 --- a/vignettes/jss.cls +++ /dev/null @@ -1,488 +0,0 @@ -\def\fileversion{3.2} -\def\filename{jss} -\def\filedate{2020/12/09} -%% -%% Package `jss' to use with LaTeX2e for JSS publications (http://www.jstatsoft.org/) -%% License: GPL-2 | GPL-3 -%% Copyright: (C) Achim Zeileis -%% Please report errors to Achim.Zeileis@R-project.org -%% -\NeedsTeXFormat{LaTeX2e} -\ProvidesClass{jss}[\filedate\space\fileversion\space jss class by Achim Zeileis] -%% options -\newif\if@article -\newif\if@codesnippet -\newif\if@bookreview -\newif\if@softwarereview -\newif\if@review -\newif\if@shortnames -\newif\if@nojss -\newif\if@notitle -\newif\if@noheadings -\newif\if@nofooter - -\@articletrue -\@codesnippetfalse -\@bookreviewfalse -\@softwarereviewfalse -\@reviewfalse -\@shortnamesfalse -\@nojssfalse -\@notitlefalse -\@noheadingsfalse -\@nofooterfalse - -\DeclareOption{article}{\@articletrue% - \@codesnippetfalse \@bookreviewfalse \@softwarereviewfalse} -\DeclareOption{codesnippet}{\@articlefalse% - \@codesnippettrue \@bookreviewfalse \@softwarereviewfalse} -\DeclareOption{bookreview}{\@articlefalse% - \@codesnippetfalse \@bookreviewtrue \@softwarereviewfalse} -\DeclareOption{softwarereview}{\@articlefalse% - \@codesnippetfalse \@bookreviewfalse \@softwarereviewtrue} -\DeclareOption{shortnames}{\@shortnamestrue} -\DeclareOption{nojss}{\@nojsstrue} -\DeclareOption{notitle}{\@notitletrue} -\DeclareOption{noheadings}{\@noheadingstrue} -\DeclareOption{nofooter}{\@nofootertrue} - -\ProcessOptions -\LoadClass[11pt,a4paper,twoside]{article} -%% required packages -\RequirePackage{graphicx,color,ae,fancyvrb} -\RequirePackage[T1]{fontenc} -\IfFileExists{upquote.sty}{\RequirePackage{upquote}}{} -\IfFileExists{lmodern.sty}{\RequirePackage{lmodern}}{} -%% bibliography -\if@shortnames - \usepackage[authoryear,round]{natbib} -\else - \usepackage[authoryear,round,longnamesfirst]{natbib} -\fi -\bibpunct{(}{)}{;}{a}{}{,} -\bibliographystyle{jss} -%% page layout -\topmargin 0pt -\textheight 46\baselineskip -\advance\textheight by \topskip -\oddsidemargin 0.1in -\evensidemargin 0.15in -\marginparwidth 1in -\oddsidemargin 0.125in -\evensidemargin 0.125in -\marginparwidth 0.75in -\textwidth 6.125in -%% paragraphs -\setlength{\parskip}{0.7ex plus0.1ex minus0.1ex} -\setlength{\parindent}{0em} -%% for all publications -\newcommand{\Address}[1]{\def\@Address{#1}} -\newcommand{\Plaintitle}[1]{\def\@Plaintitle{#1}} -\newcommand{\Shorttitle}[1]{\def\@Shorttitle{#1}} -\newcommand{\Plainauthor}[1]{\def\@Plainauthor{#1}} -\newcommand{\Volume}[1]{\def\@Volume{#1}} -\newcommand{\Year}[1]{\def\@Year{#1}} -\newcommand{\Month}[1]{\def\@Month{#1}} -\newcommand{\Issue}[1]{\def\@Issue{#1}} -\newcommand{\Submitdate}[1]{\def\@Submitdate{#1}} -%% for articles and code snippets -\newcommand{\Acceptdate}[1]{\def\@Acceptdate{#1}} -\newcommand{\Abstract}[1]{\def\@Abstract{#1}} -\newcommand{\Keywords}[1]{\def\@Keywords{#1}} -\newcommand{\Plainkeywords}[1]{\def\@Plainkeywords{#1}} -%% for book and software reviews -\newcommand{\Reviewer}[1]{\def\@Reviewer{#1}} -\newcommand{\Booktitle}[1]{\def\@Booktitle{#1}} -\newcommand{\Bookauthor}[1]{\def\@Bookauthor{#1}} -\newcommand{\Publisher}[1]{\def\@Publisher{#1}} -\newcommand{\Pubaddress}[1]{\def\@Pubaddress{#1}} -\newcommand{\Pubyear}[1]{\def\@Pubyear{#1}} -\newcommand{\ISBN}[1]{\def\@ISBN{#1}} -\newcommand{\Pages}[1]{\def\@Pages{#1}} -\newcommand{\Price}[1]{\def\@Price{#1}} -\newcommand{\Plainreviewer}[1]{\def\@Plainreviewer{#1}} -\newcommand{\Softwaretitle}[1]{\def\@Softwaretitle{#1}} -\newcommand{\URL}[1]{\def\@URL{#1}} -\newcommand{\DOI}[1]{\def\@DOI{#1}} -%% for internal use -\newcommand{\Seriesname}[1]{\def\@Seriesname{#1}} -\newcommand{\Hypersubject}[1]{\def\@Hypersubject{#1}} -\newcommand{\Hyperauthor}[1]{\def\@Hyperauthor{#1}} -\newcommand{\Footername}[1]{\def\@Footername{#1}} -\newcommand{\Firstdate}[1]{\def\@Firstdate{#1}} -\newcommand{\Seconddate}[1]{\def\@Seconddate{#1}} -\newcommand{\Reviewauthor}[1]{\def\@Reviewauthor{#1}} -%% defaults -\author{Firstname Lastname\\Affiliation} -\title{Title} -\Abstract{---!!!---an abstract is required---!!!---} -\Plainauthor{\@author} -\Volume{VV} -\Year{YYYY} -\Month{MMMMMM} -\Issue{II} -\Submitdate{yyyy-mm-dd} -\Acceptdate{yyyy-mm-dd} -\Address{ - Firstname Lastname\\ - Affiliation\\ - Address, Country\\ - E-mail: \email{name@address}\\ - URL: \url{http://link/to/webpage/} -} - -\Reviewer{Firstname Lastname\\Affiliation} -\Plainreviewer{Firstname Lastname} -\Booktitle{Book Title} -\Bookauthor{Book Author} -\Publisher{Publisher} -\Pubaddress{Publisher's Address} -\Pubyear{YYY} -\ISBN{x-xxxxx-xxx-x} -\Pages{xv + 123} -\Price{USD 69.95 (P)} -\URL{http://link/to/webpage/} -\DOI{10.18637/jss.v000.i00} -\if@article - \Seriesname{Issue} - \Hypersubject{Journal of Statistical Software} - \Plaintitle{\@title} - \Shorttitle{\@title} - \Plainkeywords{\@Keywords} -\fi - -\if@codesnippet - \Seriesname{Code Snippet} - \Hypersubject{Journal of Statistical Software -- Code Snippets} - \Plaintitle{\@title} - \Shorttitle{\@title} - \Plainkeywords{\@Keywords} -\fi - -\if@bookreview - \Seriesname{Book Review} - \Hypersubject{Journal of Statistical Software -- Book Reviews} - \Plaintitle{\@Booktitle} - \Shorttitle{\@Booktitle} - \Reviewauthor{\@Bookauthor\\ - \@Publisher, \@Pubaddress, \@Pubyear.\\ - ISBN~\@ISBN. \@Pages~pp. \@Price.\\ - \url{\@URL}} - \Plainkeywords{} - \@reviewtrue -\fi - -\if@softwarereview - \Seriesname{Software Review} - \Hypersubject{Journal of Statistical Software -- Software Reviews} - \Plaintitle{\@Softwaretitle} - \Shorttitle{\@Softwaretitle} - \Booktitle{\@Softwaretitle} - \Reviewauthor{\@Publisher, \@Pubaddress. \@Price.\\ - \url{\@URL}} - \Plainkeywords{} - \@reviewtrue -\fi - -\if@review - \Hyperauthor{\@Plainreviewer} - \Keywords{} - \Footername{Reviewer} - \Firstdate{\textit{Published:} \@Submitdate} - \Seconddate{} -\else - \Hyperauthor{\@Plainauthor} - \Keywords{---!!!---at least one keyword is required---!!!---} - \Footername{Affiliation} - \Firstdate{\textit{Submitted:} \@Submitdate} - \Seconddate{\textit{Accepted:} \@Acceptdate} -\fi -%% Sweave(-like) -\DefineVerbatimEnvironment{Sinput}{Verbatim}{fontshape=sl} -\DefineVerbatimEnvironment{Soutput}{Verbatim}{} -\DefineVerbatimEnvironment{Scode}{Verbatim}{fontshape=sl} -\newenvironment{Schunk}{}{} -\DefineVerbatimEnvironment{Code}{Verbatim}{} -\DefineVerbatimEnvironment{CodeInput}{Verbatim}{fontshape=sl} -\DefineVerbatimEnvironment{CodeOutput}{Verbatim}{} -\newenvironment{CodeChunk}{}{} -\setkeys{Gin}{width=0.8\textwidth} -%% footer -\newlength{\footerskip} -\setlength{\footerskip}{2.5\baselineskip plus 2ex minus 0.5ex} - -\newcommand{\makefooter}{% - \vspace{\footerskip} - - \if@nojss - \begin{samepage} - \textbf{\large \@Footername: \nopagebreak}\\[.3\baselineskip] \nopagebreak - \@Address \nopagebreak - \end{samepage} - \else - \begin{samepage} - \textbf{\large \@Footername: \nopagebreak}\\[.3\baselineskip] \nopagebreak - \@Address \nopagebreak - \vfill - \hrule \nopagebreak - \vspace{.1\baselineskip} - {\fontfamily{pzc} \fontsize{13}{15} \selectfont Journal of Statistical Software} - \hfill - \url{http://www.jstatsoft.org/}\\ \nopagebreak - published by the Foundation for Open Access Statistics - \hfill - \url{http://www.foastat.org/}\\[.3\baselineskip] \nopagebreak - {\@Month{} \@Year, Volume~\@Volume, \@Seriesname~\@Issue} - \hfill - \@Firstdate\\ \nopagebreak - {\href{https://doi.org/\@DOI}{\tt doi:\@DOI}} - \hfill - \@Seconddate \nopagebreak - \vspace{.3\baselineskip} - \hrule - \end{samepage} - \fi -} -\if@nofooter - %% \AtEndDocument{\makefooter} -\else - \AtEndDocument{\makefooter} -\fi -%% required packages -\RequirePackage{hyperref} -%% new \maketitle -\def\@myoddhead{ - {\color{white} JSS}\\[-1.42cm] - \hspace{-2em} \includegraphics[height=23mm,keepaspectratio]{jsslogo} \hfill - \parbox[b][23mm]{118mm}{\hrule height 3pt - \center{ - {\fontfamily{pzc} \fontsize{28}{32} \selectfont Journal of Statistical Software} - \vfill - {\it \small \@Month{} \@Year, Volume~\@Volume, \@Seriesname~\@Issue.% - \hfill \href{https://doi.org/\@DOI}{doi:\,\@DOI}}}\\[0.1cm] - \hrule height 3pt}} -\if@review - \renewcommand{\maketitle}{ - \if@nojss - %% \@oddhead{\@myoddhead}\\[3\baselineskip] - \else - \@oddhead{\@myoddhead}\\[3\baselineskip] - \fi - {\large - \noindent - Reviewer: \@Reviewer - \vspace{\baselineskip} - \hrule - \vspace{\baselineskip} - \textbf{\@Booktitle} - \begin{quotation} \noindent - \@Reviewauthor - \end{quotation} - \vspace{0.7\baselineskip} - \hrule - \vspace{1.3\baselineskip} - } - - \thispagestyle{empty} - \if@nojss - \markboth{\centerline{\@Shorttitle}}{\centerline{\@Hyperauthor}} - \else - \markboth{\centerline{\@Shorttitle}}{\centerline{\@Hypersubject}} - \fi - \pagestyle{myheadings} - } -\else - \def\maketitle{ - \if@nojss - %% \@oddhead{\@myoddhead} \par - \else - \@oddhead{\@myoddhead} \par - \fi - \begingroup - \def\thefootnote{\fnsymbol{footnote}} - \def\@makefnmark{\hbox to 0pt{$^{\@thefnmark}$\hss}} - \long\def\@makefntext##1{\parindent 1em\noindent - \hbox to1.8em{\hss $\m@th ^{\@thefnmark}$}##1} - \@maketitle \@thanks - \endgroup - \setcounter{footnote}{0} - - \if@noheadings - %% \markboth{\centerline{\@Shorttitle}}{\centerline{\@Hypersubject}} - \else - \thispagestyle{empty} - \if@nojss - \markboth{\centerline{\@Shorttitle}}{\centerline{\@Hyperauthor}} - \else - \markboth{\centerline{\@Shorttitle}}{\centerline{\@Hypersubject}} - \fi - \pagestyle{myheadings} - \fi - - \let\maketitle\relax \let\@maketitle\relax - \gdef\@thanks{}\gdef\@author{}\gdef\@title{}\let\thanks\relax - } - - \def\@maketitle{\vbox{\hsize\textwidth \linewidth\hsize - \if@nojss - %% \vskip 1in - \else - \vskip 1in - \fi - {\centering - {\LARGE\bf \@title\par} - \vskip 0.2in plus 1fil minus 0.1in - { - \def\and{\unskip\enspace{\rm and}\enspace}% - \def\And{\end{tabular}\hss \egroup \hskip 1in plus 2fil - \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\large\bf\rule{\z@}{24pt}\ignorespaces}% - \def\AND{\end{tabular}\hss\egroup \hfil\hfil\egroup - \vskip 0.1in plus 1fil minus 0.05in - \hbox to \linewidth\bgroup\rule{\z@}{10pt} \hfil\hfil - \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\large\bf\rule{\z@}{24pt}\ignorespaces} - \hbox to \linewidth\bgroup\rule{\z@}{10pt} \hfil\hfil - \hbox to 0pt\bgroup\hss \begin{tabular}[t]{c}\large\bf\rule{\z@}{24pt}\@author - \end{tabular}\hss\egroup - \hfil\hfil\egroup} - \vskip 0.3in minus 0.1in - \hrule - \begin{abstract} - \@Abstract - \end{abstract}} - \textit{Keywords}:~\@Keywords. - \vskip 0.1in minus 0.05in - \hrule - \vskip 0.2in minus 0.1in - }} -\fi -%% sections, subsections, and subsubsections -\newlength{\preXLskip} -\newlength{\preLskip} -\newlength{\preMskip} -\newlength{\preSskip} -\newlength{\postMskip} -\newlength{\postSskip} -\setlength{\preXLskip}{1.8\baselineskip plus 0.5ex minus 0ex} -\setlength{\preLskip}{1.5\baselineskip plus 0.3ex minus 0ex} -\setlength{\preMskip}{1\baselineskip plus 0.2ex minus 0ex} -\setlength{\preSskip}{.8\baselineskip plus 0.2ex minus 0ex} -\setlength{\postMskip}{.5\baselineskip plus 0ex minus 0.1ex} -\setlength{\postSskip}{.3\baselineskip plus 0ex minus 0.1ex} - -\newcommand{\jsssec}[2][default]{\vskip \preXLskip% - \pdfbookmark[1]{#1}{Section.\thesection.#1}% - \refstepcounter{section}% - \centerline{\textbf{\Large \thesection. #2}} \nopagebreak - \vskip \postMskip \nopagebreak} -\newcommand{\jsssecnn}[1]{\vskip \preXLskip% - \centerline{\textbf{\Large #1}} \nopagebreak - \vskip \postMskip \nopagebreak} - -\newcommand{\jsssubsec}[2][default]{\vskip \preMskip% - \pdfbookmark[2]{#1}{Subsection.\thesubsection.#1}% - \refstepcounter{subsection}% - \textbf{\large \thesubsection. #2} \nopagebreak - \vskip \postSskip \nopagebreak} -\newcommand{\jsssubsecnn}[1]{\vskip \preMskip% - \textbf{\large #1} \nopagebreak - \vskip \postSskip \nopagebreak} - -\newcommand{\jsssubsubsec}[2][default]{\vskip \preSskip% - \pdfbookmark[3]{#1}{Subsubsection.\thesubsubsection.#1}% - \refstepcounter{subsubsection}% - {\large \textit{#2}} \nopagebreak - \vskip \postSskip \nopagebreak} -\newcommand{\jsssubsubsecnn}[1]{\vskip \preSskip% - {\textit{\large #1}} \nopagebreak - \vskip \postSskip \nopagebreak} - -\newcommand{\jsssimplesec}[2][default]{\vskip \preLskip% -%% \pdfbookmark[1]{#1}{Section.\thesection.#1}% - \refstepcounter{section}% - \textbf{\large #1} \nopagebreak - \vskip \postSskip \nopagebreak} -\newcommand{\jsssimplesecnn}[1]{\vskip \preLskip% - \textbf{\large #1} \nopagebreak - \vskip \postSskip \nopagebreak} - -\if@review - \renewcommand{\section}{\secdef \jsssimplesec \jsssimplesecnn} - \renewcommand{\subsection}{\secdef \jsssimplesec \jsssimplesecnn} - \renewcommand{\subsubsection}{\secdef \jsssimplesec \jsssimplesecnn} -\else - \renewcommand{\section}{\secdef \jsssec \jsssecnn} - \renewcommand{\subsection}{\secdef \jsssubsec \jsssubsecnn} - \renewcommand{\subsubsection}{\secdef \jsssubsubsec \jsssubsubsecnn} -\fi -%% colors -\definecolor{Red}{rgb}{0.5,0,0} -\definecolor{Blue}{rgb}{0,0,0.5} -\if@review - \hypersetup{% - hyperindex = {true}, - colorlinks = {true}, - linktocpage = {true}, - plainpages = {false}, - linkcolor = {Blue}, - citecolor = {Blue}, - urlcolor = {Red}, - pdfstartview = {Fit}, - pdfpagemode = {None}, - pdfview = {XYZ null null null} - } -\else - \hypersetup{% - hyperindex = {true}, - colorlinks = {true}, - linktocpage = {true}, - plainpages = {false}, - linkcolor = {Blue}, - citecolor = {Blue}, - urlcolor = {Red}, - pdfstartview = {Fit}, - pdfpagemode = {UseOutlines}, - pdfview = {XYZ null null null} - } -\fi -\if@nojss - \AtBeginDocument{ - \hypersetup{% - pdfauthor = {\@Hyperauthor}, - pdftitle = {\@Plaintitle}, - pdfkeywords = {\@Plainkeywords} - } - } -\else - \AtBeginDocument{ - \hypersetup{% - pdfauthor = {\@Hyperauthor}, - pdftitle = {\@Plaintitle}, - pdfsubject = {\@Hypersubject}, - pdfkeywords = {\@Plainkeywords} - } - } -\fi -\if@notitle - %% \AtBeginDocument{\maketitle} -\else - \@ifundefined{AddToHook}{\AtBeginDocument{\maketitle}}{\AddToHook{begindocument}[maketitle]{\maketitle}} -\fi -%% commands -\newcommand\code{\bgroup\@makeother\_\@makeother\~\@makeother\$\@codex} -\def\@codex#1{{\normalfont\ttfamily\hyphenchar\font=-1 #1}\egroup} -%%\let\code=\texttt -\let\proglang=\textsf -\newcommand{\pkg}[1]{{\fontseries{m}\fontseries{b}\selectfont #1}} -\newcommand{\email}[1]{\href{mailto:#1}{\normalfont\texttt{#1}}} -\ifx\csname urlstyle\endcsname\relax - \newcommand\@doi[1]{doi:\discretionary{}{}{}#1}\else - \newcommand\@doi{doi:\discretionary{}{}{}\begingroup -\urlstyle{tt}\Url}\fi -\newcommand{\doi}[1]{\href{https://doi.org/#1}{\normalfont\texttt{\@doi{#1}}}} -\newcommand{\E}{\mathsf{E}} -\newcommand{\VAR}{\mathsf{VAR}} -\newcommand{\COV}{\mathsf{COV}} -\newcommand{\Prob}{\mathsf{P}} -\endinput -%% -%% End of file `jss.cls'. diff --git a/vignettes/jsslogo.jpg b/vignettes/jsslogo.jpg deleted file mode 100644 index 4751aef9dfb1621f743289be3b7afb26b055b204..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22731 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 + Providing computational infrastructure for handling diverse intensive care unit (ICU) datasets, the \proglang{R} package \pkg{ricu} enables writing dataset-agnostic analysis code, thereby facilitating multi-center training and validation of machine learning models. The package is designed with an emphasis on extensibility both to new datasets as well as clinical data concepts, and currently supports the loading of around 100 patient variables corresponding to a total of 395,941 ICU admissions from five data sources collected in Europe and the United States. By allowing for the addition of user-specified medical concepts and data sources, the aim of \pkg{ricu} is to foster robust, data-based intensive care research, allowing the user to externally validate their method or conclusion with relative ease, and in turn facilitating reproducible and therefore transparent work in this field. +keywords: + formatted: [electronic health records, computational physiology, critical care medicine] + plain: [electronic health records, computational physiology, critical care medicine] +preamble: > + \usepackage{amsmath} + \usepackage{booktabs} + \usepackage{makecell} + \usepackage{threeparttablex} + \usepackage{pdflscape} vignette: > - %\VignetteIndexEntry{Quick start guide} + %\VignetteIndexEntry{Accessing ICU data with R} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} +output: > + if (packageVersion("rticles") < 0.5 || rmarkdown::pandoc_version() >= 2) + rticles::jss_article else rmarkdown::html_vignette +documentclass: jss +classoption: + - notitle + - nojss + - noheadings +bibliography: ricu.bib +pkgdown: + as_is: true + extension: pdf --- -```{r, setup, include = FALSE} +```{r setup, include = FALSE} source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) -knitr::opts_chunk$set( - collapse = TRUE, - comment = "#>" -) +options(width = 76) library(ricu) +library(data.table) +library(forestmodel) +library(survival) +library(ggplot2) + +options(kableExtra.latex.load_packages = FALSE) +library(kableExtra) + +srcs <- c("mimic", "eicu", "aumc", "hirid", "miiv") ``` ```{r, assign-src, echo = FALSE} @@ -26,126 +94,1104 @@ src <- "mimic_demo" demo <- c(src, "eicu_demo") ``` +```{tikz, tikz-setup, eval = FALSE, echo = FALSE} +\usetikzlibrary{ + positioning, shadows, arrows, shapes, shapes.arrows, shapes.geometric, + arrows.meta, trees, shapes.misc +} +\tikzset{ + every node/.style = { + draw = none, align = center, fill = none, text centered, + anchor = center, font = \it + }, + every label/.style={circle, draw, fill = yellow}, + f1/.style = { + draw = , fill = gray!15, thick, inner sep = 3pt, minimum width = 10em, + minimum height = 4em, align = center, text centered}, + f2/.style = { + draw = none, fill = red!15, thick, inner sep = 3pt, minimum width = 5em, + align = center, text centered + } +} +``` + +\maketitle + +\renewcommand*{\thefootnote}{\fnsymbol{footnote}} +\footnotetext{$^{*}$These authors contributed equally.} +\renewcommand*{\thefootnote}{\arabic{footnote}} + ```{r, demo-miss, echo = FALSE, eval = !srcs_avail(demo), results = "asis"} -demo_missing_msg(demo, "ricu.html") +demo_missing_msg(demo, "ricu.pdf") knitr::opts_chunk$set(eval = FALSE) ``` -In order to set up `ricu`, download of datasets from several platforms is required. Two data sources, `mimic_demo` and `eicu_demo` are available directly as R packages, hosted on Github. The respective full-featured versions `mimic` and `eicu`, as well as the `hirid` dataset are available from [PhysioNet](https://physionet.org), while access to the remaining standard dataset `aumc` is available from yet another [website](https://amsterdammedicaldatascience.nl/). The following steps guide through package installation, data source set up and conclude with some example data queries. +# Introduction + +Collection of electronic health records has seen a significant rise in recent years \citep{evans2016}, opening up opportunities and providing the grounds for a large body of data-driven research oriented towards helping clinicians in decision-making and therefore improving patient care and health outcomes \citep{jiang2017}. While growing amounts of collected patient data might contribute to an increasingly hard task for intensivists to focus on relevant subsets thereof \citep{pickering2013}, this poses an opportunity for the application of machine learning (ML) methods. + +One example of a problem that has received much attention from the ML community is early prediction of sepsis in the intensive care unit \citep[ICU;][]{desautels2016, nemati2018, futoma2017, kam2017}. Interestingly, there is evidence that a large proportion of the publications are based on the same dataset \citep{fleuren2019}, the Medical Information Mart for Intensive Care III \citep[MIMIC-III;][]{johnson2016}, which shows a systematic lack of external validation. This issue has recently again been highlighted by a study demonstrating poor performance in external validation of a widely adopted proprietary sepsis prediction model \citep{wong2021}. + +Contributing to this problem might well be the lack of computational infrastructure handling multiple datasets. The MIMIC-III dataset consists of 26 different tables containing about 20GB of data. While much work and care has gone into data preprocessing in order to provide a self-contained ready-to-use data resource with MIMIC-III, seemingly simple tasks such as computing a Sepsis-3 label \citep{singer2016} remain non-trivial efforts^[There is considerable heterogeneity in number of patients satisfying the Sepsis-3 criterion \citep{singer2016} among studies investigating MIMIC-III. Reported Sepsis-3 prevalence ranges from 11.3% \citep{desautels2016}, over 23.9% \citep{nemati2018} and 25.4% \citep{wang2018}, up to 49.1% \citep{johnson2018}. While some of this variation may be explained by differing patient inclusion criteria, diversity in label implementation must also contribute significantly.]. This is only exacerbated when aiming to co-integrate multiple different datasets of this form, spanning hospitals and even countries, in order to capture effects of differing practice and demographics. + +The aim of \pkg{ricu} is to provide computational infrastructure allowing users to investigate complex research questions in the context of critical care medicine as easily as possible by providing a unified interface to a heterogeneous set of data sources. The package enables users to write dataset-agnostic code which can simplify implementation and shorten the time necessary for prototyping code querying different datasets. In its current form, the package handles five large-scale, publicly available intensive care databases out of the box: MIMIC-III from the Beth Israel Deaconess Medical Center in Boston, Massachusetts \citep[BIDMC;][]{johnson2016}, the eICU Collaborative Research Database \citep{pollard2018}, containing data collected from 208 hospitals across the United States, the High Time Resolution ICU Dataset (HiRID) from the Department of Intensive Care Medicine of the Bern University Hospital, Switzerland \citep{faltys2021}, the Amsterdam University Medical Center Database (AmsterdamUMCdb) from the Amsterdam University Medical Center \citep{thoral2021} and MIMIC-IV, again using data from BIDMC \citep{johnson2021}. Furthermore, \pkg{ricu} was designed with extensibility in mind such that adding new public and/or private user-provided datasets is possible. Being implemented in \proglang{R}, a programming language popular among statisticians and data analysts, it is our hope to contribute to accessible and reproducible research by using a familiar environment and requiring only few system dependencies, thereby simplifying setup considerably. + +To our knowledge, infrastructure that provides a common interface to multiple such datasets is a novel contribution. While there have been efforts \citep{adibuzzaman2016, wang2020} attempting to abstract away some specifics of a dataset, these have so far exclusively focused on MIMIC-III, the most popular of public ICU datsets, and have not been designed with dataset interoperability in mind. + +Given the somewhat narrow focus of the targeted datasets, it may come as a surprise as to how heterogeneous the resulting datasets are. In MIMIC-III and HiRID, for example, time-stamps are reported as absolute times (albeit randomly shifted due to data privacy concerns), whereas eICU and AmsterdamUMCdb use relative times (with origins being admission times). Another example involves different types of patient identifiers and their use among datasets. Common to all is the notion of an ICU admission identifier (ID), but apart from that, the amount of available information varies: While ICU (and hospital) readmissions for a given patient can be identified in some, this is not possible in other datasets. Furthermore, use of identifier systems might not be consistent over tables. In MIMIC-III, for example, some tables refer to ICU stay IDs while others use hospital stay IDs, which slightly complicates data retrieval for a fixed ID system. Additionally, table layouts vary (*long* versus *wide* data arrangement) and data organization in general is far from consistent over datasets. + +# Quick start guide + +The following list gives a quick outline of the steps required for setting up and starting to use \pkg{ricu}, alongside some section references on where to find further details. A more comprehensive version of this overview is available as a [separate vignette](https://CRAN.R-project.org/package=ricu/vignettes/ricu.html). + +1. Package installation: + + * Installation from [CRAN](https://CRAN.R-project.org) as `install.packages("ricu")` provides the most recently released version of \pkg{ricu}. + + * Alternatively, the latest development version is available from [GitHub](https://github.com/eth-mds/ricu) by running `remotes::install_github("eth-mds/ricu")`. + +1. Requesting access to datasets and data source setup: + + * Demo datasets can be set up by installing the data packages `mimic.demo` and/or `eicu.demo` from [GitHub]("https://eth-mds.github.io/physionet-demo") using `install.packages()` as shown in Section \ref{ready-to-use-datasets}. + + * The complete MIMIC-III, eICU, HiRID and MIMIC-IV datasets can be accessed by [registering](https://physionet.org/register) and setting up a [credentialed account](https://physionet.org/settings/credentialing) at [PhysioNet](https://physionet.org). + + * Access to AmsterdamUMCdb can be requested via the [Amsterdam Medical Data Science Website](https://amsterdammedicaldatascience.nl/#amsterdamumcdb). + + * The obtained credentials can be configured for PhysioNet datasets by setting environment variables `RICU_PHYSIONET_USER` and `RICU_PHYSIONET_PASS`, while the download token for AmsterdamUMCdb can be set as `RICU_AUMC_TOKEN`. -## Package installation + * Datasets are downloaded and set up either automatically upon the first access attempt or manually by running `setup_data_src()`; the environment variable `RICU_DATA_PATH` can be set to control data location. -Stable package releases are available from [CRAN](https://cran.r-project.org/package=ricu) as + * Dataset availability can be queried by calling `src_data_avail()`. -```{r, eval = FALSE} -install.packages("ricu") + A more detailed description of the supported datasets is given in Section \ref{ready-to-use-datasets}, summarized in Table \ref{tab:datasets}, while Section \ref{data-sources} provides implementation details, elaborating on how datasets are represented in code. + +1. Loading of data corresponding to clinical concepts using `load_concepts()`: + + * Currently, over 100 data concepts are available for the 4 supported datasets (see `concept_availability()`/`explain_dictionary()` for names, availability etc.). + + * For example, glucose and age data can be loaded by passing `c("age", "glu")` to `load_concepts()`. + + Section \ref{data-concepts} goes into more detail on how data concepts are represented within \pkg{ricu} and an overview of the preconfigured concepts is available from Section \ref{ready-to-use-concepts}. + +1. Extending the concept dictionary: + + * Data concepts can be specified in code using the constructors `concept()`/`item()` or `new_concept()`/`new_item()`. + + * For session persistence, data concepts can also be specified as JSON formatted objects. + + * JSON-based concept dictionaries can either extend or replace others and they can be pointed to by setting the environment variable `RICU_CONFIG_PATH`. + + The JSON format used to encode data concepts is discussed in more detail in Section \ref{concept-specification}. + +1. Adding new datasets: + + * A JSON-based dataset configuration file is required, from which the configuration objects described in Section \ref{data-source-configuration} are created. + + * In order for concepts to be available from the new dataset, the dictionary requires extension by adding new data items. + + Further information about adding a new dataset is available from Section \ref{adding-external-datasets}. Some code used when AmsterdamUMCdb was not yet fully integrated with \pkg{ricu} is available from [GitHub](https://github.com/eth-mds/aumc) and is used for demonstration purposes to set up AmsterdamUMCdb as an external dataset `aumc_ext`. + +Finally, Section \ref{examples} shows briefly how \pkg{ricu} could be used in practice to address clinical questions by presenting two small examples. + +# Ready-to-use datasets + +Several large-scale ICU datasets collected from multiple hospitals in the US and Europe can be set up for access using \pkg{ricu} with minimal user effort. Provisions in terms of required configuration information alongside functions for download and setup are part of \pkg{ricu}, opening up easy access to these datasets. Data itself, however, is not part of \pkg{ricu} and while the supported datasets are publicly available, access has to be granted by the dataset creators individually. Four datasets, MIMIC-III, MIMIC-IV, eICU and HiRID are hosted on PhysioNet \citep{goldberger2000}, access to which requires an [account](https://physionet.org/register/), while the fifth, AmsterdamUMCdb is currently distributed via a separate platform, requiring a [download link](https://amsterdammedicaldatascience.nl/#amsterdamumcdb). + +For both MIMIC-III and eICU, small subsets of data are available as demo datasets that do not require credentialed access to PhysioNet. As the terms for distribution of these demo datasets are less restrictive, they can be made available as data packages \pkg{mimic.demo} and \pkg{eicu.demo}. Due to size constraints, however they are not available via CRAN, but can be installed from GitHub as + +```{r demo-data, eval = FALSE} +install.packages( + c("mimic.demo", "eicu.demo"), + repos = "https://eth-mds.github.io/physionet-demo" +) ``` -and the latest development version is available from [GitHub](https://github.com/eth-mds/ricu) as +Provisions for datasets configured to be attached during package loading are made irrespective of whether data is actually available. Upon access of an incomplete dataset, the user is asked for permission to download in interactive sessions and an error is thrown otherwise. Credentials can either be provided as environment variables (`RICU_PHYSIONET_USER` and `RICU_PHYSIONET_PASS` for access to PhysioNet data, as well as `RICU_AUMC_TOKEN` for AmsterdamUMCdb) and if the corresponding variables are unset, user input is again required in interactive sessions. For non-interactive sessions, functionality is exported such that data can be downloaded and set up ahead of first access (see `?setup_src_data`). + +Contingent on being granted access by the data owners, download requires a stable Internet connection, as well as 50 to 100 GB of temporary disk storage for unpacking and preparing the data for efficient access. In terms of permanent storage, 5 to 10 GB per dataset are required (see Table \ref{tab:datasets}), while memory requirements are kept reasonably low by iterating over row-chunks for setup operations. Laptop class hardware (8-16 GB of memory) should suffice for setup and many analysis tasks which focus only on subsets of rows (and columns). Initial data source setup (depending on available download speeds and CPU/disk type) may take upwards of an hour per dataset. + +The following paragraphs serve to give quick introductions to the included datasets, outlining some strengths and weaknesses of each of the datasets. Especially the PhysioNet datasets [MIMIC-III and MIMIC-IV](https://mimic.mit.edu/docs/), as well as [eICU](https://eicu-crd.mit.edu/about/eicu/) offer good documentation on the respective websites. Datasets are listed in order of being added to \pkg{ricu} and the section is concluded with a table summarizing similarities and differences among the datasets (see Table \ref{tab:datasets}). + +## MIMIC-III -```{r, eval = FALSE} -remotes::install_github("eth-mds/ricu") +The [Medical Information Mart for Intensive Care III (MIMIC-III)](https://physionet.org/content/mimiciii/1.4/) represents the third iteration of the arguably most influential initiative for collecting and providing large-scale ICU data to the public^[The initial MIMIC (at the time short for Multi-parameter Intelligent Monitoring for Intensive Care) data release dates back 20 years and initially contained data on roughly 100 patients recorded from patient monitors in the medical, surgical, and cardiac intensive care units of Boston's Beth Israel Hospital during the years 1992-1999 \citep{moody1996}. Significantly broadened in scope, MIMIC-II was released 10 years after, now including data on almost 27,000 adult hospital admissions collected from ICUs of Beth Israel Deaconess Medical Center from 2001 to 2008 \citep{lee2011}.]. The dataset comprises de-identified health related data of roughly 46,000 patients admitted to critical care units of BIDMC during the years 2001-2012. Amounting to just over 61,000 individual ICU admission, data is available on demographics, routine vital sign measurements (at approximately 1 hour resolution), laboratory tests, medication, as well as critical care procedures, organized as a 26-table relational structure. + +```{r mimic-tbls, eval = TRUE} +mimic ``` -## Demo datasets +One thing of note from a data-organizational perspective is that a change in electronic health care systems occurred in 2008. Owing to this, roughly 38,000 ICU admissions spanning the years 2001 though 2008 are documented using the CareVue system, while for 2008 and onwards, data was extracted from the MetaVision clinical information system. Item identifiers differ between the two systems, requiring queries to consider both ID mappings (heart rate for example being available both as `itemid` number `211` for CareVue and `220045` for MetaVision) as does documentation of infusions and other procedures that are considered as input events (cf., `inputevents_cv` and `inputevents_mv` tables). Especially with respect to such input event data, MetaVision generally provides data of superior quality. -The demo datasets `r paste1(demo)` are listed as `Suggests` dependencies and therefore their availability is determined by the value passed as `dependencies` to the above package installation function. The following call explicitly installs the demo data set packages +In terms of patient identifiers, MIMIC-III allows for identifying both individual patients (`subject_id`) across hospital admissions (`hadm_id`) and for connecting ICU (re)admissions (`icustay_id`) to hospital admissions. Using the respective one-to-many relationships, \pkg{ricu} can retrieve patient data using any of the above IDs, irrespective of how the raw data is organized. -```{r, echo = FALSE, eval = TRUE, results = "asis"} -cat( - "```r\n", - "install.packages(\n", - " c(", paste0("\"", sub("_", ".", demo), "\"", collapse = ", "), "),\n", - " repos = \"https://eth-mds.github.io/physionet-demo\"\n", - ")\n", - "```\n", - sep = "" -) +## eICU + +Unlike the single-center focus of other datasets, the [eICU Collaborative Research Database](https://physionet.org/content/eicu-crd/2.0/) constitutes an amalgamation of data from critical care units of over 200 hospitals throughout the continental United States. Large-scale data collected via the Philips eICU program, which provides telehealth infrastructure for intensive care units, is available from the Philips eICU Research Institute (eRI), albeit neither publicly nor freely. Only data corresponding to roughly 200,000 ICU admissions, sampled from a larger population of over 3 million ICU admissions and stratified by hospital, is being made available via PhysioNet. Patients with discharge dates in 2014 or 2015 were considered, with stays in low acuity units being removed. + +```{r width-inc, include = FALSE} +old_width <- options(width = 78)[["width"]] +``` + +```{r eicu-tbls, eval = TRUE} +eicu +``` + +```{r width-dec, include = FALSE} +options(width = old_width) +``` + +The data is organized into 31 tables and includes patient demographics, routine vital signs, laboratory measurements, medication administrations, admission diagnoses, as well as treatment information. Owing to the wide range of hospitals participating in this data collection initiative, spanning small, rural, non-teaching health centers with fewer than 100 beds to large teaching hospitals with an excess of 500 beds, data availability varies. Even if data was being recorded at the bedside it might end up missing from the eICU dataset due to technical limitations of the collection process. As for patient identifiers, while it is possible to link ICU admissions corresponding to the same hospital stay, it is not possible to identify patients across hospital stays. + +Data resolution again varies considerably over included variables. The `vitalperiodic` table stands out as one of the few examples of a *wide* table organization (laying out variables as columns), as opposed to the *long* presentation (following an entity–attribute–value model) of most other tables containing patient measurement data. The average time step in `vitalperiodic` is around 5 minutes, but data missingness ranges from around 1% for heart rate and pulse oximetry to roughly 10% for respiration rate and up to 80% for systemic and 90% for pulmonary artery blood pressure measurements, therefore giving approximately hourly resolution for such variables. + +## HiRID + +Developed for early prediction of circulatory failure \citep{hyland2020}, the [High Time Resolution ICU Dataset (HiRID)](https://physionet.org/content/hirid/1.0/) contains data on almost 34,000 admissions to the Department of Intensive Care Medicine of the Bern University Hospital, Switzerland, an interdisciplinary 60-bed unit. Given the clear focus on a concrete application during data collection, this dataset is the most limited in terms of data breadth, which is also reflected in a comparatively simple data layout comprising only 5 tables^[The data is available in three states: as raw data and in two intermediary preprocessing stages explained in \cite{hyland2020}. While \pkg{ricu} focuses exclusively on raw data, the *merged* stage represents a selection of variables that were deemed most predictive for determining circulatory failure, which are then merged into 18 meta-variables, representing different clinical concepts. Time stamps in *merged* data are left unchanged, yielding irregular time series, whereas for the *imputed* stage, data is down-sampled to a 5 minute grid and missing values are imputed using a scheme discussed in \cite{hyland2020}.]. + +```{r hirid-tbls, eval = TRUE} +hirid +``` + +Collected during the period of January 2008 through June 2016, roughly 700 distinct variables covering routine vital signs, diagnostic test results and treatment parameters are available with variables monitored at the bedside being recorded with two minute time resolution. In terms of demographic information and patient identifier systems however, the data is limited. It is not possible to identify subsequent ICU admissions corresponding to the same patient and apart from patient age, sex, weight and height, very little information is available to characterize patients. There is no medical history, no admission diagnoses, only in-ICU mortality information, no unstructured patient data and no information on patient discharge. Furthermore, data on body fluid sampling has been omitted, complicating for example the construction of a Sepsis-3 label \citep{singer2016}. + +## AmsterdamUMCdb + +As a second European dataset, also focusing on increased time-resolution over the US datasets, [AmsterdamUMCdb](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) has been made available in late 2019, containing data on over 23,000 intensive care unit and high dependency unit admissions of adult patients during the years 2003 through 2016. The department of Intensive Care at Amsterdam University Medical Center is a mixed medical-surgical ICU with 32 bed intensive care and 12 bed high dependency units with an average of 1000-2000 yearly admissions. Covering middle ground between the US datasets and HiRID in terms of breadth of included data, while providing a maximal time-resolution of 1 minute, AmsterdamUMCdb constitutes a well organized high quality ICU data resource organized succinctly as a 7-table relational structure. + +```{r aumc-tbls, eval = TRUE} +aumc +``` + +For data anonymization purposes, demographic information such as patient weight, height and age only available as binned variables instead of raw numeric values. Apart from this, there is information on patient origin, mortality, admission diagnoses, as well as numerical measurements including vital parameters, lab results, outputs from drains and catheters, information on administered medication, and other medical procedures. In terms of patient identifiers, it is possible to link ICU admissions corresponding to the same individual, but it is not possible to identify separate hospital admissions. + +## MIMIC-IV + +The most recently released dataset and next iteration in the MIMIC line of datasets, MIMIC-IV, has recently been released as first stable version \citep{johnson2021} and support in \pkg{ricu} is available as dataset `miiv`. Compared to MIMIC-III, this release shifts focus to newer data, dropping all CareVue-documented patients and with that, patients who were admitted before 2008, while adding patients admitted up to and including 2019. The resulting dataset contains data on over 256,000 patients of which, 53,000 were admitted to ICUs, resulting in 76,000 unique ICU and almost 70,000 related hospital admissions. + +```{r miiv-tbls, eval = TRUE} +miiv ``` -## Full datasets +```{r src-overview, echo = FALSE, results = "asis", cache = TRUE} +as_quant <- function(x) { + + if (is_id_tbl(x)) { + x <- data_col(x) + } + + if (identical(length(x), 0L) || isTRUE(is.na(x))) { + return("-") + } + + res <- format_2(quantile(x, probs = seq(0.25, 0.75, 0.25), na.rm = TRUE)) + + paste0(res[2L], " (", res[1L], "--", res[3L], ")") +} + +big_mark <- function(x) { + + if (identical(length(x), 0L) || isTRUE(is.na(x))) { + return("-") + } + + formatC(x, big.mark = ",", format = "d") +} + +format_2 <- function(x) { + formatC(x, digits = 2L, format = "f") +} + +n_patient <- function(x, type) { + if (type %in% names(as_id_cfg(x))) nrow(stay_windows(x, type)) else NA +} + +feat_freq <- function(src, concept, time_span = "hours") { + res <- load_concepts(concept, src, interval = mins(1L), verbose = FALSE) + res <- res[, 1 / diff(as.double(get(index_var(res)), units = time_span)), + by = c(id_var(res))] + res +} + +years <- function(src) { + switch(src, + mimic = "2001--2012", + eicu = "2014--2015", + hirid = "2008--2016", + aumc = "2003--2016", + miiv = "2008--2019", + NA + ) +} + +country <- function(src) { + switch(src, + mimic = "United States", + eicu = "United States", + hirid = "Switzerland", + aumc = "Netherlands", + miiv = "United States", + NA + ) +} + +summarize <- function(src, avail) { + + ids <- as_id_cfg(src) + cnc <- avail[, src] + + nrow(stay_windows(src, "icustay")) + + los_icu <- load_concepts("los_icu", src, verbose = FALSE) + + hosp_len <- if ("hadm" %in% names(ids)) { + load_concepts("los_hosp", src, id_type = "hadm", verbose = FALSE) + } + + fil <- list.files(src_data_dir(src), recursive = TRUE, full.names = TRUE) + siz <- sum(vapply(fil, file.size, numeric(1L))) * 1e-9 + row <- vapply(as_src_env(src), nrow, integer(1L)) + + c(`Number of tables` = big_mark(length(as_src_env(src))), + `Disk storage [GB]` = format_2(siz), + `Largest table [rows]` = big_mark(max(row)), + `Available concepts` = sum(cnc), + `Time span` = years(src), + `Country of origin` = country(src), + `ICU` = big_mark(n_patient(src, "icustay")), + `Hospital` = big_mark(n_patient(src, "hadm")), + `Unique patients` = big_mark(n_patient(src, "patient")), + `ICU stays` = as_quant(los_icu), + `Hospital stays` = as_quant(hosp_len), + `Heart rate` = as_quant(feat_freq(src, "hr")), + `Mean arterial pressure` = as_quant(feat_freq(src, "map")), + `Bilirubin` = as_quant(feat_freq(src, "bili", "days")), + `Lactate` = as_quant(feat_freq(src, "lact", "days")) + ) +} + +if (srcs_avail(demo) && (!srcs_avail(srcs) || )) { + srcs <- demo +} -Included with `ricu` are functions for download and setup of the following datasets: `mimic` (MIMIC-III), `eicu`, `hirid`, `aumc` and `miiv` (MIMIC-IV), which can be invoked in several different ways. +src_names <- c( + mimic = "MIMIC-III", eicu = "eICU", hirid = "HiRID", aumc = "AmsterdamUMCdb", + miiv = "MIMIC-IV", mimic_demo = "MIMIC (demo)", eicu_demo = "eICU (demo)" +)[srcs] -- To begin with, a directory is needed where the data can permanently be stored. The default location is platform dependent and can be overridden using the environment variable `RICU_DATA_PATH`. The current value can be retrieved by calling `data_dir()`. -- Access to both the PhysioNet datasets (MIMIC-III, eICU and HiRID), as well as to AUMCdb is free but credentialed. In addition to setting up an [account with PhysioNet](https://physionet.org/register/), [credentialing](https://physionet.org/settings/credentialing/) is required, which, in the case of HiRID, must also be followed by submitting an [access request](https://physionet.org/request-access/hirid/1.1.1/) to the data-owners. Details on the procedure for requesting access to AUMCdb is available from [here](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) and consists of filling out a [form](https://amsterdammedicaldatascience.nl/arfeula_v1.6.pdf) and completing a training course such as [Data or Specimens Only Research (DSOR)](https://physionet.org/about/citi-course/) which, together with proof of training course completion, can be submitted by [email](mailto:access@amsterdammedicaldatascience.nl). -- If raw data in `.csv` form has already been downloaded, this can be decompressed and copied to an appropriate sub-folder (`mimic`, `eicu`, `hirid` or `aumc`) to the directory identified by `data_dir()`. -- In order to have `ricu` download the required data, login credentials can be supplied as environment variables `RICU_PHYSIONET_USER`/`RICU_PHYSIONET_PASS` and `RICU_AUMC_TOKEN` (the string the follows `token=` in the download URL received from the AUMCdb data owners) or entered into the terminal manually in interactive sessions. -- Enabling efficient random row/column access, `ricu` converts `.csv` files into a binary format using the [fst](https://cran.r-project.org/package=fst) package. -- Data conversion to `.fst` format (and potentially data download) is automatically triggered upon first access of a table. In interactive sessions, the user is asked for permission to setup the given data source and in non-interactive sessions, access to missing data throws an error. -- Instead of relying on first data access to trigger setup, up-front data conversion, possible preceded by data download, can be invoked by calling `setup_src_data()`. +src_names[is.na(src_names)] <- srcs[is.na(src_names)] -## Concept loading +dict <- load_dictionary(srcs) +avai <- concept_availability(dict, include_rec = FALSE) +summ <- vapply(srcs, summarize, character(15L), avai) -Many commonly used clinical data concepts are available for all data sources, where the required data exists. An overview of available concepts is available by calling `explain_dictionary()` and concepts can be loaded using `load_concepts()`: +colnames(summ) <- src_names +rownames(summ) <- rownames(summ) +rownames(summ)[4L] <- paste0(rownames(summ)[4L], + footnote_marker_symbol(1, "latex")) -```{r, load-ts} +n_rec_cpt <- nrow(concept_availability(dict, include_rec = TRUE)) - + nrow(avai) + +capt <- paste( + "Comparison of datasets supported by \\pkg{ricu}, highlighting some of", + "the major similarities and distinguishing features among the five data", + "sources described in the preceding sections. Values followed by", + "parenthesized ranges represent medians and are accompanied by", + "interquartile ranges." +) + +tbl <- kable(summ, format = "latex", escape = FALSE, booktabs = TRUE, + caption = capt, label = "datasets") +tbl <- pack_rows(tbl, "Data collection", 5, 6) +tbl <- pack_rows(tbl, "Admission counts", 7, 9) +tbl <- pack_rows(tbl, "Stay lengths [day]", 10, 11) +tbl <- pack_rows(tbl, "Vital signs [1/hour]", 12, 13) +tbl <- pack_rows(tbl, "Lab tests [1/day]", 14, 15) +tbl <- footnote(tbl, symbol = paste( + "These values represent the number of atomic concepts per data source.", + "Additionally,", n_rec_cpt, "recursive concepts are available, which", + "build on data source specific atomic concepts in a source agnostic manner", + "(see Section \\\\ref{concept-specification} for details)."), + threeparttable = TRUE, escape = FALSE +) + +if (identical(srcs, demo)) { + tbl +} else { + landscape(tbl) +} +``` + +```{r, full-miss, echo = FALSE, eval = identical(srcs, demo), results = "asis"} +demo_instead_full_msg(demo, srcs, "ricu.pdf") +``` + +In addition to including newer ICU data, this MIMIC release puts both more emphasis on data collected outside the ICU, newly making emergency department (ED) data available. In a similar vein, the set of considered data types is also expanded by including chest X-ray (CXR) imagery directly with MIMIC data, using the same patient identifiers, while expanding the amount of unstructured text data (still to be made publicly available). Despite these promising developments, the focus of \pkg{ricu} remains on data that lies in the intersection of the supported datasets and therefore both ED and CXR data cannot be accessed by the current `miiv` implementation. Finally, documentation of medication administration has been much improved by not only reporting prescriptions, but, using an electronic Medicine Administration Record (eMAR) system, including time-stamped data on administration of individual formulary units. + +# Data concepts + +One of the key components of \pkg{ricu} is a scheme for specifying how to retrieve data corresponding to predefined clinical concepts from a given data source. This abstraction provides a mechanism for hiding away the data source specific implementation of a concept, in turn enabling dataset agnostic code for analysis. Heart rate, for example can be loaded from datasets `r paste1(demo)` using the `hr` concept as + +```{r, load-conc, eval = srcs_avail(demo)} <> <> -head(explain_dictionary(src = demo)) -load_concepts("alb", src, verbose = FALSE) +load_concepts("hr", demo, verbose = FALSE) +``` + +This requires infrastructure for specifying how to retrieve data subsets (Section \ref{concept-specification}) that is both extensible (to new concepts and new datasets) and flexible enough to handle concept-specific preprocessing. Furthermore, allowing for code re-use for common data transformation tasks is important for simplifying both code development and maintenance. Building on this framework, \pkg{ricu} has included a dictionary with over 100 concepts implemented for all five supported datasets (where possible; see also Section \ref{ready-to-use-concepts} for further details). + +## Data classes + +In order to represent tabular ICU data, \pkg{ricu} provides several classes, all inheriting from `data.table`. The most basic of which, `id_tbl`, marks one (or several) columns as `id_vars` which serve to define a grouping (i.e., identify patients or unit stays). Inheriting from `id_tbl`, `ts_tbl` is capable of representing grouped time series data. In addition to `id_var` column(s), a single column is marked as `index_var` and is required to hold a base \proglang{R} `difftime` vector. Furthermore, `ts_tbl` contains a scalar-valued `difftime` object as `interval` attribute, specifying the time series step size. More recently, a further class, `win_tbl`, inheriting from `ts_tbl` has been added. Objects of this class can be used for time-stamped measurements associated with a validity period. A set of drug infusions, consisting of both rates and intervals can as such be conveniently represented by a `win_tbl` object. + +Metadata for classes inheriting from `id_tbl` is transiently added to `data.table` objects and for S3 generic functions which allow for object modifications, down-casting is implicit: + +```{r id-tbl, eval = TRUE} +(dat <- ts_tbl(a = 1:5, b = hours(1:5), c = rnorm(5))) +dat[["b"]] <- dat[["b"]] + mins(30) +dat +``` + +Due to time series step size of `dat` being specified as 1 hour, an internal inconsistency is encountered when shifting time stamps by 30 minutes, as time steps are no longer multiples of the time series interval, in turn causing down-casting to `id_tbl`. Furthermore, if column `a` were to be removed, direct down-casting to `data.table` would be required in order to resolve resulting inconsistencies^[Updating an object inheriting from `id_tbl` using `data.table::set()` bypasses consistency checks as this is not an S3 generic function and therefore its behavior cannot be tailored to requirements of `id_tbl` objects. It therefore is up to the user to avoid creating invalid `id_tbl` objects in such a way.]. + +Coercion to base classes `data.frame` and `data.table`, by stripping away the extra attributes, is easily possible using functions `as.data.frame()` and `as.data.table()`. Coercion is also available as `data.table`-style by-reference operation by passing `by_ref = TRUE` to any of the above coercion functions. User caution is advised, as this does break with base \proglang{R} by-value (or copy-on-modify) semantics and may lead to unexpected behavior. + +In its current form, `win_tbl` objects can both be used to represent for example drug rates or drug amounts, administered over a specified time-period. When calling the utility function `expand()` however, which creates a `ts_tbl` from a `win_tbl` by assigning values to the corresponding time steps, values are assumed to be *valid* for the given interval. + +```{r win-tbl, eval = TRUE} +(dat <- win_tbl(a = 1:5, b = hours(1:5), c = mins(rep(90, 5)), + d = runif(5))) +expand(dat) +``` + +In a case where `d` represented drug amounts instead of drug rates, the current implementation of `expand()` would produce incorrect results. One would expect the overall amount in such a scenario to be evenly divided by -- and the resulting fractions assigned to -- the corresponding time steps. Allowing for this distinction is being considered, but, as of yet, has not been implemented. + +Utilizing the attached metadata of objects inheriting from `id_tbl`, several utility functions can be called with concise semantics (as seen in the above example, where `expand()` is able to determine the required column names from the `win_tbl` object by default). Utilities include functions for sorting, checking for duplicates, aggregating data per combination of `id_vars` (and time step/time duration), checking time series data for gaps, verifying time series regularity and converting between irregular and regular time series, as well as functions for several types of moving window operations. Adding to those class-specific implementations, `id_tbl` objects inherit from `data.table` (and therefore from `data.frame`), ensuring compatibility with a wide range of functionality targeted at these base-classes. + +## Ready-to-use concepts + +The current selection of clinical concepts that is included with \pkg{ricu} covers many physiological variables that are available throughout the included datasets. Treatment-related information on the other hand, being more heterogeneous in nature and therefore harder to harmonize across datasets, has been added on an as-needed basis and therefore is more limited in breadth. A quick note on loading from multiple sources simultaneously: In the introductory example, heart rate was loaded from multiple data sources, resulting in a column `source` being added. This allows for identifying patient IDs corresponding to the respective data sources and the extra column is added to the set of `id_vars`. In the following calls to `load_concepts()`, only data from a single source is requested and therefore no corresponding `source` column is added. + +Available concepts can be enumerated using `load_dictionary()` and the utility function `explain_dictionary()` can be used to display some concept metadata. + +```{r, load-dict, eval = srcs_avail(demo)} +dict <- load_dictionary(demo) +head(dict) +explain_dictionary(head(dict)) +``` + +The following subsections serve to introduce some of the included concepts as well as highlight limitations that come with current implementations. Grouping the available concepts by category yields the following counts + +```{r, dict-cat, eval = srcs_avail(demo)} +table(vapply(dict, `[[`, character(1L), "category")) +``` + +### Physiological data + +The largest and most well established group of concepts (covering more than half of all currently included concepts) includes physiological patient measurements such as routine vital signs, respiratory variables, fluid discharge amounts, as well as many kinds of laboratory tests including blood gas measurements, chemical analysis of body fluids and hematology assays. + +```{r, load-phys, eval = srcs_avail(src)} +load_concepts(c("alb", "glu"), src, interval = mins(15L), + verbose = FALSE) +``` + +Most concepts of this kind are represented by `num_cncpt` objects (see Section \ref{concept-specification}) with an associated unit of measurement and a range of permissible values. Data is mainly returned as `ts_tbl` objects, representing time-dependent observations. Apart from conversion to a common unit (using functionality offered by the \pkg{units} package \citep{pebesma2016} or possibly using the `convert_unit()` callback function), little has to be done in terms of preprocessing: values are simply reported at time-points rounded to the requested interval. + +### Patient demographics + +Moving on from dynamic, time-varying patient data, this group of concepts focuses on static patient information. While the assumption of remaining constant throughout a stay is likely to hold for variables including patient sex or height this is only approximately true for others such as weight. Nevertheless, such effects are ignored and concepts of this group will be mainly returned as `id_tbl` objects with no corresponding time-stamps included. + +Whenever requesting concepts which are returned with associated time-stamps (e.g., glucose) alongside time-constant data (e.g., age), merging will duplicate static data over all time-points. + +```{r, load-demog, eval = srcs_avail(src)} +load_concepts(c("age", "glu"), src, verbose = FALSE) +``` + +Despite a best-effort approach, data availability can be a limiting factor. While for physiological variables, there is good agreement even across countries, data-privacy considerations, as well as lack of a common standard for data encoding, may cause issues that are hard to resolve. In some cases, this can be somewhat mitigated while in others, this is a limitation to be kept in mind. In AmsterdamUMCdb, for example, patient age, height and weight are not available as continuous variables, but as factor variables with patients binned into groups. Such variables are then approximated by returning the respective mid-points of groups for `aumc` data^[Prioritizing consistency over accuracy, one could apply the same binning to datasets which report numeric values, but the concepts included with \pkg{ricu} attempt to strike a balance between consistency and amount of applied preprocessing. With the extensible architecture of data concepts, however, such categorical variants of patient demographic concepts could easily be added.]. Other concepts, such as `adm` (categorizing admission types) or a potential `icd` concept (diagnoses as ICD-9 codes) can only return data if available from the data source in question. Unfortunately, neither `aumc` nor `hirid` contain ICD-9 encoded diagnoses, and in the case of `hirid`, no diagnosis information is available at all. + +### Treatment-related information + +The largest group of concepts dealing with treatment-related information is described by the `medications` category. In addition to drug administrations, only basic ventilation information is currently provided as ready-to-use concept. Just like availability of common ICU procedures, patient medication is also underdeveloped, covering mainly vasopressor administrations, as well as corticosteroids, antibiotics and dextrose infusions. The current concepts retrieving treatment-related information are mostly focused on providing data required for constructing clinical scores described in Section \ref{outcomes}. While this group of concepts lends itself to use of `win_tbl` objects, a call to `load_concepts()`, requesting multiple concepts which do not all return data as `win_tbl` (while leaving the `merge` argument at default value `TRUE`), all `win_tbl` objects are converted to `ts_tbl` in order to be merged with the non-`win_tbl` objects. + +Ventilation is represented by several concepts: a ventilation indicator variable (`vent_ind`), represented by a `win_tbl` object is constructed from start and end events (concepts `vent_start` and `vent_end`). This includes any kind of mechanical ventilation (invasive via an endotracheal or tracheostomy tube), as well as non-invasive ventilation via face or nasal masks. In line with other concepts belonging to this group, the current state is far from being comprehensive and expansion to further ventilation parameters is desirable. + +The singular concept addressing antibiotics (`abx`) returns an indicator signaling whenever an antibiotic was administered. This includes any route of administration (intravenous, oral, topical, etc.) and does neither report dosage, nor active ingredient. Finally, vasopressor administration is reported by several concepts representing different vasoactive drugs (including dopamine, dobutamine, epinephrine, norepinephrine and vasopressin), as well as different administration aspects such as rate, duration and rate administered for at least 60 minutes, which is used in Sepsis-Related Organ Failure Assessment (SOFA) scoring \citep{vincent1996}. + + + +```{r, load-treat, eval = srcs_avail(src)} +load_concepts(c("abx", "vent_ind", "norepi_rate", "norepi_dur"), src, + verbose = FALSE) ``` -Concepts representing time-dependent measurements are loaded as `ts_tbl` objects, whereas static information is retrieved as `id_tbl` object. Both classes inherit from `data.table` (and therefore also from `data.frame`) and can be coerced to any of the base classes using `as.data.table()` and `as.data.frame()`, respectively. Using `data.table` 'by-reference' operations, this is available as zero-copy operation by passing `by_ref = TRUE`^[While `data.table` by-reference operations can be very useful due to their inherent efficiency benefits, much care is required if enabled, as they break with the usual base R by-value (copy-on-modify) semantics.]. +As cautioned in Section \ref{patient-demographics}, variability in data reporting across datasets can lead to issues: the `prescriptions` table included with MIMIC-III, for example, reports time-stamps as dates only, yielding a discrepancy of up to 24 hours when merged with data where time-accuracy is on the order of minutes. Another problem exists with concepts that attempt to report administration windows, as some datasets do not describe infusions with clear cut start/endpoints but rather report infusion parameters at (somewhat) regular time intervals. This can cause artifacts when the requested time step-size deviates from the dataset inherent time grid and introduces uncertainty when attempting to determine start/endpoints for creating a `win_tbl` object. -```{r, load-id} -(dat <- load_concepts("height", src, verbose = FALSE)) -head(tmp <- as.data.frame(dat, by_ref = TRUE)) -identical(dat, tmp) +```{r, load-dex, eval = srcs_avail("mimic_demo")} +load_concepts("dex", "mimic_demo", verbose = FALSE) ``` -Many functions exported by `ricu` use `id_tbl` and `ts_tbl` objects in order to enable more concise semantics. Merging an `id_tbl` with a `ts_tbl`, for example, will automatically use the columns identified by `id_vars()` of both tables, as `by.x`/`by.y` arguments, while for two `ts_tbl` object, respective columns reported by `id_vars()` and `index_var()` will be used to merge on. +Furthermore for a concept like dextrose administration as implemented in `dex`, where infusions are returned alongside bolus administrations, this can yield large rate values, as the returned unit is ml/hr and in this particular case, values are harmonized such that they correspond to 10% dextrose solutions. A bolus administration of 50 ml dextrose 50% will therefore be reported as 15000 ml/hr administered within 1 minute. + +### Outcomes + +A group of more loosely associated concepts can be used to describe patient state. This includes common clinical endpoints, such as death or length of ICU stay, as well as scoring systems such as SOFA, the systemic inflammatory response syndrome \citep[SIRS;][]{bone1992} criterion, the National Early Warning Score \citep[NEWS;][]{jones2012} and the Modified Early Warning Score \citep[MEWS;][]{subbe2001}. -When loading form multiple data sources simultaneously, `load_concepts()` will add a `source` column (which will be among the `id_vars()` of the resulting object), thereby allowing to identify stay IDs corresponding to the individual data sources. +While the more straightforward outcomes can be retrieved directly from data, clinical scores often incorporate multiple variables, based upon which a numeric score is constructed. This can typically be achieved by using concepts of type `rec_cncpt` (see Section \ref{concept-specification}), specifying the needed components and supplying a callback function that applies rules for score construction. -```{r, load-mult} -load_concepts("weight", demo, verbose = FALSE) +```{r, load-out, eval = srcs_avail(src)} +load_concepts(c("sirs", "death"), src, verbose = FALSE, + keep_components = TRUE) ``` -## Extending the concept dictionary +Callback functions can become rather involved (especially for more complex concepts such as SOFA) and may offer arbitrary arguments to tune their behavior. As callback functions to `rec_cncpt` objects are typically called internally from `load_concepts()`, arguments not used by `load_concepts()`, such as `keep_components` in the above example (causing not only the score column, but also individual score components to be retained) are forwarded. Therefore, some care has to be taken as when requesting multiple concepts within the same call to `load_concepts()`, while passing arguments intended for concept-level callback functions, as all involved callback functions will be called with the same forwarded arguments. When for example requesting multiple scores (such as SOFA or SIRS), it is currently not possible to enable `keep_components` for only a subset thereof. This setup consequently also requires that all involved callback functions are allowed to be called with the given set of extra arguments. -In addition to the ~100 concepts that are available by default, adding user-defined concepts is possible either as R objects or more robustly, as JSON configuration files. +## Concept specification -- Data concepts consist of zero, one, or several data items per data source, encoding how to retrieve the corresponding data. The constructors `concept()` and `item()` can be used to instantiate concepts as R objects. +Just like data source configuration (as discussed in Section \ref{data-source-configuration}), concept specification relies on JSON-formatted text files, parsed by \pkg{jsonlite} \citep{ooms2014}. A default dictionary of concepts is included with \pkg{ricu}, containing a selection of commonly used clinical concepts. Several types of concepts exist within \pkg{ricu} and with extensibility in mind, new types can easily be added. A quick remark on terminology before diving into more details on how to specify data concepts: A *concept* corresponds to a clinical variable such as a bilirubin measurement or the ventilation status of a patient, and an *item* encodes how to retrieve data corresponding to a given concept from a data source. A *concept* therefore contains several *items* (zero, one or multiple are possible per data source). - ```{r, create-concept, eval = srcs_avail("mimic_demo")} - ldh <- concept("ldh", - item("mimic_demo", "labevents", "itemid", 50954), - description = "Lactate dehydrogenase", - unit = "IU/L" - ) - load_concepts(ldh, verbose = FALSE) - ``` +All concepts consist of minimal metadata including a name, target class (defaults to `ts_tbl`; see Section \ref{data-classes}), an aggregation specification^[Every concept needs a default aggregation method which can be used during data loading to return data that is unique per key (either per `id_vars` group or per combination of `ìd_vars` and `index_var`) otherwise down-stream merging of multiple concepts is ill-defined. The aggregation default can be manually overridden during loading or automatically, by specification as part of a `rec_cncpt` object. If no aggregation method is explicitly indicated the global default is `first()` for character, `median()` for numeric and `any()` for logical vectors.] and class information (`num_concept` if not otherwise specified), as well as optional `description` and `category` information. Adding to that, depending on concept class, further fields can be supplied. In the case of the most widespread concept type (`num_cncpt`; used to represent numeric data) this is `unit` which encodes one (or several synonymous) unit(s) of measurement, as well as a minimal and maximal plausible values (specified as `min` and `max`). The concept for heart rate data (`hr`) for example can be specified as + +``` +{ + "hr": { + "unit": ["bpm", "/min"], + "min": 0, + "max": 300, + "description": "heart rate", + "category": "routine vital signs", + "sources": { + ... + } + } +} +``` -- Configuration files are looked for in both the package installation directory and in user-specified locations, either using the environment variable `RICU_CONFIG_PATH` or by passing paths as function arguments (`load_dictionary()` for example accepts a `cfg_dirs` argument). +Metadata is used during concept loading for data-preprocessing. For numeric concepts, the specified measurement unit is compared to that of the data (if available), with messages being displayed in case of mismatches, while the range of plausible values is used to filter out measurements that fall outside the specified interval. Other types of concepts include categorical concepts (`fct_cncpt`), concepts representing binary data (`lgl_cncpt`), as well as recursive concepts (`rec_cncpt`), which build on other *atomic* concepts^[An example for a recursive concept is the PaO~2~/FiO~2~ ratio, used for instance to assess patients with acute respiratory distress syndrome (ARDS) or for Sepsis-Related Organ Failure Assessment (SOFA) \citep{villar2013, vincent1996}. Given both PaO~2~ and FiO~2~ as individual concepts, the PaO~2~/FiO~2~ ratio is provided by \pkg{ricu} as a recursive concept (`pafi`), requesting the two atomic concepts `pao2` and `fio2` and performing some form of imputation for when at a given time step one or both values are missing.]. -- Mechanisms for both extending and replacing existing concept dictionaries are supported by `ricu`. The file name of the default concept dictionary is called `concept-dict.json` and any file with the same name in user-specified locations will be used as extensions. In order to forgo the internal dictionary, a different file name can be chosen, which then has to be passed as function argument (`load_dictionary()` for example has a `name` argument which defaults to `concept-dict`) +Finally, the most recently added concept class, `unt_cncpt`, inheriting from `num_cncpt`, aims to simplify manual conversion to target units, leveraging capabilities provided by the \pkg{units} package. For this to work, both source and target units have to be recognized and convertible (as reported by `units::ud_are_convertible()`). Measurement units that are not available by default can be registered using `units::install_unit()`. -- A JSON-based concept akin to the one above can be specified as +Specification of how data can be retrieved from a data source is encoded by data *items*. Lists of data items (associated with data source names) are provided as `sources` element. For the demo datasets corresponding to eICU and MIMIC-III, heart rate data retrieval is specified as - ``` +``` +{ + "eicu_demo": [ { - "ldh": { - "unit": "IU/L", - "description": "Lactate dehydrogenase", - "sources": { - "mimic_demo": [ - { - "ids": 50954, - "table": "labevents", - "sub_var": "itemid" - } - ] - } + "table": "vitalperiodic", + "val_var": "heartrate", + "class": "col_itm" + } + ], + "mimic_demo": [ + { + "ids": [211, 220045], + "table": "chartevents", + "sub_var": "itemid" + } + ] +} +``` + +Analogously to how different concept classes are used to represent different types of data, different item classes handle different data loading requirements. The most common scenario is selecting a subset of rows from a table by matching a set of ID values (`sub_itm`). In the above example, heart rate data in MIMIC-III can be located by searching for ID values 211 and 220045 in column `itemid` of table `chartevents` (heart rate data is stored in *long* format). Conversely, heart rate data in eICU is stored in *wide* format, requiring no row-subsetting. Column `heartrate` of table `vitalperiodic` contains all corresponding data and such data situations are handled by the `col_itm` class. Other item classes include `rgx_itm` where a regular expression is used for selecting rows and `fun_itm` where an arbitrary function can be used for data loading. If a data loading scenario is not covered by these classes, adding further `itm` subclasses is encouraged. + +In order to extend the current concept library both to new datasets and new concepts, further JSON files can be incorporated by adding paths to their enclosing directories to `RICU_CONFIG_PATH`. Concepts with names that exist in files of the same name but with higher precedence are only used for their `sources` entries, such that `hr` for `new_dataset` can be specified as follows, while concepts with non-existing names are treated as new concepts. + +``` +"hr": { + "sources": { + "new_dataset": [ + { + "ids": 6640, + "table": "numericitems", + "sub_var": "itemid" + } + ] + } +} +``` + +Central to providing the required flexibility for loading of certain data concepts that require some specific preprocessing are callback functions that can be specified for several *item* types. Functions (with appropriate signatures), designated as callback functions, are invoked on individual data items, before concept-related preprocessing is applied. A common scenario for this is unit of measurement conversion: In MIMIC-III data for example, several `itemid` values correspond to temperature measurements, some of which refer to temperatures measured in degrees Celsius whereas others are used for measurements in degrees Fahrenheit. As the information encoding which measurement corresponds to which `itemid` values is no longer available during concept-related preprocessing, this is best resolved at the level of individual data items. Several function factories are available for generating callback functions and `convert_unit()` is intended for covering unit conversions^[The presented implementation of this concept predates the addition of automatic unit conversion using the \pkg{units} package. While the concept definition as used by \pkg{ricu} will be updated to reflect these new capabilities, this example remains for illustration purposes.]. Data *items* corresponding to the `temp` concept for MIMIC-III are specified as + +``` +{ + "mimic_demo": [ + { + "ids": [676, 677, 223762], + "table": "chartevents", + "sub_var": "itemid" + }, + { + "ids": [678, 679, 223761, 224027], + "table": "chartevents", + "sub_var": "itemid", + "callback": "convert_unit(fahr_to_cels, 'C', 'f')" + } + ] +} +``` + +indicating that for ID values 676, 677 and 223762 no preprocessing is required and for the remaining ID values the function `fahr_to_cels()` is applied to entries of the `val_var` column where the regular expression `"f"` is `TRUE` for the `unit_var` column (the values of which being ultimately replaced with `"C"`). + +# Data sources + +Every dataset is represented by an environment with class attributes and associated metadata objects stored as object attributes to that environment. Dataset environments all inherit from `src_env` and from any number of class names constructed from data source name(s) with a suffix `_env` attached. The environment representing MIMIC-III, for example inherits from `src_env` and `mimic_env`, while the corresponding demo dataset inherits from `src_env`, `mimic_env` and `mimic_demo_env`. These sub-classes are later used for tailoring the process of data loading to particularities of individual datasets. + +A `src_env` contains an active binding per associated table, which returns a `src_tbl` object representing the requested table. As is the case for `src_env` objects, `src_tbl` objects inherit from additional classes for reasons explained above. The `admissions` table of the MIMIC-III demo dataset for example, inherits from `mimic_demo_tbl` and `mimic_tbl` (alongside classes `src_tbl` and `prt`). + +```{r mimic-adm, eval = srcs_avail("mimic_demo")} +mimic_demo$admissions +``` + +Powered by the \pkg{prt} \citep{bennett2021} package, `src_tbl` objects represent row-partitioned tabular data stored as multiple binary files created by the \pkg{fst} \citep{klik2020} package. In addition to standard subsetting, `prt` objects can be subsetted via the base \proglang{R} S3 generic function `subset()` and using non-standard evaluation (NSE): + +```{r mimic-sub, eval = srcs_avail("mimic_demo")} +subset(mimic_demo$admissions, subject_id > 44000, language:ethnicity) +``` + +This syntax makes it possible to read row-subsets of *long* tables into memory with little memory overhead. While terseness of such an API does introduce potential ambiguity, this is mostly overcome by using the tidy eval framework provided by \pkg{rlang} \citep{wickham2020}: + +```{r mimic-tidy, eval = srcs_avail("mimic_demo")} +subject_id <- 44000:45000 +subset(mimic_demo$admissions, .data$subject_id %in% .env$subject_id, + subject_id:dischtime) +``` + +By using \pkg{rlang} pronouns (`.data` and `.env`), the distinction can readily be made between a name referring to an object within the context of the data and an object within the context of the calling environment. + +## Data source setup + +In order to make a dataset accessible to \pkg{ricu}, three steps are necessary, each handled by an exported S3 generic function: `download_scr()`, `import_src()` and `attach_src()`. The first two steps, data download and import, are one-time procedures, whereas attaching is carried out every time the package namespace is loaded. By default, all data sources known to \pkg{ricu} are configured to be attached and in case some data is missing for a given data source, the missing data is downloaded and imported on first access. An outline of the steps involved for data source setup is shown in Figure \ref{fig:src-setup}. + +```{tikz, src-setup, fig.cap = "Making a dataset available to \\pkg{ricu} involves several steps, starting with data download, followed by preparation for efficient access and finalized by instantiation of data structures containing relevant metadata. The functions which are used for each step are displayed above arrows and below (in red) are indicated specific configuration settings or environment variables which are need for (or can be used to customize) the specific step.", fig.ext = "png", cache = TRUE, echo = FALSE, eval = TRUE} + +<> + +\begin{tikzpicture} + + \node [f1, label={above left:{a}}] (ricu) at (0, 19) { + \texttt{ricu} installed\\ no data (apart from\\ demo datasets) + }; + \node [f1, label={above left:{b}}] (csv) at (10, 19) { + raw tables\\ (.csv files) + }; + \node [f1, label={above left:{c}}] (fst) at (0, 12) { + (partitioned) \texttt{fst}\\ tables (\texttt{prt} objects) + }; + \node [f1, label={above left:{d}}] (env) at (10, 12) { + queryable \texttt{src\_env}\\ containing \texttt{src\_tbl}\\ objects + }; + + \draw [-Stealth] (ricu) to [bend right = 0] node[above, rotate=0]{ + \texttt{download\_src()} + } node[f2, below, rotate=0]{ + \texttt{RICU\_PHYSIONET\_USER}\\ \texttt{RICU\_PHYSIONET\_PASS}\\ + \texttt{RICU\_AUMC\_TOKEN} + } (csv); + \draw [-Stealth] (csv) to [bend right = 0] node[above, rotate=35]{ + \texttt{import\_src()} + } node[f2, below, rotate=35]{ + \texttt{RICU\_DATA\_PATH}\\ \texttt{RICU\_CONFIG\_PATH}\\ + \texttt{tbl\_cfg} + } (fst); + \draw [-Stealth] (fst) to [bend right = 0] node[above, rotate=0]{ + \texttt{attach\_src()} + } node[f2, below, rotate=0]{ + \texttt{RICU\_SRC\_LOAD}\\ \texttt{id\_cfg}, \texttt{col\_cfg} + } (env); + +\end{tikzpicture} +``` + +### Data download + +The first step towards accessing data is data download, taken care of by the S3 generic function `download_src()`. For the datasets included with \pkg{ricu}, prior to calling `download_src()`, the following environment variables can be set (indicated in red in the $a \to b$ edge in Figure \ref{fig:src-setup}): + +* `RICU_PHYSIONET_USER`/`RICU_PHYSIONET_PASS`: PhysioNet login credentials with access to the requested dataset(s). +* `RICU_AUMC_TOKEN`: Download token, extracted from the download URL received after being granted data access. + +If any of the required access credentials are not available as environment variables, they can be supplied as function arguments to `download_src()` or the user is queried in interactive sessions and an error is thrown otherwise. + +As a quick reminder on system requirements for initial data setup operations: Each of the supported datasets requires 5-10 GB disk space for permanent storage and 50-100 GB of temporary disk storage during download and import. Memory requirements are kept low (8-16 GB) by performing all setup operations only on subsets of rows at the time. Initial data source setup can be expected to take upwards of an hour per dataset. + +### Data import + +After successful data download, importing prepares tables for efficient random row- and column-access, for which the raw data format (.csv) is not well suited (see edge $b \to c$ in Figure \ref{fig:src-setup}). Tables are read in using \pkg{readr} \citep{hester2020}, potentially (re-)partitioned row-wise, and re-saved using \pkg{fst}. Environment variables that can be set to customize \pkg{ricu} data handling, relevant for import and attaching include: + +* `RICU_DATA_PATH`: Optional data storage location (if unset, this defaults to a system-specific, user-specific directory). The current value used for this setting can be queried by calling `data_dir()`. +* `RICU_CONFIG_PATH`: A comma-separated set of paths to directories containing configuration files. The current set of paths is retrievable by calling `config_paths()` and the ordering of paths determines precedence of how configuration files are combined (if multiple files of the same name are available). + +For importing, the information contained in `tbl_cfg` configuration objects is most relevant. This determines column data types, table partitioning and sanity checks like number of rows per table. Please refer to Section \ref{table-configuration} for more information on the construction of `tbl_cfg` objects. + +### Data attaching + +Finally, attaching a dataset creates a corresponding `src_env` object, containing a corresponding `src_tbl` object for each table, which together with associated metadata are used by \pkg{ricu} to run queries against the data (edge $c \to d$ in Figure \ref{fig:src-setup}). The environment variable `RICU_SRC_LOAD` may contain a comma-separated list of data source names that are set up for being automatically attached on namespace loading. This defaults to all currently supported datasets and the active set of source names is available as `auto_attach_srcs()`. Apart from this automatism, the process of attaching a dataset can be manually invoked by calling `attach_src()`, which can be convenient when for example updating the data source configuration after it has been modified. + +Two configuration objects which are important for data loading (see the following Section \ref{data-loading}) are `id_cfg` and `col_cfg` (described in Sections \ref{id-configuration} and \ref{default-column-configuration}, respectively), providing default values for certain types of columns, including time-stamp, measurement value and measurement unit column names, as well as defining relationships between patient identifiers (such as hospital stay ID and ICU stay ID). + +## Data loading + +The lowest level of data access is direct subsetting of `src_tbl` objects as shown at the start of Section \ref{data-sources}. As `src_tbl` inherits from `prt`, the `subset()` implementation provided by \pkg{prt} can be used for NSE of data-expressions against on-disk, tabular data. Building on that, several S3 generic functions successively homogenize data representations as visualized in Figure \ref{fig:data-loading}. + +```{tikz, data-loading, fig.cap = "Data loading proceeds through several layers, each contributing a step towards harmonizing discrepancies among raw data representations provided by the different data sources. Raw data tables are represented by \\pkg{ricu} as \\code{src\\_tbl} objects which can be queried using \\code{load\\_src()}. Absolute time-stamps in the returned \\code{data.table} are converted to times relative to admission (in minutes) by \\code{load\\_difftime()} and finally, \\code{load\\_id()}/\\allowbreak\\code{load\\_ts()}/\\allowbreak\\code{load\\_win()} ensure a given ID system and time interval.", fig.ext = "png", cache = TRUE, echo = FALSE, eval = TRUE} + +<> + +\begin{tikzpicture} + + \node [f1, label={above left:{a}}] (fst) at (0, 19) { + \texttt{src\_tbl} object\\ on-disk table + }; + \node [f1, label={above left:{b}}] (dt) at (10, 19) { + \texttt{data.table object}\\ in-memory table + }; + \node [f1, label={above left:{c}}] (dat) at (0, 12) { + \texttt{data.table object}\\ minute resolution\\ in-data ID + }; + \node [f1, label={above left:{d}}] (tbl) at (10, 12) { + \texttt{id\_tbl} object\\ requested resolution\\ requested ID + }; + + \draw [-Stealth] (dt) to [bend right = 0] node[above, rotate=0]{ + \texttt{load\_src()} + } node[f2, below, rotate=0]{ + \texttt{subset()} + } (fst); + \draw [-Stealth] (dat) to [bend right = 0] node[above, rotate=35]{ + \texttt{load\_difftime()} + } node[f2, below, rotate=35]{ + column config\\ \texttt{id\_origin()} + } (dt); + \draw [-Stealth] (tbl) to [bend right = 0] node[above, rotate=0]{ + \texttt{load\_id()}/\texttt{load\_ts()}/\texttt{load\_win()} + } node[f2, below, rotate=0]{ + ID config\\ \texttt{id\_windows()} + } (dat); + +\end{tikzpicture} +``` + +The most basic layer in data loading is provided by the S3 generic function `load_src()`, which provides a string-based interface to the `cols` argument of `subset()` while forwarding the unevaluated expression passed as `rows` (see edge $a \to b$ in Figure \ref{fig:data-loading}). + +```{r load-src, eval = srcs_avail("mimic_demo")} +load_src(mimic_demo$admissions, subject_id > 44000, + cols = c("hadm_id", "admittime", "dischtime")) +``` + +As data sources differ in their representation of time-stamps, a next step in data homogenization is to converge to a common format: the time difference to the origin time-point of a given ID system (for example ICU admission). + +```{r load-dt, eval = FALSE} +load_difftime(mimic_demo$admissions, subject_id > 44000, + cols = c("hadm_id", "admittime", "dischtime")) +``` + +```{r load-dt-print, eval = srcs_avail("mimic_demo"), echo = FALSE} +load_difftime(mimic_demo$admissions, subject_id > 44000, + cols = c("hadm_id", "admittime", "dischtime"))[] +``` + +The function `load_difftime()` is expected to return timestamps as base \proglang{R} `difftime` vectors (in minutes; edge $b \to c$ in Figure \ref{fig:data-loading}). The argument `id_hint` can be used to specify a preferred ID system, but if not available in raw data, `load_difftime()` will return data using the ID system with highest cardinality (i.e., ICU stay ID is preferred over hospital stay ID). In the above example, if `icustay_id` were requested, data would be returned using `hadm_id`, whereas a `subject_id` request would be honored, as the corresponding ID column is available in the `admissions` table. + +Building on `load_difftime()` functionality, functions `load_id()`/\allowbreak`load_ts()`/\allowbreak`load_win()` return `id_tbl`/\allowbreak`ts_tbl`/\allowbreak`win_tbl` objects with the requested ID system (passed as `id_var` argument). This uses raw data IDs if available or calls `change_id()` in order to convert to the desired ID system (edge $c \to d$ in Figure \ref{fig:data-loading}). Similarly, where `load_difftime()` returns data with fixed time interval of one minute, `load_id()` allows for arbitrary time intervals (using `change_interval()`; defaults to 1 hour). + +```{r load-id, eval = FALSE} +load_id(mimic_demo$admissions, subject_id > 44000, + cols = c("admittime", "dischtime"), id_var = "hadm_id") +``` + +```{r load-id-print, eval = srcs_avail("mimic_demo"), echo = FALSE} +load_id(mimic_demo$admissions, subject_id > 44000, + cols = c("admittime", "dischtime"), id_var = "hadm_id")[] +``` + +Throughout several of theses functions, `col_cfg` objects are used to provide sensible defaults. In order to convert to relative times, `load_difftime()`, for example, requires names of columns for which this applies (provided by the `time_vars` entry), and `load_ts()` needs to know which of the `time_vars` to use as `index_var`. For more information on the construction of `col_cfg` objects, please refer to Section \ref{default-column-configuration}. + +A call to `change_id()` requires the construction of a table which contains the mapping between different ID systems, together with information about how to convert timestamps between these ID systems (edge $c \to d$ in Figure \ref{fig:data-loading}). The function responsible for providing the necessary information is `id_windows()` and the associated S3 generic function `id_win_helper()`. The entry point `id_windows()` wraps `id_win_helper()`, providing memoization, as the resulting structure is expensive to compute relative to the frequency of being required. + +```{r id-win, eval = srcs_avail("mimic_demo")} +id_windows(mimic_demo) +``` + +Analogously, the function pair `id_origin()` and `id_orig_helper()`, with the former wrapping the latter and again providing memoization, is used for datasets where time-stamps are represented by absolute times, returning the origin time-points for a given ID system which then can be used to calculate relative times (edge $b \to c$ in Figure \ref{fig:data-loading}). + +```{r id-orig, eval = srcs_avail("mimic_demo")} +id_origin(mimic_demo, "icustay_id") +``` + +For the included datasets, the implementations of `id_win_helper()` and `id_orig_helper()`, use information contained in `id_cfg` objects (see Section \ref{id-configuration}) to determine which columns in which tables are required for constructing the corresponding lookup tables. Doing so, however, is not necessary: an `id_win_helper()` implementation for a new dataset could forego this by hard-coding table/column names as part of the function logic, in-turn simplifying the corresponding `id_cfg` object to merely providing naming and ordering information. + +## Data source configuration + +Data source environments (and corresponding `src_tbl` objects) are constructed using source configuration objects: list-based structures, inheriting from `src_cfg` and from any number of data source specific class names with suffix `_cfg` appended (as discussed at the beginning of Section \ref{data-sources}). The exported function `load_src_cfg()` reads a JSON formatted file and creates a `src_cfg` object per data source and further therein contained objects. + +```{r mimic-cfg, eval = TRUE} +cfg <- load_src_cfg("mimic_demo") +str(cfg, max.level = 3L, width = 70L) +mi_cfg <- cfg[["mimic_demo"]] +``` + +In addition to required fields `name` and `prefix` (used as class prefix), as well as further arbitrary fields contained in `extra` (`url` in this case), several configuration objects are part of `src_cfg`: `id_cfg`, `col_cfg` and `tbl_cfg`. + +### ID configuration + +An `id_cfg` object contains an ordered set of key-value pairs representing patient identifiers in a dataset. An implicit assumption currently is that a given patient ID system is used consistently throughout a dataset, meaning that for example an ICU stay ID is always referred to by the same name throughout all tables containing a corresponding column. Owing to the relational origins of these datasets this has been fulfilled in all instances encountered so far. In MIMIC-III, ID systems + +```{r mimic-ids, eval = TRUE} +as_id_cfg(mi_cfg) +``` + +are available, allowing for identification of individual patients, their (potentially multiple) hospital admissions over the course of the years and their corresponding ICU admissions (as well as potential re-admissions). Ordering corresponds to cardinality: moving to larger values implies moving along a one-to-many relationship. This information is used in data-loading, whenever the target ID system is not contained in the raw data. + +### Default column configuration + +Again used in data loading, this per-table set of key-value pairs specifies column defaults as `col_cfg` object. Each key describes a type of column with special meaning and the corresponding value specifies said column for a given table. The print method for `col_cfg` reports all keys alongside the per-table counts of accordingly registered values (i.e., columns). + +```{r mimic-col, eval = TRUE} +as_col_cfg(mi_cfg) +``` + +The following column defaults are currently in use throughout \pkg{ricu} but the set of keys can be extended to arbitrary new values: + +* `id_var`: In case a table does not contain at least one ID column corresponding to one of the ID systems specified as `id_cfg`, the default ID column can be set on a per-table basis as `id_var`^[This for example is the case for the `d_items` table in MIMIC-III, which does not contain any patient related data, but holds information on items encoding types of measurements, procedures, etc., used throughout other tables holding actual patient data.]. +* `index_var`: A column that is used to define an ordering in time over rows, thereby providing a time series index^[For the MIMIC-III table `inputevents_mv`, of the four available time variables (`starttime`, `endtime`, `storetime`, `comments_date`), `starttime` lends itself to be used as index variable more than the other candidates and therefore is set as default.]. +* `time_vars`: Columns which will be treated as time variables (important for converting between ID systems for example), but not as time series indices^[In case of the `admissions` table in MIMIC-III for example, a total of five columns are considered to be time variables, none of which stands out as potential `index_var`.]. +* `unit_var`: Used in concept loading (more specifically for `num_cncpt` concepts, see Section \ref{concept-specification}) to identify columns that represent unit of measurement information. +* `val_var`: Again used when loading data concepts, this identified a default value variable in a table, representing the column of interest to be used as returned data column. + +While `id_var`, `index_var` and `time_vars` are used to provide sensible defaults to functions used for general data loading (Section \ref{data-loading}), `unit_var`, `val_var`, as well as potential user-defined defaults are only used in concept loading (see Section \ref{ready-to-use-concepts}) and therefore need not be prioritized when integrating new data sources until data concepts have been mapped. + +### Table configuration + +Finally, `tbl_cfg` objects are used during the initial setup of a data source. In order to create a representation of a table that is accessible by \pkg{ricu} from raw data, several key pieces of information are required: + +* File name(s): In the simplest case, a single file corresponds to a single table. Other scenarios that have been encountered (and are therefore handled) include tables partitioned into multiple files and .tar archives containing multiple tables. + +* Column specification: For each column, the expected data type has to be known, as well as a pair of names, one corresponding to the raw data column name and one corresponding to the column name to be used within \pkg{ricu}. + +* (Optional) number of rows: Used as sanity check whenever available. + +* (Optional) partitioning information: For very *long* tables it can be useful to specify a row-partitioning. This currently is only possible by applying a vector of breakpoints to a single numeric column, thereby defining a grouping. + +Table configuration objects are only used within the context of the functions `download_src()` and `import_src()` and are therefore not required if download and import are carried out manually. + +```{r mimic-tbl, eval = TRUE} +as_tbl_cfg(mi_cfg) +``` + +For the `chartevents` table of the MIMIC-III demo dataset, rows are partitioned into two groups, while all other tables are represented by a single partition. Furthermore, the expected number of rows is unknown (`??`) as this is missing from the corresponding `tbl_cfg` object. + +## Adding external datasets + +In order to add a new dataset to \pkg{ricu}, several aspects outlined in the previous subsections require consideration. For illustration purposes, code for integrating AmsterdamUMCdb as external dataset is available from [GitHub](https://github.com/eth-mds/aumc). While this is no longer needed for using the `aumc` data source, the repository will remain as it might serve as template to integration of new datasets. Throughout this repository (and the following paragraphs), the AmsterdamUMCdb data treated as an \pkg{ricu}-external dataset is referred to as `aumc_ext`. + +### Adding configuration information + +Central to adding a new dataset to \pkg{ricu} is providing some configuration information in a `data-sources.json` file pointed to by the environment variable `RICU_CONFIG_PATH`. Depending on particularities of the dataset in question, corresponding implementations of some of the S3 generic functions mentioned throughout Sections \ref{data-source-setup} and \ref{data-loading} might have to be provided. The amount of confirmation information required to get started also depends on the desired level of integration. As data download and import are one-time procedures, these steps can be carried out manually, negating the need for specifying column data types in `data-sources.json` and providing data source specific methods for the `download_src()` and `import_src()` generics. + +The basic organization of a data source configuration entry, as it could be used for `aumc_ext`, specified as JSON is as follows: + +``` +{ + "name": "aumc_ext", + "id_cfg": { + "patient": { + "id": "patientid", + "position": 1 + }, + "icustay": { + "id": "admissionid", + "position": 2 + } + }, + "tables": { + ... + } +} +``` + +The shown `id_cfg` entry represents the minimally required set of entries, where for each ID specification, `start`, `end` and `table` are omitted (when compared to the `aumc` configuration provided by \pkg{ricu}). The `tables` entry expands to something like the following: + +``` +"tables": { + "freetextitems": { + }, + "drugitems": { + "defaults": { + "index_var": "start", + "val_var": "dose", + "unit_var": "doseunit", + "time_vars": ["start", "stop"] + } + }, + "numericitems": { + "defaults": { + "index_var": "measuredat", + "val_var": "value", + "unit_var": "unit", + "time_vars": ["measuredat", "registeredat", "updatedat"] + }, + "partitioning": { + "col": "", + "breaks": [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 + ] + } + }, + ... +} +``` + +Minimally required is simply an entry indicating the data source membership of a table (if not partitioned; cf., `freetextitems`). This does slightly complicate data exploration, as if no `defaults` are available, no default values can be provided to calls to `load_ts()` and related functions and therefore repeatedly have to be specified in corresponding function calls. Also, when specifying data items in such a setup, the per-table column names for special columns such as `index_var`, `val_var`, etc., have to be repeated for each individual item entry. + +For partitioned tables, the basic structure of a `partitioning` entry is required, but the content itself is irrelevant, as this is only used for setup (cf., `numericitems`). The length of `breaks`, however, is required to match the number of partitions (i.e., a length 23 `breaks` specification corresponds to a partitioning into 24 row-groups.)^[Originally it was intended to use partitioning information during data loading in order to narrow down the set of partitions that have to be accessed. So far, this optimization has not been implemented.]. The directory containing such a `data-sources.json` can then be pointed to by the environment variable `RICU_CONFIG_PATH`, making it available to \pkg{ricu}. + +### Enabling data loading + +As for functions that are required, currently there is no default method available for the loading step provided by `load_difftime()` and most likely an implementation of the generic function `id_win_helper()` will be required as well. For `aumc_ext`, `load_difftime()` could be implemented as + +```{r, ext-difftime} +ms_as_min <- function(x) { + as.difftime(as.integer(x / 6e4), units = "mins") +} + +aumc_difftime <- function(x, rows, cols = colnames(x), + id_hint = id_vars(x), + time_vars = ricu::time_vars(x), ...) { + + if (id_hint %in% colnames(x)) { + id_sel <- id_hint + } else { + id_opt <- id_var_opts(sort(as_id_cfg(x), decreasing = TRUE)) + id_sel <- intersect(id_opt, colnames(x))[1L] + } + + stopifnot(is.character(id_sel), length(id_sel) == 1L) + + if (!id_sel %in% cols) { + cols <- c(id_sel, cols) + } + + time_vars <- intersect(time_vars, cols) + + dat <- load_src(x, {{ rows }}, cols) + dat <- dat[, c(time_vars) := lapply(.SD, ms_as_min), + .SDcols = time_vars] + + as_id_tbl(dat, id_vars = id_sel, by_ref = TRUE) +} +``` + +Such a function attempts to use the ID as requested as `id_hint`, but falls back to the best possible alternative (using the ordering as previously specified in the `id_cfg` JSON configuration) if not provided by the data. The helper function `id_var_opts()` returns the dataset-specific column names of an `id_cfg` object (as opposed to the dataset-agnostic ID names; cf., `subject_id` and `patient`). Both the row-subsetting expression and column selection are passed on to `load_src()` and all columns specified as `time_vars` are converted to `difftime` vectors in minutes. Operations can safely be carried out using by-reference semantics, as intermediate objects are not exposed to the user. + +For a possible implementation of the `id_win_helper()` generic, column and table names to assemble the desired lookup table are hard coded instead of provided by the corresponding `id_cfg` object (as is the case in the \pkg{ricu}-internal implementation). + +```{r, ext-win} +aumc_windows <- function(x) { + + ids <- c("admissionid", "patientid") + sta <- c("admittedat", "firstadmittedat") + end <- c("dischargedat", "dateofdeath") + + tbl <- as_src_tbl(x, "admissions") + + res <- tbl[, c(ids, sta[1L], end)] + res <- res[, c(sta[2L]) := 0L] + res <- res[, c(sta, end) := lapply(.SD, ms_as_min), + .SDcols = c(sta, end)] + + res <- data.table::setcolorder(res, c(ids, sta, end)) + res <- rename_cols(res, c(ids, paste0(ids, "_start"), + paste0(ids, "_end")), by_ref = TRUE) + + as_id_tbl(res, ids[2L], by_ref = TRUE) +} +``` + +As all the required information is available form the `admissions` table, `aumc_windows()` simply loads the corresponding columns, converts them to minute resolution, followed by some renaming. ICU admissions and discharges in this table are relative to initial hospital admissions and therefore an all-zero column `firstadmittedat` is added and the `id_var` of the resulting `id_tbl` is marked as `patientid`^[The patient ID created in this way is different to that available for MIMIC-III, where patient date of birth is provided. An approximate date of birth could be constructed if ages were reported more precisely, but given the rough binning available here, this might be considered an acceptable limitation of resulting patient IDs. Nevertheless awareness of such differences in data presentation is important.]. + +A final step in making a new dataset accessible to \pkg{ricu} lies in specifying concept items. To this end, a file `concept-dict.json` can be added to the directory pointed to by the environment variable `RICU_CONFIG_PATH`, containing entries like the following, which will make it possible to use the `hr` concept across all datasets included with \pkg{ricu}, alongside the newly added dataset. + +``` +{ + "hr": { + "sources": { + "aumc_ext": [ + { + "ids": 6640, + "table": "numericitems", + "sub_var": "itemid" } + ] } - ``` + } +} +``` + +The above outline serves as an example on how to proceed when adding new data to \pkg{ricu}. Aspects like having multiple patient IDs, for example, could be further simplified^[An example for such a reduced setup is available from the [AUMC GitHub repository](https://github.com/eth-mds/aumc) as `aumc_min`. Moving to only a single patient identifier also does away with the need for a `id_win_helper()` implementation, as `change_id()` will not be called in such a scenario.]. Owing to the extensive use of S3 generic functions, \pkg{ricu} offers considerable flexibility for customizing certain behavior to specifics of a given data source, while providing fallback procedures whenever more general treatment can be applied. + +### Summary of required steps + +Summarizing aspects explained in more detail in the previous sections, the following points list the required steps for adding new data in the order they should be considered in. The approach taken here being is to start simple and expand. + +1. Tables saved as `.fst` files should be moved to the folder returned by `src_data_dir()` when passed the dataset name (alternatively, methods implementing `src_download()` and `src_import()` are required). + +1. A minimal data source configuration file `data-sources.json` is required in the directory pointed to by `RICU_CONFIG_PATH`. For AmsterdamUMCdb, this could be as minimal as (assuming no partitioning): + + { + "name": "aumc_min", + "id_cfg": { + "icustay": "admissionid" + }, + "tables": { + "admissions": {}, + "drugitems": {}, + "freetextitems": {}, + "listitems": {}, + "numericitems": {}, + "procedureorderitems": {}, + "processitems": {} + } + } + + File names have to match table names, i.e., the admissions table should be named `admissions.fst`. Upon a call to `attach_src()` (or next loading of the package and having added the data source name to `RICU_SRC_LOAD`) the new data source can be explored using `load_src()`. + +1. A `load_difftime()` method is required, which: + + - passes a row-subsetting expression to `load_src()` using the \pkg{rlang} curly-curly operator, + - converts columns passed as `time_vars` to minute-resolution `difftime` vectors, + - returns an `id_tbl` object where patient identifiers are chosen such that time-stamps are relative to corresponding admission, + - (optionally) uses the column passed as `id_hint` for patient identifiers, if multiple identifiers are available from data. + + Upon registering this method with S3 dispatch, higher-level data loading functions such as `load_ts()` become available (given that no changes in patient identifiers are requested). - and this can (given that it is saved as `concept-dict.json` in a directory pointed to by `RICU_CONFIG_PATH`) then be loaded using `load_concepts()` as +1. (Optional) if the source configuration specifies multiple patient identifiers which are not all available from all tables directly, an implementation of `id_win_helper()` most likely will be required (see Section \ref{data-loading}). - ```{r, eval = FALSE} - load_concepts("ldh", "mimic_demo") - ``` +1. Now, the source configuration can be expanded with per-table column defaults and data items can be added to the concepts included with \pkg{ricu} by creating a `concept-dict.json` under the path pointed to by `RICU_CONFIG_PATH`. For more information on readily available concepts, refer to Section \ref{ready-to-use-concepts} and for specifying new concepts altogether, pointers are available in section \ref{concept-specification}. - For further details on constructing concepts, refer to documentation at `?concept` and `?item`. +# Examples + +In order to briefly illustrate how \pkg{ricu} could be applied to real-world clinical questions, two examples are provided in the following sections. The first example fully relies on data concepts that are included with \pkg{ricu}. Whereas the second one explores both how some data preprocessing can be added to an existing concept, by creating a recursive concept (or `rec_cncpt`), as well as how to create an entirely new data concept in code (instead of JSON specification as outlined in Section \ref{concept-specification}), using constructors `item()` and `concept()`. + +## Lactate and mortality + +First, the association of lactate levels and mortality is investigated. This problem has been studied before and it is widely accepted that both static and dynamic lactate indices are associated with increased mortality \citep{haas2016, nichol2011, van2013}. In order to model this relationship, a time-varying proportional hazards Cox model \citep{therneau2000, therneau2015} is fitted, which includes the SOFA score as a general predictor of illness severity, using MIMIC-III demo data. Furthermore, for the sake of this example, the patient cohort is defined to be patients admitted from 2008 onwards (corresponding to the MetaVision database) of ages 20 to 90 years old. + +```{r cox-surv, eval = srcs_avail("mimic_demo")} +src <- "mimic_demo" + +cohort <- load_id("icustays", src, dbsource == "metavision", + cols = NULL) +cohort <- load_concepts("age", src, patient_ids = cohort, + verbose = FALSE) + +dat <- load_concepts(c("lact", "death", "sofa"), src, + patient_ids = cohort[age > 20 & age < 90, ], + verbose = FALSE) + +dat <- dat[, + head(.SD, n = match(TRUE, death, .N)), by = c(id_vars(dat)) +] + +dat <- fill_gaps(dat) + +dat <- replace_na(dat, c(NA, FALSE), type = c("locf", "const"), + by_ref = TRUE, vars = c("lact", "death"), + by = id_vars(dat)) + +cox_mod <- coxph( + Surv(charttime - 1L, charttime, death) ~ lact + sofa, + data = dat +) +``` + +After loading the data, some minor preprocessing is still required before modeling: first, data is filtered such that only data up to (and including) the hour in which the `death` flag switches to `TRUE` is used. Following that, missing values for `lact` are imputed using a last observation carry forward (LOCF) scheme (observing the patient grouping) and missing `death` values are set to `FALSE`. The resulting model fit can be visualized as: + +```{r cox-plot, eval = srcs_avail(src), echo = FALSE, warning = FALSE, message = FALSE, fig.width = 8} +theme_fp <- function(...) { + theme_bw(...) + + theme(panel.grid.major = element_blank(), panel.grid.minor = element_blank(), + axis.title.y = element_blank(), axis.title.x = element_blank(), + axis.text.y = element_blank(), axis.ticks.y = element_blank()) +} + +forest_model(cox_mod, theme = theme_fp(16)) +``` + +A simple exploration already shows that the increased values of lactate are associated with mortality, even after adjusting for the SOFA score. Using abstractions provided by \pkg{ricu}, this analysis could now also be applied to other datasets with minimal effort. + +## Diabetes and insulin treatment + +For the next example, again using MIMIC-III demo data, comorbidities and treatment related information are used: the amount of insulin administered to patients in the first 24 hours from their ICU admission is analyzed, in connection with diabetic status, in order to determine whether diabetic patients receive more insulin over that time-span, when compared to non-diabetic patients. For this, two concepts are introduced: `ins24`, a binned variable representing the cumulative amount of insulin administered within the first 24 hours of an ICU admission, and `diab`, a logical variable encoding diabetes comorbidity. + +As there already is an insulin concept available, `ins24` can be implemented as `rec_cncpt`, requesting data from the `ins` concept. In order to be able to calculate the total amount of insulin administered, it is required to change the default aggregation method from `median()` to `sum()`. Failing to do so would yield under-reported values whenever several insulin administrations fall within a given time-step. The callback function `ins_cb()` is then inserted into the loading process, performing of the preprocessing steps outlined above: first data is subsetted to fall into the first 24 hours of ICU admissions, followed by binning of summed values. + +```{r ins24, eval = srcs_avail(src)} +ins_breaks <- c(0, 1, 10, 20, 40, Inf) + +ins_cb <- function(ins, ...) { + + day_one <- function(x) x >= hours(0L) & x <= hours(24L) + + idx_var <- index_var(ins) + ids_var <- id_vars(ins) + + ins <- ins[ + day_one(get(idx_var)), list(ins24 = sum(ins)), by = c(ids_var) + ] + + ins <- ins[, + ins24 := list(cut(ins24, breaks = ins_breaks, right = FALSE)) + ] + + ins +} + +ins24 <- load_dictionary(src, "ins") +ins24 <- concept("ins24", ins24, "insulin in first 24h", + aggregate = "sum", callback = ins_cb, + target = "id_tbl", class = "rec_cncpt") +``` + +The binary diabetes concept can be implemented as `lgl_cncpt`, for which ICD-9 codes are matched using a regular expression. As not only the subset of diabetic patients is of interest, a `col_itm` is more suited for diabetes status retrieval over a `rgx_itm`. For creating the required callback function, which produces a logical vector, the exported function factory `transform_fun()` can be employed, coupled with a function like `grep_diab()`, performing the desired transformation. The two concepts are then combined using `c()` and loaded via `load_concepts()`. + +```{r diab, eval = srcs_avail(src)} +grep_diab <- function(x) { + grepl("^250\\.?[0-9]{2}$", x) +} + +diab <- item(src, table = "diagnoses_icd", + callback = transform_fun(grep_diab), + class = "col_itm") + +diab <- concept("diab", diab, "diabetes", target = "id_tbl", + class = "lgl_cncpt") + +dat <- load_concepts(c(ins24, diab), id_type = "icustay", + verbose = FALSE) +dat <- replace_na(dat, "[0,1)", vars = "ins24") + +dat +``` + +Following this, the difference between the two groups can be visualized with a histogram over the binned insulin administration values: + +```{r diabetes-visualize, echo = FALSE, eval = srcs_avail(src), fig.height = 3} +dat <- dat[, weight := 1 / .N, by = diab] +ggplot(dat, aes(x = ins24, fill = diab)) + + stat_count(aes(weight = weight), alpha = 0.75, position = "dodge") + + labs(x = "Amount of administered insulin in first 24h of ICU stay [units]", + y = "Proportion of patients", + fill = "Diabetic") + + theme_bw(10) +``` + +The plot suggests that for the MetaVision cohort defined in the previous example (without age subsetting) and during the first day of ICU stay, perhaps unsurprisingly, with increasing insulin dosage, diabetic patients receive more insulin compared to non-diabetic patients. This effect is more pronounced when looking at the full MIMIC-III data instead of the demo subset which includes only data corresponding to roughly 130 ICU stays. + +# Acknowledgments + +Nicolas Bennett, Drago Plečko, Nicolai Meinshausen and Peter Bühlmann were supported by grant #2017-110 of the Strategic Focal Area "Personalized Health and Related Technologies (PHRT)" of the ETH Domain for the SPHN/PHRT Driver Project "Personalized Swiss Sepsis Study". + +```{r session-info, include = FALSE} +sessionInfo() +``` diff --git a/vignettes/jss.bib b/vignettes/ricu.bib similarity index 100% rename from vignettes/jss.bib rename to vignettes/ricu.bib diff --git a/vignettes/start.Rmd b/vignettes/start.Rmd new file mode 100644 index 00000000..6492f1ff --- /dev/null +++ b/vignettes/start.Rmd @@ -0,0 +1,151 @@ +--- +title: "Quick start guide" +output: rmarkdown::html_vignette +vignette: > + %\VignetteIndexEntry{Quick start guide} + %\VignetteEngine{knitr::rmarkdown} + %\VignetteEncoding{UTF-8} +--- + +```{r, setup, include = FALSE} +source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) + +knitr::opts_chunk$set( + collapse = TRUE, + comment = "#>" +) + +library(ricu) +``` + +```{r, assign-src, echo = FALSE} +src <- "mimic_demo" +``` + +```{r, assign-demo, echo = FALSE} +demo <- c(src, "eicu_demo") +``` + +```{r, demo-miss, echo = FALSE, eval = !srcs_avail(demo), results = "asis"} +demo_missing_msg(demo, "start.html") +knitr::opts_chunk$set(eval = FALSE) +``` + +In order to set up `ricu`, download of datasets from several platforms is required. Two data sources, `mimic_demo` and `eicu_demo` are available directly as R packages, hosted on Github. The respective full-featured versions `mimic` and `eicu`, as well as the `hirid` dataset are available from [PhysioNet](https://physionet.org), while access to the remaining standard dataset `aumc` is available from yet another [website](https://amsterdammedicaldatascience.nl/). The following steps guide through package installation, data source set up and conclude with some example data queries. + +## Package installation + +Stable package releases are available from [CRAN](https://cran.r-project.org/package=ricu) as + +```{r, eval = FALSE} +install.packages("ricu") +``` + +and the latest development version is available from [GitHub](https://github.com/eth-mds/ricu) as + +```{r, eval = FALSE} +remotes::install_github("eth-mds/ricu") +``` + +## Demo datasets + +The demo datasets `r paste1(demo)` are listed as `Suggests` dependencies and therefore their availability is determined by the value passed as `dependencies` to the above package installation function. The following call explicitly installs the demo data set packages + +```{r, echo = FALSE, eval = TRUE, results = "asis"} +cat( + "```r\n", + "install.packages(\n", + " c(", paste0("\"", sub("_", ".", demo), "\"", collapse = ", "), "),\n", + " repos = \"https://eth-mds.github.io/physionet-demo\"\n", + ")\n", + "```\n", + sep = "" +) +``` + +## Full datasets + +Included with `ricu` are functions for download and setup of the following datasets: `mimic` (MIMIC-III), `eicu`, `hirid`, `aumc` and `miiv` (MIMIC-IV), which can be invoked in several different ways. + +- To begin with, a directory is needed where the data can permanently be stored. The default location is platform dependent and can be overridden using the environment variable `RICU_DATA_PATH`. The current value can be retrieved by calling `data_dir()`. +- Access to both the PhysioNet datasets (MIMIC-III, eICU and HiRID), as well as to AUMCdb is free but credentialed. In addition to setting up an [account with PhysioNet](https://physionet.org/register/), [credentialing](https://physionet.org/settings/credentialing/) is required, which, in the case of HiRID, must also be followed by submitting an [access request](https://physionet.org/request-access/hirid/1.1.1/) to the data-owners. Details on the procedure for requesting access to AUMCdb is available from [here](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) and consists of filling out a [form](https://amsterdammedicaldatascience.nl/arfeula_v1.6.pdf) and completing a training course such as [Data or Specimens Only Research (DSOR)](https://physionet.org/about/citi-course/) which, together with proof of training course completion, can be submitted by [email](mailto:access@amsterdammedicaldatascience.nl). +- If raw data in `.csv` form has already been downloaded, this can be decompressed and copied to an appropriate sub-folder (`mimic`, `eicu`, `hirid` or `aumc`) to the directory identified by `data_dir()`. +- In order to have `ricu` download the required data, login credentials can be supplied as environment variables `RICU_PHYSIONET_USER`/`RICU_PHYSIONET_PASS` and `RICU_AUMC_TOKEN` (the string the follows `token=` in the download URL received from the AUMCdb data owners) or entered into the terminal manually in interactive sessions. +- Enabling efficient random row/column access, `ricu` converts `.csv` files into a binary format using the [fst](https://cran.r-project.org/package=fst) package. +- Data conversion to `.fst` format (and potentially data download) is automatically triggered upon first access of a table. In interactive sessions, the user is asked for permission to setup the given data source and in non-interactive sessions, access to missing data throws an error. +- Instead of relying on first data access to trigger setup, up-front data conversion, possible preceded by data download, can be invoked by calling `setup_src_data()`. + +## Concept loading + +Many commonly used clinical data concepts are available for all data sources, where the required data exists. An overview of available concepts is available by calling `explain_dictionary()` and concepts can be loaded using `load_concepts()`: + +```{r, load-ts} +<> +<> + +head(explain_dictionary(src = demo)) +load_concepts("alb", src, verbose = FALSE) +``` + +Concepts representing time-dependent measurements are loaded as `ts_tbl` objects, whereas static information is retrieved as `id_tbl` object. Both classes inherit from `data.table` (and therefore also from `data.frame`) and can be coerced to any of the base classes using `as.data.table()` and `as.data.frame()`, respectively. Using `data.table` 'by-reference' operations, this is available as zero-copy operation by passing `by_ref = TRUE`^[While `data.table` by-reference operations can be very useful due to their inherent efficiency benefits, much care is required if enabled, as they break with the usual base R by-value (copy-on-modify) semantics.]. + +```{r, load-id} +(dat <- load_concepts("height", src, verbose = FALSE)) +head(tmp <- as.data.frame(dat, by_ref = TRUE)) +identical(dat, tmp) +``` + +Many functions exported by `ricu` use `id_tbl` and `ts_tbl` objects in order to enable more concise semantics. Merging an `id_tbl` with a `ts_tbl`, for example, will automatically use the columns identified by `id_vars()` of both tables, as `by.x`/`by.y` arguments, while for two `ts_tbl` object, respective columns reported by `id_vars()` and `index_var()` will be used to merge on. + +When loading form multiple data sources simultaneously, `load_concepts()` will add a `source` column (which will be among the `id_vars()` of the resulting object), thereby allowing to identify stay IDs corresponding to the individual data sources. + +```{r, load-mult} +load_concepts("weight", demo, verbose = FALSE) +``` + +## Extending the concept dictionary + +In addition to the ~100 concepts that are available by default, adding user-defined concepts is possible either as R objects or more robustly, as JSON configuration files. + +- Data concepts consist of zero, one, or several data items per data source, encoding how to retrieve the corresponding data. The constructors `concept()` and `item()` can be used to instantiate concepts as R objects. + + ```{r, create-concept, eval = srcs_avail("mimic_demo")} + ldh <- concept("ldh", + item("mimic_demo", "labevents", "itemid", 50954), + description = "Lactate dehydrogenase", + unit = "IU/L" + ) + load_concepts(ldh, verbose = FALSE) + ``` + +- Configuration files are looked for in both the package installation directory and in user-specified locations, either using the environment variable `RICU_CONFIG_PATH` or by passing paths as function arguments (`load_dictionary()` for example accepts a `cfg_dirs` argument). + +- Mechanisms for both extending and replacing existing concept dictionaries are supported by `ricu`. The file name of the default concept dictionary is called `concept-dict.json` and any file with the same name in user-specified locations will be used as extensions. In order to forgo the internal dictionary, a different file name can be chosen, which then has to be passed as function argument (`load_dictionary()` for example has a `name` argument which defaults to `concept-dict`) + +- A JSON-based concept akin to the one above can be specified as + + ``` + { + "ldh": { + "unit": "IU/L", + "description": "Lactate dehydrogenase", + "sources": { + "mimic_demo": [ + { + "ids": 50954, + "table": "labevents", + "sub_var": "itemid" + } + ] + } + } + } + ``` + + and this can (given that it is saved as `concept-dict.json` in a directory pointed to by `RICU_CONFIG_PATH`) then be loaded using `load_concepts()` as + + ```{r, eval = FALSE} + load_concepts("ldh", "mimic_demo") + ``` + + For further details on constructing concepts, refer to documentation at `?concept` and `?item`. From b2fcc6e9c58605f63951e37349970894b978ca78 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:02:55 +0100 Subject: [PATCH 21/37] speed up vignette build --- .Rbuildignore | 4 ++-- R/ricu.R | 3 ++- inst/extdata/vignettes/helpers.R | 4 ++++ vignettes/ricu.Rmd | 2 +- vignettes/uom.Rmd | 4 ++-- 5 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.Rbuildignore b/.Rbuildignore index 27de976f..192dead2 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -10,7 +10,7 @@ ^Meta$ ^cran-comments\.md$ ^CRAN-RELEASE$ -^vignettes/jss\.(aux|bbl|blg|bst|cls|knit\.md|log|out|pdf|R|tex)$ -^vignettes/(jss|uom)_(cache|files)$ +^vignettes/ricu\.(aux|bbl|blg|bst|cls|knit\.md|log|out|pdf|R|tex)$ +^vignettes/(ricu|uom)_(cache|files)$ ^inst/local_testdata ^CRAN-SUBMISSION$ diff --git a/R/ricu.R b/R/ricu.R index 32180338..faece7fb 100644 --- a/R/ricu.R +++ b/R/ricu.R @@ -19,6 +19,7 @@ pkg_env <- function() asNamespace(pkg_name()) release_questions <- function() { c( - "Was `release()` called with `args = \"--compact-vignettes=gs+qpdf\"`?" + "Was `release()` called with `args = \"--compact-vignettes=gs+qpdf\"`?", + "Did you unset `RICU_VIGNETTE_QUICK_BUILD`?" ) } diff --git a/inst/extdata/vignettes/helpers.R b/inst/extdata/vignettes/helpers.R index 955f7d38..630bf5dc 100644 --- a/inst/extdata/vignettes/helpers.R +++ b/inst/extdata/vignettes/helpers.R @@ -1,6 +1,10 @@ srcs_avail <- function(x) all(ricu::is_data_avail(x)) +quick_build <- function() { + identical(Sys.getenv("RICU_VIGNETTE_QUICK_BUILD"), "true") +} + paste1 <- function(x) { x <- paste0("`", x, "`") diff --git a/vignettes/ricu.Rmd b/vignettes/ricu.Rmd index 0103d7d3..132cf7d9 100644 --- a/vignettes/ricu.Rmd +++ b/vignettes/ricu.Rmd @@ -370,7 +370,7 @@ summarize <- function(src, avail) { ) } -if (srcs_avail(demo) && (!srcs_avail(srcs) || )) { +if (srcs_avail(demo) && (!srcs_avail(srcs) || quick_build())) { srcs <- demo } diff --git a/vignettes/uom.Rmd b/vignettes/uom.Rmd index 08075815..7b11b2eb 100644 --- a/vignettes/uom.Rmd +++ b/vignettes/uom.Rmd @@ -219,7 +219,7 @@ If no table is printed given the modified data loading above, every single ID th Next, we will investigate the number of measurements available per concept and stay day. For each stay ID and concept we calculate the number of measurements and note the stay duration. From this we can visualize how the number of measurements per day is distributed over the datasets alongside the percentage of patients that have at least one measurement available. -```{r, full-miss, echo = FALSE, eval = srcs_avail(demo) && !srcs_avail(srcs), results = "asis"} +```{r, full-miss, echo = FALSE, eval = srcs_avail(demo) && (!srcs_avail(srcs) || quick_build()), results = "asis"} demo_instead_full_msg(demo, srcs, "uom.html") ``` @@ -269,7 +269,7 @@ counts <- merge(counts, los[, list(total_pat = .N), by = "source"], head(counts) ``` -```{r, itm-load, cache = TRUE, ref.label = c("itm-funs", if (srcs_avail(srcs)) "assign-srcs" else "demo-srcs", "itm-counts")} +```{r, itm-load, cache = TRUE, ref.label = c("itm-funs", if (srcs_avail(srcs) && !quick_build()) "assign-srcs" else "demo-srcs", "itm-counts")} ``` ```{r, count-plot, echo = FALSE, fig.width = 6} From f5da8594e1d55c82dd60aeb02a8e4dbe9912355e Mon Sep 17 00:00:00 2001 From: Drago Date: Fri, 17 Mar 2023 12:52:31 -0400 Subject: [PATCH 22/37] add OMR to miiv --- inst/extdata/config/data-sources.json | 31 +++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/inst/extdata/config/data-sources.json b/inst/extdata/config/data-sources.json index a7ceea6e..4ada87fe 100644 --- a/inst/extdata/config/data-sources.json +++ b/inst/extdata/config/data-sources.json @@ -7173,6 +7173,37 @@ } } }, + "omr" : { + "files": "core/omr.csv.gz", + "defaults": { + "timevars": ["chartdate"], + "val_var": "result_value" + }, + "num_rows": 6439169, + "cols" : { + "subject_id": { + "name": "subject_id", + "spec": "col_integer" + }, + "chartdate": { + "name": "chartdate", + "spec": "col_datetime", + "format": "%Y-%m-%d" + }, + "seq_num": { + "name": "seq_num", + "spec": "col_integer" + }, + "result_name": { + "name": "result_name", + "spec": "col_character" + }, + "result_value": { + "name": "result_value", + "spec": "col_character" + } + } + }, "transfers": { "files": "core/transfers.csv.gz", "defaults": { From 5d67457dd7851dd3a4e54fc76eb3aae76f450a58 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sat, 8 Apr 2023 15:53:24 +0200 Subject: [PATCH 23/37] fix pillar issue --- DESCRIPTION | 2 +- NEWS.md | 5 ++++ R/concept-load.R | 16 +++++----- R/tbl-base.R | 6 +++- README.md | 51 ++++++++++++++++---------------- cran-comments.md | 3 +- man/change_id.Rd | 12 ++++---- man/data_env.Rd | 29 +++++++++--------- man/load_concepts.Rd | 16 +++++----- tests/testthat/_snaps/concept.md | 48 ------------------------------ tests/testthat/test-concept.R | 4 --- vignettes/ricu.Rmd | 8 +++-- 12 files changed, 83 insertions(+), 117 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 6ea1446a..50bef9d8 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Focused on (but not exclusive to) data sets hosted on PhysioNet functions for running arbitrary queries against available data sets, a system for defining clinical concepts and encoding their representations in tabular ICU data is presented. -Version: 0.5.4 +Version: 0.5.5 Authors@R: c( person(given = "Nicolas", family = "Bennett", diff --git a/NEWS.md b/NEWS.md index 4b1e28ef..7532d584 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,8 @@ +# ricu 0.5.5 + +* maintenance release: fixes an issue introduced by pillar 1.9.0 via an update + to 0.2.0 of prt + # ricu 0.5.4 * maintenance release: tests using `mockthat` are only run when available diff --git a/R/concept-load.R b/R/concept-load.R index a0387bb8..bca69bb3 100644 --- a/R/concept-load.R +++ b/R/concept-load.R @@ -33,14 +33,16 @@ #' generic functions. #' #' @section Concept: -#' Top-level entry points are either a character vector, which is used to -#' subset a `concept` object or an entire [concept +#' Top-level entry points are either a character vector of concept names or an +#' integer vector of concept IDs (matched against `omopid` fields), which are +#' used to subset a `concept` object or an entire [concept #' dictionary][load_dictionary()], or a `concept` object. When passing a -#' character vector as first argument, the most important further arguments at -#' that level control from where the dictionary is taken (`dict_name` or -#' `dict_dirs`). At `concept` level, the most important additional arguments -#' control the result structure: data merging can be disabled using -#' `merge_data` and data aggregation is governed by the `aggregate` argument. +#' character/integer vector as first argument, the most important further +#' arguments at that level control from where the dictionary is taken +#' (`dict_name` or `dict_dirs`). At `concept` level, the most important +#' additional arguments control the result structure: data merging can be +#' disabled using `merge_data` and data aggregation is governed by the +#' `aggregate` argument. #' #' Data aggregation is important for merging several concepts into a #' wide-format table, as this requires data to be unique per observation (i.e. diff --git a/R/tbl-base.R b/R/tbl-base.R index a18d8f99..c5126380 100644 --- a/R/tbl-base.R +++ b/R/tbl-base.R @@ -56,7 +56,11 @@ print.id_tbl <- function(x, ..., n = NULL, width = NULL) { #' @export format.id_tbl <- function(x, ..., n = NULL, width = NULL) { - format(prt::trunc_dt(x, n = n, width = width)) + if (packageVersion("prt") < "0.2.0") { + format(prt::trunc_dt(x, n = n, width = width)) + } else { + prt::format_dt(x, n = n, width = width) + } } #' @importFrom tibble tbl_sum diff --git a/README.md b/README.md index 19b25dc3..09df4ea1 100644 --- a/README.md +++ b/README.md @@ -77,28 +77,29 @@ Tables are available as mimic_demo$admissions ``` -

#> # <mimic_tbl>: [129 x 19]
+
#> # <mimic_tbl>: [129 ✖ 19]
 #> # ID options:  subject_id (patient) < hadm_id (hadm) < icustay_id (icustay)
 #> # Defaults:    `admission_type` (val)
 #> # Time vars:   `admittime`, `dischtime`, `deathtime`, `edregtime`, `edouttime`
 #>     row_id subject_id hadm_id admittime           dischtime
 #>      <int>      <int>   <int> <dttm>              <dttm>
-#>   1  12258      10006  142345 2164-10-23 21:09:00 2164-11-01 17:15:00
-#>   2  12263      10011  105331 2126-08-14 22:32:00 2126-08-28 18:59:00
-#>   3  12265      10013  165520 2125-10-04 23:36:00 2125-10-07 15:13:00
-#>   4  12269      10017  199207 2149-05-26 17:19:00 2149-06-03 18:42:00
-#>   5  12270      10019  177759 2163-05-14 20:43:00 2163-05-15 12:00:00
-#> ...
+#> 1    12258      10006  142345 2164-10-23 21:09:00 2164-11-01 17:15:00
+#> 2    12263      10011  105331 2126-08-14 22:32:00 2126-08-28 18:59:00
+#> 3    12265      10013  165520 2125-10-04 23:36:00 2125-10-07 15:13:00
+#> 4    12269      10017  199207 2149-05-26 17:19:00 2149-06-03 18:42:00
+#> 5    12270      10019  177759 2163-05-14 20:43:00 2163-05-15 12:00:00
+#> 
 #> 125  41055      44083  198330 2112-05-28 15:45:00 2112-06-07 16:50:00
 #> 126  41070      44154  174245 2178-05-14 20:29:00 2178-05-15 09:45:00
 #> 127  41087      44212  163189 2123-11-24 14:14:00 2123-12-30 14:31:00
 #> 128  41090      44222  192189 2180-07-19 06:55:00 2180-07-20 13:00:00
 #> 129  41092      44228  103379 2170-12-15 03:14:00 2170-12-24 18:00:00
-#> # ... with 119 more rows, and 14 more variables: deathtime <dttm>,
-#> #   admission_type <chr>, admission_location <chr>, discharge_location <chr>,
-#> #   insurance <chr>, language <chr>, religion <chr>, marital_status <chr>,
-#> #   ethnicity <chr>, edregtime <dttm>, edouttime <dttm>, diagnosis <chr>,
-#> #   hospital_expire_flag <int>, has_chartevents_data <int>
+#> # ℹ 124 more rows
+#> # ℹ 14 more variables: deathtime <dttm>, admission_type <chr>,
+#> #   admission_location <chr>, discharge_location <chr>, insurance <chr>,
+#> #   language <chr>, religion <chr>, marital_status <chr>, ethnicity <chr>,
+#> #   edregtime <dttm>, edouttime <dttm>, diagnosis <chr>,
+#> #   hospital_expire_flag <int>, has_chartevents_data <int>
 
and data can be loaded into an R session for example using @@ -108,23 +109,23 @@ load_ts("labevents", "mimic_demo", itemid == 50862L, cols = c("valuenum", "valueuom")) ``` -
#> # A `ts_tbl`: 299 x 4
+
#> # A `ts_tbl`: 299 ✖ 4
 #> # Id var:     `icustay_id`
 #> # Index var:  `charttime` (1 hours)
 #>     icustay_id charttime valuenum valueuom
 #>          <int> <drtn>       <dbl> <chr>
-#>   1     201006 NA             2.4 g/dL
-#>   2     203766 ~6h            2   g/dL
-#>   3     203766 ~4h            1.7 g/dL
-#>   4     204132 ~7h            3.6 g/dL
-#>   5     204201 ~9h            2.3 g/dL
-#> ...
-#> 295     298685 ~5d            1.9 g/dL
-#> 296     298685 ~6d            2   g/dL
-#> 297     298685 ~8d            2   g/dL
-#> 298     298685 ~11d           2.2 g/dL
-#> 299     298685 ~12d           2.5 g/dL
-#> # ... with 289 more rows
+#> 1       201006   0 hours      2.4 g/dL
+#> 2       203766 -18 hours      2   g/dL
+#> 3       203766   4 hours      1.7 g/dL
+#> 4       204132   7 hours      3.6 g/dL
+#> 5       204201   9 hours      2.3 g/dL
+#> 
+#> 295     298685 130 hours      1.9 g/dL
+#> 296     298685 154 hours      2   g/dL
+#> 297     298685 203 hours      2   g/dL
+#> 298     298685 272 hours      2.2 g/dL
+#> 299     298685 299 hours      2.5 g/dL
+#> # ℹ 294 more rows
 
which returns time series data as `ts_tbl` object. diff --git a/cran-comments.md b/cran-comments.md index 3ae1996b..47ced910 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -10,4 +10,5 @@ 0 errors | 0 warnings | 0 notes -* Maintenance release (move mockthat off CRAN) +* Maintenance release (fixes an issue introduced by pillar 1.9.0 via an update + to 0.2.0 of prt) diff --git a/man/change_id.Rd b/man/change_id.Rd index 27bc399f..8c3fa01e 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -72,18 +72,18 @@ hospital admissions as destination IDs. #> # Id var: `icustay_id` #> icustay_id hadm_id hadm_id_start hadm_id_end #> -#> 1 201006 198503 -3290 mins 9114 mins -#> 2 201204 114648 -2 mins 6949 mins -#> 3 203766 126949 -1336 mins 8818 mins -#> 4 204132 157609 -1 mins 10103 mins -#> 5 204201 177678 -368 mins 9445 mins +#> 1 201006 198503 -3290 mins 9114 mins +#> 2 201204 114648 -2 mins 6949 mins +#> 3 203766 126949 -1336 mins 8818 mins +#> 4 204132 157609 -1 mins 10103 mins +#> 5 204201 177678 -368 mins 9445 mins #> ... #> 132 295043 170883 -10413 mins 31258 mins #> 133 295741 176805 -1 mins 3153 mins #> 134 296804 110244 -1294 mins 4599 mins #> 135 297782 167612 -1 mins 207 mins #> 136 298685 151323 -1 mins 19082 mins -#> # ... with 126 more rows +#> # i 131 more rows }\if{html}{\out{}} Both start and end columns encode the hospital admission windows relative diff --git a/man/data_env.Rd b/man/data_env.Rd index 70cec38b..fa288b7c 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -88,21 +88,22 @@ mimic_demo$icustays #> # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) #> # Defaults: `intime` (index), `last_careunit` (val) #> # Time vars: `intime`, `outtime` -#> row_id subject_id hadm_id icustay_id dbsource first_careu~ last_careun~ -#> -#> 1 12742 10006 142345 206504 carevue MICU MICU -#> 2 12747 10011 105331 232110 carevue MICU MICU -#> 3 12749 10013 165520 264446 carevue MICU MICU -#> 4 12754 10017 199207 204881 carevue CCU CCU -#> 5 12755 10019 177759 228977 carevue MICU MICU +#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit +#> +#> 1 12742 10006 142345 206504 carevue MICU MICU +#> 2 12747 10011 105331 232110 carevue MICU MICU +#> 3 12749 10013 165520 264446 carevue MICU MICU +#> 4 12754 10017 199207 204881 carevue CCU CCU +#> 5 12755 10019 177759 228977 carevue MICU MICU #> ... -#> 132 42676 44083 198330 286428 metavis~ CCU CCU -#> 133 42691 44154 174245 217724 metavis~ MICU MICU -#> 134 42709 44212 163189 239396 metavis~ MICU MICU -#> 135 42712 44222 192189 238186 metavis~ CCU CCU -#> 136 42714 44228 103379 217992 metavis~ SICU SICU -#> # ... with 126 more rows, and 5 more variables: first_wardid , -#> # last_wardid , intime , outtime , los +#> 132 42676 44083 198330 286428 metavision CCU CCU +#> 133 42691 44154 174245 217724 metavision MICU MICU +#> 134 42709 44212 163189 239396 metavision MICU MICU +#> 135 42712 44222 192189 238186 metavision CCU CCU +#> 136 42714 44228 103379 217992 metavision SICU SICU +#> # i 131 more rows +#> # i 5 more variables: first_wardid , last_wardid , intime , +#> # outtime , los }\if{html}{\out{}} Table subsets can be loaded into memory for example using the diff --git a/man/load_concepts.Rd b/man/load_concepts.Rd index f47d8cdd..100ed04c 100644 --- a/man/load_concepts.Rd +++ b/man/load_concepts.Rd @@ -155,13 +155,15 @@ generic functions. } \section{Concept}{ -Top-level entry points are either a character vector, which is used to -subset a \code{concept} object or an entire \link[=load_dictionary]{concept dictionary}, or a \code{concept} object. When passing a -character vector as first argument, the most important further arguments at -that level control from where the dictionary is taken (\code{dict_name} or -\code{dict_dirs}). At \code{concept} level, the most important additional arguments -control the result structure: data merging can be disabled using -\code{merge_data} and data aggregation is governed by the \code{aggregate} argument. +Top-level entry points are either a character vector of concept names or an +integer vector of concept IDs (matched against \code{omopid} fields), which are +used to subset a \code{concept} object or an entire \link[=load_dictionary]{concept dictionary}, or a \code{concept} object. When passing a +character/integer vector as first argument, the most important further +arguments at that level control from where the dictionary is taken +(\code{dict_name} or \code{dict_dirs}). At \code{concept} level, the most important +additional arguments control the result structure: data merging can be +disabled using \code{merge_data} and data aggregation is governed by the +\code{aggregate} argument. Data aggregation is important for merging several concepts into a wide-format table, as this requires data to be unique per observation (i.e. diff --git a/tests/testthat/_snaps/concept.md b/tests/testthat/_snaps/concept.md index 1e33e9c1..e5850e1a 100644 --- a/tests/testthat/_snaps/concept.md +++ b/tests/testthat/_snaps/concept.md @@ -43,54 +43,6 @@ gcs_raw gcs_raw ---- - - Code - print(dat1) - Output - # A `ts_tbl`: 1,914 x 3 - # Id var: `icustay_id` - # Units: `omop_4144235` [mg/dL] - # Index var: `charttime` (1 hours) - icustay_id charttime omop_4144235 - - 1 201006 -58 hours 116 - 2 201006 -45 hours 83 - 3 201006 -21 hours 91 - 4 201006 0 hours 175 - 5 201006 11 hours 129 - ... - 1,910 298685 260 hours 159 - 1,911 298685 272 hours 153 - 1,912 298685 290 hours 182 - 1,913 298685 293 hours 122 - 1,914 298685 299 hours 121 - # ... with 1,904 more rows - ---- - - Code - print(dat2) - Output - # A `ts_tbl`: 1,920 x 4 - # Id var: `icustay_id` - # Units: `omop_4144235` [mg/dL], `omop_4017497` [g/dL] - # Index var: `charttime` (1 hours) - icustay_id charttime omop_4144235 omop_4017497 - - 1 201006 -58 hours 116 NA - 2 201006 -45 hours 83 NA - 3 201006 -21 hours 91 NA - 4 201006 0 hours 175 2.4 - 5 201006 11 hours 129 NA - ... - 1,916 298685 260 hours 159 NA - 1,917 298685 272 hours 153 2.2 - 1,918 298685 290 hours 182 NA - 1,919 298685 293 hours 122 NA - 1,920 298685 299 hours 121 2.5 - # ... with 1,910 more rows - # load external dictionary Code diff --git a/tests/testthat/test-concept.R b/tests/testthat/test-concept.R index 874c8406..114be0fc 100644 --- a/tests/testthat/test-concept.R +++ b/tests/testthat/test-concept.R @@ -155,8 +155,6 @@ test_that("load concepts", { c("icustay_id", "charttime", "omop_4144235")) expect_equal(interval(dat1), hours(1L)) - expect_snapshot(print(dat1)) - dat2 <- load_concepts(c(4144235, 4017497), "mimic_demo", verbose = FALSE) expect_s3_class(dat2, "ts_tbl") @@ -167,8 +165,6 @@ test_that("load concepts", { ) expect_equal(interval(dat2), hours(1L)) - expect_snapshot(print(dat2)) - expect_warning( load_concepts(c(4144235, 123), "mimic_demo", verbose = FALSE), class = "omop_miss_id" diff --git a/vignettes/ricu.Rmd b/vignettes/ricu.Rmd index 132cf7d9..fdb3b99c 100644 --- a/vignettes/ricu.Rmd +++ b/vignettes/ricu.Rmd @@ -72,15 +72,17 @@ pkgdown: ```{r setup, include = FALSE} source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) -options(width = 76) +options( + width = 76, + kableExtra.latex.load_packages = FALSE, + crayon.enabled = FALSE +) library(ricu) library(data.table) library(forestmodel) library(survival) library(ggplot2) - -options(kableExtra.latex.load_packages = FALSE) library(kableExtra) srcs <- c("mimic", "eicu", "aumc", "hirid", "miiv") From 00ecfe1b1d5744d6649c62551b6723ca554651f8 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:04:50 +0200 Subject: [PATCH 24/37] minor fixes --- R/data-env.R | 2 +- R/tbl-base.R | 2 +- man/data_env.Rd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/data-env.R b/R/data-env.R index 29427388..fdc594d5 100644 --- a/R/data-env.R +++ b/R/data-env.R @@ -67,7 +67,7 @@ #' #' Much care has been taken to make `ricu` extensible to new datasets. For #' example the publicly available ICU database [AmsterdamUMCdb -#' ](https://www.amsterdammedicaldatascience.nl/#amsterdamumcdb) +#' ](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) #' provided by the Amsterdam University Medical Center, currently is not part #' of the core datasets of `ricu`, but code for integrating this dataset is #' available on [github](https://github.com/eth-mds/aumc). diff --git a/R/tbl-base.R b/R/tbl-base.R index c5126380..868e1730 100644 --- a/R/tbl-base.R +++ b/R/tbl-base.R @@ -56,7 +56,7 @@ print.id_tbl <- function(x, ..., n = NULL, width = NULL) { #' @export format.id_tbl <- function(x, ..., n = NULL, width = NULL) { - if (packageVersion("prt") < "0.2.0") { + if (utils::packageVersion("prt") < "0.2.0") { format(prt::trunc_dt(x, n = n, width = width)) } else { prt::format_dt(x, n = n, width = width) diff --git a/man/data_env.Rd b/man/data_env.Rd index fa288b7c..80636009 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -130,7 +130,7 @@ loaded as }\if{html}{\out{}} Much care has been taken to make \code{ricu} extensible to new datasets. For -example the publicly available ICU database \href{https://www.amsterdammedicaldatascience.nl/#amsterdamumcdb}{AmsterdamUMCdb } +example the publicly available ICU database \href{https://amsterdammedicaldatascience.nl/#amsterdamumcdb}{AmsterdamUMCdb } provided by the Amsterdam University Medical Center, currently is not part of the core datasets of \code{ricu}, but code for integrating this dataset is available on \href{https://github.com/eth-mds/aumc}{github}. From 0e092b31f972c8a009032b646ef091a716e5fccb Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:36:44 +0200 Subject: [PATCH 25/37] fix links --- R/data-env.R | 2 +- man/data_env.Rd | 2 +- vignettes/start.Rmd | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/R/data-env.R b/R/data-env.R index fdc594d5..cc16b4d8 100644 --- a/R/data-env.R +++ b/R/data-env.R @@ -67,7 +67,7 @@ #' #' Much care has been taken to make `ricu` extensible to new datasets. For #' example the publicly available ICU database [AmsterdamUMCdb -#' ](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) +#' ](https://amsterdammedicaldatascience.nl/amsterdamumcdb/) #' provided by the Amsterdam University Medical Center, currently is not part #' of the core datasets of `ricu`, but code for integrating this dataset is #' available on [github](https://github.com/eth-mds/aumc). diff --git a/man/data_env.Rd b/man/data_env.Rd index 80636009..0fd9d2a0 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -130,7 +130,7 @@ loaded as }\if{html}{\out{}} Much care has been taken to make \code{ricu} extensible to new datasets. For -example the publicly available ICU database \href{https://amsterdammedicaldatascience.nl/#amsterdamumcdb}{AmsterdamUMCdb } +example the publicly available ICU database \href{https://amsterdammedicaldatascience.nl/amsterdamumcdb/}{AmsterdamUMCdb } provided by the Amsterdam University Medical Center, currently is not part of the core datasets of \code{ricu}, but code for integrating this dataset is available on \href{https://github.com/eth-mds/aumc}{github}. diff --git a/vignettes/start.Rmd b/vignettes/start.Rmd index 6492f1ff..76c45418 100644 --- a/vignettes/start.Rmd +++ b/vignettes/start.Rmd @@ -68,7 +68,7 @@ cat( Included with `ricu` are functions for download and setup of the following datasets: `mimic` (MIMIC-III), `eicu`, `hirid`, `aumc` and `miiv` (MIMIC-IV), which can be invoked in several different ways. - To begin with, a directory is needed where the data can permanently be stored. The default location is platform dependent and can be overridden using the environment variable `RICU_DATA_PATH`. The current value can be retrieved by calling `data_dir()`. -- Access to both the PhysioNet datasets (MIMIC-III, eICU and HiRID), as well as to AUMCdb is free but credentialed. In addition to setting up an [account with PhysioNet](https://physionet.org/register/), [credentialing](https://physionet.org/settings/credentialing/) is required, which, in the case of HiRID, must also be followed by submitting an [access request](https://physionet.org/request-access/hirid/1.1.1/) to the data-owners. Details on the procedure for requesting access to AUMCdb is available from [here](https://amsterdammedicaldatascience.nl/#amsterdamumcdb) and consists of filling out a [form](https://amsterdammedicaldatascience.nl/arfeula_v1.6.pdf) and completing a training course such as [Data or Specimens Only Research (DSOR)](https://physionet.org/about/citi-course/) which, together with proof of training course completion, can be submitted by [email](mailto:access@amsterdammedicaldatascience.nl). +- Access to both the PhysioNet datasets (MIMIC-III, eICU and HiRID), as well as to AUMCdb is free but credentialed. In addition to setting up an [account with PhysioNet](https://physionet.org/register/), [credentialing](https://physionet.org/settings/credentialing/) is required, which, in the case of HiRID, must also be followed by submitting an [access request](https://physionet.org/request-access/hirid/1.1.1/) to the data-owners. Details on the procedure for requesting access to AUMCdb is available from [here](https://amsterdammedicaldatascience.nl/amsterdamumcdb/) and consists of filling out a [form](https://amsterdammedicaldatascience.nl/content/uploads/sites/2/2022/12/arfeula_v1.6.pdf) and completing a training course such as [Data or Specimens Only Research (DSOR)](https://physionet.org/about/citi-course/) which, together with proof of training course completion, can be submitted by [email](mailto:access@amsterdammedicaldatascience.nl). - If raw data in `.csv` form has already been downloaded, this can be decompressed and copied to an appropriate sub-folder (`mimic`, `eicu`, `hirid` or `aumc`) to the directory identified by `data_dir()`. - In order to have `ricu` download the required data, login credentials can be supplied as environment variables `RICU_PHYSIONET_USER`/`RICU_PHYSIONET_PASS` and `RICU_AUMC_TOKEN` (the string the follows `token=` in the download URL received from the AUMCdb data owners) or entered into the terminal manually in interactive sessions. - Enabling efficient random row/column access, `ricu` converts `.csv` files into a binary format using the [fst](https://cran.r-project.org/package=fst) package. From 4ffac299e2a3159fc012988fd7e49fc3f142e461 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 9 Apr 2023 14:07:02 +0200 Subject: [PATCH 26/37] temp fixes for cran --- R/tbl-base.R | 25 ++++++++++++++++++++++--- R/zzz.R | 5 +++++ inst/extdata/vignettes/helpers.R | 10 ++++++++++ vignettes/ricu.Rmd | 4 ++-- 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/R/tbl-base.R b/R/tbl-base.R index 868e1730..bae4faaa 100644 --- a/R/tbl-base.R +++ b/R/tbl-base.R @@ -50,19 +50,38 @@ row.names.id_tbl <- function(x) NULL #' @export print.id_tbl <- function(x, ..., n = NULL, width = NULL) { - cat_line(format(x, ..., n = n, width = width)) + + if (utils::packageVersion("pillar") < "1.9.0" || + utils::packageVersion("prt") >= "0.2.0") { + cat_line(format(x, ..., n = n, width = width)) + } else { + ptyp <- as_ptype(x) + print(unclass_tbl(x), topn = n) + x <- reclass_tbl(x, ptyp) + } + invisible(x) } +fix_print_fun <- function(x, n = 10, ...) { + print(head(x, n = n)) +} + #' @export format.id_tbl <- function(x, ..., n = NULL, width = NULL) { if (utils::packageVersion("prt") < "0.2.0") { - format(prt::trunc_dt(x, n = n, width = width)) + format(prt_fmt(x, n = n, width = width)) } else { - prt::format_dt(x, n = n, width = width) + prt_fmt(x, n = n, width = width) } } +if (utils::packageVersion("prt") < "0.2.0") { + prt_fmt <- prt::trunc_dt +} else { + prt_fmt <- prt::format_dt +} + #' @importFrom tibble tbl_sum #' @export tbl_sum.win_tbl <- function(x) { diff --git a/R/zzz.R b/R/zzz.R index 38352fe3..9ded07a5 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -55,6 +55,11 @@ fix_base_fun(base::cbind.data.frame, cbind_fix) fix_base_fun(base::rbind.data.frame, rbind_fix) } + + if (utils::packageVersion("pillar") >= "1.9.0" && + utils::packageVersion("prt") < "0.2.0") { + registerS3method("print", "prt", fix_print_fun) + } } # nocov end .onAttach <- function(libname, pkgname) { diff --git a/inst/extdata/vignettes/helpers.R b/inst/extdata/vignettes/helpers.R index 630bf5dc..a6cdfa89 100644 --- a/inst/extdata/vignettes/helpers.R +++ b/inst/extdata/vignettes/helpers.R @@ -46,3 +46,13 @@ demo_missing_msg <- function(demo, file) { ").", sep = "" ) } + +if (utils::packageVersion("pillar") >= "1.9.0" && + utils::packageVersion("prt") < "0.2.0") { + + fix_print_fun <- function(x, n = 10, ...) { + print(head(x, n = n)) + } + + registerS3method("print", "prt", fix_print_fun) +} diff --git a/vignettes/ricu.Rmd b/vignettes/ricu.Rmd index fdb3b99c..75951fe8 100644 --- a/vignettes/ricu.Rmd +++ b/vignettes/ricu.Rmd @@ -70,8 +70,6 @@ pkgdown: --- ```{r setup, include = FALSE} -source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) - options( width = 76, kableExtra.latex.load_packages = FALSE, @@ -85,6 +83,8 @@ library(survival) library(ggplot2) library(kableExtra) +source(system.file("extdata", "vignettes", "helpers.R", package = "ricu")) + srcs <- c("mimic", "eicu", "aumc", "hirid", "miiv") ``` From 8d37e14fe99ba0c5e74d587437d607df38481bc0 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 9 Apr 2023 14:41:57 +0200 Subject: [PATCH 27/37] add pillar as suggests --- DESCRIPTION | 3 ++- R/tbl-base.R | 3 ++- R/zzz.R | 3 ++- inst/extdata/vignettes/helpers.R | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 50bef9d8..3aded6ab 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -68,7 +68,8 @@ Suggests: kableExtra, units, pdftools, - magick + magick, + pillar RoxygenNote: 7.2.3 Additional_repositories: https://eth-mds.github.io/physionet-demo VignetteBuilder: knitr diff --git a/R/tbl-base.R b/R/tbl-base.R index bae4faaa..e32dc377 100644 --- a/R/tbl-base.R +++ b/R/tbl-base.R @@ -51,7 +51,8 @@ row.names.id_tbl <- function(x) NULL #' @export print.id_tbl <- function(x, ..., n = NULL, width = NULL) { - if (utils::packageVersion("pillar") < "1.9.0" || + if ((is_pkg_installed("pillar") && + utils::packageVersion("pillar") < "1.9.0") || utils::packageVersion("prt") >= "0.2.0") { cat_line(format(x, ..., n = n, width = width)) } else { diff --git a/R/zzz.R b/R/zzz.R index 9ded07a5..ea11a390 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -56,7 +56,8 @@ fix_base_fun(base::rbind.data.frame, rbind_fix) } - if (utils::packageVersion("pillar") >= "1.9.0" && + if (is_pkg_installed("pillar") && + utils::packageVersion("pillar") >= "1.9.0" && utils::packageVersion("prt") < "0.2.0") { registerS3method("print", "prt", fix_print_fun) } diff --git a/inst/extdata/vignettes/helpers.R b/inst/extdata/vignettes/helpers.R index a6cdfa89..52a63de2 100644 --- a/inst/extdata/vignettes/helpers.R +++ b/inst/extdata/vignettes/helpers.R @@ -47,7 +47,8 @@ demo_missing_msg <- function(demo, file) { ) } -if (utils::packageVersion("pillar") >= "1.9.0" && +if (is_pkg_installed("pillar") && + utils::packageVersion("pillar") >= "1.9.0" && utils::packageVersion("prt") < "0.2.0") { fix_print_fun <- function(x, n = 10, ...) { From c5b4e2dee37862e4b90343f3cfd27619d8919275 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 9 Apr 2023 14:43:46 +0200 Subject: [PATCH 28/37] minifix --- inst/extdata/vignettes/helpers.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/extdata/vignettes/helpers.R b/inst/extdata/vignettes/helpers.R index 52a63de2..58a18aa1 100644 --- a/inst/extdata/vignettes/helpers.R +++ b/inst/extdata/vignettes/helpers.R @@ -47,7 +47,7 @@ demo_missing_msg <- function(demo, file) { ) } -if (is_pkg_installed("pillar") && +if (requireNamespace("pillar", quietly = TRUE) && utils::packageVersion("pillar") >= "1.9.0" && utils::packageVersion("prt") < "0.2.0") { From 7beadabe9643c18b4b663bae71d1c9ced889b16d Mon Sep 17 00:00:00 2001 From: Drago Date: Tue, 11 Apr 2023 17:23:45 -0400 Subject: [PATCH 29/37] add miiv omr --- inst/extdata/config/data-sources.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/inst/extdata/config/data-sources.json b/inst/extdata/config/data-sources.json index 4ada87fe..b8548290 100644 --- a/inst/extdata/config/data-sources.json +++ b/inst/extdata/config/data-sources.json @@ -7176,7 +7176,7 @@ "omr" : { "files": "core/omr.csv.gz", "defaults": { - "timevars": ["chartdate"], + "time_vars": ["chartdate"], "val_var": "result_value" }, "num_rows": 6439169, From 6493c9a8880f84bcfb788cd65d15d72e77be78d2 Mon Sep 17 00:00:00 2001 From: Drago Date: Mon, 1 May 2023 12:04:45 -0400 Subject: [PATCH 30/37] load_concepts() concepts arg doc fix --- R/concept-load.R | 7 ++-- man/change_id.Rd | 58 ++++++++++---------------- man/data_env.Rd | 99 +++++++++++++------------------------------- man/load_concepts.Rd | 5 ++- 4 files changed, 57 insertions(+), 112 deletions(-) diff --git a/R/concept-load.R b/R/concept-load.R index bca69bb3..174a089c 100644 --- a/R/concept-load.R +++ b/R/concept-load.R @@ -166,8 +166,9 @@ load_concepts <- function(x, ...) UseMethod("load_concepts", x) #' @param src A character vector, used to subset the `concepts`; `NULL` #' means no subsetting -#' @param concepts The concepts to be used or `NULL` in which case -#' [load_dictionary()] is called +#' @param concepts The concepts to be used, or `NULL`. In the latter case the +#' standard ricu dictionary (obtained by calling [load_dictionary()]) is used +#' for loading the objects specified in `x`. #' @param dict_name,dict_dirs In case not concepts are passed as `concepts`, #' these are forwarded to [load_dictionary()] as `name` and `file` arguments #' @@ -179,8 +180,6 @@ load_concepts.character <- function(x, src = NULL, concepts = NULL, ..., if (is.null(concepts)) { - assert_that(not_null(src)) - load_concepts( load_dictionary(src, x, name = dict_name, cfg_dirs = dict_dirs), src = NULL, ... diff --git a/man/change_id.Rd b/man/change_id.Rd index 8c3fa01e..6193adf5 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -57,34 +57,19 @@ and \code{downgrade_id()} when the target ID system is of lower cardinality } \details{ In order to provide ID system conversion for a data source, the (internal) -function \code{\link[=id_map]{id_map()}} must be able to construct an ID mapping for that data +function [id_map()] must be able to construct an ID mapping for that data source. Constructing such a mapping can be expensive w.r.t. the frequency -it might be re-used and therefore, \code{\link[=id_map]{id_map()}} provides caching +it might be re-used and therefore, [id_map()] provides caching infrastructure. The mapping itself is constructed by the (internal) -function \code{\link[=id_map_helper]{id_map_helper()}}, which is expected to provide source and +function [id_map_helper()], which is expected to provide source and destination ID columns as well as start and end columns corresponding to the destination ID, relative to the source ID system. In the following -example, we request for \code{mimic_demo}, with ICU stay IDs as source and +example, we request for `mimic_demo`, with ICU stay IDs as source and hospital admissions as destination IDs. -\if{html}{\out{
}}\preformatted{id_map_helper(mimic_demo, "icustay_id", "hadm_id") -#> # An `id_tbl`: 136 x 4 -#> # Id var: `icustay_id` -#> icustay_id hadm_id hadm_id_start hadm_id_end -#> -#> 1 201006 198503 -3290 mins 9114 mins -#> 2 201204 114648 -2 mins 6949 mins -#> 3 203766 126949 -1336 mins 8818 mins -#> 4 204132 157609 -1 mins 10103 mins -#> 5 204201 177678 -368 mins 9445 mins -#> ... -#> 132 295043 170883 -10413 mins 31258 mins -#> 133 295741 176805 -1 mins 3153 mins -#> 134 296804 110244 -1294 mins 4599 mins -#> 135 297782 167612 -1 mins 207 mins -#> 136 298685 151323 -1 mins 19082 mins -#> # i 131 more rows -}\if{html}{\out{
}} +```{r, eval = is_data_avail("mimic_demo")} +id_map_helper(mimic_demo, "icustay_id", "hadm_id") +``` Both start and end columns encode the hospital admission windows relative to each corresponding ICU stay start time. It therefore comes as no @@ -93,21 +78,22 @@ occurs before ICU stay start time), while end times are often days in the future (as hospital discharge typically occurs several days after ICU admission). -In order to use the ID conversion infrastructure offered by \code{ricu} for a -new dataset, it typically suffices to provide an \code{id_cfg} entry in the -source configuration (see \code{\link[=load_src_cfg]{load_src_cfg()}}), outlining the available ID +In order to use the ID conversion infrastructure offered by `ricu` for a +new dataset, it typically suffices to provide an `id_cfg` entry in the +source configuration (see [load_src_cfg()]), outlining the available ID systems alongside an ordering, as well as potentially a class specific -implementation of \code{\link[=id_map_helper]{id_map_helper()}} for the given source class, specifying +implementation of [id_map_helper()] for the given source class, specifying the corresponding time windows in 1 minute resolution (for every possible pair of IDs). -While both up- and downgrades for \code{id_tbl} objects, as well as downgrades -for \code{ts_tbl} objects are simple merge operations based on the ID mapping -provided by \code{\link[=id_map]{id_map()}}, ID upgrades for \code{ts_tbl} objects are slightly more -involved. As an example, consider the following setting: we have \code{data} -associated with \code{hadm_id} IDs and times relative to hospital admission: +While both up- and downgrades for `id_tbl` objects, as well as downgrades +for `ts_tbl` objects are simple merge operations based on the ID mapping +provided by [id_map()], ID upgrades for `ts_tbl` objects are slightly more +involved. As an example, consider the following setting: we have `data` +associated with `hadm_id` IDs and times relative to hospital admission: -\if{html}{\out{
}}\preformatted{ 1 2 3 4 5 6 7 8 +``` + 1 2 3 4 5 6 7 8 data ---*------*-------*--------*-------*-------*--------*------*--- 3h 10h 18h 27h 35h 43h 52h 59h @@ -117,17 +103,17 @@ hadm_id |-------------------------------------------------------------| icustay_id |------------------| |---------------| 0h 19h 0h 16h ICU_1 ICU_2 -}\if{html}{\out{
}} +``` -The mapping of data points from \code{hadm_id} to \code{icustay_id} is created as +The mapping of data points from `hadm_id` to `icustay_id` is created as follows: ICU stay end times mark boundaries and all data that is recorded after the last ICU stay ended is assigned to the last ICU stay. Therefore -data points 1-3 are assigned to \code{ICU_1}, while 4-8 are assigned to \code{ICU_2}. +data points 1-3 are assigned to `ICU_1`, while 4-8 are assigned to `ICU_2`. Times have to be shifted as well, as timestamps are expected to be relative to the current ID system. Data points 1-3 therefore are assigned to time stamps -4h, 3h and 11h, while data points 4-8 are assigned to -10h, -2h, 6h, 15h and 22h. Implementation-wise, the mapping is computed using an -efficient \code{data.table} rolling join. +efficient `data.table` rolling join. } \examples{ if (require(mimic.demo)) { diff --git a/man/data_env.Rd b/man/data_env.Rd index 0fd9d2a0..6249b595 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -42,98 +42,57 @@ hosted data source is available as well. As with the PhysioNet datasets, access is public but has to be granted by the data collectors. } \details{ -Setting up a dataset for use with \code{ricu} requires a configuration object. +Setting up a dataset for use with `ricu` requires a configuration object. For the included datasets, configuration can be loaded from -\if{html}{\out{
}}\preformatted{system.file("extdata", "config", "data-sources.json", package = "ricu") -}\if{html}{\out{
}} +``` +system.file("extdata", "config", "data-sources.json", package = "ricu") +``` -by calling \code{\link[=load_src_cfg]{load_src_cfg()}} and for dataset that are external to \code{ricu}, +by calling [load_src_cfg()] and for dataset that are external to `ricu`, additional configuration can be made available by setting the environment -variable \code{RICU_CONFIG_PATH} (for more information, refer to -\code{\link[=load_src_cfg]{load_src_cfg()}}). Using the dataset configuration object, data can be -downloaded (\code{\link[=download_src]{download_src()}}), imported (\code{\link[=import_src]{import_src()}}) and attached -(\code{\link[=attach_src]{attach_src()}}). While downloading and importing are one-time procedures, +variable `RICU_CONFIG_PATH` (for more information, refer to +[load_src_cfg()]). Using the dataset configuration object, data can be +downloaded ([download_src()]), imported ([import_src()]) and attached +([attach_src()]). While downloading and importing are one-time procedures, attaching of the dataset is repeated every time the package is loaded. Briefly, downloading loads the raw dataset from the internet (most likely -in \code{.csv} format), importing consists of some preprocessing to make the -data available more efficiently (by converting it to \code{\link[fst:fst]{.fst}} +in `.csv` format), importing consists of some preprocessing to make the +data available more efficiently (by converting it to [`.fst`][fst::fst()] format) and attaching sets up the data for use by the package. For more information on the individual steps, refer to the respective documentation pages. A dataset that has been successfully made available can interactively be explored by typing its name into the console and individual tables can be -inspected using the \code{$} function. For example for the MIMIC-III demo -dataset and the \code{icustays} table, this gives - -\if{html}{\out{
}}\preformatted{mimic_demo -#> -#> admissions callout caregivers chartevents -#> [129 x 19] [77 x 24] [7,567 x 4] [758,355 x 15] -#> cptevents d_cpt d_icd_diagnoses d_icd_procedures -#> [1,579 x 12] [134 x 9] [14,567 x 4] [3,882 x 4] -#> d_items d_labitems datetimeevents diagnoses_icd -#> [12,487 x 10] [753 x 6] [15,551 x 14] [1,761 x 5] -#> drgcodes icustays inputevents_cv inputevents_mv -#> [297 x 8] [136 x 12] [34,799 x 22] [13,224 x 31] -#> labevents microbiologyevents outputevents patients -#> [76,074 x 9] [2,003 x 16] [11,320 x 13] [100 x 8] -#> prescriptions procedureevents_mv procedures_icd services -#> [10,398 x 19] [753 x 25] [506 x 5] [163 x 6] -#> transfers -#> [524 x 13] +inspected using the `$` function. For example for the MIMIC-III demo +dataset and the `icustays` table, this gives + +```{r, eval = is_data_avail("mimic_demo")} +mimic_demo mimic_demo$icustays -#> # : [136 x 12] -#> # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) -#> # Defaults: `intime` (index), `last_careunit` (val) -#> # Time vars: `intime`, `outtime` -#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit -#> -#> 1 12742 10006 142345 206504 carevue MICU MICU -#> 2 12747 10011 105331 232110 carevue MICU MICU -#> 3 12749 10013 165520 264446 carevue MICU MICU -#> 4 12754 10017 199207 204881 carevue CCU CCU -#> 5 12755 10019 177759 228977 carevue MICU MICU -#> ... -#> 132 42676 44083 198330 286428 metavision CCU CCU -#> 133 42691 44154 174245 217724 metavision MICU MICU -#> 134 42709 44212 163189 239396 metavision MICU MICU -#> 135 42712 44222 192189 238186 metavision CCU CCU -#> 136 42714 44228 103379 217992 metavision SICU SICU -#> # i 131 more rows -#> # i 5 more variables: first_wardid , last_wardid , intime , -#> # outtime , los -}\if{html}{\out{
}} +``` Table subsets can be loaded into memory for example using the -\code{\link[base:subset]{base::subset()}} function, which uses non-standard evaluation (NSE) to +[base::subset()] function, which uses non-standard evaluation (NSE) to determine a row-subsetting. This design choice stems form the fact that some tables can have on the order of 10^8 rows, which makes loading full tables into memory an expensive operation. Table subsets loaded into -memory are represented as \code{\link[data.table:data.table]{data.table}} objects. +memory are represented as [`data.table`][data.table::data.table()] objects. Extending the above example, if only ICU stays corresponding to the patient -with \code{subject_id == 10124} are of interest, the respective data can be +with `subject_id == 10124` are of interest, the respective data can be loaded as -\if{html}{\out{
}}\preformatted{subset(mimic_demo$icustays, subject_id == 10124) -#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit -#> 1: 12863 10124 182664 261764 carevue MICU MICU -#> 2: 12864 10124 170883 222779 carevue MICU MICU -#> 3: 12865 10124 170883 295043 carevue CCU CCU -#> 4: 12866 10124 170883 237528 carevue MICU MICU -#> first_wardid last_wardid intime outtime los -#> 1: 23 23 2192-03-29 10:46:51 2192-04-01 06:36:00 2.8258 -#> 2: 50 50 2192-04-16 20:58:32 2192-04-20 08:51:28 3.4951 -#> 3: 7 7 2192-04-24 02:29:49 2192-04-26 23:59:45 2.8958 -#> 4: 23 23 2192-04-30 14:50:44 2192-05-15 23:34:21 15.3636 -}\if{html}{\out{
}} - -Much care has been taken to make \code{ricu} extensible to new datasets. For -example the publicly available ICU database \href{https://amsterdammedicaldatascience.nl/amsterdamumcdb/}{AmsterdamUMCdb } +```{r, eval = is_data_avail("mimic_demo")} +subset(mimic_demo$icustays, subject_id == 10124) +``` + +Much care has been taken to make `ricu` extensible to new datasets. For +example the publicly available ICU database [AmsterdamUMCdb +](https://amsterdammedicaldatascience.nl/amsterdamumcdb/) provided by the Amsterdam University Medical Center, currently is not part -of the core datasets of \code{ricu}, but code for integrating this dataset is -available on \href{https://github.com/eth-mds/aumc}{github}. +of the core datasets of `ricu`, but code for integrating this dataset is +available on [github](https://github.com/eth-mds/aumc). } \section{MIMIC-III}{ diff --git a/man/load_concepts.Rd b/man/load_concepts.Rd index 100ed04c..f2c687ff 100644 --- a/man/load_concepts.Rd +++ b/man/load_concepts.Rd @@ -92,8 +92,9 @@ load_concepts(x, ...) \item{src}{A character vector, used to subset the \code{concepts}; \code{NULL} means no subsetting} -\item{concepts}{The concepts to be used or \code{NULL} in which case -\code{\link[=load_dictionary]{load_dictionary()}} is called} +\item{concepts}{The concepts to be used, or \code{NULL}. In the latter case the +standard ricu dictionary (obtained by calling \code{\link[=load_dictionary]{load_dictionary()}}) is used +for loading the objects specified in \code{x}.} \item{dict_name, dict_dirs}{In case not concepts are passed as \code{concepts}, these are forwarded to \code{\link[=load_dictionary]{load_dictionary()}} as \code{name} and \code{file} arguments} From f6a7c3687e00730336c2d9986891673c4c74eea0 Mon Sep 17 00:00:00 2001 From: Drago Date: Mon, 1 May 2023 12:13:37 -0400 Subject: [PATCH 31/37] load_concepts.integer() src NULL fix --- R/concept-load.R | 2 -- 1 file changed, 2 deletions(-) diff --git a/R/concept-load.R b/R/concept-load.R index 174a089c..a420f1bf 100644 --- a/R/concept-load.R +++ b/R/concept-load.R @@ -201,8 +201,6 @@ load_concepts.integer <- function(x, src = NULL, concepts = NULL, ..., if (is.null(concepts)) { - assert_that(not_null(src)) - concepts <- load_dictionary(src, name = dict_name, cfg_dirs = dict_dirs) } else if (not_null(src)) { From fb51e0c284741b1a41ef7868a0a634f708522962 Mon Sep 17 00:00:00 2001 From: Malte Londschien Date: Wed, 26 Jul 2023 10:40:15 +0200 Subject: [PATCH 32/37] Fix typo. --- R/setup-download.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/setup-download.R b/R/setup-download.R index 65b673cc..985ce13f 100644 --- a/R/setup-download.R +++ b/R/setup-download.R @@ -228,7 +228,7 @@ download_src.aumc_cfg <- function(x, data_dir = src_data_dir(x), fil <- file.path(tmp, name) if (isTRUE(verbose)) { - prg <- progress_init(size, msg = "Donwloading `aumc` data", + prg <- progress_init(size, msg = "Downloading `aumc` data", what = FALSE) } else { prg <- FALSE From d161178e536bf85c09a89c5860d43383dbb20024 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 3 Sep 2023 20:29:30 +0200 Subject: [PATCH 33/37] maintenance release --- DESCRIPTION | 2 +- NEWS.md | 4 ++++ cran-comments.md | 3 +-- vignettes/ricu.Rmd | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index 3aded6ab..df824129 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -6,7 +6,7 @@ Description: Focused on (but not exclusive to) data sets hosted on PhysioNet functions for running arbitrary queries against available data sets, a system for defining clinical concepts and encoding their representations in tabular ICU data is presented. -Version: 0.5.5 +Version: 0.5.6 Authors@R: c( person(given = "Nicolas", family = "Bennett", diff --git a/NEWS.md b/NEWS.md index 7532d584..7e881dd5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,3 +1,7 @@ +# ricu 0.5.6 + +* maintenance release: fixes non-character numeric version input issues + # ricu 0.5.5 * maintenance release: fixes an issue introduced by pillar 1.9.0 via an update diff --git a/cran-comments.md b/cran-comments.md index 47ced910..317d0f8c 100644 --- a/cran-comments.md +++ b/cran-comments.md @@ -10,5 +10,4 @@ 0 errors | 0 warnings | 0 notes -* Maintenance release (fixes an issue introduced by pillar 1.9.0 via an update - to 0.2.0 of prt) +* Maintenance release (fixes non-character numeric version input issues) diff --git a/vignettes/ricu.Rmd b/vignettes/ricu.Rmd index 75951fe8..63e74a9b 100644 --- a/vignettes/ricu.Rmd +++ b/vignettes/ricu.Rmd @@ -56,7 +56,7 @@ vignette: > %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} output: > - if (packageVersion("rticles") < 0.5 || rmarkdown::pandoc_version() >= 2) + if (packageVersion("rticles") < "0.5" || rmarkdown::pandoc_version() >= "2") rticles::jss_article else rmarkdown::html_vignette documentclass: jss classoption: From cd67675081823cafac23fd6f15ce99166c98a34f Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Sun, 3 Sep 2023 21:10:11 +0200 Subject: [PATCH 34/37] fix exports --- NAMESPACE | 3 ++ R/concept-utils.R | 1 + R/tbl-class.R | 4 +- man/change_id.Rd | 58 ++++++++++++++++----------- man/data_env.Rd | 99 +++++++++++++++++++++++++++++++++-------------- 5 files changed, 112 insertions(+), 53 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 15748ab3..2010f7ad 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -43,6 +43,8 @@ S3method(as_item,item) S3method(as_item,itm) S3method(as_item,list) S3method(as_item,rec_cncpt) +S3method(as_ptype,data.frame) +S3method(as_ptype,data.table) S3method(as_ptype,default) S3method(as_ptype,id_tbl) S3method(as_src_cfg,default) @@ -249,6 +251,7 @@ S3method(set_callback,default) S3method(set_callback,itm) S3method(set_target,cncpt) S3method(set_target,concept) +S3method(set_target,default) S3method(set_target,item) S3method(set_target,itm) S3method(setup_src_data,character) diff --git a/R/concept-utils.R b/R/concept-utils.R index 114bd1ea..ae77feac 100644 --- a/R/concept-utils.R +++ b/R/concept-utils.R @@ -806,6 +806,7 @@ set_target.concept <- function(x, target) { new_concept(lapply(x, set_target, target)) } +#' @export set_target.default <- function(x, target) stop_generic(x, .Generic) #' @rdname item_utils diff --git a/R/tbl-class.R b/R/tbl-class.R index a19707c5..3adf82ee 100644 --- a/R/tbl-class.R +++ b/R/tbl-class.R @@ -636,11 +636,11 @@ as_ptype.id_tbl <- function(x) { reclass_tbl(as.data.table(lapply(x, `[`, 0L)[meta_vars(x)]), x) } -#' @method reclass_tbl data.table +#' @method as_ptype data.table #' @export as_ptype.data.table <- function(x) data.table() -#' @method reclass_tbl data.frame +#' @method as_ptype data.frame #' @export as_ptype.data.frame <- function(x) data.frame() diff --git a/man/change_id.Rd b/man/change_id.Rd index 6193adf5..d1770344 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -57,19 +57,34 @@ and \code{downgrade_id()} when the target ID system is of lower cardinality } \details{ In order to provide ID system conversion for a data source, the (internal) -function [id_map()] must be able to construct an ID mapping for that data +function \code{\link[=id_map]{id_map()}} must be able to construct an ID mapping for that data source. Constructing such a mapping can be expensive w.r.t. the frequency -it might be re-used and therefore, [id_map()] provides caching +it might be re-used and therefore, \code{\link[=id_map]{id_map()}} provides caching infrastructure. The mapping itself is constructed by the (internal) -function [id_map_helper()], which is expected to provide source and +function \code{\link[=id_map_helper]{id_map_helper()}}, which is expected to provide source and destination ID columns as well as start and end columns corresponding to the destination ID, relative to the source ID system. In the following -example, we request for `mimic_demo`, with ICU stay IDs as source and +example, we request for \code{mimic_demo}, with ICU stay IDs as source and hospital admissions as destination IDs. -```{r, eval = is_data_avail("mimic_demo")} -id_map_helper(mimic_demo, "icustay_id", "hadm_id") -``` +\if{html}{\out{
}}\preformatted{id_map_helper(mimic_demo, "icustay_id", "hadm_id") +#> # An `id_tbl`: 136 x 4 +#> # Id var: `icustay_id` +#> icustay_id hadm_id hadm_id_start hadm_id_end +#> +#> 1 201006 198503 ~17h ~6d +#> 2 201204 114648 ~23h ~4d +#> 3 203766 126949 ~1h ~6d +#> 4 204132 157609 ~23h ~7d +#> 5 204201 177678 ~17h ~6d +#> ... +#> 132 295043 170883 ~18h ~21d +#> 133 295741 176805 ~23h ~2d +#> 134 296804 110244 ~2h ~3d +#> 135 297782 167612 ~23h ~3h +#> 136 298685 151323 ~23h ~13d +#> # i 131 more rows +}\if{html}{\out{
}} Both start and end columns encode the hospital admission windows relative to each corresponding ICU stay start time. It therefore comes as no @@ -78,22 +93,21 @@ occurs before ICU stay start time), while end times are often days in the future (as hospital discharge typically occurs several days after ICU admission). -In order to use the ID conversion infrastructure offered by `ricu` for a -new dataset, it typically suffices to provide an `id_cfg` entry in the -source configuration (see [load_src_cfg()]), outlining the available ID +In order to use the ID conversion infrastructure offered by \code{ricu} for a +new dataset, it typically suffices to provide an \code{id_cfg} entry in the +source configuration (see \code{\link[=load_src_cfg]{load_src_cfg()}}), outlining the available ID systems alongside an ordering, as well as potentially a class specific -implementation of [id_map_helper()] for the given source class, specifying +implementation of \code{\link[=id_map_helper]{id_map_helper()}} for the given source class, specifying the corresponding time windows in 1 minute resolution (for every possible pair of IDs). -While both up- and downgrades for `id_tbl` objects, as well as downgrades -for `ts_tbl` objects are simple merge operations based on the ID mapping -provided by [id_map()], ID upgrades for `ts_tbl` objects are slightly more -involved. As an example, consider the following setting: we have `data` -associated with `hadm_id` IDs and times relative to hospital admission: +While both up- and downgrades for \code{id_tbl} objects, as well as downgrades +for \code{ts_tbl} objects are simple merge operations based on the ID mapping +provided by \code{\link[=id_map]{id_map()}}, ID upgrades for \code{ts_tbl} objects are slightly more +involved. As an example, consider the following setting: we have \code{data} +associated with \code{hadm_id} IDs and times relative to hospital admission: -``` - 1 2 3 4 5 6 7 8 +\if{html}{\out{
}}\preformatted{ 1 2 3 4 5 6 7 8 data ---*------*-------*--------*-------*-------*--------*------*--- 3h 10h 18h 27h 35h 43h 52h 59h @@ -103,17 +117,17 @@ hadm_id |-------------------------------------------------------------| icustay_id |------------------| |---------------| 0h 19h 0h 16h ICU_1 ICU_2 -``` +}\if{html}{\out{
}} -The mapping of data points from `hadm_id` to `icustay_id` is created as +The mapping of data points from \code{hadm_id} to \code{icustay_id} is created as follows: ICU stay end times mark boundaries and all data that is recorded after the last ICU stay ended is assigned to the last ICU stay. Therefore -data points 1-3 are assigned to `ICU_1`, while 4-8 are assigned to `ICU_2`. +data points 1-3 are assigned to \code{ICU_1}, while 4-8 are assigned to \code{ICU_2}. Times have to be shifted as well, as timestamps are expected to be relative to the current ID system. Data points 1-3 therefore are assigned to time stamps -4h, 3h and 11h, while data points 4-8 are assigned to -10h, -2h, 6h, 15h and 22h. Implementation-wise, the mapping is computed using an -efficient `data.table` rolling join. +efficient \code{data.table} rolling join. } \examples{ if (require(mimic.demo)) { diff --git a/man/data_env.Rd b/man/data_env.Rd index 6249b595..0fd9d2a0 100644 --- a/man/data_env.Rd +++ b/man/data_env.Rd @@ -42,57 +42,98 @@ hosted data source is available as well. As with the PhysioNet datasets, access is public but has to be granted by the data collectors. } \details{ -Setting up a dataset for use with `ricu` requires a configuration object. +Setting up a dataset for use with \code{ricu} requires a configuration object. For the included datasets, configuration can be loaded from -``` -system.file("extdata", "config", "data-sources.json", package = "ricu") -``` +\if{html}{\out{
}}\preformatted{system.file("extdata", "config", "data-sources.json", package = "ricu") +}\if{html}{\out{
}} -by calling [load_src_cfg()] and for dataset that are external to `ricu`, +by calling \code{\link[=load_src_cfg]{load_src_cfg()}} and for dataset that are external to \code{ricu}, additional configuration can be made available by setting the environment -variable `RICU_CONFIG_PATH` (for more information, refer to -[load_src_cfg()]). Using the dataset configuration object, data can be -downloaded ([download_src()]), imported ([import_src()]) and attached -([attach_src()]). While downloading and importing are one-time procedures, +variable \code{RICU_CONFIG_PATH} (for more information, refer to +\code{\link[=load_src_cfg]{load_src_cfg()}}). Using the dataset configuration object, data can be +downloaded (\code{\link[=download_src]{download_src()}}), imported (\code{\link[=import_src]{import_src()}}) and attached +(\code{\link[=attach_src]{attach_src()}}). While downloading and importing are one-time procedures, attaching of the dataset is repeated every time the package is loaded. Briefly, downloading loads the raw dataset from the internet (most likely -in `.csv` format), importing consists of some preprocessing to make the -data available more efficiently (by converting it to [`.fst`][fst::fst()] +in \code{.csv} format), importing consists of some preprocessing to make the +data available more efficiently (by converting it to \code{\link[fst:fst]{.fst}} format) and attaching sets up the data for use by the package. For more information on the individual steps, refer to the respective documentation pages. A dataset that has been successfully made available can interactively be explored by typing its name into the console and individual tables can be -inspected using the `$` function. For example for the MIMIC-III demo -dataset and the `icustays` table, this gives - -```{r, eval = is_data_avail("mimic_demo")} -mimic_demo +inspected using the \code{$} function. For example for the MIMIC-III demo +dataset and the \code{icustays} table, this gives + +\if{html}{\out{
}}\preformatted{mimic_demo +#> +#> admissions callout caregivers chartevents +#> [129 x 19] [77 x 24] [7,567 x 4] [758,355 x 15] +#> cptevents d_cpt d_icd_diagnoses d_icd_procedures +#> [1,579 x 12] [134 x 9] [14,567 x 4] [3,882 x 4] +#> d_items d_labitems datetimeevents diagnoses_icd +#> [12,487 x 10] [753 x 6] [15,551 x 14] [1,761 x 5] +#> drgcodes icustays inputevents_cv inputevents_mv +#> [297 x 8] [136 x 12] [34,799 x 22] [13,224 x 31] +#> labevents microbiologyevents outputevents patients +#> [76,074 x 9] [2,003 x 16] [11,320 x 13] [100 x 8] +#> prescriptions procedureevents_mv procedures_icd services +#> [10,398 x 19] [753 x 25] [506 x 5] [163 x 6] +#> transfers +#> [524 x 13] mimic_demo$icustays -``` +#> # : [136 x 12] +#> # ID options: subject_id (patient) < hadm_id (hadm) < icustay_id (icustay) +#> # Defaults: `intime` (index), `last_careunit` (val) +#> # Time vars: `intime`, `outtime` +#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit +#> +#> 1 12742 10006 142345 206504 carevue MICU MICU +#> 2 12747 10011 105331 232110 carevue MICU MICU +#> 3 12749 10013 165520 264446 carevue MICU MICU +#> 4 12754 10017 199207 204881 carevue CCU CCU +#> 5 12755 10019 177759 228977 carevue MICU MICU +#> ... +#> 132 42676 44083 198330 286428 metavision CCU CCU +#> 133 42691 44154 174245 217724 metavision MICU MICU +#> 134 42709 44212 163189 239396 metavision MICU MICU +#> 135 42712 44222 192189 238186 metavision CCU CCU +#> 136 42714 44228 103379 217992 metavision SICU SICU +#> # i 131 more rows +#> # i 5 more variables: first_wardid , last_wardid , intime , +#> # outtime , los +}\if{html}{\out{
}} Table subsets can be loaded into memory for example using the -[base::subset()] function, which uses non-standard evaluation (NSE) to +\code{\link[base:subset]{base::subset()}} function, which uses non-standard evaluation (NSE) to determine a row-subsetting. This design choice stems form the fact that some tables can have on the order of 10^8 rows, which makes loading full tables into memory an expensive operation. Table subsets loaded into -memory are represented as [`data.table`][data.table::data.table()] objects. +memory are represented as \code{\link[data.table:data.table]{data.table}} objects. Extending the above example, if only ICU stays corresponding to the patient -with `subject_id == 10124` are of interest, the respective data can be +with \code{subject_id == 10124} are of interest, the respective data can be loaded as -```{r, eval = is_data_avail("mimic_demo")} -subset(mimic_demo$icustays, subject_id == 10124) -``` - -Much care has been taken to make `ricu` extensible to new datasets. For -example the publicly available ICU database [AmsterdamUMCdb -](https://amsterdammedicaldatascience.nl/amsterdamumcdb/) +\if{html}{\out{
}}\preformatted{subset(mimic_demo$icustays, subject_id == 10124) +#> row_id subject_id hadm_id icustay_id dbsource first_careunit last_careunit +#> 1: 12863 10124 182664 261764 carevue MICU MICU +#> 2: 12864 10124 170883 222779 carevue MICU MICU +#> 3: 12865 10124 170883 295043 carevue CCU CCU +#> 4: 12866 10124 170883 237528 carevue MICU MICU +#> first_wardid last_wardid intime outtime los +#> 1: 23 23 2192-03-29 10:46:51 2192-04-01 06:36:00 2.8258 +#> 2: 50 50 2192-04-16 20:58:32 2192-04-20 08:51:28 3.4951 +#> 3: 7 7 2192-04-24 02:29:49 2192-04-26 23:59:45 2.8958 +#> 4: 23 23 2192-04-30 14:50:44 2192-05-15 23:34:21 15.3636 +}\if{html}{\out{
}} + +Much care has been taken to make \code{ricu} extensible to new datasets. For +example the publicly available ICU database \href{https://amsterdammedicaldatascience.nl/amsterdamumcdb/}{AmsterdamUMCdb } provided by the Amsterdam University Medical Center, currently is not part -of the core datasets of `ricu`, but code for integrating this dataset is -available on [github](https://github.com/eth-mds/aumc). +of the core datasets of \code{ricu}, but code for integrating this dataset is +available on \href{https://github.com/eth-mds/aumc}{github}. } \section{MIMIC-III}{ From 88c61e9366dc9fe98e688219f130609a40b918db Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 4 Sep 2023 11:31:35 +0200 Subject: [PATCH 35/37] rm empty first lines --- R/assertions.R | 1 - R/callback-cncpt.R | 1 - R/callback-itm.R | 1 - R/callback-scores.R | 1 - R/callback-sep3.R | 1 - R/callback-sofa.R | 1 - R/concept-load.R | 1 - R/concept-utils.R | 1 - R/config-class.R | 1 - R/config-utils.R | 1 - R/data-env.R | 1 - R/data-load.R | 1 - R/data-utils.R | 1 - R/ricu.R | 1 - R/setup-attach.R | 1 - R/setup-download.R | 1 - R/setup-import.R | 1 - R/tbl-base.R | 1 - R/tbl-class.R | 1 - R/tbl-utils.R | 1 - R/utils-cli.R | 1 - R/utils-export.R | 1 - R/utils-file.R | 1 - R/utils-misc.R | 1 - R/utils-ts.R | 1 - R/zzz.R | 1 - README.md | 1 - _pkgdown.yml | 1 - man/change_id.Rd | 20 ++++++++++---------- tests/testthat.R | 1 - tests/testthat/helpers.R | 1 - tests/testthat/test-assertions.R | 1 - tests/testthat/test-callback.R | 1 - tests/testthat/test-concept.R | 1 - tests/testthat/test-config.R | 1 - tests/testthat/test-data-env.R | 1 - tests/testthat/test-data-tbl.R | 1 - tests/testthat/test-data-utils.R | 1 - tests/testthat/test-helpers.R | 1 - tests/testthat/test-scores.R | 1 - tests/testthat/test-setup-attach.R | 1 - tests/testthat/test-setup-download.R | 1 - tests/testthat/test-setup-import.R | 1 - tests/testthat/test-tbl-base.R | 1 - tests/testthat/test-tbl-class.R | 1 - tests/testthat/test-tbl-utils.R | 1 - tests/testthat/test-utils-cli.R | 1 - tests/testthat/test-utils-export.R | 1 - tests/testthat/test-utils-file.R | 1 - tests/testthat/test-utils-ts.R | 1 - vignettes/ricu.bib | 1 - 51 files changed, 10 insertions(+), 60 deletions(-) diff --git a/R/assertions.R b/R/assertions.R index 14e40701..3bef13b2 100644 --- a/R/assertions.R +++ b/R/assertions.R @@ -1,4 +1,3 @@ - #' @importFrom assertthat see_if on_failure<- validate_that #' @importFrom assertthat is.string is.flag is.dir is.count is.scalar #' @importFrom assertthat has_name has_attr are_equal is.number diff --git a/R/callback-cncpt.R b/R/callback-cncpt.R index 4d416ae4..cd919e0f 100644 --- a/R/callback-cncpt.R +++ b/R/callback-cncpt.R @@ -1,4 +1,3 @@ - collect_dots <- function(concepts, interval, ..., merge_dat = FALSE) { assert_that(is.character(concepts)) diff --git a/R/callback-itm.R b/R/callback-itm.R index 5f2521be..3e0f5a75 100644 --- a/R/callback-itm.R +++ b/R/callback-itm.R @@ -1,4 +1,3 @@ - vent_flag <- function(x, val_var, ...) { x <- x[as.logical(get(val_var)), ] set(x, j = c(index_var(x), val_var), diff --git a/R/callback-scores.R b/R/callback-scores.R index a38dce02..8ec0c956 100644 --- a/R/callback-scores.R +++ b/R/callback-scores.R @@ -1,4 +1,3 @@ - #' SIRS score label #' #' The SIRS (Systemic Inflammatory Response Syndrome) score is a commonly used diff --git a/R/callback-sep3.R b/R/callback-sep3.R index 79e4de72..b49aba12 100644 --- a/R/callback-sep3.R +++ b/R/callback-sep3.R @@ -1,4 +1,3 @@ - #' Sepsis 3 label #' #' The sepsis 3 label consists of a suspected infection combined with an acute diff --git a/R/callback-sofa.R b/R/callback-sofa.R index 07a1fa27..a2fff8fa 100644 --- a/R/callback-sofa.R +++ b/R/callback-sofa.R @@ -1,4 +1,3 @@ - #' SOFA score label #' #' The SOFA (Sequential Organ Failure Assessment) score is a commonly used diff --git a/R/concept-load.R b/R/concept-load.R index a420f1bf..6774a5dd 100644 --- a/R/concept-load.R +++ b/R/concept-load.R @@ -1,4 +1,3 @@ - #' Load concept data #' #' Concept objects are used in `ricu` as a way to specify how a clinical diff --git a/R/concept-utils.R b/R/concept-utils.R index ae77feac..47f7d96b 100644 --- a/R/concept-utils.R +++ b/R/concept-utils.R @@ -1,4 +1,3 @@ - #' Data items #' #' Item objects are used in `ricu` as a way to specify how individual data diff --git a/R/config-class.R b/R/config-class.R index a0190db3..39cef8cd 100644 --- a/R/config-class.R +++ b/R/config-class.R @@ -1,4 +1,3 @@ - #' Internal utilities for working with data source configurations #' #' Data source configuration objects store information on data sources used diff --git a/R/config-utils.R b/R/config-utils.R index faf09d75..ca9e6a49 100644 --- a/R/config-utils.R +++ b/R/config-utils.R @@ -1,4 +1,3 @@ - #' @param x Object to coerce/query #' #' @rdname src_cfg diff --git a/R/data-env.R b/R/data-env.R index cc16b4d8..fb55bdeb 100644 --- a/R/data-env.R +++ b/R/data-env.R @@ -1,4 +1,3 @@ - #' ICU datasets #' #' The [Laboratory for Computational Physiology diff --git a/R/data-load.R b/R/data-load.R index 711f6d43..53187a3c 100644 --- a/R/data-load.R +++ b/R/data-load.R @@ -1,4 +1,3 @@ - #' Low level functions for loading data #' #' Data loading involves a cascade of S3 generic functions, which can diff --git a/R/data-utils.R b/R/data-utils.R index 5dcac99a..ebb3a6f3 100644 --- a/R/data-utils.R +++ b/R/data-utils.R @@ -1,4 +1,3 @@ - #' Data loading utilities #' #' Two important tools for smoothing out differences among used datasets are diff --git a/R/ricu.R b/R/ricu.R index faece7fb..37af4d6e 100644 --- a/R/ricu.R +++ b/R/ricu.R @@ -1,4 +1,3 @@ - #' @keywords internal #' @importFrom data.table setattr setcolorder set setnames setorderv #' @importFrom data.table setDT setDF fifelse rbindlist data.table diff --git a/R/setup-attach.R b/R/setup-attach.R index 57fa650e..2ec2df5b 100644 --- a/R/setup-attach.R +++ b/R/setup-attach.R @@ -1,4 +1,3 @@ - #' Data attach utilities #' #' Making a dataset available to `ricu` consists of 3 steps: downloading diff --git a/R/setup-download.R b/R/setup-download.R index 65b673cc..6d9e41e2 100644 --- a/R/setup-download.R +++ b/R/setup-download.R @@ -1,4 +1,3 @@ - #' Data download utilities #' #' Making a dataset available to `ricu` consists of 3 steps: downloading diff --git a/R/setup-import.R b/R/setup-import.R index 95640867..0d2e536d 100644 --- a/R/setup-import.R +++ b/R/setup-import.R @@ -1,4 +1,3 @@ - #' Data import utilities #' #' Making a dataset available to `ricu` consists of 3 steps: downloading diff --git a/R/tbl-base.R b/R/tbl-base.R index e32dc377..c7547b0d 100644 --- a/R/tbl-base.R +++ b/R/tbl-base.R @@ -1,4 +1,3 @@ - #' @export `[.id_tbl` <- function(x, ...) wrap_ptype(as_ptype(x), NextMethod()) diff --git a/R/tbl-class.R b/R/tbl-class.R index 3adf82ee..2eff5eae 100644 --- a/R/tbl-class.R +++ b/R/tbl-class.R @@ -1,4 +1,3 @@ - #' Tabular ICU data classes #' #' In order to simplify handling or tabular ICU data, `ricu` provides diff --git a/R/tbl-utils.R b/R/tbl-utils.R index ad40ba51..7d23c402 100644 --- a/R/tbl-utils.R +++ b/R/tbl-utils.R @@ -1,4 +1,3 @@ - #' ICU class meta data utilities #' #' The two data classes `id_tbl` and `ts_tbl`, used by `ricu` to represent ICU diff --git a/R/utils-cli.R b/R/utils-cli.R index c013e251..715d9498 100644 --- a/R/utils-cli.R +++ b/R/utils-cli.R @@ -1,4 +1,3 @@ - is_interactive <- function() { !isTRUE(getOption('knitr.in.progress')) && interactive() } diff --git a/R/utils-export.R b/R/utils-export.R index d25a55e1..cb86ea88 100644 --- a/R/utils-export.R +++ b/R/utils-export.R @@ -1,4 +1,3 @@ - #' Read and write utilities #' #' Support for reading from and writing to pipe separated values (`.psv`) diff --git a/R/utils-file.R b/R/utils-file.R index a970f3f5..4ee8465a 100644 --- a/R/utils-file.R +++ b/R/utils-file.R @@ -1,4 +1,3 @@ - #' File system utilities #' #' Determine the location where to place data meant to persist between diff --git a/R/utils-misc.R b/R/utils-misc.R index d3a1de5f..fcedbd37 100644 --- a/R/utils-misc.R +++ b/R/utils-misc.R @@ -1,4 +1,3 @@ - agg_or_na <- function(agg_fun) { function(x) { if (all(is.na(x))) return(x[1L]) diff --git a/R/utils-ts.R b/R/utils-ts.R index 9bfcf103..c62796de 100644 --- a/R/utils-ts.R +++ b/R/utils-ts.R @@ -1,4 +1,3 @@ - #' Time series utility functions #' #' ICU data as handled by `ricu` is mostly comprised of time series data and as diff --git a/R/zzz.R b/R/zzz.R index ea11a390..be7ea631 100644 --- a/R/zzz.R +++ b/R/zzz.R @@ -1,4 +1,3 @@ - .onLoad <- function(libname, pkgname) { # nocov start fix_base_fun <- function(fun, fix) { diff --git a/README.md b/README.md index 09df4ea1..c9498e0e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # [ricu](https://eth-mds.github.io/ricu/) diff --git a/_pkgdown.yml b/_pkgdown.yml index 45fc8806..4d0e4b13 100644 --- a/_pkgdown.yml +++ b/_pkgdown.yml @@ -1,4 +1,3 @@ - title: ICU data with R url: https://eth-mds.github.io/ricu diff --git a/man/change_id.Rd b/man/change_id.Rd index d1770344..8c3fa01e 100644 --- a/man/change_id.Rd +++ b/man/change_id.Rd @@ -72,17 +72,17 @@ hospital admissions as destination IDs. #> # Id var: `icustay_id` #> icustay_id hadm_id hadm_id_start hadm_id_end #> -#> 1 201006 198503 ~17h ~6d -#> 2 201204 114648 ~23h ~4d -#> 3 203766 126949 ~1h ~6d -#> 4 204132 157609 ~23h ~7d -#> 5 204201 177678 ~17h ~6d +#> 1 201006 198503 -3290 mins 9114 mins +#> 2 201204 114648 -2 mins 6949 mins +#> 3 203766 126949 -1336 mins 8818 mins +#> 4 204132 157609 -1 mins 10103 mins +#> 5 204201 177678 -368 mins 9445 mins #> ... -#> 132 295043 170883 ~18h ~21d -#> 133 295741 176805 ~23h ~2d -#> 134 296804 110244 ~2h ~3d -#> 135 297782 167612 ~23h ~3h -#> 136 298685 151323 ~23h ~13d +#> 132 295043 170883 -10413 mins 31258 mins +#> 133 295741 176805 -1 mins 3153 mins +#> 134 296804 110244 -1294 mins 4599 mins +#> 135 297782 167612 -1 mins 207 mins +#> 136 298685 151323 -1 mins 19082 mins #> # i 131 more rows }\if{html}{\out{}} diff --git a/tests/testthat.R b/tests/testthat.R index adccaadd..0b68b1cb 100644 --- a/tests/testthat.R +++ b/tests/testthat.R @@ -1,4 +1,3 @@ - library(testthat) library(ricu) diff --git a/tests/testthat/helpers.R b/tests/testthat/helpers.R index f7e35fdf..2cbfbee1 100644 --- a/tests/testthat/helpers.R +++ b/tests/testthat/helpers.R @@ -1,4 +1,3 @@ - expect_all_identical <- function(object, expected = object[[1L]], ..., info = NULL, label = NULL, expected_label = NULL) { diff --git a/tests/testthat/test-assertions.R b/tests/testthat/test-assertions.R index 5ae3a024..c2ec5596 100644 --- a/tests/testthat/test-assertions.R +++ b/tests/testthat/test-assertions.R @@ -1,4 +1,3 @@ - test_that("assertions", { expect_true(is_dt(data.table::data.table(a = 1, b = 2))) diff --git a/tests/testthat/test-callback.R b/tests/testthat/test-callback.R index 5fabd919..0d151157 100644 --- a/tests/testthat/test-callback.R +++ b/tests/testthat/test-callback.R @@ -1,4 +1,3 @@ - test_that("aumc callbacks", { skip_if_not_installed("mockthat") diff --git a/tests/testthat/test-concept.R b/tests/testthat/test-concept.R index 114be0fc..bde36140 100644 --- a/tests/testthat/test-concept.R +++ b/tests/testthat/test-concept.R @@ -1,4 +1,3 @@ - test_that("load hirid items", { skip_if_not_installed("mockthat") diff --git a/tests/testthat/test-config.R b/tests/testthat/test-config.R index a14b9ff9..73810d48 100644 --- a/tests/testthat/test-config.R +++ b/tests/testthat/test-config.R @@ -1,4 +1,3 @@ - skip_if_srcs_missing(c("mimic_demo", "eicu_demo")) test_that("config classes", { diff --git a/tests/testthat/test-data-env.R b/tests/testthat/test-data-env.R index d5703483..c6830363 100644 --- a/tests/testthat/test-data-env.R +++ b/tests/testthat/test-data-env.R @@ -1,4 +1,3 @@ - skip_if_srcs_missing(c("mimic_demo", "eicu_demo")) test_that("demo envs", { diff --git a/tests/testthat/test-data-tbl.R b/tests/testthat/test-data-tbl.R index 5b2b7241..76afab76 100644 --- a/tests/testthat/test-data-tbl.R +++ b/tests/testthat/test-data-tbl.R @@ -1,4 +1,3 @@ - skip_if_srcs_missing(c("mimic_demo", "eicu_demo")) test_that("load_src()", { diff --git a/tests/testthat/test-data-utils.R b/tests/testthat/test-data-utils.R index 63864547..f537ee5b 100644 --- a/tests/testthat/test-data-utils.R +++ b/tests/testthat/test-data-utils.R @@ -1,4 +1,3 @@ - skip_if_srcs_missing(c("mimic_demo", "eicu_demo")) test_that("stay windows", { diff --git a/tests/testthat/test-helpers.R b/tests/testthat/test-helpers.R index c2620d72..14b7093f 100644 --- a/tests/testthat/test-helpers.R +++ b/tests/testthat/test-helpers.R @@ -1,4 +1,3 @@ - test_that("expect_all_identical", { expect_success(expect_all_identical("foo", "foo")) diff --git a/tests/testthat/test-scores.R b/tests/testthat/test-scores.R index db79d72f..169c43c4 100644 --- a/tests/testthat/test-scores.R +++ b/tests/testthat/test-scores.R @@ -1,4 +1,3 @@ - skip_if_srcs_missing(c("mimic_demo", "eicu_demo")) so_mi <- load_concepts("sofa", "mimic_demo", verbose = FALSE) diff --git a/tests/testthat/test-setup-attach.R b/tests/testthat/test-setup-attach.R index d577d84c..6eea978b 100644 --- a/tests/testthat/test-setup-attach.R +++ b/tests/testthat/test-setup-attach.R @@ -1,4 +1,3 @@ - test_that("auto attach env var", { srcs <- c("mimic_demo", "eicu_demo") diff --git a/tests/testthat/test-setup-download.R b/tests/testthat/test-setup-download.R index cfd4789c..7c862eda 100644 --- a/tests/testthat/test-setup-download.R +++ b/tests/testthat/test-setup-download.R @@ -1,4 +1,3 @@ - mock_fetch_memory <- function(url, handle, ...) { file <- system.file("testdata", paste0(basename(url), ".rds"), diff --git a/tests/testthat/test-setup-import.R b/tests/testthat/test-setup-import.R index 2efc9394..999e5337 100644 --- a/tests/testthat/test-setup-import.R +++ b/tests/testthat/test-setup-import.R @@ -1,4 +1,3 @@ - skip_on_cran() tmp_cars <- withr::local_tempdir() diff --git a/tests/testthat/test-tbl-base.R b/tests/testthat/test-tbl-base.R index 0d16ef9c..5ed86e16 100644 --- a/tests/testthat/test-tbl-base.R +++ b/tests/testthat/test-tbl-base.R @@ -1,4 +1,3 @@ - test_that("icu_tbl subsetting", { col <- runif(10) diff --git a/tests/testthat/test-tbl-class.R b/tests/testthat/test-tbl-class.R index 3862604e..86115420 100644 --- a/tests/testthat/test-tbl-class.R +++ b/tests/testthat/test-tbl-class.R @@ -1,4 +1,3 @@ - test_that("id_tbl constructors", { tbl <- id_tbl(a = 1:10, b = rnorm(10)) diff --git a/tests/testthat/test-tbl-utils.R b/tests/testthat/test-tbl-utils.R index 5c5cf477..87c5a367 100644 --- a/tests/testthat/test-tbl-utils.R +++ b/tests/testthat/test-tbl-utils.R @@ -1,4 +1,3 @@ - test_that("rename_cols for id_tbl", { tbl <- id_tbl(a = 1:10, b = rnorm(10), c = rnorm(10)) diff --git a/tests/testthat/test-utils-cli.R b/tests/testthat/test-utils-cli.R index c6977a86..0f118fe9 100644 --- a/tests/testthat/test-utils-cli.R +++ b/tests/testthat/test-utils-cli.R @@ -1,4 +1,3 @@ - test_that("cli progress", { skip_if_not_installed("mockthat") diff --git a/tests/testthat/test-utils-export.R b/tests/testthat/test-utils-export.R index eec6aa9f..eb810932 100644 --- a/tests/testthat/test-utils-export.R +++ b/tests/testthat/test-utils-export.R @@ -1,4 +1,3 @@ - skip_on_cran() test_that("psv export", { diff --git a/tests/testthat/test-utils-file.R b/tests/testthat/test-utils-file.R index fae1467c..09c368aa 100644 --- a/tests/testthat/test-utils-file.R +++ b/tests/testthat/test-utils-file.R @@ -1,4 +1,3 @@ - test_that("data dir", { dir <- withr::with_envvar( diff --git a/tests/testthat/test-utils-ts.R b/tests/testthat/test-utils-ts.R index ca51ff18..bb3e7735 100644 --- a/tests/testthat/test-utils-ts.R +++ b/tests/testthat/test-utils-ts.R @@ -1,4 +1,3 @@ - test_that("collapse/expand", { tbl <- ts_tbl(x = 1:5, y = hours(1:5), z = hours(2:6), val = rnorm(5), diff --git a/vignettes/ricu.bib b/vignettes/ricu.bib index 0b2d25ab..27535199 100644 --- a/vignettes/ricu.bib +++ b/vignettes/ricu.bib @@ -1,4 +1,3 @@ - @article{johnson2016, title = {MIMIC-III, A Freely Accessible Critical Care Database}, author = {Johnson, Alistair EW and Pollard, Tom J and Shen, Lu and Li-wei, H Lehman and Feng, Mengling and Ghassemi, Mohammad and Moody, Benjamin and Szolovits, Peter and Celi, Leo Anthony and Mark, Roger G}, From 47f99d2ae8342e4e2ce0d9c67ce0cb4044fe4560 Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 4 Sep 2023 12:08:18 +0200 Subject: [PATCH 36/37] rm omr for now --- inst/extdata/config/data-sources.json | 31 --------------------------- 1 file changed, 31 deletions(-) diff --git a/inst/extdata/config/data-sources.json b/inst/extdata/config/data-sources.json index b8548290..a7ceea6e 100644 --- a/inst/extdata/config/data-sources.json +++ b/inst/extdata/config/data-sources.json @@ -7173,37 +7173,6 @@ } } }, - "omr" : { - "files": "core/omr.csv.gz", - "defaults": { - "time_vars": ["chartdate"], - "val_var": "result_value" - }, - "num_rows": 6439169, - "cols" : { - "subject_id": { - "name": "subject_id", - "spec": "col_integer" - }, - "chartdate": { - "name": "chartdate", - "spec": "col_datetime", - "format": "%Y-%m-%d" - }, - "seq_num": { - "name": "seq_num", - "spec": "col_integer" - }, - "result_name": { - "name": "result_name", - "spec": "col_character" - }, - "result_value": { - "name": "result_value", - "spec": "col_character" - } - } - }, "transfers": { "files": "core/transfers.csv.gz", "defaults": { From 91c60be1ce7ddd449e06d95775a1ebef078f887d Mon Sep 17 00:00:00 2001 From: Nicolas Bennett <3158446+nbenn@users.noreply.github.com> Date: Mon, 4 Sep 2023 13:46:38 +0200 Subject: [PATCH 37/37] dont run ts util examples --- R/utils-ts.R | 2 ++ man/ts_utils.Rd | 2 ++ 2 files changed, 4 insertions(+) diff --git a/R/utils-ts.R b/R/utils-ts.R index c62796de..74afa62a 100644 --- a/R/utils-ts.R +++ b/R/utils-ts.R @@ -63,6 +63,7 @@ #' `has_gaps()`/`has_no_gaps()`/`is_regular()`, which return logical flags. #' #' @examples +#' if (FALSE) { #' tbl <- ts_tbl(x = 1:5, y = hours(1:5), z = hours(2:6), val = rnorm(5), #' index_var = "y") #' exp <- expand(tbl, "y", "z", step_size = 1L, new_index = "y", @@ -88,6 +89,7 @@ #' tbl[6, 2] <- hours(2) #' has_no_gaps(tbl) #' is_regular(tbl) +#' } #' #' @rdname ts_utils #' @export diff --git a/man/ts_utils.Rd b/man/ts_utils.Rd index 6412598e..79315042 100644 --- a/man/ts_utils.Rd +++ b/man/ts_utils.Rd @@ -168,6 +168,7 @@ can be seen as a compromise between the two, where windows are spanned for certain time-points, specified by \code{index}. } \examples{ +if (FALSE) { tbl <- ts_tbl(x = 1:5, y = hours(1:5), z = hours(2:6), val = rnorm(5), index_var = "y") exp <- expand(tbl, "y", "z", step_size = 1L, new_index = "y", @@ -193,5 +194,6 @@ is_regular(tbl) tbl[6, 2] <- hours(2) has_no_gaps(tbl) is_regular(tbl) +} }