Case Report
A descriptive observational design that documents the detailed clinical course, exposures, interventions, and outcomes of a single patient (or a handful), reported under the CARE checklist to generate hypotheses and surface safety signals that aggregate analyses miss.
In plain language
A case report is a detailed written story of what happened to a single patient: their history, the drug or treatment they got, the problem that showed up afterward, and the doctor's reasoning about whether the treatment might have caused it. Its job is to describe and to raise a question ("has anyone ever seen this before?"), not to measure anything. Because there is only one patient and no comparison group, you cannot turn it into a rate or a risk — there is no group of people to divide by, so "3 out of how many?" has no answer. That is the whole point of the design, not a flaw to fix.
A case report is a structured narrative of one patient's clinical course — relevant history, the index exposure (drug, device, procedure, vaccine), co-interventions, the outcome, and the investigator's reasoning about whether the exposure plausibly caused the outcome. In RWE and pharmacoepidemiology its job is signal generation and detailed description, not measurement: it documents an unexpected adverse event, a novel phenotype, or an exceptional treatment response in enough granularity that others can recognize the same pattern. The contemporary reporting standard is the CARE checklist (title, keywords, abstract, introduction, patient information, clinical findings, timeline, diagnostic assessment, therapeutic intervention, follow-up and outcomes, discussion, patient perspective, informed consent); a report that omits the timeline or the consent statement is not publishable to current standards.
Why there is no estimand here
A case report has no denominator and no comparison group, so it cannot estimate a rate, a risk, or an effect — there is nothing to be unbiased about. This is the defining feature, not a limitation to be patched. It means the familiar machinery of comparative RWE (time zero, washout, propensity scores, competing-risk estimands) does not apply to the inference, even though that machinery reappears below as a case-finding tool. The practical decision rule: if your question is "does A cause/prevent Y?" or "how common is Y?", a case report is the wrong design and you need a comparative cohort, case-control, or self-controlled analysis. If your question is "has anyone ever seen Y after A, and what did it look like?", the case report is the right and often only feasible design — it is how thalidomide embryopathy, clozapine agranulocytosis, and rofecoxib thrombosis first entered the literature.
Pros, cons, and trade-offs
- vs case series: A single report is faster, cheaper, and can stand alone for a truly first-in-kind event, but it supports no pattern recognition and invites over-interpretation of a coincidence. Prefer a single report only for the first description of a novel signal or an N-of-1 response; the moment you have 2-5 similar patients, a case series with a denominator-aware discussion is more persuasive. - vs case-control / cohort: A case report needs no sampling frame, control group, or analysis plan and turns around in weeks, but it cannot quantify association or incidence and its selection ("only the interesting one got written up") and information biases are extreme. Prefer a comparative design the instant the goal shifts from "describe" to "estimate." - vs spontaneous-report systems (FAERS / EudraVigilance): A spontaneous report is a coded line item optimized for disproportionality screening across millions of records; a case report is a rich, adjudicated narrative optimized for causality assessment of one patient. They are complements: a disproportionality signal motivates pulling and publishing illustrative case reports, and a striking case report motivates querying the spontaneous-report database for corroboration. - vs systematic review: Individual case reports are the raw material a later review of rare events synthesizes, but each report carries severe publication bias (positive/dramatic cases are written up, null observations are not), so a review of case reports estimates the literature, not the world.
When to use
First description of a novel or extremely rare adverse event, phenotype, or treatment response; an N-of-1 response worth flagging before any formal study is feasible; a regulatory or pharmacovigilance signal that needs an adjudicated narrative to accompany a coded spontaneous report; teaching and recognition of a clinical pattern. Dechallenge (event resolves when the drug is stopped) and rechallenge (event recurs on re-exposure) are the strongest within-case causality evidence and should be documented whenever they occurred naturally — never engineered.
When NOT to use — and when it is actively misleading
- As evidence of frequency or effect. Reporting "we observed 3 cases" with no denominator and treating it as an incidence, or implying causality from a single temporal association, is the classic abuse. A case report cannot rule out coincidence, confounding by indication, or progression of the underlying disease. - When a comparative design is feasible and the question is comparative. Publishing a case report of an outcome that a powered cohort could actually study substitutes anecdote for evidence and can entrench a false signal that later studies waste resources refuting. - As a denominator-free safety claim in a regulatory submission. Case reports support hypothesis generation and causality narratives; they do not establish risk magnitude and cannot anchor a labeling change on their own. - When consent/de-identification is impossible. A single patient is inherently identifiable from a rich timeline plus rare diagnosis plus dates; HIPAA-compliant de-identification and documented informed consent are mandatory, and IRB or privacy-board review is often required even though the study is "just one patient."
Data-source operational depth
In RWD, claims/EHR/registry play two distinct roles: (a) case-finding — efficiently flagging chart-review candidates from millions of records — and (b) narrative source — supplying the clinical detail the report ultimately needs. They are good at very different things. - Claims (FFS or commercial): Excellent for screening (rare diagnosis code + specific NDC within a defined window after dispensing + continuous enrollment + no prior history of the event), poor for narrative (no labs, notes, imaging, or causality detail). Failure modes: Medicare Advantage and capitated person-time drop fee-for-service claims, so an apparent "no prior event" can be missingness rather than a true negative history — restrict screening to enrollees with observable A/B/D (or commercial medical+pharmacy) across the full lookback. `days_supply` and 90-day mail-order distort the exposure timeline; claim reversals and coordination-of-benefits create phantom or duplicated exposures; place of service matters for infused biologics (outpatient admin vs inpatient). Always pull the chart before believing a claims-only case. - EHR: The primary narrative source — notes, problem lists, labs, pathology, imaging, exact dosing/administration, and patient-reported outcomes — and the right substrate for documenting dechallenge/rechallenge with serial labs. Limit: visit-driven capture means care delivered outside the system is invisible, so a "complete" EHR timeline may have silent gaps; NLP or manual abstraction is needed to assemble the CARE timeline. - Registry (device, pregnancy, rare disease): Often mandates case reporting for enrolled patients and is strong for indication, severity, and adjudicated outcomes; typically weak for full pharmacy exposure. Pregnancy registries with mother-infant linkage enable reports on in-utero exposure outcomes that no single data source captures alone. - Linked claims-EHR-vital-records: The ideal substrate — claims completeness for exposure/utilization, EHR for the adjudicated narrative, and a death index for fatal outcomes — but report the linkage quality and any date discrepancies among order, fill, and service dates.
Worked claims example — suspected drug-induced liver injury (DILI)
Goal: surface chart-review candidates for a case report of acute hepatocellular injury after initiation of a hepatotoxic agent, e.g., a newly marketed kinase inhibitor. (1) Index exposure: first pharmacy fill of any NDC on the drug's list (`fill_date` = candidate index date); the patient must be a new user — no fill of that NDC in the prior 180 days. (2) Continuous, observable enrollment: require 365 days of continuous medical + pharmacy FFS-observable enrollment before the index fill (so absence of prior liver disease is real, not unobserved), and at least 90 observable days after it. (3) Clean lookback: no diagnosis of chronic liver disease, viral hepatitis, alcoholic liver disease, or biliary obstruction in the 365-day baseline, and no dispensing of a known competing hepatotoxin (e.g., high-dose acetaminophen, isoniazid, methotrexate) in the 30 days around index. (4) Outcome window: a new diagnosis of acute hepatocellular/hepatic injury (relevant ICD-10 codes) within 90 days after the index fill — the latency window appropriate to idiosyncratic DILI. (5) Output: a short candidate list (typically a handful of patients), each flagged for chart review of LFT trajectories (ALT/AST/alkaline phosphatase/bilirubin), R-ratio, exclusion of alternative causes, and — critically — any documented dechallenge (LFTs normalizing after stopping the drug) or inadvertent rechallenge. The claims query does not establish causality; its only job is to find the patient whose adjudicated timeline becomes the CARE-structured report and feeds downstream pharmacovigilance and, if a pattern emerges, a formal comparative study.
Worked example
Scenario
One adult patient starts a newly marketed cancer drug on January 1, 2025 — their first-ever fill of it, with no liver problems in the year before. About seven weeks later they develop sudden liver injury. The drug is stopped, and their liver tests return to normal over the next month. We want to write up exactly what happened to this one person. Notice what we are NOT doing: we are not asking how often this happens or whether the drug causes liver injury in general, because we have one patient and no comparison group.
Dataset
The patient's event log — the kind of date-and-event rows an analyst pulls from the chart and claims to build a timeline. One patient, listed in time order.
| person_id | date | event |
|---|---|---|
| 4471 | 2024-01-01 | Continuous insurance coverage begins (clean year, no liver disease on record) |
| 4471 | 2025-01-01 | First fill of suspect cancer drug (day zero for this patient) |
| 4471 | 2025-02-20 | Symptom onset: jaundice and fatigue; liver tests sharply elevated |
| 4471 | 2025-02-22 | Acute liver injury diagnosed |
| 4471 | 2025-03-01 | Suspect drug stopped |
| 4471 | 2025-03-31 | Liver tests back to normal (resolution) |
Steps
Lay every row out in calendar order for this one patient — that ordered list of dates and events IS the case report's timeline.
The clean year before day zero (Jan 2024 to Jan 2025) shows no prior liver disease, so the new liver problem did not exist before the drug.
Liver injury appears on Feb 20, 2025, which is 50 days after the first fill — inside the few-month window in which this kind of reaction typically shows up.
The drug is stopped on Mar 1 and the liver tests return to normal by Mar 31; the problem going away after stopping is the strongest within-one-patient clue that the drug was involved.
Count the patients you could divide by: there is exactly one, and no untreated comparison person, so no rate, risk, or percentage can be calculated — only a vivid description of this single course.
Result
This is a description of ONE patient (person 4471), not a measurement. The timeline shows a clean year, a first drug fill on day zero, liver injury 50 days later, and recovery once the drug stopped. There is no denominator, so no rate, risk, or percentage exists — the report's value is the detailed, credible story and the question it raises, not any number.
Timeline Spec
- Title
Case-report timeline for one patient: suspected drug-induced liver injury
- Window
- Start
2024-01-01
- End
2025-04-01
- Label
One patient only — no comparison group, so no rate can be computed
- Events
- Label
First fill of suspect drug (day zero)
- Start
2025-01-01
- Length Days
1
- Quantity
1 patient, first-ever fill
- Label
Symptom onset (jaundice, fatigue)
- Start
2025-02-20
- Length Days
2
- Quantity
day 50 after first fill
- Label
Acute liver injury diagnosed
- Start
2025-02-22
- Length Days
1
- Quantity
day 52
- Label
Suspect drug stopped
- Start
2025-03-01
- Length Days
1
- Quantity
day 59
- Label
Liver tests back to normal (resolution)
- Start
2025-03-31
- Length Days
1
- Quantity
30 days after stopping
- Spans
- Kind
exposed
- Start
2025-01-01
- End
2025-03-01
- Label
On the suspect drug (59 days)
- Kind
followup
- Start
2025-03-01
- End
2025-04-01
- Label
Drug stopped — dechallenge: liver tests normalize
- Result
- Label
One patient described — no denominator, no rate computable
- Value
1
- Caption
A single patient's clinical course: a clean baseline year, the first drug fill on day zero, liver injury 50 days later, the drug stopped on day 59, and recovery 30 days after stopping (a dechallenge). Because this is one patient with no comparison group, the timeline can describe what happened but cannot produce any rate or risk.
- Alt Text
Horizontal timeline for one patient showing a clean baseline year, a first drug fill marked as day zero on 2025-01-01, an exposed period of 59 days, symptom onset and liver-injury diagnosis at about day 50, the drug stopped at day 59, and liver tests returning to normal 30 days later; labeled as one patient with no denominator and no rate computable.
Runnable example
python implementation
Case-FINDING screen for a drug-induced-liver-injury (DILI) case report. This does NOT estimate anything; it returns a short list of chart-review candidates for adjudication. Required inputs (cleaned, de-duplicated): rx : pharmacy fills -> person_id,...
import pandas as pd
LOOKBACK_DAYS = 365 # clean-baseline + observable-time requirement before index
NEWUSER_DAYS = 180 # no prior target-drug fill within this window => incident exposure
LATENCY_DAYS = 90 # idiosyncratic DILI onset window after first dispense
FOLLOWUP_DAYS = 90 # minimum observable follow-up to see (or rule out) the event
def find_dili_candidates(rx: pd.DataFrame, dx: pd.DataFrame, enroll: pd.DataFrame) -> pd.DataFrame:
rx = rx.sort_values(["person_id", "fill_date"])
# Index = first fill of the suspect (TARGET) drug.
target = rx[rx["drug_group"] == "TARGET"]
idx = (target.groupby("person_id", as_index=False)["fill_date"].min()
.rename(columns={"fill_date": "index_date"}))
# New-user: drop anyone with a TARGET fill in the NEWUSER_DAYS before their index date.
t = target.merge(idx, on="person_id")
prior = t[(t["fill_date"] < t["index_date"]) &
(t["fill_date"] >= t["index_date"] - pd.Timedelta(days=NEWUSER_DAYS))]
idx = idx[~idx["person_id"].isin(prior["person_id"])].copy()
# Continuous, FFS-observable enrollment across full lookback through index + minimum follow-up (no MA-only gaps).
e = enroll.merge(idx, on="person_id")
covers = ((e["enroll_start"] <= e["index_date"] - pd.Timedelta(days=LOOKBACK_DAYS)) &
(e["enroll_end"] >= e["index_date"] + pd.Timedelta(days=FOLLOWUP_DAYS)) &
(~e["ma_only"]))
idx = idx[idx["person_id"].isin(e.loc[covers, "person_id"])].copy()
# Clean baseline: exclude pre-existing chronic liver disease in the 365-day lookback.
cl = dx[dx["dx_group"] == "CHRONIC_LIVER"].merge(idx, on="person_id")
chronic = cl[(cl["dx_date"] < cl["index_date"]) &
(cl["dx_date"] >= cl["index_date"] - pd.Timedelta(days=LOOKBACK_DAYS))]
idx = idx[~idx["person_id"].isin(chronic["person_id"])].copy()
# Clean baseline: exclude a competing hepatotoxin dispensed within +/-30 days of index.
hep = rx[rx["drug_group"] == "HEPATOTOXIN"].merge(idx, on="person_id")
confounded = hep[(hep["fill_date"] >= hep["index_date"] - pd.Timedelta(days=30)) &
(hep["fill_date"] <= hep["index_date"] + pd.Timedelta(days=30))]
idx = idx[~idx["person_id"].isin(confounded["person_id"])].copy()
# Outcome: NEW acute liver injury within the latency window after index.
li = dx[dx["dx_group"] == "LIVER_INJURY"].merge(idx, on="person_id")
events = li[(li["dx_date"] > li["index_date"]) &
(li["dx_date"] <= li["index_date"] + pd.Timedelta(days=LATENCY_DAYS))]
first_event = events.groupby("person_id", as_index=False)["dx_date"].min() \
.rename(columns={"dx_date": "event_date"})
candidates = idx.merge(first_event, on="person_id")
candidates["days_to_event"] = (candidates["event_date"] - candidates["index_date"]).dt.days
# Hand off to chart review (LFT trajectory, R-ratio, alternative causes, dechallenge/rechallenge).
return candidates[["person_id", "index_date", "event_date", "days_to_event"]] \
.sort_values("days_to_event").reset_index(drop=True)r implementation
Case-FINDING screen for a DILI case report, data.table. Inputs mirror the Python version: rx : person_id, fill_date (Date), ndc, drug_group ('TARGET'/'HEPATOTOXIN') dx : person_id, dx_date (Date), icd10, dx_group ('LIVER_INJURY'/'CHRONIC_LIVER') enroll :...
library(data.table)
LOOKBACK <- 365L; NEWUSER <- 180L; LATENCY <- 90L; FOLLOWUP <- 90L
find_dili_candidates <- function(rx, dx, enroll) {
setDT(rx); setDT(dx); setDT(enroll)
# Index = first fill of the suspect (TARGET) drug.
idx <- rx[drug_group == "TARGET", .(index_date = min(fill_date)), by = person_id]
# New-user: drop anyone with a TARGET fill within NEWUSER days before index.
t <- merge(rx[drug_group == "TARGET"], idx, by = "person_id")
prior_ids <- unique(t[fill_date < index_date & fill_date >= index_date - NEWUSER, person_id])
idx <- idx[!person_id %chin% prior_ids]
# Continuous FFS-observable enrollment across lookback through index + minimum follow-up.
e <- merge(enroll, idx, by = "person_id")
ok <- e[enroll_start <= index_date - LOOKBACK &
enroll_end >= index_date + FOLLOWUP & !ma_only, unique(person_id)]
idx <- idx[person_id %chin% ok]
# Clean baseline: no chronic liver disease in lookback.
cl <- merge(dx[dx_group == "CHRONIC_LIVER"], idx, by = "person_id")
chronic_ids <- unique(cl[dx_date < index_date & dx_date >= index_date - LOOKBACK, person_id])
idx <- idx[!person_id %chin% chronic_ids]
# Clean baseline: no competing hepatotoxin within +/-30 days of index.
hep <- merge(rx[drug_group == "HEPATOTOXIN"], idx, by = "person_id")
conf_ids <- unique(hep[fill_date >= index_date - 30L & fill_date <= index_date + 30L, person_id])
idx <- idx[!person_id %chin% conf_ids]
# Outcome: new acute liver injury within the latency window after index.
li <- merge(dx[dx_group == "LIVER_INJURY"], idx, by = "person_id")
ev <- li[dx_date > index_date & dx_date <= index_date + LATENCY,
.(event_date = min(dx_date)), by = .(person_id, index_date)]
ev[, days_to_event := as.integer(event_date - index_date)]
setorder(ev, days_to_event)
ev[, .(person_id, index_date, event_date, days_to_event)] # -> chart review
}