← Methods repository
concept

Case Series

A descriptive, single-group study design that reports the characteristics, treatment, and outcomes of a set of patients selected because they share an exposure or diagnosis, with no comparator group and no defined denominator, and therefore no basis for rate or effect estimation.

Study_Designdescriptivestudy-designsingle-grouphypothesis-generatingsignal-detectionpharmacovigilanceno-comparatorrare-disease
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

A case series takes a group of patients picked because they share one thing — the same diagnosis, drug, or procedure — and simply describes them: who they are, what was done, and what happened. There is no comparison group, and no count of how many similar people were out there to begin with (no population at risk). That means you can report what you saw inside the group ("5 of 8 patients improved") but you cannot turn it into a rate or claim the treatment caused the result, because you have nothing to compare against and no denominator.

A case series assembles a group of patients who share a defining feature — a treatment, a procedure, a diagnosis, or a rare exposure — and describes them: who they are, what was done, and what happened. It is the simplest analytic unit above the single case report, and the defining feature is what it lacks: there is no comparison group and, in the pure form, no enumerated denominator (population at risk). Because of those two absences, a case series can describe ("12 of 40 patients had remission") but cannot legitimately estimate a rate (it has no population at risk and no person-time) or a causal effect (it has no counterfactual). Its scientific job is description and hypothesis generation, not estimation or confirmation.

Core conceptual distinction — case series vs cohort

This is the single most-confused boundary in observational taxonomy, and getting it wrong inflates the apparent evidence base. The discriminating question (Dekkers et al. 2012; Mathes & Pieper 2017) is: was the group defined by exposure/disease and then followed for outcomes within a population whose denominator and person-time you can count? If yes — sampled from a defined source population, with a denominator and follow-up — it is a cohort study, even if it has only one arm. If the group was defined by already having the outcome or having been treated, assembled retrospectively without a denominator, it is a case series. A "single-arm study" with continuous enrollment, an index date, and computable person-time is a cohort and should be analyzed and reported as one; calling it a case series understates its rigor, while calling an uncontrolled, denominator-free series a cohort overstates its rigor. The related boundaries: a case report is a series of n=1; a case-control study starts from cases and controls (it has a comparator and is analytic, not descriptive); the self-controlled case series (SCCS) uses only cases but is a fully causal within-person method (each case is its own control across exposed and unexposed time) — it merely borrows the word "case series" and is a different design entirely.

Pros, cons, and trade-offs

