← Methods repository
concept

Descriptive Epidemiology in RWE

The estimation of disease and exposure frequency in real-world data — incidence rates per unit of observed person-time, cumulative incidence (risk), prevalence, and age/sex-standardized rates — built on explicitly constructed denominators rather than on the comparative contrasts of causal analysis.

Descriptive_Epidemiologydescriptive-epidemiologyincidence-ratecumulative-incidenceprevalencestandardizationperson-timedisease-burdenpharmacoepidemiology
Methods reference only. Use primary source citations and local policy before applying this in a study protocol, regulatory submission, payer dossier, or clinical decision.

In plain language

Descriptive epidemiology answers three questions about a disease or condition in a population: who gets it, where it occurs, and when it happens — without asking whether any particular cause is responsible. In real-world data studies, analysts count events (such as hospitalizations or diagnoses) and divide by the total time the population was under observation to get a rate, or snapshot a moment in time to measure how common a condition currently is. A descriptive study never tests a hypothesis; it maps the terrain so future causal research knows where to look.

Descriptive epidemiology in real-world data

answers "how much, in whom, and when" rather than "does exposure cause the outcome." Its deliverables are frequency measures: the incidence rate (new events divided by accrued person-time among those at risk), cumulative incidence / risk (the proportion developing the event over a fixed horizon, properly accounting for competing events and censoring), prevalence (point, period, or annual — the proportion alive and meeting a state definition at or during a window), and standardized rates (age/sex-adjusted via direct standardization to an external standard population, or indirectly via the SMR/SIR when stratum-specific rates are unstable). It is the quantitative backbone of disease burden estimates, drug-utilization studies, natural-history characterization, label expansion support, and the "Table 1 + base rates" that every comparative RWE protocol depends on. This entry is both the working reference for these measures and the hub for the operational child concepts that handle each piece in depth (linked under Relations).

Core conceptual distinction

. Three frequency measures answer three different questions and are not interchangeable. (1) The incidence rate has person-time in the denominator (events per 1,000 person-years); it is the right measure when follow-up is censored, enrollment is dynamic, or the at-risk period varies across people — the dominant situation in claims and EHR. (2) Cumulative incidence (risk) is a probability bounded in [0,1] over a stated horizon; it requires either complete follow-up or a survival estimator (Kaplan–Meier complement, or the Aalen–Johansen / cumulative-incidence function when competing risks such as death are present). Naively dividing events by the closed cohort overstates risk whenever anyone is censored early. (3) Prevalence is a snapshot of existing state, governed by both incidence and duration (prevalence ≈ incidence × average duration in steady state); it must never be reported as a rate or a risk. Standardization is orthogonal to all three: it removes the confounding-by-composition that makes a crude rate uninterpretable when comparing populations with different age/sex structures. Direct standardization applies study stratum-specific rates to a fixed standard population; indirect standardization (SMR/SIR) applies standard rates to the study population's person-time and is preferred when study stratum counts are too sparse for stable direct estimates. The choice of measure, horizon, competing-event handling, and standard population must be pre-specified in the estimand, not chosen after seeing the data.

Pros, cons, and trade-offs

. - Incidence rate vs cumulative incidence: The rate is robust to censoring and dynamic enrollment and is the natural output of Poisson/person-time models, but it assumes a roughly constant hazard over the interval and is not a probability a clinician can interpret directly. Cumulative incidence is directly interpretable and is what payers and patients want, but it demands correct handling of competing risks and censoring — a 1-minus-Kaplan–Meier "risk" overestimates the event when death is common (e.g., elderly cohorts), where the cumulative incidence function is mandatory. Prefer the rate for surveillance, utilization, and when follow-up is heavily censored; prefer the CIF for fixed-horizon clinical risk communication. - Crude vs standardized rates: Crude rates describe the actual burden in the population as it exists (correct for resource planning); standardized rates enable fair across-population or across-time comparison but describe a hypothetical population and can hide important effect modification. Report both, and never compare crude rates across populations with different age/sex mixes. - Direct vs indirect standardization (SMR/SIR): Direct standardization yields rates comparable across any pair of populations sharing the standard, but is unstable when study strata have few events; indirect standardization (SMR/SIR) is stable with sparse data but only validly compares each study population to the standard, not study populations to each other. Prefer indirect when stratum-specific study rates are sparse or unstable. - Descriptive vs causal framing: Descriptive measures require no exchangeability or positivity assumptions and are far less fragile than causal contrasts — but precisely because they make no causal claim, a between-group descriptive difference (e.g., higher event rate in treated patients) must not be read as a treatment effect. Confusing the two is the single most common misuse.

