← Methods repository
concept

Confounding by Indication and Channeling Bias

The dominant structural confounding in pharmacoepidemiology: the medical indication for a drug — and especially its severity — simultaneously determines who receives treatment and independently elevates (or lowers) the outcome risk, so that crude observational associations between drug use and outcomes reflect the underlying disease rather than any true causal drug effect; channeling is the specific variant in which newer drugs are preferentially prescribed to patients who are sicker, have failed prior therapy, or carry more comorbidity.

Bias_Controlconfounding-by-indicationchannelingchanneling-biaspharmacoepidemiologybiasseverity-confoundingcox-2active-comparator
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

Confounding by indication happens when the very reason a doctor prescribes a drug — for example, treating severe diabetes — also makes the patient more likely to have bad outcomes, so a naive comparison makes the drug look harmful even if it has no true effect. Channeling is a related pattern where newer drugs get prescribed mainly to the sickest patients or those who failed older drugs, so studies comparing the new drug to the old one find worse outcomes in the new-drug group — not because the drug is worse, but because the patients were already sicker before they started it. In insurance claims data this bias is especially hard to fix because severity is only partly coded: the database knows a patient has diabetes, but not how severe or well-controlled it is. The most reliable design fix is to compare two drugs prescribed for the same condition, so that the indication and its severity are shared across both groups and cancel out.

Core definition and the backdoor path

Confounding by indication (CBI) is the systematic error that arises when the clinical reason a drug is prescribed — the indication — is itself a determinant of the outcome under study. In a directed acyclic graph, the indication and its severity open a backdoor path: Indication → Treatment and Indication → Outcome. Because healthcare database studies cannot randomize patients to treatment, patients who receive a given drug will always differ systematically from those who do not, in exactly the ways prescribers consider clinically relevant. This is not a data-quality flaw that more careful collection can eliminate — it is a structural consequence of how medical prescribing works.

The aggravating feature in claims and EHR data is that severity within a diagnosis is substantially unmeasured. An ICD code for "type 2 diabetes" is recorded for a patient whose HbA1c is 6.5% on diet alone and for a patient with HbA1c of 11%, recurrent hospitalizations, and emerging neuropathy. The prescriber knows the difference; the claims database records neither. The same binary code masks a severity gradient that drives both treatment escalation and outcome risk. CBI is therefore the strongest and most difficult-to-fix confounding source in pharmacoepidemiology, because the most important part of the confounder — the part that drives the prescribing decision — is the part the database cannot see.

Channeling: the original Petri and Urquhart framing

Channeling is a specific, common manifestation of CBI in which a new drug is systematically prescribed to a different — and typically sicker — patient population than an established comparator. Petri and Urquhart (1991) coined the term to describe how initial prescribers of a new agent tend to be specialists who see the most severely ill, treatment-refractory patients: exactly the patients who would be expected to have worse outcomes regardless of the new drug's true efficacy or safety.

The COX-2 selective inhibitor era (celecoxib, rofecoxib, 1999–2004) is the canonical case study. COX-2 inhibitors were approved and marketed as safer alternatives to traditional NSAIDs for patients at elevated gastrointestinal risk — patients with prior GI bleeding, peptic ulcer history, or concurrent anticoagulant use. Observational studies in the early post-market period found higher cardiovascular event rates in COX-2 users than in NSAID users, partly because the channeled patient population — older, more comorbid, more cardiovascularly vulnerable — was already at higher baseline cardiovascular risk. Disentangling the true pharmacological cardiovascular signal from the channeling signal required careful active-comparator designs and restriction analyses; both effects were real, but their relative magnitudes could not be estimated reliably without controlling the channeling.

Confounding by contraindication and the reverse direction

CBI operates in both directions. When an indication increases both treatment probability and outcome risk, treated patients appear sicker in the crude analysis (the drug appears harmful or less effective than it truly is). When a contraindication — a condition that makes the drug dangerous — drives withholding of treatment, the untreated patients carry the higher risk: anticoagulants are withheld from patients with high bleeding risk; ACE inhibitors are avoided in bilateral renal artery stenosis; beta-blockers were historically avoided in asthma. In these settings the treated patients are the healthier ones, and the crude association flatters the drug — the mirror image of the channeling pattern. Both directions of CBI are operationally invisible in claims unless the contraindication is fully and consistently coded.

Direction-of-bias reasoning

