Answer the question "is the character string x a valid ICD code?"
ICD codes should be character vectors. is_icd will assess for both
"full codes" (decimal point present when appropriate) and "compact codes"
(decimal point omitted).
ICD-10 code "C00" is a header code because the four-character codes C00.0, C00.1, C00.2, C00.3, C00.4, C00.5, C00.6, C00.7, C00.8, and C00.9 exist. Those four-character codes are assignable (as of 2025) because no five-character descendants (e.g., C00.40) exist.
When the source is the World Health Organization (WHO) or CDC Mortality, years refer to calendar years. CDC/CMS sources use the U.S. federal fiscal year, which starts on October 1 (e.g., fiscal year 2024 runs 2023-10-01 to 2024-09-30).
Arguments
- x
Character vector of ICD codes (full or compact form).
- icdv
Integer vector of allowed ICD versions. Use
9Land/or10L. Defaults to both.- dx
Integer vector indicating allowed code type(s):
1Lfor diagnostic (ICD-9-CM, ICD-10-CM, CDC mortality, WHO),0Lfor procedural (ICD-9-PCS, ICD-10-PCS). Defaults to both.- src
Character vector of code sources. One or more of
"cms","who","cdc". Defaults to all.- year
Numeric scalar. Calendar or fiscal year to reference. Default is the most current year available per source. For ICD-9, the latest year is 2015; ICD-10 source are updated annually. Calendar year for WHO and CDC mortality. Fiscal year for CMS.
- headerok
Logical scalar. If
FALSE(default), only assignable codes are considered valid; ifTRUE, header codes are also accepted.- ever.assignable
Logical scalar. If
TRUEthen ignoreyearand returnTRUEif thexwas ever an assignable code.- warn.ambiguous
Logical scalar. If
TRUE(default), warn when a code matches more than one ICD version and/or type (e.g., both CM and PCS).- full.codes
Logical scalar. If
TRUE(default), match codes that include the decimal point where applicable.- compact.codes
Logical scalar. If
TRUE(default), match codes without the decimal point.
Details
Similarly for ICD-9-CM: "055" is a header for measles; 055.0, 055.1, 055.2, 055.8, and 055.9 are assignable. Codes 055.3–055.6 do not exist. Code 055.7 is a header because 055.71 and 055.72 exist.
Some codes change status across years. For example, ICD-9-CM 516.3 was assignable in fiscal years 2006–2011, then became a header in 2012–2015.
See also
get_icd_codes()to retrieve the internal lookup table of ICD codes.lookup_icd_codes()for retrieving details on a specific set of ICD codes.icd_compact_to_full()converts a string from a compact format to the full format based on ICD version and type (diagnostic or procedure).
Other ICD tools:
icd_compact_to_full(),
lookup_icd_codes()
Examples
################################################################################
# Some ICD-9 diagnostic codes
x <- c("136.2", "718.60", "642.02")
is_icd(x, icdv = 9, dx = 1)
#> [1] TRUE TRUE TRUE
is_icd(x, icdv = 9, dx = 0)
#> [1] FALSE FALSE FALSE
is_icd(x, icdv = 10, dx = 1)
#> [1] FALSE FALSE FALSE
is_icd(x, icdv = 10, dx = 0)
#> [1] FALSE FALSE FALSE
is_icd(x, icdv = 9, dx = 1, headerok = TRUE)
#> [1] TRUE TRUE TRUE
is_icd(x, icdv = 9, dx = 1, year = 2006)
#> [1] TRUE TRUE TRUE
################################################################################
# ICD code with, or without a dot. The ICD-9 diagnostic code 799.3 and ICD-9
# procedure code 79.93 both become 7993 when assessed against the ICD code look
# up tables. As such "7993" is a valid ICD-9 diagnostic and procedure code,
# whereas 799.3 is only a valid dx code, and 79.93 is only a valid pr code.
# Further, codes such as ".7993", "7.993", "7993.", are all non-valid codes.
x <- c("7993", ".7993", "7.993", "79.93", "799.3", "7993.")
data.frame(
x,
dx = is_icd(x, icdv = 9, dx = 1),
pr = is_icd(x, icdv = 9, dx = 0)
)
#> x dx pr
#> 1 7993 TRUE TRUE
#> 2 .7993 FALSE FALSE
#> 3 7.993 FALSE FALSE
#> 4 79.93 FALSE TRUE
#> 5 799.3 TRUE FALSE
#> 6 7993. FALSE FALSE
################################################################################
# example of a ICD-9 code that was assignable, but became a header when
# more descriptive codes were introduced: ICD-9 diagnostic code 516.3
lookup_icd_codes(paste0("516.3", c("", as.character(0:9))))
#> input_code match_type icdv dx full_code code src known_start known_end
#> 1 516.3 full_code 9 1 516.3 5163 cms 1997 2015
#> 2 516.30 full_code 9 1 516.30 51630 cms 2012 2015
#> 3 516.31 full_code 9 1 516.31 51631 cms 2012 2015
#> 4 516.32 full_code 9 1 516.32 51632 cms 2012 2015
#> 5 516.33 full_code 9 1 516.33 51633 cms 2012 2015
#> 6 516.34 full_code 9 1 516.34 51634 cms 2012 2015
#> 7 516.35 full_code 9 1 516.35 51635 cms 2012 2015
#> 8 516.36 full_code 9 1 516.36 51636 cms 2012 2015
#> 9 516.37 full_code 9 1 516.37 51637 cms 2012 2015
#> 10 516.38 <NA> NA NA <NA> <NA> <NA> NA NA
#> 11 516.39 <NA> NA NA <NA> <NA> <NA> NA NA
#> assignable_start assignable_end
#> 1 1997 2011
#> 2 2012 2015
#> 3 2012 2015
#> 4 2012 2015
#> 5 2012 2015
#> 6 2012 2015
#> 7 2012 2015
#> 8 2012 2015
#> 9 2012 2015
#> 10 NA NA
#> 11 NA NA
# ICD-9 code 516.3 was an assignable code through fiscal year 2011.
is_icd("516.3")
#> [1] TRUE
# If `year` is omitted, and `ever.assignable = FALSE` then the `year` is
# implied to be the max `known_end` year for ICD codes matched by `icdv`,
# `dx`, and `src`.
is_icd("516.3", ever.assignable = FALSE)
#> [1] FALSE
# when `year` is provided then `ever.assignable` is `FALSE` by default and
# the return is TRUE when 516.3 was assignable and FALSE otherwise.
is_icd("516.3", year = 2015)
#> [1] FALSE
is_icd("516.3", year = 2011)
#> [1] TRUE
# when year is a non-assignable year, but `ever.assignable = TRUE` the return
# will be TRUE. Useful if you know the data is retrospective and collected
# through fiscal year 2015.
is_icd("516.3", year = 2015, ever.assignable = TRUE)
#> [1] TRUE
################################################################################
# Consiser the string E010
# - This could be a ICD-9-CM full code
# - Could be a ICD-10-CM compact code
lookup_icd_codes("E010")
#> input_code match_type icdv dx full_code code src known_start known_end
#> 1 E010 full_code 9 1 E010 E010 cms 2010 2015
#> 2 E010 compact_code 10 1 E01.0 E010 cms 2014 2026
#> 3 E010 compact_code 10 1 E01.0 E010 cdc 2001 2025
#> 4 E010 compact_code 10 1 E01.0 E010 who 2008 2019
#> assignable_start assignable_end
#> 1 NA NA
#> 2 2014 2026
#> 3 2001 2025
#> 4 2008 2019
subset(get_icd_codes(with.descriptions = TRUE), grepl("^E010$", code))
#> icdv dx full_code code src known_start known_end assignable_start
#> 28019 9 1 E010 E010 cms 2010 2015 NA
#> 124027 10 1 E01.0 E010 cms 2014 2026 2014
#> 124028 10 1 E01.0 E010 who 2008 2019 2008
#> 124029 10 1 E01.0 E010 cdc 2001 2025 2001
#> assignable_end desc
#> 28019 NA Activity involving other muscle strengthening exercises
#> 124027 2026 Iodine-deficiency related diffuse (endemic) goiter
#> 124028 2019 Iodine-deficiency-related diffuse (endemic) goitre
#> 124029 2025 Iodine-deficiency-related diffuse (endemic) goiter
#> desc_start desc_end
#> 28019 2010 2015
#> 124027 2014 2026
#> 124028 2008 2019
#> 124029 2001 2025
is_icd("E010")
#> [1] TRUE
is_icd("E010", icdv = 9) # FALSE because it is a header code and was never assignable
#> [1] FALSE
is_icd("E010", icdv = 9, ever.assignable = TRUE) # FALSE
#> [1] FALSE
is_icd("E010", icdv = 9, headerok = TRUE) # TRUE
#> [1] TRUE