When to use

. Disease burden and natural-history characterization; drug- and procedure-utilization trends; incidence/prevalence for sample-size and feasibility planning; background/expected rates for safety-signal contextualization (observed-vs-expected); HTA burden-of-illness sections; and the descriptive base rates and attrition tables that anchor any comparative RWE study. Use the rate whenever person-time is dynamic; use standardization whenever you compare populations or calendar periods that differ in composition.

When NOT to use — and when it is actively misleading or dangerous

. - Do not present descriptive between-group differences as causal. A crude rate ratio between exposed and unexposed groups carries every confounder; reporting it without the causal caveats invites readers to infer effects the design cannot support. - Do not compute risk by dividing events by the baseline cohort when follow-up is censored. This understates the denominator-at-risk and, in the presence of competing risks, the 1−KM complement overstates the event probability — use person-time rates or the CIF. - Do not compare crude rates across populations with different age/sex structure — composition, not biology, can drive the entire difference. Standardize first. - Do not report prevalence as incidence or risk. A point-prevalence "rate" conflates frequency with duration; chronic, low-mortality conditions look common on prevalence and rare on incidence. - Do not estimate rates on person-time you cannot observe. Counting events while undercounting denominator (or vice versa) is the dominant failure mode in claims (see below) and can move a rate by an order of magnitude.

Data-source operational depth

. - Claims (FFS): Denominator = sum of continuously enrolled, observable person-time with the relevant benefit (Parts A/B for medical events, Part D for drug events); numerator = first qualifying event coded in the claim stream during that observed time. Failure modes: (1) Medicare Advantage gaps — MA-only enrollment does not generate FFS encounter claims, so person-time accrued under MA must be excluded from the denominator or events will be missed while time is counted, deflating the rate; restrict to A/B(/D)-eligible FFS person-time. (2) Differential competing risks by exposure in the elderly — death competes with the event of interest and is captured incompletely in claims (use a death-index linkage); if one group dies faster, naive risk estimates diverge from the CIF. (3) Prevalent vs incident counting — without a washout/clean lookback, a recurrent or chronic condition's first observed code is mistaken for a first ever event, inflating incidence at enrollment ("look-back ramp"). (4) Immortal time in procedure/initiation studies — defining the at-risk start after a procedure that itself requires survival builds guaranteed event-free time into the denominator. Workarounds: require continuous enrollment + washout before time zero, anchor the denominator to observable benefit windows, and link to a mortality source to handle the competing risk. - EHR: Capture is visit-driven and within-system. Denominator-at-risk is ambiguous — a patient who stops visiting is not necessarily event-free, just unobserved — so rates computed on "all patients with a record" are biased by informal disenrollment. Define an explicit observation period (e.g., activity-anchored windows) and treat care delivered outside the network as missing, not absent. Phenotype the numerator with a validated algorithm; raw single-code numerators overcount. - Registry: Strong for adjudicated numerators (validated incident cases, cancer stage) but the denominator (catchment person-time, eligibility) is often the weak link; link to claims or census person-time for valid rates, and verify completeness/ascertainment before reporting incidence. - Linked claims–EHR–vital-records: The ideal substrate — EHR/registry for numerator validity, claims for complete observable person-time, vital records for the competing-risk denominator — but the rate must be computed on the linkable subset, which is a selected population; report the linkage proportion and assess selection before generalizing.

Worked example (claims)