- vs case report (n=1): A series buys you pattern, range, and a rough sense of frequency within the reported group (e.g., "8 of 30 developed the adverse event"), which a single report cannot. Cost: it is still selected and denominator-free. Prefer a series over scattered reports when several cases of a novel pattern exist. - vs single-arm cohort / single-arm external-control study: A cohort defines a source population, an index date, and person-time, so it yields incidence and supports time-to-event analysis; with an external or historical comparator it can support (cautious) comparative inference. A case series yields none of that. Cost: a cohort requires a denominator and continuous observation you may not have for a rare event. Prefer a cohort whenever a denominator is obtainable — in claims/EHR it almost always is, which makes a true denominator-free "case series" rare and usually a sign of a missing denominator rather than a deliberate design. - vs case-control / case-crossover / SCCS: These are analytic designs with internal comparators (external controls, or the case's own time) and do support effect estimation from cases. A descriptive case series does not. Prefer the self-controlled designs when you have cases and want a causal estimate while controlling time-invariant confounding. - Speed and feasibility (the genuine advantage): A case series is fast, cheap, needs no comparator or sampling frame, and is often the only feasible design for an ultra-rare event or a brand-new exposure where no denominator yet exists. It is the natural first description of an emerging signal.

When to use

Early characterization of a new drug/device/procedure or a newly recognized adverse event; natural history of an ultra-rare disease where no cohort denominator is assemblable; documenting the clinical spectrum, presentation, and management of a condition; hypothesis generation and safety-signal description that a later cohort, case-control, SCCS, or trial will test. In pharmacovigilance, aggregated spontaneous reports and case series are the substrate of signal detection (FDA Sentinel signal identification, EMA/PRAC PSUR review).

When NOT to use — and when a case series is actively misleading or dangerous

- Any comparative or causal claim. "Patients on drug X did better than expected" is uninterpretable: there is no comparator and no adjustment for confounding by indication. Treating an uncontrolled series as evidence of effectiveness is the classic error that controlled designs exist to prevent. - Reporting a "rate," "incidence," or "response rate" computed from the series alone. With no population at risk and no person-time, the denominator is the reported cases themselves — a selected set (only patients who survived to be treated, were referred to a center, and were chosen for write-up). "Response in 24/30 = 80%" silently conditions on selection and survival; it is not the response rate in any real population. If you can compute a defensible denominator, you are no longer doing a case series — report it as a cohort with incidence. - Confusing a denominator-bearing single-arm cohort with a case series (Dekkers/Mathes-Pieper): mislabeling demotes a rigorous cohort and lets a denominator-free series masquerade as one — in either direction the evidence grade is wrong. - Signal confirmation, label changes, or HTA comparative-effectiveness decisions. Signal detection (acceptable) is not signal confirmation (not acceptable from a series). HTA bodies (NICE, ICER) and reimbursement committees reject uncontrolled case series for comparative effectiveness; an unanchored single arm cannot support a relative-effect claim.

Data-source operational depth

The recurring failure mode in real-world data is that an apparent "case series" is really a cohort with an unmeasured or discarded denominator — fix the denominator rather than lowering the design. - Claims (FFS vs MA): You can almost always reconstruct a denominator (all continuously enrolled members with the indication), so a denominator-free series is rarely justified — the honest design is usually a cohort. If you do characterize a case set (e.g., all members with a rare procedure), build it from `person_id` + `service_date` + dx/proc codes with a continuous-enrollment requirement, and state explicitly that no rate is computed. Failure modes: Medicare Advantage person-time lacks fee-for-service claims, so MA-only members appear as "no events" purely from non-capture — never mix MA-only and FFS person-time when any frequency is reported. Differential competing risks by exposure in the elderly (death removing patients before the outcome is coded) distort any naive "proportion with outcome" — a case series cannot handle competing risks at all, which is itself a reason to escalate to a cohort with Fine-Gray/cause-specific analysis. Immortal time in procedure series (counting survival from a landmark the patient had to survive to reach) inflates apparent outcomes; a series has no machinery to correct it. - EHR: Rich for clinical detail (labs, notes, severity, imaging) — the natural strength of a descriptive series — but capture is visit-driven, so patients who leave the system vanish (informative loss), and "the cases we have notes on" is a referral-selected set. Good for characterizing presentation and management; poor for any frequency claim. - Registry: A disease/product registry with defined enrollment criteria and a denominator is a cohort; a registry used only to pull "all the cases of X" for description is functioning as a case-series sampling frame. Registries are the strongest substrate for rare-disease natural-history description (adjudicated phenotypes, longitudinal detail). - Linked claims–EHR–registry: Linkage adds clinical depth and mortality, but the linkable subset is selected; if any frequency is reported, the denominator must be the linkable population, not the whole source.

Worked claims example

Question: characterize patients who received a newly approved, rarely used gene therapy in a national claims database (no comparator; description only). (1) Case identification: in `medical_claims`, find each `person_id` with the procedure/HCPCS code for the therapy; set `index_date` = first such `service_date`. (2) Observability: require 365 days of continuous medical + pharmacy enrollment before `index_date` and exclude MA-only person-time so baseline characteristics are actually observable in FFS claims. (3) Baseline characterization (the deliverable): in the 365-day lookback summarize demographics, comorbidities (Elixhauser/Charlson from dx codes), prior therapies (`drug_class`, `days_supply`), and healthcare utilization. (4) Post-index description: among the identified cases, tabulate documented outcomes/AEs and time from `index_date` to first occurrence — reported as counts and within-series proportions only. (5) Explicit non-claims: state that no incidence rate, no comparative effect, and no causal claim is made because there is no denominator and no comparator. (6) Escalation note: if a credible denominator exists (all members with the indication), re-cast as a retrospective cohort with incidence and time-at-risk — at which point competing risks (death) and immortal time must be handled, which the case series intentionally does not.

Worked example

Scenario

A clinician notices that eight patients in her practice all received the same newly approved gene therapy and wonders how they fared. She pulls their records into a small table to describe the group. She wants to know: did the therapy work? The catch is that these eight are simply the patients who happened to get the drug and get written up — there is no list of everyone who could have received it, and no untreated group to hold them against.

Dataset

The eight collected patients — exactly the rows the clinician has, and nothing about anyone outside this group.

patient_idagetreatmentoutcome
157gene_therapy_Ximproved
263gene_therapy_Ximproved
349gene_therapy_Xno_change
471gene_therapy_Ximproved
555gene_therapy_Xno_change
668gene_therapy_Ximproved
760gene_therapy_Xworsened
852gene_therapy_Ximproved

Steps

  • Notice that every patient shares the same exposure (gene_therapy_X) and there is no second column of untreated patients — the whole table is one group, so there is no comparator.

  • Count the outcomes you can actually see inside the group: 5 improved (patients 1, 2, 4, 6, 8), 2 had no change (patients 3, 5), and 1 worsened (patient 7). That is a fair description of these eight people.

  • Try to turn '5 improved' into a rate and you hit a wall: a rate needs a denominator — everyone who could have improved — but the only people in the data are the 8 you already chose, so the 'denominator' would just be the cases themselves.

  • Try to claim the therapy worked and you hit a second wall: with no untreated comparator group, you cannot know whether these 5 would have improved anyway, so no treatment effect can be computed.

  • Contrast this with a cohort: if instead you had started from all patients with the indication in a database, given each an index date, and counted person-time, you could compute incidence and compare arms. The moment you have a countable denominator and follow-up, it is a cohort — not a case series.

Result

Descriptive summary: among the 8 collected patients, 5 improved, 2 had no change, and 1 worsened (a within-series proportion of 5/8 = 62.5% improved). No rate, incidence, or response rate can be computed because there is no population at risk (denominator) and no person-time, and no treatment effect can be claimed because there is no comparator group. The series can only describe these patients and generate a hypothesis for a later cohort or controlled study to test.

Runnable example

python implementation

Case identification and descriptive characterization from claims-style inputs (NOT estimation). Required inputs (cleaned, de-duplicated): med : medical claims -> person_id, service_date (datetime), code (dx/proc), code_type rx : pharmacy fills -> person_id,...

import pandas as pd

LOOKBACK_DAYS = 365          # baseline-characterization window before index
THERAPY_CODES = {"J9999"}    # HCPCS/procedure code(s) defining the case-series exposure

def build_case_series(med: pd.DataFrame, rx: pd.DataFrame, enroll: pd.DataFrame) -> dict:
    med = med.sort_values(["person_id", "service_date"])

    # 1. Case identification: first occurrence of the defining exposure = index_date.
    hits = med[med["code"].isin(THERAPY_CODES)]
    cases = (hits.groupby("person_id")["service_date"].min()
                 .reset_index(name="index_date"))

    # 2. Observability: continuous, FFS-observable enrollment across the full lookback (no MA-only gaps),
    #    so baseline characteristics are actually captured rather than missing.
    e = enroll.merge(cases, on="person_id")
    e["covers"] = ((e["enroll_start"] <= e["index_date"] - pd.Timedelta(days=LOOKBACK_DAYS)) &
                   (e["enroll_end"]   >= e["index_date"]) &
                   (~e["ma_only"]))
    eligible = e.loc[e["covers"], "person_id"].unique()
    cases = cases[cases["person_id"].isin(eligible)].copy()
    cases["baseline_start"] = cases["index_date"] - pd.Timedelta(days=LOOKBACK_DAYS)

    # 3. Baseline characterization (the deliverable) — prior therapy use in the lookback window.
    rxm = rx.merge(cases[["person_id", "index_date", "baseline_start"]], on="person_id")
    prior = rxm[(rxm["fill_date"] >= rxm["baseline_start"]) & (rxm["fill_date"] < rxm["index_date"])]
    prior_tx = (prior.groupby("person_id")["drug_class"].nunique()
                     .reindex(cases["person_id"], fill_value=0))

    n = len(cases)
    summary = {
        "n_cases": n,
        "n_with_any_prior_therapy": int((prior_tx > 0).sum()),
        # within-series PROPORTION (descriptive) — NOT a population rate (no denominator/comparator)
        "pct_with_prior_therapy": round(100 * (prior_tx > 0).sum() / n, 1) if n else None,
    }
    return {"cases": cases, "summary": summary}
r implementation

Case identification and descriptive characterization with data.table. Inputs mirror the Python version: med : person_id, service_date (Date), code, code_type rx : person_id, fill_date (Date), drug_class, days_supply enroll : person_id, enroll_start,...

library(data.table)

LOOKBACK_DAYS <- 365L
THERAPY_CODES <- c("J9999")   # defining exposure code(s)

build_case_series <- function(med, rx, enroll) {
  setDT(med); setDT(rx); setDT(enroll)
  setorder(med, person_id, service_date)

  # 1. Case identification: first occurrence of the defining exposure.
  cases <- med[code %chin% THERAPY_CODES,
               .(index_date = min(service_date)), by = person_id]

  # 2. Observability: continuous FFS-observable enrollment across the lookback (drop MA-only).
  e <- merge(enroll, cases, by = "person_id")
  ok <- e[enroll_start <= index_date - LOOKBACK_DAYS &
          enroll_end   >= index_date & !ma_only, unique(person_id)]
  cases <- cases[person_id %chin% ok]
  cases[, baseline_start := index_date - LOOKBACK_DAYS]

  # 3. Baseline characterization: distinct prior therapy classes in the lookback window.
  rxm <- merge(rx, cases[, .(person_id, index_date, baseline_start)], by = "person_id")
  prior <- rxm[fill_date >= baseline_start & fill_date < index_date,
               .(n_classes = uniqueN(drug_class)), by = person_id]
  cases <- merge(cases, prior, by = "person_id", all.x = TRUE)
  cases[is.na(n_classes), n_classes := 0L]

  n <- nrow(cases)
  summary <- list(
    n_cases = n,
    n_with_any_prior_therapy = sum(cases$n_classes > 0L),
    # within-series proportion only — NOT a population rate
    pct_with_prior_therapy = if (n) round(100 * mean(cases$n_classes > 0L), 1) else NA_real_
  )
  list(cases = cases[], summary = summary)
}