← Methods repository
CONCEPTINTERMEDIATEPYTHON · R · SAS4 citations

Diagnosis Phenotype Algorithm (1 IP / 2 OP, Time Window)

A claims-based case-finding rule that classifies a patient as having a condition if they have at least one inpatient claim, or two outpatient claims on different service dates within a defined time window, carrying the relevant diagnosis code, whose performance (PPV, sensitivity, specificity) is condition-, position-, and data-source-specific and must be validated.

Outcome Measurephenotypediagnosis-algorithmclaims-phenotyping1ip-2opppv-validationoutcome-algorithmmisclassificationdata-quality
On this page
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

A diagnosis phenotype algorithm is a rule that combs through billing records to decide whether a patient actually has a given condition, rather than just having a code that was entered to rule something out. The '1 inpatient OR 2 outpatient' version says a patient counts as a case if they were admitted to the hospital with the relevant billing code at least once, OR if a doctor's office or outpatient clinic billed that code on at least two separate visits that are spread far enough apart in time.

The gap between the two outpatient visits and the overall time window both matter — without them, a single 'could this be condition X?' code from one afternoon visit would create a false case. Every such rule carries error: it will flag some patients who do not truly have the condition (false positives) and miss others who do (false negatives), so analysts always report how accurate the rule was when tested against actual medical records.

When to use it
Almost any outcome, covariate, or cohort-entry diagnosis whose accuracy materially affects the effect estimate.
When outpatient-managed disease must be captured and missed cases would bias the estimate, no reliable confirmatory data element exists, and the study needs to harmonize across databases without procedure/lab/drug...
Large comparative studies where claims are the feasible source; use linked EHR/registry as the validation gold standard, not the primary ascertainment.
Watch out for
Misses the small number of true cases with only one outpatient code who die or disenroll before a second visit; more implementation logic (windowing, deduplication, washout).
Lower PPV than a confirmation-augmented rule; more vulnerable to rule-out codes in the OP arm.
Lower clinical specificity; misses out-of-network and uncoded events; payer coding differences add heterogeneity.

The 1 inpatient or 2 outpatient (1 IP / 2 OP) diagnosis phenotype is the workhorse case-finding rule of administrative-claims research. Because claims carry billing codes (ICD-9/10-CM) rather than clinical findings, a patient is classified as a case when they accumulate enough coded evidence to make a billing artifact an unlikely explanation: a single inpatient claim with the diagnosis (one institutional encounter that was adjudicated and paid), OR two outpatient claims with the diagnosis on different service dates within a pre-specified window.

The single-IP arm leans on the higher specificity of an admission diagnosis; the two-OP arm requires temporal corroboration so that a one-off "rule-out" or screening code does not by itself create a case. The window length encodes the disease's tempo: 7-30 days for acute events (MI, ischemic stroke, PE), 180-365 days for chronic conditions (atrial fibrillation, diabetes, COPD).

For incident (new-onset) phenotypes the rule is paired with a lookback washout (e.g., no qualifying code in the prior 365 days of continuous enrollment); without it the algorithm captures prevalent, not incident, disease.

Core conceptual distinction

. The rule is a measurement instrument with error, not a diagnosis. Three knobs trade positive predictive value (PPV) against sensitivity. (1) Setting / arm structure — 1 IP OR 2 OP balances the specificity of admissions against the sensitivity of outpatient capture; an IP-only rule raises PPV but misses outpatient-managed disease. (2) Code position — primary (principal) position reflects the reason for the encounter and is more specific; any-position captures comorbidity coding but is noisier and more vulnerable to copy-forward and rule-out codes.

(3) Window and washout — the OP-to-OP window and the incident washout jointly determine whether you measure new onset, and how much delayed-second-visit loss you accept. The two failure directions are not symmetric: false positives (rule-out/screening codes coded as confirmed disease) dilute toward the null in a comparative study, while differential misclassification by exposure (e.g., new initiators are seen more often and coded more completely) biases in an unpredictable direction.

Never treat a code as a clinical diagnosis without validating the algorithm in your own data source and population, and pre-specify position, window, and washout in the protocol before looking at outcomes.