The direction of crude confounding depends on whether the indication/contraindication is a risk factor or protective factor for the outcome. If the indication increases outcome risk (severe heart failure → digoxin → higher baseline mortality), treated patients have higher baseline risk and the crude drug-outcome association is confounded upward — the drug appears more harmful or less beneficial than it truly is. If the contraindication increases outcome risk and those patients are left untreated, the crude association is confounded downward — the drug appears more protective than it truly is. Conducting this direction-of-bias reasoning before analysis is essential for interpreting whether residual confounding would inflate or deflate the effect estimate, and whether it favors or disfavors the study drug — a critical input to sensitivity analysis and regulatory communication.

Why standard covariate adjustment fails: residual severity

Including diagnosis codes, comorbidity indices (Charlson, Elixhauser), and propensity scores in the outcome model attenuates CBI but rarely eliminates it. The fundamental problem is that the severity signals driving prescribing decisions — HbA1c trajectory, spirometry values, frailty score, symptom burden, the clinician's gestalt — are not recorded in claims. Even high-dimensional propensity scores (hdPS), which empirically screen hundreds of claims codes for proxy confounders, can recover indirect severity signals (hospitalizations, emergency encounters, specialist contacts, polypharmacy) but cannot reconstruct continuous physiological measurements the prescriber used. Residual confounding by unmeasured severity therefore persists in essentially every pharmacoepidemiological cohort study, and its direction is predictable from the indication structure. The claims-based frailty index (CFBI) partially rescues this for elderly populations by aggregating frailty-adjacent codes into a validated composite, but it remains a proxy for the underlying physiological state, not a direct measure of it.

Design fixes, ranked by effectiveness

The only complete fix is randomization. Among observational designs, the hierarchy is: (1) Active-comparator, new-user (ACNU) design: by restricting to initiators of two drugs used for the same indication, the indication itself cancels across arms. Both groups had the indication; the severity confounding that remains is the within-indication severity differential between prescribers' choices, which is typically far smaller than the cross-indication gap. (2) Restriction to a single indication with a narrow severity band reduces the gradient but depends on having severity explicitly coded or measurable. (3) Self-controlled designs (SCCS, case-crossover): the patient acts as their own control across time periods so stable confounders including the indication and baseline severity cancel. Appropriate for acute, transient exposures; cannot handle time-varying confounders including disease progression. (4) Instrumental variable / regression discontinuity: when a valid instrument exists (physician prescribing preference, formulary threshold, geographic variation), IV methods achieve causal identification even with unmeasured severity, at the cost of estimating only a local average treatment effect and requiring strong, verifiable IV assumptions. (5) Negative controls and E-value: after the primary analysis, negative-control outcomes or exposures that share the indication-confounding structure without a causal path to the outcome measure the residual bias; the E-value quantifies the minimum unmeasured confounding strength needed to explain away the observed association.

Pros, cons, and trade-offs

Confounding by indication is not a design choice — it is a structural feature of non-randomized prescribing. The trade-offs are among strategies for controlling it:

  • Active-comparator new-user design: the most powerful single structural fix. CBI cancels by
  • Propensity score adjustment (partial fix): addresses measured severity proxies and produces
  • Restriction and eligibility narrowing: tightening the cohort to a homogeneous severity
  • Instrumental variable / RDD: can achieve causal identification with unmeasured confounding.

When to use

(meaning: when to treat CBI as a primary design priority)

CBI is a design priority in essentially every observational study of therapeutic drugs where the comparison is not driven by an exogenous mechanism. Specifically, actively address CBI when: (a) the drug is prescribed for a condition that is itself a risk factor for the outcome — any study of diabetes drugs on cardiovascular outcomes, COPD drugs on respiratory hospitalizations, antihypertensives on stroke, anticoagulants on thromboembolic outcomes; (b) the study compares a new drug to an older drug for the same indication (channeling toward newer agents for sicker or treatment-refractory patients is the default pattern at market entry); (c) the study uses a non-user comparison group (CBI is strongest here because comparators have no indication at all). Default to an active-comparator new-user design for all head-to-head drug comparisons; reserve non-user designs for questions where no interchangeable comparator exists and quantify the CBI risk explicitly with direction-of-bias reasoning and an E-value.

When NOT to use

(meaning: when CBI-specific adjustments are unnecessary or counterproductive)

  • When the design already eliminates it structurally: a self-controlled analysis comparing
  • When the cohort is tightly restricted to a single, narrow indication stratum with documented
  • Do not adjust for post-baseline severity markers: conditioning on outcomes of disease
  • Do not interpret a balanced propensity score as eliminating CBI: balance on measured

