Guide index | Guide 4 of 5
This standalone guide uses invented diagnosis records.
icd_flag() accepts local data; it neither queries a
database nor chooses clinical definitions.
1. Supply the lists you want
library(thipR)
library(dplyr)
#>
#> Attaching package: 'dplyr'
#> The following objects are masked from 'package:stats':
#>
#> filter, lag
#> The following objects are masked from 'package:base':
#>
#> intersect, setdiff, setequal, union
records <- data.frame(
record_id = 1:5, person = c("A", "A", "B", "C", "D"),
pdx = c(" e11.9 ", "J06", "F20.0", NA, "J06"),
sdx = c(NA, "F12.1", NA, "", NA)
)
conditions <- list(diabetes = "E11", cannabis = c("F12.1", "F12.2"), psychosis = "F20")Named list elements become output columns. Empty code vectors are allowed and match no recorded codes. These lists illustrate syntax, not validated phenotypes.
2. Flag each record
flags <- icd_flag(
records, conditions,
id = "record_id", cols = c("pdx", "sdx"), match = "prefix"
)
records |> left_join(flags, by = "record_id")
#> record_id person pdx sdx diabetes cannabis psychosis
#> 1 1 A e11.9 <NA> TRUE FALSE FALSE
#> 2 2 A J06 F12.1 FALSE TRUE FALSE
#> 3 3 B F20.0 <NA> FALSE FALSE TRUE
#> 4 4 C <NA> NA NA NA
#> 5 5 D J06 <NA> FALSE FALSE FALSEThe result is a separate table: one row per unique ID, with logical condition columns. Join it explicitly if you want the original fields alongside flags.
3. Change the aggregation ID
person_flags <- icd_flag(
records, conditions,
id = "person", cols = c("pdx", "sdx"), match = "prefix"
)
person_flags
#> person diabetes cannabis psychosis
#> 1 A TRUE TRUE FALSE
#> 2 B FALSE FALSE TRUE
#> 3 C NA NA NA
#> 4 D FALSE FALSE FALSEPerson A’s diagnoses are combined across rows. For entry-window
records, id = "entry_id" instead combines diagnoses within
each entry. Define the history/follow-up window before
flagging; the function does not apply dates. Do not aggregate by person
if separate entry-specific histories are needed.
4. Exact versus prefix
exact <- icd_flag(records, list(diabetes = "E11"),
id = "record_id", cols = "pdx", match = "exact")
prefix <- icd_flag(records, list(diabetes = "E11"),
id = "record_id", cols = "pdx", match = "prefix")
data.frame(record_id = exact$record_id, exact = exact$diabetes, prefix = prefix$diabetes)
#> record_id exact prefix
#> 1 1 FALSE TRUE
#> 2 2 FALSE FALSE
#> 3 3 FALSE FALSE
#> 4 4 NA NA
#> 5 5 FALSE FALSEexact is the default. prefix includes
literal subcodes: E11 matches E11.9. Dots, case and surrounding
whitespace are normalized. Prefixes are not regular expressions; a
wildcard or regex is not a substitute for a code list.
Thai ICD-10-TM codes can have a fifth character
Thai records often add a fifth character for clinical detail, for
example F10.30 and F10.31 beneath the
four-character F10.3. Exact matching on F10.3
silently misses those records. A code list built at four-character
resolution, including lists exported from tools that only offer
four-character codes, needs prefix matching to include the more detailed
codes:
detail <- data.frame(id = 1:3, dx = c("F10.3", "F10.31", "F10.30"))
data.frame(
id = detail$id, dx = detail$dx,
exact = icd_flag(detail, list(withdrawal = "F10.3"), "id", "dx", "exact")$withdrawal,
prefix = icd_flag(detail, list(withdrawal = "F10.3"), "id", "dx", "prefix")$withdrawal
)
#> id dx exact prefix
#> 1 1 F10.3 TRUE TRUE
#> 2 2 F10.31 FALSE TRUE
#> 3 3 F10.30 FALSE TRUEBefore freezing a definition, tabulate which recorded codes, at which lengths and diagnosis positions, actually occur in your source.
Range notation such as "Q20-Q26", SQL/regex wildcards
(%, _, ?, ^,
brackets) and comma- or space-separated lists stop with an error instead
of quietly matching nothing. Write the reviewed codes out
explicitly:
congenital_heart <- paste0("Q", 20:26) # Q20-Q26, reviewed
congenital_heart
#> [1] "Q20" "Q21" "Q22" "Q23" "Q24" "Q25" "Q26"*, + and / are kept as literal
characters because some sources record dagger/asterisk or similar
notation.
Missingness and counting
-
TRUE: at least one selected recorded code matches. -
FALSE: there are recorded codes but none match; this is not proof of disease absence. -
NA: all selected codes for the ID are blank or missing.
No input row means no output ID; preserve the cohort roster using a left join. Unknown flags and absent records need an explicit study policy. Flags express presence, not frequency: requiring two admissions needs a reliable admission key and an explicit distinct-admission count, not duplicated diagnosis-row counts.
Filtering versus flagging
thip_filter_icd() is a lazy DB row
filter, with a single code vector and cols.
icd_flag() is a local aggregation for
multiple named conditions. Use standard collect() to make
the transition explicit. No automatic download or backend switch occurs.
Choose primary versus secondary diagnosis columns visibly in the
call.
Next: worked cohort example.