Pros, cons, and trade-offs

.

  • vs a broad single-code rule (1 claim, any position, no window): the 1 IP / 2 OP rule sharply cuts false positives from isolated rule-out and screening codes and is widely validated to PPV >80% for many conditions. Cost: it loses the few true cases who have only one outpatient code and die or disenroll before a second visit; it is more code to build. Prefer 1 IP / 2 OP for almost any outcome or covariate that drives an effect estimate.
  • vs a high-specificity rule (IP-only, primary position, plus procedure/lab/drug confirmation): the standard rule has higher sensitivity and is easier to harmonize across databases. Cost: lower PPV than a confirmation-augmented rule. Prefer the high-specificity variant when a false positive is costly (e.g., a serious safety outcome that triggers a label change) and you can tolerate missing milder, outpatient-managed cases.
  • vs EHR / registry phenotypes (problem lists, NLP on notes, registry adjudication): claims rules scale to millions of patients and decades of history and are consistent across payers if harmonized. Cost: lower clinical specificity than a chart-adjudicated or NLP-confirmed phenotype; they miss out-of-network and uncoded events. Prefer claims for large comparative studies, and use linked EHR/registry as the validation gold standard rather than the primary ascertainment source.

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

.

  • No validation is feasible and PPV is plausibly low or differential. If the condition is heavily coded as "rule-out" (chest pain coded as MI before troponin returns; "screening for malignancy" before biopsy) and you cannot estimate PPV, an unvalidated rule can manufacture or erase an effect — quantitative bias analysis or a linked validation subset is mandatory before the estimate is trusted.
  • The window or washout is misaligned with the disease tempo. A 30-day chronic-disease window or a too-short incident washout converts prevalent cases into "incident" ones, importing immortal time and survivor effects into the analysis.
  • MA-only person-time is treated as observed. In Medicare Advantage, fee-for-service institutional and carrier claims are largely absent in legacy claims products; "no prior code" in MA-only spans is missingness, not a clean washout, and silently fabricates incident cases. Restrict the washout to FFS-observable (Parts A/B) enrollment.
  • The algorithm differs across arms. Applying a different position/window/setting rule, or a different data source, to exposed vs comparator patients guarantees differential misclassification — the single most dangerous error here.

Data-source operational depth

.

  • Claims (Medicare FFS / commercial): build from institutional/facility claims (use the discharge date for the IP arm) plus carrier/professional and outpatient-facility claims (use the service date for the OP arm). Deduplicate same-day OP claims so two line items on one visit count as one. Require continuous medical enrollment across the entire washout so absence of a prior code is observed, not unobserved. Failure modes: rule-out/screening codes inflate OP false positives (mitigate with primary position or a confirmatory procedure/drug); bundled or interim claims and later adjustments perturb counts (prefer final paid claims); MA-only person-time lacks FFS claims so washout and OP counting break (exclude or flag MA-only spans); differential competing risks — in elderly claims, patients who die before a second OP visit are missed for the 2-OP arm, and if death rates differ by exposure this is differential.
  • EHR: the IP/OP distinction maps to encounter type; the "second OP claim" can be a second encounter or one encounter plus an active problem-list entry, and NLP on notes can confirm a coded diagnosis. Strength: clinical detail to adjudicate. Weakness: visit-driven capture — sicker, more-engaged patients are coded more completely, and care that leaks out of the system is invisible; link to claims for a complete encounter history.
  • Registry: disease registries (SEER for cancer, stroke/MI registries) are the validation gold standard for PPV and for the cases the claims rule misses, and registry death data anchor competing-risk and censoring logic. Weakness: incomplete pharmacy/encounter timing — link to claims to apply the 1 IP / 2 OP rule and to recover timing.
  • Linked claims-EHR / claims-registry: the ideal substrate (scale + adjudication), but probabilistic linkage error can be differential by severity (sicker patients link more reliably), biasing sensitivity estimates; quantify linkage error and reconcile service vs discharge vs registry diagnosis dates before assigning the index date.

Worked claims example (incident atrial fibrillation)

