← Methods repository
concept

Proportion of Days Covered (PDC)

A claims-based adherence measure equal to the number of unique days in a fixed observation window on which a patient had medication supply on hand, divided by the number of days in that window, bounded in [0, 1].

Exposure_Definitionadherencepersistencemedication-utilizationclaimspdcmprpqa-cmsdays-supply
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

Proportion of Days Covered (PDC) measures how much of a treatment period a patient actually had medication on hand. You take every prescription fill, mark the calendar days each fill is meant to cover, count the unique covered days, and divide by the number of days in the window you are studying. The result runs from 0 to 1, and 0.80 is the usual cut-off for calling someone "adherent" — but that line is a convention, not a law of nature. PDC cannot see free samples, cash-paid fills, or drugs given in the hospital, so it can make a well-treated patient look like they missed doses.

Proportion of Days Covered (PDC)

is the dominant operationalization of medication adherence in administrative-claims research and the basis of the CMS/PQA adherence Star Ratings measures. It counts the unique days "covered" by dispensed supply within a fixed denominator window and divides by the length of that window. Coverage on a given calendar day is a 0/1 indicator: any positive `days_supply` reaching that day makes it covered, regardless of how many fills overlap it. That single design decision — count days, not supply — is what bounds PDC at 1.0 and is the entire reason PDC exists.

Core conceptual distinction — PDC vs MPR

The Medication Possession Ratio (MPR) sums `days_supply` across fills and divides by the interval, so two early refills can push MPR above 1.0 (often reported as 150%+), conflating stockpiling with adherence. PDC instead asks, day by day, "did the patient have any supply?" and so cannot exceed 100%. The difference is not cosmetic. Under oversupply, MPR overstates adherence and, when truncated at 1.0, the two converge; but for patients who refill early and then stop, MPR's overlap credit can mask a coverage gap that PDC correctly exposes. PDC is therefore the more conservative, more interpretable, and regulatorily standardized choice. PDC is a summary of cumulative daily coverage, not a measure of treatment duration — that is the job of persistence (time to first permitted gap). A patient can have high PDC with several short gaps (restart after each), and high persistence with mediocre PDC (continuous fills but frequent partial coverage). Report both when the question spans "how much" and "how long."

The estimand PDC actually targets

PDC is a measure, not a causal estimator; but how you use it commits you to an estimand. As a continuous exposure in an outcome model it represents average daily coverage; dichotomized at the conventional PDC ≥ 0.80 threshold it represents the contrast of "adherent vs non-adherent" patients — a threshold popularized for chronic cardiometabolic therapy and endorsed by PQA, but empirically derived, drug-class-specific, and not a law of nature (Karve et al. showed the outcome-optimal cut-point varies by drug and endpoint). Whatever the form, prespecify the denominator window, the carry-over rule, the inpatient rule, and the threshold in the SAP; post-hoc tuning of these knobs is the field's most common silent source of non-reproducibility.

Pros, cons, and trade-offs

- vs MPR (medication-possession-ratio): PDC caps at 1.0 and is immune to oversupply inflation, is the CMS/PQA standard, and is more conservative for "adherent/not" classification. Cost: it discards the magnitude of stockpiling (which can itself predict waste, hoarding, or measurement error), is slightly more work to implement (you must resolve overlapping fills into a daily array rather than just summing supply), and can understate possession when early refills are clinically appropriate. Prefer PDC for comparative effectiveness/safety classification and any quality-measure or regulatory context. - vs persistence (time-to-discontinuation): PDC captures intensity of coverage during follow-up; persistence captures duration to the first qualifying gap. PDC rewards a patient who reaches 0.85 via on-off-on refilling; persistence flags that same patient at the first long gap. Cost: PDC alone hides the gap pattern — clinically, a single 60-day interruption and six scattered 10-day gaps can yield identical PDC but very different risk. Prefer PDC for cumulative-exposure questions; pair it with persistence (and, for time-varying analyses, with actual exposure-episode construction) when the gap pattern matters. - vs a daily-defined-dose / time-varying exposure model: PDC collapses a longitudinal exposure into one scalar per patient, which is simple and communicable but throws away timing — it cannot represent immortal time, cannot be used as a time-varying covariate, and invites the classic adherence-as-baseline bias if measured over a window that overlaps follow-up. Prefer a time-varying exposure when the causal contrast is dose-response over time or when adherence is on the causal pathway.