Question: the 2022 annual incidence rate of hospitalized acute myocardial infarction (AMI) among adults age ≥65 in U.S. Medicare fee-for-service. (1) Denominator population: enrollees with continuous Parts A+B FFS enrollment (no MA-only months) at any point in 2022. (2) Person-time: for each person, accrue observed days from the later of 2022-01-01 or FFS-enrollment start to the earliest of first AMI, death, switch to MA, disenrollment, or 2022-12-31; sum observed person-days and divide by 365.25 to get person-years. (3) Numerator (incident, not prevalent): first inpatient claim with ICD-10-CM I21.x in the primary position and a 12-month clean lookback with continuous enrollment and no prior I21.x/I22.x, so a reinfarction or a chronic-history code is not counted as incident. (4) Rate = events ÷ person-years × 1,000, with an exact-Poisson 95% CI. (5) Because crude rates are not comparable across regions or years with different age/sex mixes, also compute the age-/sex-standardized rate by direct standardization to the 2000 U.S. standard million: estimate stratum-specific rates within 5-year age × sex cells, weight by the standard population proportions, and sum — yielding a single adjusted rate per 1,000 person-years whose CI uses a normal-approximation (Wald) interval on the Dobson stratum-variance estimator. Report crude and standardized rates side by side, the person-time accounting, and the attrition funnel (eligible → continuously enrolled → clean-lookback → contributing person-time).

Interpreting the output

A Medicare FFS descriptive study of hospitalized AMI reports age-group-specific incidence rates of 2.00 per 1,000 person-years (age 65–74), 4.50 per 1,000 person-years (age 75–84), and 5.20 per 1,000 person-years (age 85+), alongside a directly age-/sex-standardized rate for cross-year or cross-region comparison.

(1) Formal interpretation. Each rate is the count of incident AMI hospitalizations per 1,000 person- years of FFS-enrolled observation time in that age stratum, with a clean 12-month lookback to exclude prevalent AMI. The denominator accrues person-days from FFS enrollment start (or January 1) to the first of: AMI event, death, MA switch, disenrollment, or December 31, then converts to person-years (days / 365.25). Rates are not risks: they are not bounded at 1, they are expressed per 1,000 person- years of observed time, and they should not be compared to study-period cumulative incidence figures. The standardized rate removes the confounding effect of age composition when comparing populations with different age structures; the crude rates are required alongside it to show the actual distribution of events.

(2) Practical interpretation. The 2.6-fold gradient from the 65–74 stratum (2.00) to the 85+ stratum (5.20) quantifies the age dependency of AMI incidence in this population and provides the baseline rate inputs needed to size a prevention trial, project budget impact, or calculate an externally transported NNT anchored to a local baseline. These are descriptive measures only — the rates do not estimate a causal effect of age on AMI, and no confounding adjustment is implied.

Worked example

Scenario

A health economist wants to describe the burden of acute heart attacks (acute myocardial infarction, AMI) among US Medicare fee-for-service enrollees in 2022, broken down by age group. She is not comparing a drug or intervention — she just wants to know how many heart attacks occur in each age band per year of follow-up. She pulls three summary rows from a claims analysis: the number of first-ever AMI hospitalizations and the total person-years of observation in each group.

Dataset

Summary of incident AMI hospitalizations and observed follow-up by age group, US Medicare fee-for-service 2022.

age_groupincident_ami_eventsobserved_person_yearsrate_per_1000_py
65-74820041000002.0
75-841417531500004.5
85+910017500005.2

Steps

  • For the 65-74 group: divide 8,200 events by 4,100,000 person-years, then multiply by 1,000 to express per 1,000 person-years: 8,200 / 4,100,000 x 1,000 = 2.00.

  • For the 75-84 group: 14,175 / 3,150,000 x 1,000 = 4.50 per 1,000 person-years.

  • For the 85+ group: 9,100 / 1,750,000 x 1,000 = 5.20 per 1,000 person-years.

  • Read the pattern across rows: the rate climbs steadily with age, from 2.00 in the youngest group to 5.20 in the oldest — more than a 2.5-fold difference.

  • Note what this analysis does NOT do: it does not compare a treated group to an untreated group, does not adjust for confounders, and makes no causal claim. It describes who (older adults) bears more burden, and by how much.