Question: incident AF ascertainment in a Medicare FFS + commercial database, ICD-10 I48.x (atrial fibrillation/flutter). Algorithm: 1 IP discharge diagnosis (any position) OR 2 OP claims on service dates ≥7 days apart within a 365-day window, with a 365-day incident washout (no I48.x in the prior year) and continuous Part A/B (or commercial medical) enrollment across that washout; same rule applied identically to every study arm. Suppose 5,000 patients have at least one I48.x code.

Applying the rule: 1,200 qualify on a single inpatient discharge code; of the remainder, 2,800 have ≥2 OP codes ≥7 days apart within 365 days (the other outpatient-only patients have a single isolated code — many of these are "rule-out" or pre-test codes and are correctly not counted). That leaves 4,000 algorithm-positive patients. Applying the 365-day incident washout drops 1,000 with a prior-year I48.x (prevalent), yielding 3,000 incident AF cases; index date = the discharge date (IP arm) or the first of the two qualifying OP dates (OP arm).

Production checks: exclude MA-only person-time before counting (otherwise "no prior code" is missingness and over-counts incident cases); flag that MA risk-adjustment HCC capture makes AF look more prevalent in MA than in FFS, so a multi-database pooled estimate must report algorithm performance by payer and sensitivity-test the window (30 vs 90 vs 365 days) and position (any vs primary).

Validate PPV in a chart-reviewed or registry-linked subset and report it with a 95% CI; payer-specific heterogeneity and differential ascertainment by exposure are the dominant threats to validity, not the rule itself.

Interpreting the output

. The output of the 1 IP / 2 OP algorithm is a classified patient list: each patient either meets the rule or does not, with an assigned index date. In the Medicare AF example, Patient 2201 qualifies via the 2-OP arm — two I48.x outpatient claims 42 days apart, both in primary position — with an index date at the first qualifying OP date. Patient 2202 qualifies via the 1-IP arm — one inpatient claim with I48.x as principal diagnosis — with index date at discharge.

After applying the 365-day incident washout, 3,000 patients remain as incident AF cases.

Formal interpretation: meeting the 1 IP / 2 OP rule means a patient accumulated billing evidence that makes a documentation artifact an unlikely sole explanation. It is not a clinical confirmation of atrial fibrillation. The rule's PPV — the fraction of flagged patients who truly had AF when charts were reviewed — must be reported alongside the result. Rule-out codes are the leading false-positive driver: an I48.x entered to justify a rhythm monitor that came back negative can satisfy the 1-IP arm on its own.

The 2-OP arm's 30-day separation requirement suppresses isolated rule-out codes; tightening or loosening that window is a planned sensitivity analysis, not a post-hoc fix.

Practical interpretation: report the algorithm's PPV and the window/position choices prominently in the methods, because those choices — not the underlying condition — largely determine the false-positive rate. For any regulatory or HTA submission, provide a chart-review validation substudy and a Wilson 95% confidence interval on the PPV estimate.

Decision diagram

flowchart TD
  Pop[Enrollees with continuous medical enrollment<br/>FFS-observable across washout] --> Wash[Incident washout:<br/>no target code in prior 365 days]
  Wash --> Arms{Case-finding rule}
  Arms -->|1 inpatient discharge claim<br/>with target code| IP[IP arm qualifies<br/>index = discharge date]
  Arms -->|2 outpatient claims, different<br/>service dates within window| OP[OP arm qualifies<br/>index = 2nd qualifying date]
  Arms -->|Single isolated OP code<br/>e.g. rule-out / screening| Excl[Not a case]
  IP --> Idx[Index date = earliest qualifying event]
  OP --> Idx
  Idx --> Valid[Validate PPV in chart-reviewed or<br/>registry-linked subset; report 95% CI]
  Idx --> Sens[Apply identical rule to all arms;<br/>sensitivity on window / position / payer]