When to use

Chronic, orally self-administered, refillable therapy in claims (or claims-linked) data where the research or quality question is "what fraction of the intended treatment period did the patient actually have drug on hand?" — statins, oral antidiabetics, RAAS inhibitors, DOACs, oral oncolytics, immunosuppressants. PDC is also the right primary measure whenever you must align with CMS Star Ratings or PQA specifications.

When NOT to use — and when it is actively misleading

- Measuring adherence over a window that overlaps the outcome follow-up. If PDC is computed during the same period in which outcomes accrue, sicker patients who die or are hospitalized early accrue fewer covered days and look "non-adherent," manufacturing a spurious adherence-outcome association (reverse causation / immortal-time-adjacent bias). Fix it by measuring PDC in a landmark window that ends before follow-up begins, or by modeling exposure as time-varying. - As-needed, single-fill, titrated, or non-oral therapies. PRN inhalers, one-time antibiotic courses, insulin with variable dosing, and clinician-administered infusions break the "one fill covers a known number of days" assumption; `days_supply` is meaningless or absent, and PDC is uninterpretable. - Incomplete capture of dispensing. Free samples, 340B, cash/discount-card fills (GoodRx), inpatient and clinic-administered drug, and care obtained outside the plan are invisible to pharmacy claims, so PDC understates coverage — differentially if one comparator is more often sampled or sold at retail discount. - EHR prescribing data used as a dispensing proxy. An order is not a fill; primary non-adherence (never picking up the script) is invisible, and PDC computed from orders systematically overstates adherence. - Crediting supply for days the patient could not have taken the drug at home. During inpatient or SNF stays the facility supplies medication, so outpatient fills neither span nor are needed for those days; naively including them distorts both numerator and denominator (see inpatient variant).

Data-source operational depth

- Claims (FFS or commercial): The native substrate. Coverage = `fill_date` + `days_supply` per NDC. Require continuous medical and pharmacy enrollment across the full PDC window so that an empty day is a true uncovered day, not unobserved. Failure mode — Medicare Advantage / capitated person-time: MA-only or capitated members generate no fee-for-service pharmacy claims, so their PDC window is structurally missing; restrict to enrollees with the relevant Part D / commercial pharmacy benefit and exclude MA-only spans rather than scoring them as zero coverage. Failure mode — `days_supply` errors: 90-day mail-order, sample fills, and data-entry errors distort supply; winsorize implausible `days_supply` and reconcile mail vs retail. Combination products and mid-window NDC switches require a regimen-level decision (PDC for the class vs per-component) made before, not after, looking at results. - EHR: Use only when linked to dispensing (or to medication administration for inpatient questions). Prescribing/order data alone overstate adherence because primary non-adherence is unobserved, and visit-driven capture means a patient who seeks care elsewhere looks like a coverage gap. Reconcile order/fill date discrepancies before defining the window. - Registry: Variable — some product registries carry patient-reported or pharmacy-card adherence at coarser granularity than claims. Best linked to claims for the full fill history and to a death index so that disenrollment/death truncate the denominator correctly. - Linked claims–EHR: The strongest substrate (EHR severity for landmark covariate adjustment + claims completeness for fills), at the cost of linkage selection and date reconciliation across order, fill, and service dates.

Competing risks and the elderly-claims trap

PDC denominators are often censored at death or disenrollment. In elderly or oncology populations, death is a competing event that differs by exposure; if one arm dies sooner, its members accrue fewer denominator days, and naive PDC comparisons can be distorted exactly where the clinical stakes are highest. Decide a priori whether death truncates the denominator (the usual choice) and report follow-up time alongside PDC so reviewers can see differential censoring.

