Medication Possession Ratio (MPR)
A refill-based adherence measure equal to the total days' supply dispensed during an observation interval divided by the number of days in that interval, which (unlike PDC) is not inherently capped at 1.0 and can exceed 100% when fills overlap or refills arrive early.
On this page
The Medication Possession Ratio (MPR) measures how much medication a patient picked up from the pharmacy relative to how long they were supposed to be on it. You add up the total days' worth of pills dispensed across all prescription fills during the study period, then divide by the number of days in that period. Unlike the Proportion of Days Covered (PDC), which counts only the unique calendar days a patient had pills on hand (and therefore stays at or below 100%), MPR simply adds raw supply — so when a patient refills early and two fills overlap, those overlapping days get counted twice in the numerator, and the result can exceed 1.0 (or 100%). That "over 100%" signal is not a flaw to hide; it tells you the patient was stockpiling supply.
Medication Possession Ratio (MPR)
is the oldest and still one of the most common refill-based ("secondary") adherence measures derived from pharmacy dispensing records or claims. In its canonical (simple, uncapped) form, MPR = (sum of `days_supply` across fills in the interval) / (number of days in the interval). It estimates the proportion of the interval during which the patient possessed enough medication to take it as prescribed.
Because the numerator sums supply without regard to whether two fills cover the same calendar day, an early refill or a 90-day mail-order fill stacked on a retail fill can push MPR above 1.0 — a value that has no coherent interpretation as "fraction of days covered."
Core conceptual distinction
MPR and PDC answer almost the same question with one decisive difference in how they treat overlap. PDC (proportion of days covered) is a set-cardinality measure: it counts the number of unique calendar days in the interval on which the patient had any supply on hand, divided by interval days; overlapping supply is counted once, so PDC is bounded in [0, 1] and stockpiled days carry forward only as future coverage.
MPR is a quantity measure: it sums total supply acquired, so overlap is double-counted and the ceiling is broken. The practical consequence is systematic: uncapped MPR ≥ PDC for the same patient and window, and the gap grows with early refilling and supply stockpiling.
The right framing of the estimand therefore matters — if the research question is "what share of time was this patient covered?" PDC (or capped MPR) is the target; if it is "how much drug did this patient acquire relative to need?" (a supply/utilization or cost question), uncapped MPR is the natural quantity. Choosing MPR for an "adherent vs non-adherent" dichotomy and then capping ad hoc at 1.0 silently redefines the measure into a coarse PDC, and reproducibility demands the cap and overlap rules be pre-specified.
Pros, cons, and trade-offs
- vs PDC (pdc-proportion-of-days-covered): MPR is computationally trivial (one SUM, one division — no day-level set construction), preserves the magnitude of oversupply (useful for waste, cost, and supply-chain analyses), and maintains comparability with the large pre-2010 literature and some legacy HEDIS-era reporting. Cost: uncapped MPR overestimates coverage whenever fills overlap, produces uninterpretable >100% values, and is not the PQA/CMS-endorsed measure for the Medicare Part D Star Ratings adherence triple-weighted measures (diabetes, hypertension RAS-antagonists, statins) — those use PDC. Prefer PDC for almost all contemporary comparative-effectiveness, quality-measurement, and regulatory work; prefer MPR only for total-acquisition/cost questions or explicit historical replication.
- vs capped MPR (MPR truncated at 1.0): Capping removes the absurd >100% values and makes MPR converge toward PDC, but it is not identical to PDC: capping is applied to the summed ratio after the fact, whereas PDC removes overlap at the day level, so the two can still disagree when fills overlap heavily within the window (capped MPR caps the whole interval at 1.0 but does not reallocate stockpiled days to later gaps the way carry-over PDC does). Prefer the shift-forward (date-adjusted) variant if you want overlap to push later coverage outward rather than be discarded.
- vs persistence (persistence-time-to-discontinuation): MPR measures intensity of coverage within a fixed denominator window and says nothing about when therapy stopped; persistence measures duration until a permissible-gap is exceeded. A patient can be highly persistent yet have low MPR (chronically under-refilling) or non-persistent yet high MPR over the period they were on therapy. Report both for a complete adherence picture; never substitute one for the other.
When NOT to use — and when it is actively misleading or dangerous
- As the adherence metric for quality reporting or a regulated effectiveness endpoint. PQA-defined PDC is the standard for CMS Star Ratings; submitting uncapped MPR (or silently capping it) misrepresents performance and is not defensible to FDA/EMA/HTA reviewers who expect PDC for "proportion covered."
- When supply overlap is structural and differential. In therapies with 90-day mail-order, free manufacturer samples, dose titration, or PRN use, uncapped MPR inflates, and if overlap differs by exposure arm (e.g., one drug favored for mail-order), the bias is differential and can manufacture or mask an adherence-outcome association. This is the dangerous case: the measure is not just noisy, it is confounded.
- As an exposure that is itself on the causal pathway. Conditioning on or matching on post-baseline MPR (a time-updated consequence of being on and tolerating the drug) induces healthy-adherer bias and collider/mediator bias — adherent patients are healthier in ways unmeasured in claims. Use MPR as an outcome or pre-specified time-varying exposure with appropriate g-methods, not as a baseline confounder.
- As a switching/combination summary across a drug class without a class-level day map: summing `days_supply` across two interchangeable agents double-counts overlap days and breaks the measure; build a class-level coverage map first.
Data-source operational depth
- Administrative claims (FFS): The native substrate. MPR needs only `person_id`, `fill_date` (service date), and `days_supply` (plus NDC to define the cohort). Require continuous pharmacy + medical enrollment spanning the full observation window so that a missing fill is a true non-fill, not unobserved care. Failure modes: `days_supply` is miscoded for insulin, inhalers, topicals, and titrated drugs (canister/vial counts, not days); the last fill's supply extends past the window end and inflates a fixed-window MPR unless truncated at window end; inpatient and skilled-nursing days are covered by the facility (Part A), not Part D pharmacy, so supply on hand during admissions is invisible and depresses MPR — censor or exclude inpatient days.
- Medicare Advantage (MA) vs FFS: MA encounter data historically under-capture pharmacy fills relative to FFS Part D, so MA-only person-time can show artifactually low fill counts and low MPR; restrict to enrollees with observable Part D (or commercial pharmacy benefit) and exclude MA-only spans, exactly as for any claims exposure definition. Differential competing risks (death, disenrollment) by exposure shorten the at-risk window — define whether the denominator is fixed (e.g., 365 days) or curtailed at disenrollment/death, and keep the rule identical across arms.
- EHR / e-prescribing: Captures the order (quantity, sig, refills authorized), not the dispensing. Order-based "MPR" overestimates possession because written ≠ filled (primary non-adherence, see primary-non-adherence-initiation). Link to pharmacy claims or a dispensing feed (e.g., Surescripts) before computing MPR; unlinked EHR MPR is a measure of prescribing intent, not acquisition.
- Registry / linked claims–EHR: Registries rarely hold complete fill histories; link to claims for `days_supply`. Linkage introduces a selected, linkable subpopulation and order/fill/service-date discrepancies that must be reconciled before windowing — an off-by-one in date alignment systematically biases the supply sum.
Worked claims example
A hypertension cohort member has a fixed 180-day observation window starting at the index fill (day 0). Retail pharmacy claims show four 30-day fills of one ACE inhibitor (NDC fixed): fill 1 on day 0, fill 2 on day 30, fill 3 on day 50 (a 10-day-early refill — the patient still had 10 days of fill 2 on hand), fill 4 on day 90. Total `days_supply` = 30 + 30 + 30 + 30 = 120.
Simple (uncapped) MPR
= 120 / 180 = 0.667 — but this understates nothing here because there is genuine oversupply at the early refill that the sum already includes; the patient's acquired coverage is 120 days.
PDC
counts unique covered days: day 0–59 (fills 1–2 run contiguous through day 59), the early fill 3 on day 50 carries the extra 10 days forward so coverage extends to day 80, then a 10-day gap (days 80–89), fill 4 covers days 90–119 — unique covered days = 60 + 20 + 30 = 110, so PDC = 110 / 180 = 0.611.
Shift-forward (date-adjusted) MPR
moves fill 3's start to day 60 (the exhaustion date of fill 2) so its supply is not double-counted against fill 2, covering days 60–89 and closing the gap that PDC leaves open — coverage now runs contiguously from day 0 through day 119, yielding 120 covered days and 120 / 180 = 0.667. Shift-forward is therefore more generous than union PDC here because it re-allocates the early supply forward rather than discarding the overlap.
Now suppose fill 3 had instead been a 90-day mail-order fill arriving 10 days early: uncapped MPR = (30+30+90+30)/180 = 180/180 = 1.0 even though, after the stockpile from the 90-day fill (days 50–139) and fill 4 run out, the patient is uncovered over the tail of the window (roughly days 140–180) in reality — capped MPR = 1.0 hides the gap, whereas carry-over PDC = (covered unique days)/180 < 1.0 surfaces it.
This is precisely why uncapped MPR is unsafe as a coverage metric when 90-day supply and early refills coexist.
Decision diagram
flowchart TD
Q{What is the estimand?}
Q -->|Total drug acquired<br/>vs need: cost / waste / supply| ACQ[Uncapped MPR<br/>sum days_supply / window<br/>may exceed 1.0]
Q -->|Share of days covered:<br/>quality / effectiveness| COV{Overlap expected?<br/>early refills / 90-day mail-order}
COV -->|No / minimal| SIMPLE[Simple MPR == capped MPR<br/>here they agree]
COV -->|Yes| HANDLE{Overlap rule}
HANDLE -->|Cap whole ratio at 1.0| CAP[Capped MPR<br/>bounded but hides post-stockpile gaps]
HANDLE -->|Shift later fill start<br/>to prior supply-end| SHIFT[Shift-forward MPR<br/>approximates carry-over PDC]
HANDLE -->|Day-level unique coverage| PDC[Use PDC instead<br/>pdc-proportion-of-days-covered]
CAP --> DOC[Pre-specify window + cap + overlap rule<br/>report per Raebel 2013 terminology]
SHIFT --> DOC
PDC --> DOCgantt title MPR vs PDC for one patient, 180-day window, four 30-day fills dateFormat YYYY-MM-DD axisFormat day %j section Fills (days_supply) Fill 1 day 0 :f1, 2024-01-01, 30d Fill 2 day 30 :f2, 2024-01-31, 30d Fill 3 day 50 (10-day early refill) :crit, f3, 2024-02-20, 30d Fill 4 day 90 :f4, 2024-03-31, 30d section Coverage Unique covered days (PDC = 110/180 = 0.61) :active, cov, 2024-01-01, 119d
Worked example
Scenario
Imagine a patient, ID 2001, who starts a 180-day statin treatment window on January 1, 2024. The pharmacy records show three fills: a 90-day fill picked up at the start, another 90-day fill picked up on March 15 (sixteen days before the first fill runs out — an early refill), and a smaller 30-day fill picked up on June 20. The window closes on June 28 (day 180). We want to compute the MPR and compare it to what PDC would give. The early refill is the key: those sixteen overlapping days are counted once in PDC but counted twice in MPR's simple sum.
Dataset
Raw pharmacy claims rows for patient 2001 — exactly what an analyst pulls from a pharmacy table. The days_supply on Fill C extends past the window end (June 28), so only 9 days count toward the denominator.
| person_id | fill_date | drug | days_supply |
|---|---|---|---|
| 2001 | 2024-01-01 | atorvastatin 40 mg | 90 |
| 2001 | 2024-03-15 | atorvastatin 40 mg | 90 |
| 2001 | 2024-06-20 | atorvastatin 40 mg | 30 |
Steps
Result
- Label
MPR = 189 days_supply in window / 180 window days = 1.050 (above the 0.80 adherent threshold, and above 1.0 due to the 16-day overlap). PDC = 173 unique covered days / 180 window days = 0.961. Both measures classify this patient as adherent, but only MPR can exceed 1.0 — PDC is bounded.
- Value
1.05
Trade-offs
Runnable example
Compute fixed-window MPR (uncapped, capped, and shift-forward variants) from a cleaned pharmacy fills table. Required input (one row per dispensing, de-duplicated): fills : person_id, fill_date (datetime64), days_supply (int), ndc (str) The observation window is index-anchored: day 0 = each person's first qualifying...
import pandas as pd
import numpy as np
WINDOW_DAYS = 180 # fixed observation interval anchored at the first fill
def compute_mpr(fills: pd.DataFrame, window_days: int = WINDOW_DAYS) -> pd.DataFrame:
f = fills.sort_values(["person_id", "fill_date"]).copy()
# Index = first qualifying fill; window = [index_date, index_date + window_days).
f["index_date"] = f.groupby("person_id")["fill_date"].transform("min")
f["window_end"] = f["index_date"] + pd.to_timedelta(window_days, unit="D")
f = f[(f["fill_date"] >= f["index_date"]) & (f["fill_date"] < f["window_end"])]
# Truncate each fill's supply at the window end (no credit for supply past the window).
f["supply_end"] = f["fill_date"] + pd.to_timedelta(f["days_supply"], unit="D")
capped_end = np.minimum(f["supply_end"].values, f["window_end"].values)
f["days_in_window"] = (capped_end - f["fill_date"].values) / np.timedelta64(1, "D")
f["days_in_window"] = f["days_in_window"].clip(lower=0)
# (1) Simple uncapped MPR = sum(days_supply in window) / window_days.
simple = (f.groupby("person_id")["days_in_window"].sum() / window_days)
# (3) Shift-forward MPR: walk fills, push a fill's start to the prior supply-end
# so overlapping (early-refill / stockpiled) days are carried forward, not
# double-counted. Approximates carry-over PDC.
def shift_forward(g: pd.DataFrame) -> float:
cursor = g["index_date"].iloc[0] # earliest uncovered day
win_end = g["window_end"].iloc[0]
covered = 0.0
for fill_date, ds in zip(g["fill_date"], g["days_supply"]):
start = max(fill_date, cursor) # never start before existing supply ends
end = min(start + pd.Timedelta(days=int(ds)), win_end)
covered += max((end - start).days, 0)
cursor = max(cursor, end)
return covered / window_days
shifted = f.groupby("person_id", group_keys=False).apply(shift_forward)
out = pd.DataFrame({"mpr_simple": simple, "mpr_shift_forward": shifted})
out["mpr_capped"] = out["mpr_simple"].clip(upper=1.0) # (2) cap at 1.0
return out.reset_index()Compute fixed-window MPR (uncapped, capped, shift-forward) with data.table. Input mirrors the Python version: fills : person_id, fill_date (Date), days_supply (integer), ndc (character) Window is index-anchored at each person's first fill; the last fill's supply is truncated at the window end.
library(data.table)
WINDOW_DAYS <- 180L
compute_mpr <- function(fills, window_days = WINDOW_DAYS) {
f <- as.data.table(fills)
setorder(f, person_id, fill_date)
f[, index_date := min(fill_date), by = person_id]
f[, window_end := index_date + window_days]
f <- f[fill_date >= index_date & fill_date < window_end]
# Truncate supply at window end -> days actually inside the fixed window.
f[, supply_end := fill_date + days_supply]
f[, days_in_window := pmax(0L, as.integer(pmin(supply_end, window_end) - fill_date))]
# (1) Simple uncapped MPR.
simple <- f[, .(mpr_simple = sum(days_in_window) / window_days), by = person_id]
# (3) Shift-forward MPR: carry overlapping supply forward instead of double-counting.
shift_one <- function(fd, ds, idx, wend) {
cursor <- idx; covered <- 0L
for (i in seq_along(fd)) {
start <- max(fd[i], cursor)
end <- min(start + ds[i], wend)
covered <- covered + max(as.integer(end - start), 0L)
cursor <- max(cursor, end)
}
covered / window_days
}
shifted <- f[, .(mpr_shift_forward = shift_one(fill_date, days_supply,
index_date[1], window_end[1])),
by = person_id]
out <- merge(simple, shifted, by = "person_id")
out[, mpr_capped := pmin(mpr_simple, 1.0)] # (2) cap at 1.0
out[]
}Compute fixed-window MPR in SAS. Required input (post data-management, one row per dispensing): work.fills with person_id, fill_date, days_supply, ndc. PROC SQL builds the index-anchored window and the simple/capped variants via aggregation;
%let window = 180;
/* Index date = first fill per person; keep only fills inside [index, index+window). */
proc sql;
create table f as
select fl.person_id, fl.fill_date, fl.days_supply,
ix.index_date,
(ix.index_date + &window) as window_end format=date9.,
/* days of this fill that fall inside the fixed window (truncate at window end) */
max(0, min(fl.fill_date + fl.days_supply, ix.index_date + &window)
- fl.fill_date) as days_in_window
from work.fills fl
inner join (select person_id, min(fill_date) as index_date
from work.fills group by person_id) ix
on fl.person_id = ix.person_id
where fl.fill_date >= ix.index_date
and fl.fill_date < ix.index_date + &window
order by person_id, fill_date;
quit;
/* (1) Simple uncapped MPR and (2) capped MPR. */
proc sql;
create table mpr_simple as
select person_id,
sum(days_in_window) / &window as mpr_simple,
min(sum(days_in_window) / &window, 1.0) as mpr_capped
from f
group by person_id;
quit;
/* (3) Shift-forward MPR: walk fills in date order, carry overlapping supply forward. */
data mpr_shift(keep=person_id mpr_shift_forward);
set f;
by person_id;
retain cursor covered;
if first.person_id then do; cursor = index_date; covered = 0; end;
start = max(fill_date, cursor); /* never start before existing supply ends */
end = min(start + days_supply, window_end); /* truncate at window end */
covered = covered + max(end - start, 0);
cursor = max(cursor, end);
if last.person_id then do;
mpr_shift_forward = covered / &window;
output;
end;
run;
/* Combine all three variants for sensitivity comparison. */
proc sql;
create table mpr_all as
select s.person_id, s.mpr_simple, s.mpr_capped, sh.mpr_shift_forward
from mpr_simple s left join mpr_shift sh
on s.person_id = sh.person_id;
quit;Citations
- [1]Steiner JF, Prochazka AV. The assessment of refill compliance using pharmacy records: methods, validity, and applications. J Clin Epidemiol. 1997;50(1):105-116.
- [2]Andrade SE, Kahler KH, Frech F, Chan KA. Methods for evaluation of medication adherence and persistence using automated databases. Pharmacoepidemiol Drug Saf. 2006;15(8):565-574.
- [3]Hess LM, Raebel MA, Conner DA, Malone DC. Measurement of adherence in pharmacy administrative databases: a proposal for standard definitions and preferred measures. Ann Pharmacother. 2006;40(7-8):1280-1288.