Interpreting the output

In the worked example: 1000 severe patients (60% treated, risk = 0.30) and 1000 mild patients (20% treated, risk = 0.10). The drug has no true causal effect (RR = 1.00 within each stratum). The stratum-adjusted RRs are both 1.00. The crude pooled analysis yields treated risk = 200 / 800 = 0.25 and crude RR = 1200 / 800 = 1.50.

(1) Formal interpretation. The crude RR of 1.50 is a confounded estimate. The Mantel-Haenszel stratum-adjusted RR equals 1.00, confirming that the drug has no causal effect within either severity stratum. The 1.50 arises entirely because severe patients — who have both a higher treatment probability (60%) and a higher outcome risk (30%) — are overrepresented among the treated in the pooled analysis. The severity stratum is an unmeasured confounder: if it were perfectly controlled, the association would vanish. The direction of confounding is positive (crude RR > true RR), consistent with the indication pattern in which sicker patients are more likely to receive treatment and face higher baseline risk. This is the mathematical signature of confounding by indication in a 2-stratum model.

(2) Practical interpretation. A study that does not adequately control for severity will report that the drug increases risk by 50% — potentially triggering regulatory action or prescriber hesitation — when the drug has no true effect. The scale of bias (1.50 vs 1.00) is proportional to the severity gradient in both treatment probability (60% vs 20%) and outcome risk (30% vs 10%). In real pharmacoepidemiological data, these gradients are typically larger and partly unmeasured, so residual confounding after standard covariate adjustment can be substantial and in a predictable direction. The design remedy — an active-comparator design restricting to the same indication — removes this bias structurally rather than requiring it to be modeled away from partially observed data.

Worked example

Scenario

A claims-based cohort study asks whether a new oral anti-inflammatory drug increases the risk of a serious adverse event over one year. The database holds 2 000 patients: 1 000 with severe disease and 1 000 with mild disease. Physicians prescribe the new drug to 60% of severe patients (who are at highest need) but to only 20% of mild patients. The true pharmacological effect of the drug is null — within each severity stratum the drug neither increases nor decreases the adverse-event risk (stratum-specific RR = 1.00). The analyst first ignores severity and computes a crude RR, then stratifies by severity to recover the true RR.

Dataset

Four cells of a 2x2-by-stratum table: two severity strata (severe/mild), each split into treated vs untreated. Events and risks are exact by construction (no true drug effect).

stratumtreatment_statuspatientseventsrisk
severetreated6001800.3
severeuntreated4001200.3
mildtreated200200.1
milduntreated800800.1

Steps

  • Severe stratum (1 000 patients, 60% treated): treated group has 600 patients, untreated has 400. True risk = 0.30 for all severe patients regardless of treatment (RR = 1.00 by design). Events in treated: 600 0.30 = 180. Events in untreated: 400 0.30 = 120.

  • Within-stratum check for severe patients: treated risk = 180 / 600 = 0.30; untreated risk = 120 / 400 = 0.30; RR_severe = 0.30 / 0.30 = 1.00. No drug effect.

  • Mild stratum (1 000 patients, 20% treated): treated group has 200 patients, untreated has 800. True risk = 0.10 for all mild patients (RR = 1.00). Events in treated: 200 0.10 = 20. Events in untreated: 800 0.10 = 80.

  • Within-stratum check for mild patients: treated risk = 20 / 200 = 0.10; untreated risk = 80 / 800 = 0.10; RR_mild = 0.10 / 0.10 = 1.00. No drug effect.

  • Pool both strata (crude, ignoring severity): total treated = 600 + 200 = 800; total untreated = 400 + 800 = 1200; total treated events = 180 + 20 = 200; total untreated events = 120 + 80 = 200.

  • Crude risk in treated = 200 / 800 = 0.25. Crude risk in untreated is approximately 200 / 1200 ≈ 0.167. Simplify the ratio: crude RR = 1200 / 800 = 1.50. (Cancel the 200s: (200 / 800) / (200 / 1200) = 1200 / 800 = 1.50.)

  • The crude RR of 1.50 makes the drug look harmful (a 50% risk increase) even though both stratum-specific RRs equal 1.00. The entire inflation is confounding by indication: severe patients (60% treated, 30% risk) dominate the treated arm while mild patients (20% treated, 10% risk) dominate the untreated arm, so the crude treated group is on average sicker than the crude untreated group — an imbalance the drug did not create.

