Case-Cohort Design
A sampling-efficient design nested inside a fully assembled cohort that draws a single random subcohort at baseline and ascertains expensive covariates (biomarker assays, chart abstraction, genotyping) only on subcohort members plus any cases emerging from the full cohort; one subcohort serves multiple outcomes and supports absolute-risk estimation, but analysis requires weighted Cox regression with Prentice or Barlow weights and robust variance — unweighted Cox is a well-known error.
On this page
A case-cohort study starts with a large group of patients all followed over time, then picks a smaller random sample — the subcohort — at the very beginning, before anyone develops the outcome of interest. Researchers only run expensive tests (like biomarker blood assays or detailed chart reviews) on subcohort members plus any patients who later develop the outcome, instead of testing everyone. The big advantage over a similar approach called nested case-control is that the same subcohort can be reused for several different outcomes, and you can also calculate how common the outcome was in the whole group — not just compare those who got it to those who did not. However, the statistical analysis requires a special weighted version of the Cox survival model; using the standard unweighted version on this kind of data is a known mistake that produces incorrect results.
Design mechanics
The case-cohort design begins, like all nested sampling designs, with a fully assembled and enrolled cohort — every member has a time-zero, an eligibility record, and an observable follow-up period. At baseline (time-zero), before any outcomes are observed, the analyst draws a random subcohort of size m from the full cohort of size N, yielding a sampling fraction π = m / N. During follow-up, the study team ascertains expensive covariates — stored biospecimen assays, manual chart abstraction, genotyping, imaging reads — for every subcohort member, regardless of whether they later develop the outcome.
As cases accumulate anywhere in the full cohort, any case not already in the subcohort is added to the measurement queue; the covariate is ascertained for them as well. The analytic dataset therefore contains: (1) all subcohort members (cases and non-cases alike), and (2) cases from outside the subcohort. The key quantity is the overlap: cases who happen to fall inside the subcohort by chance are counted once, not twice. Total assays = m + (total cases) − (cases inside subcohort).
The killer advantage over nested case-control: one subcohort, many outcomes
In a nested case-control (NCC) design, controls are sampled fresh at each case's event time and are therefore outcome-specific — a new control sample is needed for every endpoint.
The case-cohort subcohort is drawn once at baseline and reused for every outcome the study examines: fatal myocardial infarction, incident diabetes, all-cause mortality, and any post-hoc endpoint can each use the same m subcohort members, with only the new-case set varying. This makes case-cohort the preferred design for multi-endpoint biobank and registry substudies.
Absolute risk is estimable
Because the subcohort is a probability sample of the full cohort, its person-time is a known fraction of the total cohort person-time, and event rates (incidence densities) and cumulative incidence can be estimated with appropriate weighting. NCC, by contrast, cannot recover absolute risks without additional data because the risk-set sampling probabilities depend on cohort size in a time-varying way that is not always recorded.
Pros, cons, and trade-offs
- vs full-cohort Cox: The case-cohort design's sole advantage is measurement cost when an exposure or covariate is expensive for every cohort member. When the exposure and confounders are already universally available — as in ordinary claims or EHR data where the drug, diagnosis, and covariates are coded for everyone — the case-cohort design discards information and the full-cohort Cox model dominates. Choosing case-cohort for cheap-exposure claims data is indefensible.
- vs nested case-control: NCC re-samples controls at each event time, providing tight time-matching that can be more efficient than case-cohort for a single time-to-event outcome with strong time-related confounding (age drift, calendar-period effects). The case-cohort design sacrifices some per-outcome efficiency to gain the multi-outcome reusability and absolute-risk advantages described above. Prefer NCC for a single outcome with strong time confounding or highly time-varying expensive exposures; prefer case-cohort for multi-outcome biobank substudies, registry biomarker layers, and when absolute risk is needed.
- vs self-controlled designs (SCCS, case-crossover): Self-controlled designs eliminate all time-fixed confounding by within-person comparison but require transient reversible exposures and acute outcomes. Case-cohort accommodates chronic exposures and stable biomarkers and handles between-person confounders via covariate adjustment, at the cost of residual unmeasured between-person confounding that self-controlled designs remove by design.
The classic analytic mistake: unweighted Cox
Because the subcohort is a biased sample of the risk sets at later event times (subcohort members who died or were censored early are underrepresented in later risk sets), naive Cox regression on the case-cohort dataset without weights produces a biased hazard-ratio estimate. The correct analysis uses a weighted pseudo-partial likelihood in which the contribution of each subcohort non-case at each event time is up-weighted by 1/π to represent the full cohort's at-risk pool.
Two main weighting schemes exist: Prentice (1986) weights, which use 1/π for subcohort members who have not yet failed and 1 for cases at their event time; and Barlow et al. (1999) weights (also called self-weighted or "Barlow"), which assign a constant weight of 1/π to subcohort members throughout follow-up and 1 to all cases at their event time, yielding slightly simpler implementation and the same asymptotic estimator.
Both require a robust (sandwich) variance estimator because the same subcohort members appear in multiple pseudo-risk sets and the standard Cox variance ignores this correlation. The `survival::cch` function in R implements both methods and the robust variance directly.
When NOT to use — and when it is actively misleading or dangerous
- Cheap, complete exposure in claims/EHR. If the covariate of interest is already coded in the database for every cohort member, the case-cohort design wastes information and inflates variance; full-cohort Cox is strictly preferred.
- Unweighted Cox on the case-cohort dataset. Fitting a standard (unweighted) Cox model on the raw case-cohort data produces a biased hazard-ratio estimate. This is the most common analysis error in the literature. The estimator must use Prentice or Barlow weights plus robust variance; failing to do so is quantitatively incorrect, not conservative.
- Highly time-varying expensive exposure. If the expensive item is a time-varying biomarker that must be re-ascertained at multiple person-specific time points, the case-cohort's single baseline-sampling advantage erodes; NCC with time-matched controls may be more efficient in this scenario.
- Very small cohorts. With N < several hundred, the subcohort may be nearly the full cohort, eliminating the cost advantage. Check whether the expected number of cases in the subcohort and outside it provides enough statistical power before committing to the design.
- Ignoring overlap between cases and subcohort. Double-counting cases who are also subcohort members (by including them in both the subcohort non-case row and a separate case row) inflates the apparent efficiency and biases the estimate. Analytic code must de-duplicate so that each subject appears with the correct weight-indexed contribution.
Interpreting the output
The weighted Cox estimator from a correctly analyzed case-cohort produces a hazard-ratio coefficient for each covariate. Using the worked example: cohort N = 50,000, subcohort m = 1,000 (π = 0.02), 400 total cases, 8 inside the subcohort; suppose the Barlow-weighted analysis yields HR = 1.73 (robust 95% CI 1.31–2.28) for the exposure of interest.
Formal interpretation: The Barlow-weighted Cox partial-likelihood estimator, with robust sandwich variance to account for the repeated appearance of subcohort members across pseudo-risk sets, estimates an instantaneous rate ratio of 1.73 comparing exposed to unexposed subjects among those still at risk at each event time.
The confidence interval has the repeated-sampling interpretation: if this analysis were repeated many times under the same sampling design, approximately 95% of such intervals would contain the true hazard ratio in the source cohort. The estimate is conditional on measured covariates and requires the untestable assumption that unmeasured confounders are not materially associated with both exposure and outcome.
Practical interpretation: At any moment during follow-up, exposed patients had approximately 73% higher instantaneous risk of the outcome than unexposed patients of the same measured characteristics. The confidence interval (1.31 to 2.28) excludes 1.0, indicating this association is unlikely to be due to chance, though residual unmeasured confounding cannot be ruled out in an observational study.
Data-source operational depth
- Claims with chart-validated outcomes: The classic RWE application is a large claims cohort (potentially hundreds of thousands of patients) where the primary outcome requires manual chart review for validation. Pull the full cohort from claims for exposure and covariate ascertainment; draw the subcohort for chart validation plus all incident cases flagged by the claims algorithm. This yields validated outcomes without reviewing every chart.
- Registry biomarker substudies: A disease registry fixes the case set (all diagnoses are adjudicated); the subcohort is drawn from registry enrollees without the event during the baseline period; stored specimens or additional labs are run only on the subcohort plus cases. Multiple biomarkers can be assayed from the same specimen bank across several hypotheses.
- EHR cohorts: Define the cohort from encounter-based enrollment windows; the subcohort is a random draw at index; expensive items (NLP-derived phenotypes, expert severity scores) are applied to subcohort + cases. Observation windows and loss-to-follow-up rules apply as for any EHR cohort.
- Competing risks: In elderly or seriously ill cohorts, death may compete with the primary outcome. Because the subcohort includes decedents who contributed person-time, the case-cohort estimand can be extended to cause-specific hazards (exclude competing-event person-time in the denominator) or to subdistribution hazards with additional weighting. Specify the estimand before analysis; the choice affects both the weights and the interpretation.
Decision diagram
flowchart TD Cohort["Full assembled cohort<br/>N = 50,000 (time-zero, eligibility, follow-up defined)"] Cohort --> Draw["Draw random subcohort at baseline<br/>m = 1,000 (pi = 0.02)<br/>BEFORE any outcomes observed"] Draw --> AssaySC["Ascertain expensive covariate<br/>for ALL m subcohort members"] Cohort --> Cases["Identify all cases during follow-up<br/>400 total; 8 inside subcohort"] Cases --> NewCases["392 cases OUTSIDE subcohort<br/>(400 - 8 = 392 new assay subjects)"] NewCases --> AssayNew["Ascertain expensive covariate<br/>for 392 additional cases"] AssaySC --> Analytic["Analytic dataset<br/>1000 subcohort + 392 extra cases = 1392 subjects"] AssayNew --> Analytic Analytic --> Weighted["Weighted Cox (Barlow / Prentice)<br/>+ robust sandwich variance"] Weighted --> HR["Hazard ratio + robust 95% CI<br/>for each outcome"] HR --> MultiOutcome["Reuse same subcohort for Outcome 2, 3 ...<br/>No new subcohort draw needed"]
flowchart LR
subgraph NCC["Nested Case-Control"]
direction TB
E1["Case A at t=120 → sample 4 controls<br/>from risk set at t=120"]
E2["Case B at t=240 → sample 4 controls<br/>from risk set at t=240 (NEW sample)"]
E3["Case C at t=400 → sample 4 controls<br/>from risk set at t=400 (NEW sample)"]
E1 -.- E2 -.- E3
end
subgraph CC["Case-Cohort"]
direction TB
Sub["Subcohort m = 1000<br/>drawn ONCE at t = 0"]
Sub --> CA["Case A at t=120: adds if not in subcohort"]
Sub --> CB["Case B at t=240: adds if not in subcohort"]
Sub --> CC2["Case C at t=400: adds if not in subcohort"]
Sub --> Out2["Second outcome: SAME subcohort"]
endWorked example
Scenario
A research team assembles a cohort of 50,000 adults from a linked claims-registry database to study whether a costly biomarker measured from stored serum predicts incident cardiovascular events. Running the assay on all 50,000 patients would cost roughly $500 per assay. Instead, they draw a subcohort of 1,000 patients at random at baseline (day zero), before any events occur. Over three years of follow-up, 400 patients across the full cohort develop the outcome; of those 400 cases, 8 were already in the subcohort. The team needs to calculate exactly how many assays are required and compare that to the full-cohort alternative.
Dataset
Summary counts for the case-cohort calculation — not one row per patient but the key group totals an analyst would record before deciding on the design.
| group | count | assay_needed |
|---|---|---|
| Full cohort (N) | 50000 | would require 50000 assays |
| Subcohort drawn at baseline (m) | 1000 | assayed regardless of outcome |
| Total cases in full cohort | 400 | assayed because they are cases |
| Cases already inside subcohort | 8 | already counted in subcohort — not duplicated |
| Cases outside subcohort (new additions) | 392 | assayed as additional cases |
Steps
Result
Total assays = 1000 + 400 - 8 = 1392 versus 50000 for full-cohort measurement. Cost ratio 1392 / 50000 = 0.02784 (approximately 2.8% of the full-cohort burden). The subcohort is then reused for every additional outcome at no extra baseline cost.
Trade-offs
Runnable example
Case-cohort dataset construction and Barlow-weighted Cox analysis in Python. Because lifelines does not expose a dedicated case-cohort (cch) interface, this implementation builds the Barlow weight variable explicitly and uses lifelines CoxPHFitter with the weights_col argument.
import pandas as pd
from lifelines import CoxPHFitter
def build_case_cohort_weights(cohort: pd.DataFrame, pi: float) -> pd.DataFrame:
"""
Assign Barlow weights to the case-cohort analytic dataset.
Barlow rule:
- All subcohort members (cases + non-cases): weight = 1 / pi during
their non-event follow-up.
- All cases (in subcohort or not): weight = 1 at their event time.
For a counting-process approximation in lifelines, we use a single-row
per subject and set weight = 1 for cases, 1/pi for subcohort non-cases.
Subcohort members who are also cases receive weight = 1 (case dominates).
This is a Barlow approximation; use survival::cch in R for the exact estimator.
"""
df = cohort.copy()
# Only include subjects in the analytic dataset:
# - all subcohort members
# - all cases (whether in subcohort or not)
analytic = df[df["in_subcohort"] | (df["event"] == 1)].copy()
# Barlow weight: 1 for cases, 1/pi for subcohort non-cases.
analytic["weight"] = analytic.apply(
lambda r: 1.0 if r["event"] == 1 else 1.0 / pi, axis=1
)
return analytic
# --- example usage ---
# cohort = pd.DataFrame(...) # one row per subject, biomarker ascertained for subcohort + cases
# N = len(cohort) # full cohort size
# m = cohort["in_subcohort"].sum()
# pi = m / N # sampling fraction
pi = 0.02 # 1000 / 50000 in the worked example
# Build the analytic dataset.
analytic = build_case_cohort_weights(cohort, pi)
# Compute follow-up duration in days.
analytic["duration"] = (
pd.to_datetime(analytic["exit_date"]) - pd.to_datetime(analytic["entry_date"])
).dt.days
# Fit Barlow-weighted Cox with robust variance.
cph = CoxPHFitter()
cph.fit(
analytic[["duration", "event", "biomarker", "weight"]],
duration_col="duration",
event_col="event",
weights_col="weight",
robust=True # sandwich variance — required for case-cohort; corrects for
# repeated subcohort membership across pseudo-risk sets
)
cph.print_summary()
# exp(coef) for biomarker is the Barlow-weighted hazard ratio.
# For publishable analyses use R survival::cch or SAS PROC PHREG with COVS(AGGREGATE).Canonical case-cohort analysis using survival::cch, which implements both the Prentice (1986) and Barlow et al. (1999) estimators with the correct robust variance. This is the authoritative R implementation; exp(coef) from the fitted object is the case-cohort hazard ratio.
library(survival)
# --- worked example parameters ---
N <- 50000L # full cohort size
m <- 1000L # subcohort size (drawn at baseline)
pi <- m / N # sampling fraction: 1000 / 50000 = 0.02
# dat: analytic dataset — subcohort members + all cases.
# Build in advance; biomarker is NA for non-subcohort non-cases (they are excluded).
# One row per subject; in_subcohort = TRUE/FALSE; event = 0/1.
# Barlow method (recommended for most applied settings):
fit_barlow <- cch(
Surv(entry, exit, event) ~ biomarker + exposure,
data = dat,
subcoh = ~in_subcohort, # logical column marking subcohort membership
id = ~person_id,
cohort.size = N, # full cohort N — required for weight computation
method = "Barlow" # alternatives: "Prentice", "II.Borgan"
)
summary(fit_barlow)
# exp(coef) is the Barlow-weighted hazard ratio with robust 95% CI.
# The robust variance (sandwich) is applied automatically by cch().
# Prentice method for comparison:
fit_prentice <- cch(
Surv(entry, exit, event) ~ biomarker + exposure,
data = dat,
subcoh = ~in_subcohort,
id = ~person_id,
cohort.size = N,
method = "Prentice"
)
summary(fit_prentice)
# Both should produce similar HR estimates; CIs will differ slightly.
# In the worked example (pi = 0.02, 400 cases, 8 in subcohort):
# assays required = 1000 + 400 - 8 = 1392 vs 50000 full-cohort.Barlow-weighted case-cohort analysis in SAS using PROC PHREG with a WEIGHT statement and COVS(AGGREGATE) for the robust sandwich variance. This follows the approach described in Barlow et al. (1999).
%let pi = 0.02; /* sampling fraction: m / N = 1000 / 50000 */
%let inv_pi = 50; /* 1 / pi = 1 / 0.02 = 50 */
/* Assign Barlow weights:
- cases (event = 1): weight = 1 (whether in subcohort or not)
- subcohort non-cases (in_subcohort = 1, event = 0): weight = 1/pi
Cases outside the subcohort (in_subcohort = 0, event = 1) receive weight = 1.
*/
data work.cc_weighted;
set work.analytic;
if event = 1 then weight = 1;
else weight = &inv_pi; /* subcohort non-cases: up-weighted to represent full cohort */
run;
/* Weighted Cox with robust sandwich variance (COVS(AGGREGATE) clusters on person_id). */
proc phreg data = work.cc_weighted
covs(aggregate); /* AGGREGATE = sandwich variance; required for case-cohort */
model exit * event(0) = biomarker exposure / ties = efron;
weight weight; /* Barlow weight variable */
id person_id; /* clustering unit for COVS(AGGREGATE) */
hazardratio biomarker / diff = ref;
hazardratio exposure / diff = ref;
run;
/*
exp(Parameter Estimate) = Barlow-weighted hazard ratio.
The "Robust" column in the PROC PHREG output gives sandwich-based SEs and 95% CIs.
Do NOT use the model-based (non-robust) standard errors for case-cohort data.
Worked-example audit:
Subcohort m = 1000, N = 50000, pi = 1000 / 50000 = 0.02, inv_pi = 50.
Total cases = 400; cases in subcohort = 8; cases outside = 400 - 8 = 392.
Analytic N = 1000 + 392 = 1392 subjects (work.analytic rows).
Assay cost ratio vs full cohort: 1392 / 50000 = 0.02784.
*/Citations
- [1]Prentice RL. A case-cohort design for epidemiologic cohort studies and disease prevention trials. Biometrika. 1986;73(1):1-11.
- [2]Barlow WE, Ichikawa L, Rosner D, Mangat S. Analysis of case-cohort designs. Journal of Clinical Epidemiology. 1999;52(12):1165-1172.