← Methods repository
concept

Treatment Patterns and Lines of Therapy (LOT)

An algorithmic exposure-construction method that converts a longitudinal sequence of drug fills or administrations in claims/EHR data into discrete, ordered lines of therapy (LOT1, LOT2, ...) and characterizes initiation, persistence, switching, augmentation/add-on, and advancement to the next line over time.

Exposure_Definitiontreatment-patternslines-of-therapylotsequencingswitchaugmentationdiscontinuationclaims
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

Lines of therapy (LOT) describe the ordered sequence of drug regimens a patient moves through over time, like chapters in their treatment story. An algorithm reads a patient's prescription fills in claims data, decides when one regimen ends and the next begins — based on a patient stopping one drug and starting another (a switch) or going without any fills for too long (a gap) — and labels each chapter LOT1, LOT2, and so on. The result tells researchers how many patients ever reach a second or third treatment, what drugs they moved to, and how long each treatment chapter lasted. It cannot see drugs given in a doctor's office that are billed separately from the pharmacy, so some infused cancer drugs may be invisible to a pharmacy-only analysis.

Treatment patterns and lines of therapy (LOT)

are an exposure-definition construct, not an estimator: the deliverable is a derived, analysis-ready exposure variable (a per-patient ordered set of regimens with start/stop dates and a `lot_number`) built deterministically from temporal fill/administration sequences. Downstream comparative analyses (survival, HCRU, cost) then treat that variable as the exposure. Because the variable is constructed by an algorithm, its validity is a property of the rules — gap length, minimum claims per line, add-on vs substitution logic, and progression triggers — and those rules must be pre-specified and, for regulatory or HTA use, validated against medical chart review with reported agreement statistics (e.g., kappa, line-count and regimen concordance). LOT algorithms should be developed or reviewed with practicing clinicians familiar with the disease and its guidelines; in oncology the FLAURA vs CheckMate-style line conventions, maintenance-therapy handling, and combination-regimen windows are not derivable from fills alone.

Core conceptual distinction

— three things must be separated and pre-specified. (1) A line vs an episode of a single drug: a line is a regimen (one or more drugs started together within a short combination window, e.g., 28 days) carried forward until it ends. (2) What ends a line and starts the next one: the canonical events are a switch (a new agent not in the current regimen, with the prior agent stopped — substitution), an augmentation/add-on (a new agent added while the prior agent continues — this does NOT advance the line under most oncology conventions but DOES under some chronic-disease conventions, so it must be declared), and a gap-then-restart (the regimen lapses beyond a permissible gap and a later fill begins a new line). (3) The estimand the LOT feeds: time-to-next-treatment-or-death (TTNTD) and time-to-discontinuation are duration estimands defined within a line; line-of-therapy distribution and attrition (the share reaching LOT2, LOT3) are sequencing estimands defined across lines. The same fill data yield different numbers under a cause-specific hazard for "advance to next line" (treating death as a censoring event) versus a Fine–Gray subdistribution for the cumulative incidence of advancement (treating death as a competing event) — in older oncology cohorts where death is common, this choice materially changes the reported share advancing and must be stated in the estimand, not chosen post hoc.

Pros, cons, and trade-offs

(specific and comparative). - vs persistence / time-to-discontinuation (single-drug): LOT captures sequencing and advancement — what the patient moves to after the index regimen fails, progresses, or causes toxicity — which persistence alone cannot describe. Persistence is one ingredient (it defines when a line lapses), but a persistence analysis answers "how long on drug A," whereas LOT answers "A then B then C." Prefer LOT in oncology, rheumatology, MS, and any progressive/multi-regimen disease. Cost: LOT rules are disease- and algorithm-specific, far less standardized than a simple permissible-gap persistence rule, and chart validation is resource-intensive. - vs a cascade-of-care / funnel analysis: the cascade is a population funnel from diagnosis through linkage, treatment, and control; LOT is the post-initiation sequencing engine inside the treated arm of that funnel. They are complementary, not substitutes. Cost: LOT says nothing about the undiagnosed/untreated upstream losses. - vs treating each NDC fill as the exposure (no line construction): raw fills overcount "treatments" — sample fills, bridging, mail-order stockpiling, and dose splits all look like distinct events — and cannot express regimens or advancement. LOT collapses these correctly but at the price of analyst-defined windows that, if mis-set, manufacture or erase lines. Prefer raw fills only for pure utilization counting where sequencing is irrelevant.

When to use

