Time-Updated Exposures and Cumulative Dose
Operational construction of exposure variables that change over follow-up — current use, recent use, cumulative duration, cumulative dose, dose intensity, and weighted cumulative exposure — built as long-format person-time so that time-dependent models estimate effects without the immortal time and exposure misclassification that static ever/never definitions create.
On this page
When researchers study how a drug affects the body over time, they need to track not just whether a patient ever took the drug, but exactly when the drug was active and how much had been taken up to each point.
Time-updated exposure construction does this by dividing each patient's follow-up into small intervals and labeling each one with the exposure status at that moment — for example, whether a pill supply was currently on hand (current use) or how many total milligrams had accumulated up to that day (cumulative dose). This approach prevents a well-known counting error called immortal time bias, which occurs when a study incorrectly marks days before a patient even started the drug as if they were already exposed.
Without it, a static ever/never label collapses the entire timeline into one flag and hides the most important information: the timing and amount of what was actually taken.
Real treatments are rarely a single fixed dose held over follow-up. Patients fill late, stockpile, titrate, switch route, pause for surgery, combine therapies, and discontinue for toxicity or cost. Time-updated exposure construction turns a stream of pharmacy fills, infusion administrations, or procedure claims into a person-time representation in which the exposure value assigned to each interval reflects what was actually being taken at that point in follow-up. This data step is not preprocessing to be rushed — it is the analysis.
A correct estimand and a sophisticated model cannot rescue an exposure variable that mislabels person-time.
The common exposure scales, all defined as of a time `t` strictly before any outcome it could plausibly cause: current use (an active supply/administration episode covers `t`); recent use (exposure within a risk window, e.g. the prior 30 days, to capture carry-over pharmacology); cumulative duration (total exposed days before `t`); cumulative dose (sum of daily-dose × exposed days, or dispensed quantity, before `t`); dose intensity (cumulative dose per unit of exposed or protocol time); and weighted cumulative exposure (WCE), in which past doses contribute to current hazard through an estimated recency/latency weight function rather than a flat sum.
Core conceptual distinction
The decisive choice is what biologic quantity drives risk, and it dictates both the exposure variable and the estimand. A static `ever_exposed` flag answers "did this person ever take the drug" and, when used as a baseline covariate in a survival model, mislabels the unexposed lead-in as exposed (or forces follow-up to begin before the exposure decision), manufacturing immortal time.
A time-varying current-use indicator answers "is risk elevated while on drug" (acute pharmacology); cumulative dose/duration answers "does risk accrue with total burden" (cumulative toxicity, e.g. anthracycline cardiomyopathy); WCE answers "how does the timing of past doses shape current risk" when a flat cumulative sum is biologically wrong (recent doses matter more, or a latency lag applies).
These are not interchangeable: the estimand — a hazard ratio per active-use interval, per 1000 cumulative mg, or a WCE weight curve — must be pre-specified, and each maps to a different model (time-dependent Cox / pooled logistic on the long format, or, when the time-varying exposure is affected by prior outcomes/confounders, a marginal structural model fit by IPTW).
Pros, cons, and trade-offs
- vs static ever/never or baseline-only exposure: Time-updated exposure removes immortal time and the gross misclassification of pre-initiation and post-discontinuation person-time, and it lets the contrast match the pharmacology. Cost: it demands clean episode construction, lagging decisions, and far more programming; it is also vulnerable to treatment-by-indication feedback (dose is changed in response to evolving disease), which a naive time-dependent Cox cannot handle. Prefer for any safety/effectiveness question where exposure genuinely varies over follow-up.
- vs PDC / MPR adherence summaries (pdc-proportion-of-days-covered): PDC collapses a fill history into one scalar over a fixed denominator — useful for an adherence predictor or descriptor, but it discards when exposure occurred and cannot represent time-varying risk. Time-updated exposure preserves the timeline. Cost: more complex, less standardized. Prefer PDC when adherence itself is the quantity of interest and timing is not; prefer time-updated when the hazard model needs interval-level exposure.
- vs WCE specifically (within this family): A flat cumulative sum assumes every past mg counts equally forever; WCE relaxes that with a spline-weighted history and often fits short-latency or wash-in pharmacology far better. Cost: WCE needs dense exposure histories, is prone to overfitting sparse high-dose tails, and is harder to communicate to regulators than a pre-specified current-use or cumulative-dose contrast. Prefer WCE only when the flat-sum assumption is biologically indefensible and the data support estimating a weight curve.
- vs marginal structural models (marginal-structural-models-g-methods): A standard time-dependent Cox on time-updated exposure is valid only when time-varying confounders are not themselves affected by prior exposure. When they are (e.g. labs that respond to the drug and also drive subsequent dosing), conditioning on them biases the effect and omitting them confounds it — the classic g-method trap. The exposure-history long format built here is the input to an MSM; the difference is the weighting/estimation layer, not the exposure construction. Escalate to an MSM/IPTW when that feedback exists.
When NOT to use — and when it is actively misleading or dangerous
Do not reach for time-varying exposure when the estimand is an initiation (intention-to-treat-like) contrast — there, exposure is fixed at time zero by design and adding post-baseline time-varying status re-introduces the very informative-censoring and mediator-adjustment problems the new-user design was built to avoid.
It is actively dangerous to
- feed cumulative dose into a model without lagging: using exposure measured up to and including the outcome day lets reverse causation and protopathic effects (the prodrome drives the prescription) masquerade as a dose-response, fabricating a spurious gradient;
- update exposure using information that is a consequence of incipient disease, which conditions on a collider;
- apply a standard time-dependent Cox when time-varying confounders are affected by prior treatment — this silently produces a biased estimate that looks rigorous.
When dose is changed in response to the outcome process, only g-methods recover the causal effect.
Data-source operational depth
- Claims (FFS): Exposure episodes are built from `fill_date` + `days_supply` + daily dose derived from NDC strength and quantity. Real failure modes: (a) Medicare Advantage / capitated person-time lacks FFS fill claims, so any interval drawn from an MA-only member shows phantom "no current use" and a frozen cumulative dose — exclude MA-only person-time or restrict to Parts A/B/D (commercial: require an active pharmacy benefit), and treat the boundary as administrative censoring, not discontinuation. (b) 90-day mail-order and stockpiling inflate `days_supply` and apparent current use; decide a stockpiling/carry-over rule (cap accumulated supply) and a grace period explicitly. (c) Inpatient stays: most inpatient drugs are bundled and invisible to outpatient pharmacy, so an outpatient supply spanning a hospitalization may not reflect what was actually given — choose by design to bridge (assume continuation) or censor the stay, and report the choice.
- EHR: "Current use" can come from the medication list (often stale, carried forward indefinitely), the order (intent, not receipt), the administration (MAR — true for inpatient infusions), or the e-prescribe feed (closest to a fill but missing whether it was picked up). These answer different questions; pick the source that matches the exposure definition (administrations for infusional oncology; linked fills for oral adherence) and never silently mix them. Visit- driven capture means a patient who leaves the system shows a spurious exposure gap — treat loss to follow-up as potentially informative.
- Registry: Cycle-level dose and dose intensity are often captured more cleanly than in claims, but dose holds and reductions are frequently documented only in unstructured notes, so structured cycle data can overstate received dose; link to claims for complete outpatient fills/administrations and to a death index for censoring.
- Linked claims–EHR–registry: The ideal substrate (registry dose + claims completeness + EHR labs to model feedback), but order/fill/administration date discrepancies must be reconciled before any interval boundary is set, and linkage selects the linkable subset.
Worked claims example
Question: dose-dependent risk of acute kidney injury (AKI) with an oral nephrotoxin, FFS claims. (1) Eligibility/time zero: first qualifying fill (`index_date`) after 365 days of continuous A/B/D enrollment and a drug-free washout; exclude members with any MA-only span in the lookback.
(2) Build episodes: sort fills by `person_id`, `fill_date`; stitch with carry-over (start of fill `i` = `max(fill_date_i, covered_until_{i-1})`, `stop = start + days_supply`, daily dose from NDC strength × quantity / days_supply); cap stockpiled supply at 30 days; allow a 14-day grace before an episode is closed as discontinued. (3) Long format: split each person's follow-up at every episode boundary, every outcome/censoring date, and at the first day of each calendar month, emitting `(person_id, tstart, tstop, current_use, cum_days, cum_dose_mg)`.
(4) Lag for latency/protopathic protection: compute `cum_dose_mg` and `current_use` as of `tstart` and lagged 30 days — exposure on the AKI day itself is excluded so a prodromal creatinine bump that triggers a refill cannot create false dose-response. (5) Censor: at disenrollment, death, end of data, and — for an as-treated contrast — at the end of the last episode + grace; bridge any inpatient stay by design and flag it.
(6) Model: `coxph(Surv(tstart, tstop, aki) ~ cum_dose_100mg + current_use + baseline_covariates)` (or weighted pooled logistic), with the cumulative-dose coefficient reported per 100 mg; if creatinine both responds to the drug and drives subsequent dosing, escalate to an IPTW marginal structural model rather than adjusting for time-varying creatinine in the Cox model.
Decision diagram
flowchart TD
Q{What biologic quantity<br/>drives the outcome?} -->|Active pharmacology<br/>while on drug| CU[Current / recent use<br/>time-varying indicator]
Q -->|Risk accrues with<br/>total burden| CD[Cumulative duration / dose<br/>lagged running sum]
Q -->|Timing of past doses<br/>matters / latency| WCE[Weighted cumulative exposure<br/>spline weight function]
CU --> FB{Time-varying confounders<br/>affected by prior exposure?}
CD --> FB
WCE --> FB
FB -->|No| TDC[Time-dependent Cox /<br/>pooled logistic on long format]
FB -->|Yes| MSM[Marginal structural model<br/>IPTW]timeline title One patient, day 90 outcome - the chosen metric changes the exposure value Day 0 : Fill 30 days supply, low dose : current_use = 1 Day 25 : Refill (stockpiling begins) : cum_days rising Day 55 : Supply exhausted, no refill : current_use - 0 Day 80 : High-dose 14-day course starts : current_use = 1 again Day 90 : Outcome assessed : current=yes, recent=yes, cum_dose high, WCE weights recent dose heavily
Worked example
Scenario
A researcher is studying whether long-term methotrexate use raises the risk of liver injury. She is following one patient, Maria (ID 2001), who started methotrexate on January 10, 2024. She has three pharmacy fills over about 100 days. Rather than label Maria as simply ever-exposed, the researcher builds a person-time table that records, for each interval of follow-up, whether Maria had an active supply on hand (current use = 1 or 0) and exactly how many total milligrams she had accumulated up to the start of that interval. This lets the survival model ask: does the liver-injury rate go up as cumulative dose grows?
Dataset
Maria's three pharmacy fills as they appear in a claims pharmacy table. Each row is one dispensed prescription.
| person_id | fill_date | drug | days_supply | daily_dose_mg |
|---|---|---|---|---|
| 2001 | 2024-01-10 | methotrexate | 30 | 10 |
| 2001 | 2024-02-15 | methotrexate | 30 | 10 |
| 2001 | 2024-03-20 | methotrexate | 30 | 10 |
Steps
Result
At the start of the third fill interval (March 20), Maria's cumulative dose is 600 mg (Fill 1: 300 mg + Fill 2: 300 mg). Her current_use flag on March 20 is 1 (active supply). During the two gap intervals (Feb 9-14, Mar 16-19) current_use = 0, but cumulative dose continues to hold its running value of 300 mg and 600 mg respectively because dose already taken does not disappear.
Trade-offs
Runnable example
Build time-updated exposure intervals from pharmacy claims. Required input (cleaned, de-duplicated): fills : person_id, fill_date (datetime64), days_supply (int), daily_dose (mg/day, from NDC strength) Returns one row per gap-handled exposure episode with carry-over (stockpiling) and per-episode cumulative dose.
import pandas as pd
STOCKPILE_CAP_DAYS = 30 # max carry-over of unused supply
GRACE_DAYS = 14 # gap tolerated before an episode is closed as discontinued
def build_exposure_episodes(fills: pd.DataFrame) -> pd.DataFrame:
fills = fills.sort_values(["person_id", "fill_date"])
rows = []
for pid, g in fills.groupby("person_id", sort=False):
covered_until = None # running end of stockpiled supply
cum_dose = 0.0 # mg accrued so far for this person
for r in g.itertuples():
if covered_until is None or r.fill_date > covered_until + pd.Timedelta(days=GRACE_DAYS):
start = r.fill_date # new episode
else:
carry = min((covered_until - r.fill_date).days, STOCKPILE_CAP_DAYS)
start = r.fill_date + pd.Timedelta(days=max(carry, 0)) # cap stockpiling
stop = start + pd.Timedelta(days=int(r.days_supply))
episode_dose = int(r.days_supply) * float(r.daily_dose)
rows.append({
"person_id": pid, "tstart": start, "tstop": stop,
"daily_dose": r.daily_dose, "current_use": 1,
"cum_dose_start": cum_dose, # cum dose BEFORE this episode (lag-ready)
"cum_dose_end": cum_dose + episode_dose,
})
cum_dose += episode_dose
covered_until = stop
return pd.DataFrame(rows)Same exposure-episode construction in data.table, then a join to outcome/censoring intervals producing the (tstart, tstop, event) long format consumed by a time-dependent Cox model. Inputs: fills : person_id, fill_date (Date), days_supply (int), daily_dose (numeric) followup : person_id, fu_start (Date), fu_end...
library(data.table)
library(survival)
STOCKPILE_CAP <- 30L; GRACE <- 14L
build_episodes <- function(fills) {
setDT(fills); setorder(fills, person_id, fill_date)
fills[, {
covered_until <- as.Date(NA); cum <- 0; out <- list()
for (i in seq_len(.N)) {
fd <- fill_date[i]; ds <- days_supply[i]; dd <- daily_dose[i]
if (is.na(covered_until) || fd > covered_until + GRACE) {
start <- fd
} else {
carry <- min(as.integer(covered_until - fd), STOCKPILE_CAP)
start <- fd + max(carry, 0L)
}
stop <- start + ds
out[[i]] <- data.table(tstart = start, tstop = stop, daily_dose = dd,
current_use = 1L, cum_dose_start = cum)
cum <- cum + ds * dd; covered_until <- stop
}
rbindlist(out)
}, by = person_id]
}
epi <- build_episodes(fills)
# Restrict episodes to each person's follow-up and attach the event flag at the closing interval.
long <- epi[followup, on = .(person_id), nomatch = 0L][
tstart < fu_end & tstop > fu_start]
long[, `:=`(tstart = pmax(tstart, fu_start), tstop = pmin(tstop, fu_end))]
long[, event := as.integer(!is.na(event_date) & event_date > tstart & event_date <= tstop)]
coxph(Surv(as.numeric(tstart), as.numeric(tstop), event) ~
I(cum_dose_start / 100) + current_use, data = long) # HR per 100 mg cumulative doseRETAIN/BY-group construction of gap-handled exposure episodes with stockpiling cap, then a PROC SQL split against follow-up to emit the long-format (person_id, tstart, tstop, current_use, cum_dose_start, event) consumed by PROC PHREG with a counting-process (tstart, tstop) specification.
%let cap = 30; /* stockpiling carry-over cap (days) */
%let grace = 14; /* gap tolerated before episode closes */
proc sort data=work.fills; by person_id fill_date; run;
data episodes;
set work.fills; by person_id;
retain covered_until cum_dose;
if first.person_id then do; covered_until = .; cum_dose = 0; end;
if covered_until = . or fill_date > covered_until + &grace then
tstart = fill_date; /* new episode */
else do;
carry = min(covered_until - fill_date, &cap);
tstart = fill_date + max(carry, 0); /* cap stockpiling */
end;
tstop = tstart + days_supply;
current_use = 1;
cum_dose_start = cum_dose; /* dose BEFORE this episode (lagged) */
output;
cum_dose = cum_dose + days_supply * daily_dose;
covered_until = tstop;
format tstart tstop date9.;
run;
/* Clip episodes to follow-up and flag the event in its closing interval. */
proc sql;
create table long as
select e.person_id,
max(e.tstart, f.fu_start) as tstart format=date9.,
min(e.tstop, f.fu_end) as tstop format=date9.,
e.current_use, e.cum_dose_start,
(f.event_date is not null and
f.event_date > max(e.tstart, f.fu_start) and
f.event_date <= min(e.tstop, f.fu_end)) as event
from episodes e inner join work.followup f
on e.person_id = f.person_id
where e.tstart < f.fu_end and e.tstop > f.fu_start;
quit;
/* Time-dependent Cox via counting-process intervals; HR per 100 mg cumulative dose. */
proc phreg data=long;
model (tstart, tstop) * event(0) = cum_dose100 current_use;
cum_dose100 = cum_dose_start / 100;
run;Citations
- [1]Sylvestre MP, Abrahamowicz M. Flexible modeling of the cumulative effects of time-dependent exposures on the hazard. Statistics in Medicine. 2009;28(27):3437-3453.
- [2]Suissa S. Immortal time bias in observational studies of drug effects. Pharmacoepidemiology and Drug Safety. 2007;16(3):241-249.
- [3]Abrahamowicz M, Beauchamp ME, Sylvestre MP. Comparison of alternative models for linking drug exposure with adverse effects. Statistics in Medicine. 2012;31(11-12):1014-1030.
- [4]Cole SR, Hernán MA. Constructing inverse probability weights for marginal structural models. American Journal of Epidemiology. 2008;168(6):656-664.
- [5]Kelly TL, Salter A, Pratt NL. The weighted cumulative exposure method and its application to pharmacoepidemiology: a narrative review. Pharmacoepidemiology and Drug Safety. 2024;33(1):e5701.