Result

Within-stratum results: RR_severe = 0.30 / 0.30 = 1.00; RR_mild = 0.10 / 0.10 = 1.00. Both stratum-specific RRs are null — no true drug effect in either severity group. Crude pooled result: treated risk = 200 / 800 = 0.25; crude RR = 1200 / 800 = 1.50. The crude RR of 1.50 is entirely spurious confounding by indication. Stratifying by severity (or using an active-comparator design where the indication cancels) restores the true RR = 1.00.

Runnable example

python implementation

Simulate a confounding-by-indication dataset replicating the worked-example structure (1 000 severe + 1 000 mild patients; treatment probability 60% / 20%; outcome risk 0.30 / 0.10; true drug RR = 1.00 in both strata). Compute the crude RR and the...

import math
import random

random.seed(42)

# ── Simulation parameters matching the worked example ──────────────────────────────
# True drug effect: RR = 1.00 in both strata (no causal effect)
strata = [
    {"stratum": "severe", "n": 1000, "p_treat": 0.60, "base_risk": 0.30},
    {"stratum": "mild",   "n": 1000, "p_treat": 0.20, "base_risk": 0.10},
]

# Use exact counts from worked example rather than random draws for reproducibility.
# severe: 600 treated (180 events), 400 untreated (120 events)
# mild:   200 treated ( 20 events), 800 untreated ( 80 events)
cells = [
    {"stratum": "severe", "treated": 1, "patients": 600, "events": 180},
    {"stratum": "severe", "treated": 0, "patients": 400, "events": 120},
    {"stratum": "mild",   "treated": 1, "patients": 200, "events":  20},
    {"stratum": "mild",   "treated": 0, "patients": 800, "events":  80},
]

# ── Stratum-specific RRs (should both equal 1.00) ──────────────────────────────────
print("=== Stratum-specific RRs ===")
for stratum_name in ("severe", "mild"):
    c = {c["treated"]: c for c in cells if c["stratum"] == stratum_name}
    r_treat = c[1]["events"] / c[1]["patients"]
    r_untr  = c[0]["events"] / c[0]["patients"]
    rr      = r_treat / r_untr
    print(f"  {stratum_name}: treated risk = {r_treat:.4f}, "
          f"untreated risk = {r_untr:.4f}, RR = {rr:.4f}")

# ── Crude (pooled, ignoring stratum) RR ───────────────────────────────────────────
tot_treat_events = sum(c["events"] for c in cells if c["treated"] == 1)
tot_treat_n      = sum(c["patients"] for c in cells if c["treated"] == 1)
tot_untr_events  = sum(c["events"] for c in cells if c["treated"] == 0)
tot_untr_n       = sum(c["patients"] for c in cells if c["treated"] == 0)

crude_r_treat = tot_treat_events / tot_treat_n   # = 200/800 = 0.25
crude_r_untr  = tot_untr_events  / tot_untr_n    # = 200/1200 ≈ 0.167
crude_rr      = crude_r_treat    / crude_r_untr   # = 1.50

print("\n=== Crude (pooled) RR ===")
print(f"  Treated:   {tot_treat_events}/{tot_treat_n} = {crude_r_treat:.4f}")
print(f"  Untreated: {tot_untr_events}/{tot_untr_n} = {crude_r_untr:.6f}")
print(f"  Crude RR   = {crude_rr:.4f}  (expected: 1.50 — entirely from CBI)")

# ── Mantel-Haenszel RR (stratum-adjusted) ─────────────────────────────────────────
# MH numerator term per stratum: (events_treat * n_untr) / total_in_stratum
# MH denominator term: (events_untr  * n_treat) / total_in_stratum
mh_num = 0.0
mh_den = 0.0
for stratum_name in ("severe", "mild"):
    c = {c["treated"]: c for c in cells if c["stratum"] == stratum_name}
    n_total   = c[1]["patients"] + c[0]["patients"]
    mh_num   += (c[1]["events"] * c[0]["patients"]) / n_total
    mh_den   += (c[0]["events"] * c[1]["patients"]) / n_total