— describing real-world treatment sequencing in chronic or progressive disease; sizing the population reaching each later line (a direct input to budget-impact and Markov/DES transition probabilities); defining a line-specific index date for a downstream comparative study (e.g., 2L comparative effectiveness); and quantifying switch, add-on, and discontinuation rates for HTA dossiers. A defensible LOT requires a clinically grounded combination window, permissible gap, minimum-claims rule, and explicit maintenance and progression handling, all pre-registered.

When NOT to use — and when it is actively misleading or dangerous

- No clinical anchor for "line" exists for the disease. In conditions treated with continuous single-agent therapy and no orderly sequencing (most uncomplicated hypertension), forcing LOT manufactures structure that does not exist; use persistence/switching instead. - The data cannot see the regimen. Provider-administered oncolytics billed under the medical benefit (J-codes, HCPCS), inpatient chemotherapy bundled into a DRG, and 340B/buy-and-bill arrangements are frequently invisible or incompletely coded in pharmacy-only datasets — an algorithm run on pharmacy fills alone will silently drop entire lines and over-report watch-and-wait. Require medical-claim drug capture before claiming LOT in oncology. - Immortal time and procedure-anchored lines. If a line's start is keyed to a procedure (e.g., surgery, transplant) and follow-up is measured from an earlier landmark (diagnosis), the interval in which the patient must survive to receive that line is immortal — advancement rates and within-line survival are inflated. Anchor each line's time zero to its own first fill/administration. - Differential competing risks by exposure. In elderly claims cohorts, sicker first-line regimens are followed by higher early mortality; a cause-specific "advance to next line" analysis that censors those deaths overstates the share advancing in the sicker arm relative to a Fine–Gray subdistribution view. Pre-specify the competing-risk handling. - Maintenance miscounted as a new line. In ovarian cancer and lymphoma, PARP inhibitors or rituximab maintenance started after active therapy are part of the same line under modern conventions; coding them as LOT2 inflates line counts (the Simmons et al. validation found first-line maintenance regimen-match required explicit maintenance rules to reach agreement).

Data-source operational depth

- Claims (FFS or commercial): Pharmacy fills give NDC + `fill_date` + `days_supply`; provider-administered drugs are in medical claims as HCPCS/J-codes with a service date but no days_supply, so durations must be imputed from cycle schedules. Require continuous medical AND pharmacy enrollment across the baseline and follow-up so that "no further fill" is true discontinuation, not unobserved care. Failure mode: Medicare Advantage encounter data lack the complete fee-for-service claim stream — MA-only person-time produces phantom gaps and missing lines; restrict to enrollees with Parts A/B/D (or a complete commercial medical+pharmacy benefit) and exclude MA-only spans. Failure mode: sample fills, 90-day mail order, and stockpiling distort `days_supply`, shifting gap-defined line boundaries. Failure mode: differential competing risks by exposure in elderly claims bias advancement estimates (see above). - EHR: Orders and medication-administration records (MAR) capture provider-administered oncolytics that pharmacy claims miss, and problem lists/labs/staging sharpen progression triggers; but visit-driven capture means a patient who receives a line outside the system is differentially lost, and an unobserved out-of-network line looks like discontinuation. Prefer EHR linked to claims to reassemble the full regimen history. - Registry: Often records protocol-defined lines and adjudicated progression prospectively — the gold standard for validating a claims LOT algorithm — but typically incomplete for the full longitudinal pharmacy stream and for out-of-registry care. - Linked claims–EHR–registry: The ideal substrate (medical-benefit drug capture + staging/progression + complete enrollment), but linkage selects the linkable subset and introduces order/fill/service date discrepancies that must be reconciled before assigning line start dates.

Worked claims example

Question: real-world LOT distribution and time-to-LOT2 in metastatic non–small-cell lung cancer in a commercial + Medicare FFS database with medical-benefit drug capture. (1) Cohort: adults with ≥2 mNSCLC diagnoses, 365 days of continuous A/B/D (or commercial medical+pharmacy) enrollment before the first antineoplastic, and exclude MA-only person-time. (2) Antineoplastic events: union of pharmacy NDC fills and medical-claim HCPCS/J-code administrations for the curated mNSCLC drug list, each with a `service_date` and (for fills) `days_supply`. (3) LOT1 start: the first antineoplastic `service_date` after the metastatic-diagnosis washout. (4) Regimen window: all distinct agents within 28 days of LOT1 start form the LOT1 regimen (combination capture). (5) Line advancement: LOT2 begins at the first event of an agent not in the LOT1 regimen accompanied by stopping ≥1 LOT1 agent (substitution), OR the first antineoplastic after a permissible gap of >90 days following the LOT1 regimen's last `days_supply` end (restart); an added agent that continues alongside the full LOT1 regimen is logged as augmentation and does NOT advance the line. (6) Maintenance rule: a single-agent continuation (e.g., pemetrexed/immunotherapy maintenance) after a defined induction is held within LOT1, not counted as LOT2. (7) Estimand: cumulative incidence of reaching LOT2 with death as a competing event (Fine–Gray), reported alongside the cause-specific advancement hazard; time-to-LOT2 measured from LOT1 start. (8) Sensitivity: vary the combination window (14/28/42 days), permissible gap (60/90/120 days), and the medical-benefit drug list; report chart-validation agreement (line count, first-line regimen match) before the algorithm is used for decisions.