Standard 1 IP / 2 OP incident diagnosis phenotype. The washout establishes incidence, the two-arm rule trades PPV against sensitivity, and performance must be validated and tested for differential misclassification by exposure or payer.
gantt
  title 1 IP / 2 OP incident phenotype timeline (one patient, OP arm)
  dateFormat YYYY-MM-DD
  axisFormat %b %Y
  section Washout
  Continuous FFS enrollment, no target code :done, w, 2023-01-01, 2023-12-31
  section Case-finding window
  First outpatient target code :milestone, op1, 2024-01-05, 0d
  Second outpatient code (>=7 days later, within 365d) :milestone, op2, 2024-02-10, 0d
  Index date = second qualifying OP date :crit, idx, 2024-02-10, 1d
Outpatient-arm timeline. The 365-day washout precedes time zero; the case is confirmed only when a second qualifying outpatient code appears at least seven days after the first, and the index date is that second date.

Worked example

Scenario

We are building an incident atrial fibrillation (AF) cohort from a Medicare claims database using ICD-10 code I48.x. The rule is: qualify as an AF case if you have (a) at least one inpatient discharge claim with I48.x, OR (b) at least two outpatient claims with I48.x on different service dates that are at least 7 days apart and both fall within a 365-day case-finding window. We look at two patients. Patient 2201 has only outpatient visits; we trace her two AF codes to see whether and when she qualifies. Patient 2202 was hospitalized with AF; his single inpatient discharge code qualifies him immediately.

Dataset

Raw claim rows an analyst would see after filtering to I48.x codes. claim_type: IP = inpatient discharge, OP = outpatient service. service_date for IP rows already carries the discharge date.

person_idclaim_typeservice_datedx_codedx_position
2201OP2024-01-08I48.11primary
2201OP2024-02-19I48.11primary
2202IP2024-03-05I48.19primary
FIG. 1 — DESIGN TIMELINE
Horizontal timeline for patient 2201 showing a 365-day case-finding window from 2024-01-08 to 2025-01-07. Two short bars mark outpatient claims: Claim 1 on January 8 and Claim 2 on February 19. A span labeled '42-day gap (≥7 days required)' stretches between them. Both claims fall inside a span labeled 'Case-finding window (≤365 days)'. A result marker at February 19 reads 'Index date — patient qualifies'.
Patient 2201 has two outpatient claims for atrial fibrillation. The gap between them is 42 days, which satisfies both the 7-day minimum and the 365-day window. The second claim date becomes her index date. A patient with only the first claim would not qualify.

Steps

1Patient 2201 has no inpatient claim, so we check the outpatient arm.
2Her two outpatient claims are on 2024-01-08 and 2024-02-19 — these are different service dates, so they are not the same visit.
3Gap between the two dates: from January 8 to February 19 is 42 days (23 remaining days in January plus 19 days into February).
442 days is ≥ 7 (the minimum gap required) and ≤ 365 (the window), so the pair qualifies.
5Patient 2201's index date is 2024-02-19 — the date of the second, confirming outpatient claim.
6Patient 2202 has one inpatient discharge claim on 2024-03-05; a single inpatient claim always qualifies under the IP arm.
7Patient 2202's index date is 2024-03-05 — the discharge date of that inpatient claim.
8If patient 2201 had only the January 8 claim and never returned, she would NOT be a case — one isolated outpatient code is not enough.

Result

Patient 2201: qualifies via the outpatient arm (2 OP claims, gap = 42 days, within 365-day window); index date = 2024-02-19. Patient 2202: qualifies via the inpatient arm (1 IP discharge claim); index date = 2024-03-05.

Trade-offs

vs. Broad single code rule (one claim, any position, no time window)
Pros of this
Sharply reduces false positives from isolated rule-out and screening codes; widely validated to PPV >80% for many conditions; temporal corroboration in the OP arm.
vs. High specificity rule (IP only, primary position, procedure/lab/drug confirmation)
Pros of this
Higher sensitivity and easier cross-database harmonization; captures outpatient-managed disease.
vs. EHR or registry phenotypes (problem lists, NLP, adjudication)
Pros of this
Scales to millions of patients and decades of history; consistent across payers if code lists are harmonized.

Runnable example