mh_rr = mh_num / mh_den
print("\n=== Mantel-Haenszel Adjusted RR ===")
print(f"  MH numerator   = {mh_num:.4f}")
print(f"  MH denominator = {mh_den:.4f}")
print(f"  MH RR          = {mh_rr:.4f}  (expected: 1.00 — true null effect)")
print()
print(f"Conclusion: Crude RR = {crude_rr:.2f} vs MH-adjusted RR = {mh_rr:.2f}.")
print("The entire excess (1.50 vs 1.00) is confounding by indication.")
print("Design fix: active-comparator new-user design restricts to the same indication,")
print("making severity balance within-stratum rather than requiring stratification.")
r implementation

Replicate the worked-example arithmetic in R and compute the Mantel-Haenszel stratified RR using a 2x2 array (epiR::epi.2by2 or manual computation). Demonstrates the crude vs adjusted discrepancy and how the E-value bounds the unmeasured-severity confounder...

# ── Exact cell counts from the worked example ──────────────────────────────────────
# Rows: treated (1), untreated (0); Columns: event (1), no event (0)
severe_2x2 <- matrix(
  c(180, 420,   # treated:   180 events, 420 no-events
    120, 280),  # untreated: 120 events, 280 no-events
  nrow = 2, byrow = TRUE,
  dimnames = list(treat = c("treated","untreated"), event = c("yes","no"))
)
mild_2x2 <- matrix(
  c( 20, 180,   # treated:   20 events, 180 no-events
     80, 720),  # untreated: 80 events, 720 no-events
  nrow = 2, byrow = TRUE,
  dimnames = list(treat = c("treated","untreated"), event = c("yes","no"))
)

rr_stratum <- function(m) {
  r_treat <- m["treated",   "yes"] / sum(m["treated",   ])
  r_untr  <- m["untreated", "yes"] / sum(m["untreated", ])
  list(r_treat = r_treat, r_untr = r_untr, rr = r_treat / r_untr)
}

cat("=== Stratum-specific RRs ===\n")
sev <- rr_stratum(severe_2x2)
mld <- rr_stratum(mild_2x2)
cat(sprintf("  Severe: treated risk = %.4f, untreated = %.4f, RR = %.4f\n",
            sev$r_treat, sev$r_untr, sev$rr))
cat(sprintf("  Mild:   treated risk = %.4f, untreated = %.4f, RR = %.4f\n",
            mld$r_treat, mld$r_untr, mld$rr))

# ── Crude (pooled) RR ──────────────────────────────────────────────────────────────
pooled <- severe_2x2 + mild_2x2
crude  <- rr_stratum(pooled)
cat(sprintf("\n=== Crude RR ===\n  Treated risk = %.4f, Untreated = %.6f, Crude RR = %.4f\n",
            crude$r_treat, crude$r_untr, crude$rr))

# ── Manual Mantel-Haenszel RR ─────────────────────────────────────────────────────
mh_term <- function(m) {
  N <- sum(m)
  list(num = m["treated","yes"] * sum(m["untreated",]) / N,
       den = m["untreated","yes"] * sum(m["treated",]) / N)
}
ts <- mh_term(severe_2x2)
tm <- mh_term(mild_2x2)
mh_rr <- (ts$num + tm$num) / (ts$den + tm$den)
cat(sprintf("\n=== Mantel-Haenszel Adjusted RR ===\n  MH RR = %.4f (expected 1.00)\n", mh_rr))

# ── E-value for the crude RR of 1.50 ─────────────────────────────────────────────
# E-value formula (VanderWeele & Ding 2017): E = RR + sqrt(RR * (RR - 1))
rr_obs  <- crude$rr
e_value <- rr_obs + sqrt(rr_obs * (rr_obs - 1))
cat(sprintf("\n=== E-value for crude RR = %.2f ===\n", rr_obs))
cat(sprintf("  E-value = %.4f + sqrt(%.4f * (%.4f - 1)) = %.4f\n",
            rr_obs, rr_obs, rr_obs, e_value))
cat("  Interpretation: an unmeasured confounder would need to be associated\n")
cat(sprintf("  with both treatment and outcome by a factor of >=%.2f to explain\n", e_value))
cat("  the crude RR of 1.50. Severity in claims (partially unmeasured) could\n")
cat("  plausibly reach this threshold, supporting the CBI diagnosis.\n")

# ── Conclusion ────────────────────────────────────────────────────────────────────
cat(sprintf("\nCrude RR = %.2f vs MH-adjusted RR = %.2f.\n", crude$rr, mh_rr))
cat("The entire excess is confounding by indication — eliminated by stratification.\n")
cat("Design fix: active-comparator new-user design shares the indication across arms.\n")