Worked example

Scenario

Patient 7042 has metastatic non-small-cell lung cancer (mNSCLC). Their oncologist starts them on erlotinib, an oral targeted therapy. The patient fills erlotinib three times between February and late March 2023, then goes completely off treatment for 95 days. In late July they start docetaxel, a chemotherapy drug. We want to know: how many lines of therapy did this patient have, what was in each line, and how many days passed from the start of line 1 to the start of line 2?

Dataset

Pharmacy claims table — one row per fill, exactly as an analyst sees it.

person_idfill_datedrugdays_supply
70422023-02-01erlotinib30
70422023-03-01erlotinib30
70422023-03-28erlotinib30
70422023-07-31docetaxel21

Steps

  • LOT1 starts on 2023-02-01, the date of the first fill. The regimen is erlotinib (a single drug).

  • Fill A covers Feb 1 through Mar 2 (30 days). Fill B starts Mar 1 — a 1-day overlap, which is fine; the union rule extends coverage through Mar 30. Fill C starts Mar 28 — another overlap — and extends coverage through Apr 26. LOT1 supply therefore runs out on Apr 26.

  • After Apr 26, no new erlotinib fill appears. The gap between the end of LOT1 supply (Apr 26) and the next fill (Jul 31) is Apr 27 through Jul 30 = 95 days.

  • The permissible gap threshold is 90 days. Because 95 days > 90 days, the algorithm declares that LOT1 has lapsed.

  • On Jul 31 the patient fills docetaxel — a drug that was NOT in the LOT1 regimen. A new drug arriving after a lapse triggers a new line: LOT2 starts on 2023-07-31 with regimen docetaxel.

  • Time-to-LOT2 is measured from LOT1 start (Feb 1) to LOT2 start (Jul 31) = 180 days.

Result

Patient 7042 had 2 lines of therapy. LOT1 regimen = erlotinib, started 2023-02-01, ended 2023-04-26. LOT2 regimen = docetaxel, started 2023-07-31. Advancement reason = gap-then-restart with a new agent (95-day gap exceeded the 90-day permissible threshold). Time-to-LOT2 = 180 days.

Timeline Spec

Title

Lines of therapy for one mNSCLC patient — gap-restart advancing from LOT1 to LOT2

Window
Start

2023-02-01

End

2023-08-20

Label

Observation window: first fill through end of LOT2 supply

Events
  • Label

    Fill A (erlotinib)

    Start

    2023-02-01

    Length Days

    30

    Quantity

    30 days_supply

  • Label

    Fill B (erlotinib)

    Start

    2023-03-01

    Length Days

    30

    Quantity

    30 days_supply

  • Label

    Fill C (erlotinib)

    Start

    2023-03-28

    Length Days

    30

    Quantity

    30 days_supply

  • Label

    Fill D (docetaxel)

    Start

    2023-07-31

    Length Days

    21

    Quantity

    21 days_supply

Spans
  • Kind

    exposed

    Start

    2023-02-01

    End

    2023-04-26

    Label

    LOT1 regimen: erlotinib (supply Feb 1 - Apr 26)

  • Kind

    gap

    Start

    2023-04-27

    End

    2023-07-30

    Label

    Gap: 95 days (exceeds 90-day threshold — LOT1 lapses)

  • Kind

    exposed

    Start

    2023-07-31

    End

    2023-08-20

    Label

    LOT2 regimen: docetaxel (supply Jul 31 - Aug 20)

Result
Label

2 lines of therapy; time-to-LOT2 = 180 days (Feb 1 to Jul 31); advancement = gap-restart + new agent

Value

180

Caption

Each colored bar is one pharmacy fill drawn to scale by days_supply. The LOT1 span (blue) covers the union of the three erlotinib fills. The 95-day gap (red) exceeds the 90-day permissible threshold, ending LOT1. LOT2 (blue) begins when docetaxel — a drug not in LOT1 — is filled after the lapse.

Alt Text

Horizontal timeline from February 2023 to August 2023. Three overlapping blue bars labeled erlotinib fills span February through late April, forming LOT1. A red gap bar spans late April through late July labeled 95-day gap. A fourth blue bar labeled docetaxel starts July 31, forming LOT2.