Worked claims example

Statin adherence over a fixed 365-day landmark window. Patient `person_id = 1001`, index (first statin fill) `2023-01-01`; observation window = the 365 days `[2023-01-01, 2023-12-31]`; require continuous medical+pharmacy enrollment across the whole window and a 180-day pre-index statin washout to define a new user. Fills (each `days_supply = 90`, last covered day = `fill_date + 89`): (a) `2023-01-01` covers `2023-01-01`→`2023-03-31`; (b) `2023-03-20` covers `2023-03-20`→`2023-06-17` (it arrives 11 days early, so `2023-03-20`→`2023-03-31` is double-covered but each calendar day is counted once); (c) `2023-06-25` covers `2023-06-25`→`2023-09-22`; no further fills. The union of these intervals is `2023-01-01`→`2023-06-17` (168 days) plus `2023-06-25`→`2023-09-22` (90 days) = 258 unique covered days, with a true 7-day gap `2023-06-18`→`2023-06-24` between fills (b) and (c) and no coverage after `2023-09-22`. PDC = 258 / 365 = 0.71, below the 0.80 threshold — this patient is classified non-adherent. Contrast MPR, which simply sums supply: `90+90+90 = 270` days / 365 = 0.74; the 12 double-covered days that MPR credits but PDC counts once are exactly the gap between the two measures. Had the early refill been larger (or had more fills overlapped), MPR could exceed 1.0 while true coverage stayed at 0.71 — the oversupply artifact PDC is designed to remove. (A stricter "carry-over with surplus shifting" rule, in which the 12 surplus days extend later coverage so no supply is wasted, would push the numerator to 270 and PDC to 0.74, numerically equal to MPR here; most PQA-style implementations use the union rule above, which the code below computes.)

Worked example

Scenario

One patient, person_id 1001, starts a statin on 2023-01-01 (their index date). We want to know what fraction of the next 365 days they had statin supply on hand — their PDC over the window 2023-01-01 to 2023-12-31. The patient must be continuously enrolled in medical and pharmacy coverage across the whole window, so an empty day means "no pills," not "we couldn't see." Below are the three pharmacy fills an analyst would actually pull from the claims table.

Dataset

The raw pharmacy-claim rows for patient 1001 (each fill is a 90-day supply).

person_idfill_datedrugdays_supply
10012023-01-01atorvastatin90
10012023-03-20atorvastatin90
10012023-06-25atorvastatin90

Steps

  • Each 90-day fill covers its fill date plus the next 89 days. Fill A covers Jan 1 to Mar 31.

  • Fill B is filled Mar 20 — 11 days early — so it covers Mar 20 to Jun 17 and overlaps Fill A by 12 days (Mar 20-31). The union rule counts each calendar day only once, so those 12 days are not double-credited.

  • Together Fills A and B cover one unbroken stretch, Jan 1 to Jun 17 = 168 days.

  • Fill C is filled Jun 25, leaving a 7-day gap (Jun 18-24) with no supply. It covers Jun 25 to Sep 22 = 90 days.

  • There are no more fills, so Sep 23 to Dec 31 is uncovered. Total covered = 168 + 90 = 258 unique days.

Result

PDC = 258 unique covered days / 365 window days = 0.71, which is below the 0.80 threshold, so this patient is classified non-adherent. (MPR, which just sums supply, gives 270/365 = 0.74 — the 12-day gap between the two measures is exactly the overlap PDC refuses to double-count.)

Timeline Spec

Title

PDC over a 365-day window for one statin user (carry-over / union rule)

Window
Start

2023-01-01

End

2023-12-31

Label

Denominator: 365-day observation window

Events
  • Label

    Fill A

    Start

    2023-01-01

    Length Days

    90

    Quantity

    90 days_supply

  • Label

    Fill B (11-day early refill)

    Start

    2023-03-20

    Length Days

    90

    Quantity

    90 days_supply

  • Label

    Fill C

    Start

    2023-06-25

    Length Days

    90

    Quantity

    90 days_supply