1 IP / 2 OP incident phenotype construction from claims-style inputs. Required inputs (already cleaned, de-duplicated to one row per claim line): dx : diagnosis claims -> person_id, claim_type in {'IP','OP'}, service_date (datetime;

requires: pandas · numpy
import pandas as pd
import numpy as np

TARGET_CODES = ("I48",)          # ICD-10 prefix match for atrial fibrillation/flutter (I48.x)
OP_GAP_DAYS  = 7                 # minimum days between the two qualifying outpatient claims
OP_WINDOW    = 365              # the two OP claims must fall within this many days of each other
WASHOUT_DAYS = 365              # incident clean period: no qualifying code in the prior year

def build_phenotype(dx: pd.DataFrame, enroll: pd.DataFrame,
                    any_position: bool = True) -> pd.DataFrame:
    d = dx.copy()
    d = d[d["dx_code"].str.startswith(TARGET_CODES)]
    if not any_position:
        d = d[d["dx_position"] == "primary"]
    d = d.sort_values(["person_id", "service_date"])

    # IP arm: a single inpatient claim qualifies; index = that discharge date.
    ip = d[d["claim_type"] == "IP"].groupby("person_id", as_index=False)["service_date"].min()
    ip = ip.rename(columns={"service_date": "ip_date"})

    # OP arm: ANY pair of DIFFERENT service dates OP_GAP_DAYS..OP_WINDOW apart (sliding pair, not
    # anchored to the first code) so qualifying pairs after day 365 are not missed.
    op = d[d["claim_type"] == "OP"][["person_id", "service_date"]].drop_duplicates()
    pairs = op.merge(op, on="person_id", suffixes=("_1", "_2"))
    gap = (pairs["service_date_2"] - pairs["service_date_1"]).dt.days
    qual = pairs[(gap >= OP_GAP_DAYS) & (gap <= OP_WINDOW)]  # service_date_2 is the confirming code
    op_idx = qual.groupby("person_id", as_index=False)["service_date_2"].min()  # earliest confirming date
    op_idx = op_idx.rename(columns={"service_date_2": "op_date"})

    cand = ip.merge(op_idx, on="person_id", how="outer")
    cand["index_date"] = cand[["ip_date", "op_date"]].min(axis=1)             # earliest qualifying event

    # Incident washout: drop anyone with a target code in [index - WASHOUT_DAYS, index).
    prior = cand.merge(d[["person_id", "service_date"]], on="person_id")
    in_wash = prior[(prior["service_date"] < prior["index_date"]) &
                    (prior["service_date"] >= prior["index_date"] - pd.Timedelta(days=WASHOUT_DAYS))]
    cand = cand[~cand["person_id"].isin(in_wash["person_id"])]

    # Continuous, FFS-observable enrollment across the washout through index (exclude MA-only spans).
    e = enroll.merge(cand[["person_id", "index_date"]], on="person_id")
    e["covers"] = ((e["enroll_start"] <= e["index_date"] - pd.Timedelta(days=WASHOUT_DAYS)) &
                   (e["enroll_end"]   >= e["index_date"]) & (~e["ma_only"]))
    eligible = e.loc[e["covers"], "person_id"].unique()

    out = cand[cand["person_id"].isin(eligible)].copy()
    return out[["person_id", "index_date"]].sort_values("person_id")

Citations

FOUNDATIONAL / METHODS
  1. [1]Djibo DA, Goldberg JF, McMahill-Walraven CN, et al. Validation of an ICD-10 case-finding algorithm for endometrial cancer in US insurance claims. Pharmacoepidemiology and Drug Safety. 2023.
  2. [2]Cutrona SL, Toh S, Iyer A, et al. Design for validation of acute myocardial infarction cases in Mini-Sentinel. Pharmacoepidemiology and Drug Safety. 2012;21(S1):274-281.
  3. [3]Quan H, Sundararajan V, Halfon P, et al. Coding algorithms for defining comorbidities in ICD-9-CM and ICD-10 administrative data. Medical Care. 2005;43(11):1130-1139.
APPLIED EXAMPLES
  1. [4]Kiyota Y, Schneeweiss S, Glynn RJ, Cannuscio CC, Avorn J, Solomon DH. Accuracy of Medicare claims-based diagnosis of acute myocardial infarction: estimating positive predictive value on the basis of review of hospital records. American Heart Journal. 2004;148(1):99-104.