Comorbidity

Comorbidity as a covariate: individual diagnoses or a combined measure

Published

September 9, 2026

Comorbidity at baseline is a common covariate. How you handle it belongs to your analysis plan and is typically governed by your DAG (see Phase 1 - Choose your covariates using a DAG). There are essentially two approaches:

  1. Adjust for individual comorbidities as separate covariates, e.g. specific diagnoses you identified as confounders in your DAG. You extract these as ordinary diagnosis variables (the same LPR pattern as Outcomes).
  2. Adjust for the overall comorbidity burden with one summary measure - a count or index built from hospital diagnoses and, usually, prescriptions. Note: NMI, despite its name, is a mortality-prediction score, not a comorbidity-burden measure - use it only for mortality risk (see NMI).
Which is best: individual comorbidities or a combined measure?

It depends on your DAG and your outcome:

  • Individual comorbidities give the most control and transparency, but require you to know in advance exactly which diagnoses are confounders. Use this when your DAG points to a few specific comorbidities.
  • A combined measure captures the general comorbidity burden in one variable and is practical when many comorbidities are potential confounders, or when you just want to describe the burden in Table 1. (NMI is not such a measure - it targets mortality; see NMI.)
  • Beware of over-adjustment: your own outcome must not be part of the measure you adjust for. If your outcome, or a strong predictor of it, is one of the conditions it counts, you partly adjust for the outcome itself. NMI handles dementia studies, for example, by dropping the dementia predictor (see NMI).

Diagnoses, prescriptions, or both?

Whether a condition is defined from hospital diagnoses, from dispensed prescriptions, or from both is a design decision you make per condition, and it changes who counts as having it.

Some conditions are captured well by LPR on its own - the ones that reliably bring people to hospital, such as myocardial infarction or hip fracture. Others are mostly treated outside hospital, and hypertension is the clearest case: many people with it are managed entirely in general practice and never appear in LPR with an I10 diagnosis, but they do collect antihypertensives and so appear in LMDB.

Both single-source definitions fail, in opposite directions:

  • Diagnoses only miss everyone treated outside hospital - they look healthy.
  • Prescriptions only catch everyone on the drug, including those taking it for something else. Antihypertensives are also prescribed for heart failure and angina, so an ATC-only definition of hypertension over-counts.

Combining the two is the usual compromise, and if you do, the halves normally need different rules:

Diagnoses (ICD-10, from LPR) Medication (ATC, from LMDB)
Typical window ever, or a lookback before index a recent window, e.g. the last year
How many events count one recorded diagnosis usually more than one dispensing
Onset date first qualifying contact date the dispensing rule is first met

The middle row is the one people miss: a single dispensing is weak evidence of treatment, because people fill a prescription once and stop. The pattern for “at least N dispensings within a window” is on Medication - from dispensings to is this person a user?.

Whichever you choose, write the definition into your methods: “hypertension” is not one thing, and two studies using different definitions are not measuring the same variable.

An extraction package is in development

Noteregmorbidity - not released yet

Jie Zhang and Sara Schwartz are building regmorbidity, an R package for deriving comorbidity from Danish register data with editable code lists. It is not published yet and there is nothing to install - this note is here so you know it exists rather than building the same thing twice. If you would like to use it, or you have code lists to contribute, get in touch.

The design, in case it is useful for your own code:

  • Conditions live in plain CSV code lists, not in R code. One row per code, with columns for the condition, the vocabulary (ICD10 or ATC), the code, the time frame and the minimum number of dispensings. So one condition can mix diagnoses and prescriptions, and the definitions can be edited, extended and version-controlled without touching the code that runs them.
  • Medication conditions take a configurable count and window, with the onset date being the date of the qualifying dispensing.
  • Conditions are extracted one at a time and checkpointed to disk, so a long run over the full register resumes after an interruption instead of starting over.
  • The starting code lists take Prior et al. (2016) (doi:10.1093/aje/kwv324) as their starting point - a starting point only. This is not an implementation of that index, and the lists are meant to be revised for your own study.

Until it is available, build conditions with the ordinary patterns: diagnoses as in Extract from LPR, prescriptions as in Medication, then combine the two per person.

The extraction pattern

Filter LPR to your cohort and to the window before index, then reduce to one row per person per condition. read_register() assumes fastreg is pointed at your registers.

library(fastreg) # read_register()
library(dplyr) # %>%, semi_join(), collect()
library(tibble) # tibble()

cohort_pnrs <- unique(readRDS("path/to/full_cohort.rds")$pnr)

diagnoses_before_index <- read_register("lpr_diag") %>% # without fastreg: open_dataset("path/to/lpr_diag/")
  rename_with(tolower) %>%
  semi_join(tibble(pnr = cohort_pnrs), by = "pnr") %>% # ONLY your cohort
  # ... join to lpr_adm for the contact date, keep diagnoses before index_date ...
  collect()

Turning that event log into one 0/1 flag per condition per person is the any() pattern in Extract from LPR - from event log to one row per person. Save the result as .rds and link it in Phase 12.

See also

Back to top