Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: medicalcoder
Title: A Package for Working with ICD Codes and Comorbidity Algorithms
Version: 0.7.0.9000
Version: 0.7.0.9001
Authors@R: c(
person(given = "Peter", family = "DeWitt", email = "[email protected]", role = c("aut", "cre", "cov"), comment = c(ORCID = "0000-0002-6391-0795")),
person(given = "Tell", family = "Bennett", email = "[email protected]", role = c("ctb"), comment = c(ORCID = "0000-0003-1483-4236")),
Expand Down
14 changes: 10 additions & 4 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,18 @@
change will generally result in less computation time than base R
`data.frames` (`data.tables` require even less time).

* Add the `elixhauser_ahrq2026` method for `comorbidities()` (#32)

## Bug Fixes

* `summary.medicalcoder_comorbidites()` no longer crashes when a zero row input
is passed in. Consisently return `NA` instead of `NaN` when counts are zeros.
is passed in. Consistently return `NA` instead of `NaN` when counts are zeros.
(#26, #27)

* Improve the conditional and multiple comorbidities mapped by a code under AHRQ
ICD-10 codes for fiscal years 2023 through 2026. The bug was found and fixed
as part of the extension #32.

## Other Changes

* Extend and improve the internal ICD-9 database to distinguish between CDC and
Expand All @@ -25,13 +31,13 @@
`mdcr_set()`, and added inline guidance in the longitudinal section of
`comorbidities()` to explain the first-occurrence logic.

* Improve cumulative flagging to apply first-occurrence logic more effecently.
* Improve cumulative flagging to apply first-occurrence logic more efficiently.

* Extend documentation for the expected default behavior between the
present-on-admission flags and `flag.method` argument in `comorbidities()`
(re: #28)

* Add `mdcr_unique()` to the data.frame utilties. This reduced the computational
* Add `mdcr_unique()` to the data.frame utilities. This reduced the computational
time required to apply `comorbidities()` to `tibble`s and `data.table`s. (#31)

# medicalcoder 0.7.0
Expand All @@ -42,7 +48,7 @@
* exclusions for less severer conditions when more severer conditions are flagged
* Improved POA, NPOA, and EXEMPTPOA. This came about from #20.

* Make internal data.frame tool `mdcr_duplicated` data.table aware.
* Make internal data.frame tool `mdcr_duplicated()` data.table aware.

* Elixhauser (Quan 2005) - added missing ICD-10 codes to the mappings

Expand Down
8 changes: 4 additions & 4 deletions R/comorbidities.R
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@
#' * Elixhauser Comorbidities:
#'
#' * Agency for Healthcare Research and Quality (AHRQ). Elixhauser
#' Comorbidity Software Refined for ICD-10-CM Diagnoses, v2025.1 \[Internet\].
#' 2025. Available from:
#' Comorbidity Software Refined for ICD-10-CM Diagnoses, v2026.1 \[Internet\].
#' 2026. Available from:
#' https://www.hcup-us.ahrq.gov/toolssoftware/comorbidityicd10/comorbidity_icd10.jsp
#'
#' @seealso
Expand Down Expand Up @@ -676,7 +676,7 @@ comorbidities_methods <- function() {
"charlson_cdmf2019",
"elixhauser_elixhauser1988", "elixhauser_ahrq_web", "elixhauser_quan2005",
"elixhauser_ahrq2022", "elixhauser_ahrq2023", "elixhauser_ahrq2024",
"elixhauser_ahrq2025", "elixhauser_ahrq_icd10")
"elixhauser_ahrq2025", "elixhauser_ahrq2026", "elixhauser_ahrq_icd10")
}


Expand All @@ -689,7 +689,7 @@ comorbidities_methods <- function() {
"transplant_flag", "tech_dep_flag",
"pccc_v3.1", "pccc_v3.0", "pccc_v2.1", "pccc_v2.0",
"elixhauser_ahrq_web", "elixhauser_elixhauser1988", "elixhauser_quan2005",
"elixhauser_ahrq2022", "elixhauser_ahrq2023", "elixhauser_ahrq2024", "elixhauser_ahrq2025",
"elixhauser_ahrq2022", "elixhauser_ahrq2023", "elixhauser_ahrq2024", "elixhauser_ahrq2025", "elixhauser_ahrq2026",
"elixhauser_ahrq_icd10",
"charlson_cdmf2019", "charlson_deyo1992", "charlson_quan2005", "charlson_quan2011"
)
35 changes: 26 additions & 9 deletions R/elixhauser.R
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@
rtn <- mdcr_set(rtn, j = "readmission_index", value = results$readmission_index)
rownames(rtn) <- NULL
rtn

}

.elixhauser_post2022 <- function(ccc, id.vars, iddf, cmrb, poa.var, primarydx.var, method) {
Expand Down Expand Up @@ -93,23 +92,37 @@
XANYPOA[cbind(ri[keep], ci[keep])] <- 1L
}

# Assign comorbidities which are nutral to POA
# Assign comorbidities which are neutral to POA
from_to <-
c("DRUG_ABUSEPSYCHOSES" = "DRUG_ABUSE",
"HFHTN_CX" = "HTN_CX",
"HTN_CXRENLFL_SEV"= "HTN_CX",
"HFHTN_CXRENLFL_SEV"= "HTN_CX",
"ALCOHOLLIVER_MLD" = "ALCOHOL",
"VALVE_AUTOIMMUNE" = "AUTOIMMUNE"
"VALVE_AUTOIMMUNE" = "AUTOIMMUNE",
"LIVER_MLD_NEURO" = "LIVER_MLD",
"LIVER_MLD_NEURO" = "NEURO_OTH",
"NEURO_OTH_SEIZ" = "NEURO_OTH",
"NEURO_OTH_SEIZ" = "NEURO_SEIZ",
"LIVER_MLD_PULMCIRC" = "LIVER_MLD",
"LIVER_MLD_PULMCIRC" = "PULMCIRC"
)

for (i in seq_len(length(from_to))) {
f <- names(from_to)[i]
t <- unname(from_to[i])
XPOA[ XPOA[, f] == 1L, t] <- 1L
XPOAEXEMPT[XPOAEXEMPT[, f] == 1L, t] <- 1L
XANYPOA[ XANYPOA[, f] == 1L, t] <- 1L
XNPOA[ XNPOA[, f] == 1L, t] <- 1L
if (f %in% colnames(XPOA)) {
XPOA[XPOA[, f] == 1L, t] <- 1L
}
if (f %in% colnames(XPOAEXEMPT)) {
XPOAEXEMPT[XPOAEXEMPT[, f] == 1L, t] <- 1L
}
if (f %in% colnames(XANYPOA)) {
XANYPOA[XANYPOA[, f] == 1L, t] <- 1L
}
if (f %in% colnames(XNPOA)) {
XNPOA[XNPOA[, f] == 1L, t] <- 1L
}
}

# flag if poa expempt or POA
Expand All @@ -128,8 +141,12 @@
for (i in seq_len(length(from_to))) {
f <- names(from_to)[i]
t <- unname(from_to[i])
XPOA[ XPOA[, f] == 1L, t] <- 1L
XPOAEXEMPT[XPOAEXEMPT[, f] == 1L, t] <- 1L
if (f %in% colnames(XPOA)) {
XPOA[XPOA[, f] == 1L, t] <- 1L
}
if (f %in% colnames(XPOAEXEMPT)) {
XPOAEXEMPT[XPOAEXEMPT[, f] == 1L, t] <- 1L
}
}

# CBVD_NPOA is unique in that it requires that the condition is not POA
Expand Down
Binary file modified R/sysdata.rda
Binary file not shown.
3 changes: 2 additions & 1 deletion README.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -274,14 +274,15 @@ vignette(topic = "charlson", package = "medicalcoder")
* `method = elixhauser_elixhauser1988`
* [Quan et al. (2005)](https://doi.org/10.1097/01.mlr.0000182534.19832.83)
* `method = elixhauser_quan2005`
* AHRQ (2017, 2022, 2023, 2024, 2025, ICD10)
* AHRQ (2017, 2022, 2023, 2024, 2025, 2026, ICD10)
* [For ICD-9 codes](https://hcup-us.ahrq.gov/toolssoftware/comorbidity/comorbidity.jsp)
* `method = elixhauser_ahrq_web`
* [For ICD-10 codes](https://hcup-us.ahrq.gov/toolssoftware/comorbidityicd10/comorbidity_icd10.jsp)
* `method = elixhauser_ahrq2022`
* `method = elixhauser_ahrq2023`
* `method = elixhauser_ahrq2024`
* `method = elixhauser_ahrq2025`
* `method = elixhauser_ahrq2026`
* `method = elixhauser_ahrq_icd10`

```{r}
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,14 +312,15 @@ vignette(topic = "charlson", package = "medicalcoder")
* `method = elixhauser_elixhauser1988`
* [Quan et al. (2005)](https://doi.org/10.1097/01.mlr.0000182534.19832.83)
* `method = elixhauser_quan2005`
* AHRQ (2017, 2022, 2023, 2024, 2025, ICD10)
* AHRQ (2017, 2022, 2023, 2024, 2025, 2026, ICD10)
* [For ICD-9 codes](https://hcup-us.ahrq.gov/toolssoftware/comorbidity/comorbidity.jsp)
* `method = elixhauser_ahrq_web`
* [For ICD-10 codes](https://hcup-us.ahrq.gov/toolssoftware/comorbidityicd10/comorbidity_icd10.jsp)
* `method = elixhauser_ahrq2022`
* `method = elixhauser_ahrq2023`
* `method = elixhauser_ahrq2024`
* `method = elixhauser_ahrq2025`
* `method = elixhauser_ahrq2026`
* `method = elixhauser_ahrq_icd10`


Expand Down
Binary file added data-raw/elixhauser/ahrq/CMR-v2026-1.zip
Binary file not shown.
5 changes: 1 addition & 4 deletions data-raw/elixhauser/elixhauser.R
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,7 @@ codes <- codes[,
"ahrq_web",
"elixhauser1988",
"quan2005",
"ahrq2022",
"ahrq2023",
"ahrq2024",
"ahrq2025",
sort(grep("ahrq\\d{4}", names(codes), value = TRUE)),
"ahrq_icd10")]

# Which conditions require a POA required flag?
Expand Down
96 changes: 62 additions & 34 deletions data-raw/elixhauser/elixhauser_ahrq_icd10.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# ahrq/CMR_v2023-1.zip
# ahrq/CMR_v2024-1.zip
# ahrq/CMR_v2025.1.zip
# ahrq/CMR-v2026-1.zip
# ../icd/icd_codes.rds
#
# output:
Expand Down Expand Up @@ -47,38 +48,43 @@ if (interactive()) {
unzip("ahrq/CMR_v2023-1.zip", list = TRUE) # AHRQ based on ICD-10
unzip("ahrq/CMR_v2024-1.zip", list = TRUE) # AHRQ based on ICD-10
unzip("ahrq/CMR_v2025.1.zip", list = TRUE) # AHRQ based on ICD-10
unzip("ahrq/CMR-v2026-1.zip", list = TRUE) # AHRQ based on ICD-10
}

unzip("ahrq/CMR_v2022-1.zip", exdir = tmpdir)
unzip("ahrq/CMR_v2023-1.zip", exdir = tmpdir)
unzip("ahrq/CMR_v2024-1.zip", exdir = tmpdir)
unzip("ahrq/CMR_v2025.1.zip", exdir = tmpdir, junkpaths = TRUE)
unzip("ahrq/CMR-v2026-1.zip", exdir = tmpdir)

################################################################################
# import all the SAS Programs for the ICD-10 version of Elixhauser

format_programs <-
list(
"ahrq2022" = scan(file = paste0(tmpdir, "/CMR_Format_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = paste0(tmpdir, "/CMR_Format_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = paste0(tmpdir, "/CMR_Format_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = paste0(tmpdir, "/CMR_Format_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive())
"ahrq2022" = scan(file = file.path(tmpdir, "CMR_Format_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = file.path(tmpdir, "CMR_Format_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = file.path(tmpdir, "CMR_Format_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = file.path(tmpdir, "CMR_Format_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2026" = scan(file = file.path(tmpdir, "CMR_Format_Program_v2026-1.sas"), what = character(), sep = "\n", quiet = !interactive())
)

mapping_programs <-
list(
"ahrq2022" = scan(file = paste0(tmpdir, "/CMR_Mapping_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = paste0(tmpdir, "/CMR_Mapping_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = paste0(tmpdir, "/CMR_Mapping_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = paste0(tmpdir, "/CMR_Mapping_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive())
"ahrq2022" = scan(file = file.path(tmpdir, "CMR_Mapping_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = file.path(tmpdir, "CMR_Mapping_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = file.path(tmpdir, "CMR_Mapping_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = file.path(tmpdir, "CMR_Mapping_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2026" = scan(file = file.path(tmpdir, "CMR_Mapping_Program_v2026-1.sas"), what = character(), sep = "\n", quiet = !interactive())
)

index_programs <-
list(
"ahrq2022" = scan(file = paste0(tmpdir, "/CMR_Index_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = paste0(tmpdir, "/CMR_Index_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = paste0(tmpdir, "/CMR_Index_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = paste0(tmpdir, "/CMR_Index_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive())
"ahrq2022" = scan(file = file.path(tmpdir, "CMR_Index_Program_v2022-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2023" = scan(file = file.path(tmpdir, "CMR_Index_Program_v2023-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2024" = scan(file = file.path(tmpdir, "CMR_Index_Program_v2024-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2025" = scan(file = file.path(tmpdir, "CMR_Index_Program_v2025-1.sas"), what = character(), sep = "\n", quiet = !interactive()),
"ahrq2026" = scan(file = file.path(tmpdir, "CMR_Index_Program_v2026-1.sas"), what = character(), sep = "\n", quiet = !interactive())
)

################################################################################
Expand All @@ -100,29 +106,31 @@ values <-
lapply(format_programs, get_value_start_stop) |>
rbindlist(idcol = "version")

build_comfmt <-
function(x) {
x <- sub(pattern = '=', replacement = "->", x)
x <- sub(pattern = '-> \\"(.+)\\"', replacement = ") -> \\1;", x = x)
strt <- grep("->", x) + 1
x[1] <- paste("c(", x[1])
x[strt] <- paste("c(", x[strt])
other_idx <- grep("other", x)
x <- x[-seq(other_idx, length(x))]
tmpfile <- tempfile()
cat(x, file = tmpfile)
e <- new.env()
source(tmpfile, local = e)
as.list(e)
}
build_comfmt <- function(x) {
x <- sub(pattern = '=', replacement = "->", x)
x <- sub(pattern = '-> \\"(.+)\\"', replacement = ") -> \\1;", x = x)
strt <- grep("->", x) + 1
x[1] <- paste("c(", x[1])
x[strt] <- paste("c(", x[strt])
other_idx <- grep("other", x)
x <- x[-seq(other_idx, length(x))]
tmpfile <- tempfile()
cat(x, file = tmpfile)
e <- new.env()
source(tmpfile, local = e)
as.list(e)
}

build_poaexmpt <- function(x) {
other_idx <- grep("other", x)
x <- x[-seq(other_idx, length(x))]
# for 2022 - 2025 the last element ended with "1"
# for 2026 the last element ended with '1'
# Change from double quotes to single quotes.
x <- sub('= \\"1\\"', ')', x)
x <- sub("= '1'", ')', x)
x <- trimws(x)
x[1] <- paste("poaexmpt <- c(", x[1])
x
tmpfile <- tempfile()
cat(x, file = tmpfile)
e <- new.env()
Expand Down Expand Up @@ -253,7 +261,9 @@ elixhauser_poa <-
list("ahrq2022" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2022-1.xlsx"), sheet = 2, skip = 1),
"ahrq2023" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2023-1.xlsx"), sheet = 2, skip = 1),
"ahrq2024" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2024-1.xlsx"), sheet = 2, skip = 1),
"ahrq2025" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2025-1.xlsx"), sheet = 2, skip = 1))
"ahrq2025" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2025-1.xlsx"), sheet = 2, skip = 1),
"ahrq2026" = readxl::read_xlsx(paste0(tmpdir, "/CMR-Reference-File-v2026-1.xlsx"), sheet = 2, skip = 1)
)

elixhauser_poa <- lapply(elixhauser_poa, setDT)

Expand All @@ -279,15 +289,26 @@ elixhauser_poa[, condition := sub("CMR_", "", condition)]

# Extend conditions -- several of the conditions in the elixhauser_poa set are
# catch alls and more granular conditions are in the codes. Extend the
# elixhauser_poa set to include th more granular conditions
# elixhauser_poa set to include the more granular conditions
#
#> qwraps2::set_diff(elixhauser_poa$condition, elixhauser_codes$condition)
# 2026 added a new condition, LIVER_MLD_PULMCIRC
#> qwraps2::set_diff(
#> elixhauser_codes[ahrq2025 == 1, condition],
#> elixhauser_codes[ahrq2026 == 1, condition]
#> )
#+ Total number of unique values: 49
#+ Number of elements in both elixhauser_codes[ahrq2025 == 1, condition] and elixhauser_codes[ahrq2026 == 1, condition]: 48
#+ Number of elements only in elixhauser_codes[ahrq2025 == 1, condition]: 0
#+ Number of elements only in elixhauser_codes[ahrq2026 == 1, condition]: 1
#+ unique elements: LIVER_MLD_PULMCIRC

#> qwraps2::set_diff(elixhauser_poa$condition, elixhauser_codes$condition)
#+ Total number of unique values: 50
#+ Number of elements in both elixhauser_poa$condition and elixhauser_codes$condition: 37
#+ Number of elements only in elixhauser_poa$condition: 1
#+ unique elements: CBVD
#+ Number of elements only in elixhauser_codes$condition: 11
#+ unique elements: DRUG_ABUSEPSYCHOSES, CBVD_POA, NEURO_OTH_SEIZ, HFHTN_CX, HTN_CXRENLFL_SEV, HFHTN_CXRENLFL_SEV, CBVD_SQLA, CBVD_SQLAPARALYSIS, ALCOHOLLIVER_MLD, LIVER_MLD_NEURO, VALVE_AUTOIMMUNE
#+ Number of elements only in elixhauser_codes$condition: 12
#+ unique elements: DRUG_ABUSEPSYCHOSES, CBVD_POA, NEURO_OTH_SEIZ, HFHTN_CX, HTN_CXRENLFL_SEV, HFHTN_CXRENLFL_SEV, LIVER_MLD_PULMCIRC, CBVD_SQLA, CBVD_SQLAPARALYSIS, ALCOHOLLIVER_MLD, LIVER_MLD_NEURO, VALVE_AUTOIMMUNE

elixhauser_poa <-
rbind(
Expand All @@ -302,9 +323,16 @@ elixhauser_poa <-
elixhauser_poa[condition == "HF"][, condition := "HFHTN_CXRENLFL_SEV"],
elixhauser_poa[condition == "NEURO_OTH"][, condition := "NEURO_OTH_SEIZ"],
elixhauser_poa[condition == "AUTOIMMUNE"][, condition := "VALVE_AUTOIMMUNE"],
elixhauser_poa[condition == "LIVER_MLD"][, condition := "LIVER_MLD_NEURO"]
elixhauser_poa[condition == "LIVER_MLD"][, condition := "LIVER_MLD_NEURO"],
elixhauser_poa[condition == "LIVER_MLD"][, condition := "LIVER_MLD_PULMCIRC"]
)

#> qwraps2::set_diff(elixhauser_poa$condition, elixhauser_codes$condition)
#+ Total number of unique values: 50
#+ Number of elements in both elixhauser_poa$condition and elixhauser_codes$condition: 49
#+ Number of elements only in elixhauser_poa$condition: 1
#+ unique elements: CBVD
#+ Number of elements only in elixhauser_codes$condition: 0

# Under the assumption that the POA required flag is static over the years, then
# the this data structure is not needed and a POA required flag cold just be
Expand Down
4 changes: 2 additions & 2 deletions man/comorbidities.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/build-expected-ahrq-results/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
*.sas
*.csv
Loading