Runnable example

python implementation

Pharmacy + medical-benefit LOT construction from claims-style inputs. Required input (already cleaned, de-duplicated, restricted to the curated antineoplastic code list, and filtered to continuously enrolled non-MA-only person-time): tx : one row per...

import pandas as pd

COMBO_WINDOW = pd.Timedelta(days=28)   # agents starting within this of a line start = same regimen
GAP_DAYS     = 90                       # permissible gap; a fill after a longer lapse starts a new line
DEFAULT_DOS  = 30                       # fallback days_supply for administered drugs with no duration

def build_lot(tx: pd.DataFrame) -> pd.DataFrame:
    tx = tx.sort_values(["person_id", "service_date"]).copy()
    tx["days_supply"] = tx["days_supply"].fillna(DEFAULT_DOS).astype(int)
    tx["supply_end"]  = tx["service_date"] + pd.to_timedelta(tx["days_supply"], unit="D")

    lines = []
    for pid, g in tx.groupby("person_id", sort=False):
        g = g.reset_index(drop=True)
        lot = 1
        line_start = g.loc[0, "service_date"]
        regimen = set()                       # agents in the current line's regimen
        line_supply_end = line_start          # latest supply coverage of regimen agents
        reason = "initiation"

        def flush(end):
            lines.append({"person_id": pid, "lot_number": lot,
                          "regimen": "+".join(sorted(regimen)),
                          "line_start": line_start, "line_end": end,
                          "advance_reason": reason})

        for _, row in g.iterrows():
            d, drug, send = row["service_date"], row["drug"], row["supply_end"]
            if (d - line_start) <= COMBO_WINDOW:          # still assembling the regimen
                regimen.add(drug); line_supply_end = max(line_supply_end, send); continue
            gap = (d - line_supply_end).days
            substitution = drug not in regimen           # new agent not in current regimen
            if substitution or gap > GAP_DAYS:           # advance to the next line
                flush(line_supply_end)
                lot += 1
                line_start, regimen = d, {drug}
                line_supply_end = send
                reason = "switch/substitution" if (substitution and gap <= GAP_DAYS) else "gap_restart"
            else:                                         # continuation or augmentation (same line)
                regimen.add(drug); line_supply_end = max(line_supply_end, send)
        flush(line_supply_end)
    return pd.DataFrame(lines).sort_values(["person_id", "lot_number"])
r implementation

Pharmacy + medical-benefit LOT construction with data.table, mirroring the Python logic and parameters. Required input: tx : data.table -> person_id, drug (character), service_date (Date), days_supply (integer; impute administered-drug durations upstream,...

library(data.table)

COMBO_WINDOW <- 28L   # days: agents starting within this of a line start = same regimen
GAP_DAYS     <- 90L   # permissible gap before a later fill starts a new line
DEFAULT_DOS  <- 30L   # fallback days_supply for administered drugs

build_lot <- function(tx) {
  setDT(tx)
  tx[is.na(days_supply), days_supply := DEFAULT_DOS]
  tx[, supply_end := service_date + days_supply]
  setorder(tx, person_id, service_date)

  one_person <- function(g) {
    lot <- 1L; line_start <- g$service_date[1L]
    regimen <- character(0); line_supply_end <- line_start; reason <- "initiation"
    out <- list()
    flush <- function(end) list(lot_number = lot,
                                regimen = paste(sort(unique(regimen)), collapse = "+"),
                                line_start = line_start, line_end = end, advance_reason = reason)
    for (i in seq_len(nrow(g))) {
      d <- g$service_date[i]; drug <- g$drug[i]; send <- g$supply_end[i]
      if (as.integer(d - line_start) <= COMBO_WINDOW) {            # assembling the regimen
        regimen <- union(regimen, drug); line_supply_end <- max(line_supply_end, send); next
      }
      gap <- as.integer(d - line_supply_end)
      substitution <- !(drug %in% regimen)
      if (substitution || gap > GAP_DAYS) {                        # advance to next line
        out[[length(out) + 1L]] <- flush(line_supply_end)
        lot <- lot + 1L; line_start <- d; regimen <- drug
        line_supply_end <- send
        reason <- if (substitution && gap <= GAP_DAYS) "switch/substitution" else "gap_restart"
      } else {                                                     # continuation / augmentation (same line)
        regimen <- union(regimen, drug); line_supply_end <- max(line_supply_end, send)
      }
    }
    out[[length(out) + 1L]] <- flush(line_supply_end)
    rbindlist(out)
  }
  tx[, one_person(.SD), by = person_id]
}