Spans
  • Kind

    covered

    Start

    2023-01-01

    End

    2023-06-17

    Label

    168 covered days

  • Kind

    gap

    Start

    2023-06-18

    End

    2023-06-24

    Label

    7-day gap

  • Kind

    covered

    Start

    2023-06-25

    End

    2023-09-22

    Label

    90 covered days

Result
Label

258 unique covered days / 365 = PDC 0.71 (below 0.80 -> non-adherent)

Value

0.71

Runnable example

python implementation

Union-rule (carry-over / stockpiling-allowed) PDC over a fixed observation window from claims-style inputs. Required inputs (already cleaned, de-duplicated, restricted to the target drug/class, and to person-time with continuous medical+pharmacy enrollment;...

import pandas as pd
import numpy as np

def compute_pdc(rx: pd.DataFrame, windows: pd.DataFrame) -> pd.DataFrame:
    rx = rx.merge(windows, on="person_id", how="inner")

    # Each fill covers [fill_date, fill_date + days_supply) (half-open: last covered day is
    # fill_date + days_supply - 1). Clip the coverage interval to the observation window.
    cover_start = rx["fill_date"].clip(lower=rx["window_start"])
    cover_end = (rx["fill_date"] + pd.to_timedelta(rx["days_supply"], unit="D")
                 - pd.Timedelta(days=1)).clip(upper=rx["window_end"])

    rows = []
    for (pid, w_start, w_end), g in rx.assign(cs=cover_start, ce=cover_end).groupby(
            ["person_id", "window_start", "window_end"]):
        covered = set()
        for cs, ce in zip(g["cs"], g["ce"]):
            if ce < cs:          # fill falls entirely outside the window after clipping
                continue
            # Union of covered calendar days; the set dedupes overlapping (carried-over) days.
            covered.update(pd.date_range(cs, ce, freq="D"))
        window_days = (w_end - w_start).days + 1   # inclusive denominator
        covered_days = len(covered)
        rows.append((pid, covered_days, window_days, covered_days / window_days))

    out = pd.DataFrame(rows, columns=["person_id", "covered_days", "window_days", "pdc"])
    out["pdc"] = out["pdc"].clip(upper=1.0)        # guard against rounding; PDC is bounded at 1.0
    out["adherent"] = (out["pdc"] >= 0.80).astype(int)   # PQA-style threshold (prespecify!)
    return out
r implementation

Union-rule (carry-over) PDC with data.table, mirroring the Python version. Inputs: rx : person_id, fill_date (Date), days_supply (integer) windows : person_id, window_start (Date), window_end (Date, inclusive) Returns person_id, covered_days, window_days,...

library(data.table)

compute_pdc <- function(rx, windows, threshold = 0.80) {
  setDT(rx); setDT(windows)
  d <- merge(rx, windows, by = "person_id")

  # Clip each fill's coverage interval [fill_date, fill_date + days_supply - 1] to the window.
  d[, cs := pmax(fill_date, window_start)]
  d[, ce := pmin(fill_date + days_supply - 1L, window_end)]
  d <- d[ce >= cs]   # drop fills that fall outside the window after clipping

  pdc <- d[, {
    wdays <- as.integer(window_end[1] - window_start[1]) + 1L   # inclusive denominator
    covered <- logical(wdays)                                   # one slot per window day
    off <- as.integer(window_start[1])
    for (i in seq_len(.N)) {
      idx <- (as.integer(cs[i]) - off + 1L):(as.integer(ce[i]) - off + 1L)
      covered[idx] <- TRUE                                      # union -> overlaps counted once
    }
    cd <- sum(covered)
    .(covered_days = cd, window_days = wdays, pdc = min(cd / wdays, 1.0))
  }, by = person_id]

  pdc[, adherent := as.integer(pdc >= threshold)]   # PQA-style threshold (prespecify!)
  pdc[]
}