Result

Rates per 1,000 person-years: 65-74 = 2.00, 75-84 = 4.50, 85+ = 5.20. AMI incidence more than doubles from the youngest to the oldest age band. This is a descriptive finding — age is associated with higher rates, but this table alone cannot say why.

Runnable example

python implementation

Crude and age/sex-standardized incidence rate from claims-style inputs. Required tables (already cleaned): person_time : person_id, age_band, sex, py (observed person-YEARS at risk after enrollment/washout rules) events : person_id, age_band, sex (one row...

import pandas as pd
import numpy as np
from scipy.stats import chi2

SCALE = 1000.0  # report per 1,000 person-years

def standardized_rate(person_time: pd.DataFrame, events: pd.DataFrame,
                      standard: pd.DataFrame) -> dict:
    keys = ["age_band", "sex"]
    pt = person_time.groupby(keys, as_index=False)["py"].sum()
    ev = events.groupby(keys, as_index=False).size().rename(columns={"size": "events"})
    strata = pt.merge(ev, on=keys, how="left").fillna({"events": 0})
    strata["rate"] = strata["events"] / strata["py"]            # stratum-specific rate

    # Crude rate with exact-Poisson 95% CI on the total event count.
    tot_ev, tot_py = strata["events"].sum(), strata["py"].sum()
    crude = tot_ev / tot_py
    lo = chi2.ppf(0.025, 2 * tot_ev) / 2 / tot_py if tot_ev > 0 else 0.0
    hi = chi2.ppf(0.975, 2 * (tot_ev + 1)) / 2 / tot_py

    # Direct standardization: weight stratum rates by the external standard population.
    s = strata.merge(standard, on=keys, how="inner")
    std_rate = float(np.sum(s["std_weight"] * s["rate"]))
    # Variance of the directly standardized rate (Dobson-style on stratum events).
    var = float(np.sum((s["std_weight"] ** 2) * s["events"] / s["py"] ** 2))
    se = np.sqrt(var)
    return {
        "crude_per_1000": crude * SCALE,
        "crude_ci": (lo * SCALE, hi * SCALE),
        "standardized_per_1000": std_rate * SCALE,
        "standardized_ci": ((std_rate - 1.96 * se) * SCALE,
                            (std_rate + 1.96 * se) * SCALE),
    }
r implementation

Crude and directly-standardized incidence rate with data.table. Inputs mirror the Python version: person_time : person_id, age_band, sex, py (observed person-years after enrollment/washout rules) events : person_id, age_band, sex (one row per incident event...

library(data.table)
SCALE <- 1000

standardized_rate <- function(person_time, events, standard) {
  setDT(person_time); setDT(events); setDT(standard)
  pt <- person_time[, .(py = sum(py)), by = .(age_band, sex)]
  ev <- events[, .(events = .N), by = .(age_band, sex)]
  strata <- merge(pt, ev, by = c("age_band", "sex"), all.x = TRUE)
  strata[is.na(events), events := 0]
  strata[, rate := events / py]                         # stratum-specific rate

  tot_ev <- sum(strata$events); tot_py <- sum(strata$py)
  crude <- tot_ev / tot_py
  # Exact-Poisson 95% CI on the total count.
  lo <- if (tot_ev > 0) qchisq(0.025, 2 * tot_ev) / 2 / tot_py else 0
  hi <- qchisq(0.975, 2 * (tot_ev + 1)) / 2 / tot_py

  s <- merge(strata, standard, by = c("age_band", "sex"))
  std_rate <- sum(s$std_weight * s$rate)                # direct standardization
  v <- sum(s$std_weight^2 * s$events / s$py^2)          # variance (Dobson-style)
  se <- sqrt(v)
  list(crude_per_1000 = crude * SCALE,
       crude_ci = c(lo, hi) * SCALE,
       standardized_per_1000 = std_rate * SCALE,
       standardized_ci = c(std_rate - 1.96 * se, std_rate + 1.96 * se) * SCALE)
}