[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-aws-labs-rwd-cohort-analysis":3,"mdc-8vnova-key":52,"related-org-aws-labs-rwd-cohort-analysis":2957,"related-repo-aws-labs-rwd-cohort-analysis":3135},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":29,"repoUrl":30,"updatedAt":31,"license":32,"forks":33,"topics":34,"repo":47,"sourceUrl":50,"mdContent":51},"rwd-cohort-analysis","analyze real-world healthcare data cohorts","Real-world data cohort analysis pipeline for claims and EHR data — cohort identification with ICD-10, NDC, and CPT codes, medication adherence metrics (PDC\u002FMPR), propensity score estimation and balance diagnostics, Kaplan-Meier survival curves, and Cox proportional hazards models. Use when the user mentions claims data, cohort identification, ICD-10 codes, NDC codes, CPT codes, medication adherence, PDC, MPR, propensity score, SMD balance, Kaplan-Meier, Cox model, Schoenfeld residuals, survival analysis on claims, or real-world evidence pipeline.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"aws-labs","AWS Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Faws-labs.png","awslabs",[13,17,20,23,26],{"name":14,"slug":15,"type":16},"Research","research","tag",{"name":18,"slug":19,"type":16},"Healthcare","healthcare",{"name":21,"slug":22,"type":16},"Data Analysis","data-analysis",{"name":24,"slug":25,"type":16},"Life Sciences","life-sciences",{"name":27,"slug":28,"type":16},"AWS","aws",4,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fhcls-agent-skills","2026-07-12T08:37:56.830247",null,0,[35,36,37,38,39,40,41,42,43,25,44,45,46],"agent-skills","agentcore","ai-agents","amazon-quick-desktop","claims-processing","drug-discovery","genomics","healthcare-ai","kiro","medical-imaging","risk-adjustment","strands-agents",{"repoUrl":30,"stars":29,"forks":33,"topics":48,"description":49},[35,36,37,38,39,40,41,42,43,25,44,45,46],"Agent skills for healthcare and life sciences: genomics, imaging, claims, drug discovery, and more. Works with Amazon Quick, Kiro, Amazon AgentCore, AWS Strands SDK, Claude Code, Codex, and any Agent Skills-compatible platform.","https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fhcls-agent-skills\u002Ftree\u002FHEAD\u002Fskills\u002Frwd-cohort-analysis","---\nname: rwd-cohort-analysis\ndescription: Real-world data cohort analysis pipeline for claims and EHR data — cohort identification with ICD-10, NDC, and CPT codes, medication adherence metrics (PDC\u002FMPR), propensity score estimation and balance diagnostics, Kaplan-Meier survival curves, and Cox proportional hazards models. Use when the user mentions claims data, cohort identification, ICD-10 codes, NDC codes, CPT codes, medication adherence, PDC, MPR, propensity score, SMD balance, Kaplan-Meier, Cox model, Schoenfeld residuals, survival analysis on claims, or real-world evidence pipeline.\nusage: Invoke when building cohort analysis pipelines from claims or EHR data with adherence metrics, propensity scores, and survival analysis.\nversion: 1.0.0\nvalidated_against:\n  date: 2025-01-15\n  packages: {lifelines: \"0.28\", statsmodels: \"0.14\", pandas: \"2.2\"}\ntags: [skill, category:pipeline, real-world-data, claims, survival-analysis, propensity-score, cohort, hcls]\n---\n\n# Real-World Data Cohort Analysis Pipeline\n\n## Overview\n\nThis skill encodes a reproducible pipeline for analyzing administrative claims\nor EHR data to estimate comparative treatment effects. It covers the full\nworkflow from raw claims tables to adjusted survival estimates:\n\n1. Cohort identification using diagnosis (ICD-10-CM), procedure (CPT\u002FHCPCS),\n   and drug (NDC) code lists\n2. Medication adherence measurement (PDC and MPR)\n3. Propensity score estimation and balance diagnostics\n4. Kaplan-Meier curves and Cox proportional hazards regression\n\nCode snippets use Python (pandas, scikit-learn, lifelines) and R\n(MatchIt, cobalt, survival, survminer). The user is responsible for data\naccess, IRB\u002Fprivacy compliance, and clinical code-list validation.\n\nOut of scope: data extraction from source systems, natural language processing\nof clinical notes, interrupted time series, instrumental variable analyses.\n\n## Usage\n\nInvoke when the user asks to:\n- Build a cohort from claims or EHR data using diagnosis\u002Fprocedure\u002Fdrug codes\n- Calculate PDC or MPR for medication adherence\n- Estimate propensity scores and check covariate balance\n- Run Kaplan-Meier or Cox regression on a claims-derived cohort\n- Assess the proportional hazards assumption\n\nThe skill emits exact code. The user supplies the data and code lists.\n\n\n## Response Format\n\n- Lead with the command or code the user needs — explain after\n- Structure as: confirm inputs → working code → key parameters explained → gotchas\n- One complete working example per task; do not show every alternative\n- Keep code comments minimal and functional (what, not why-it-exists)\n- Target: 50-100 lines of code with brief surrounding explanation\n\n## Core Concepts\n\n### Data model assumptions\n\nThe pipeline expects these tables (column names are illustrative):\n\n| Table | Key columns |\n|---|---|\n| `enrollment` | `patient_id`, `enroll_start`, `enroll_end` |\n| `diagnoses` | `patient_id`, `dx_date`, `icd10_code`, `dx_position` |\n| `procedures` | `patient_id`, `proc_date`, `cpt_code` |\n| `pharmacy` | `patient_id`, `fill_date`, `ndc_code`, `days_supply`, `quantity` |\n| `demographics` | `patient_id`, `birth_date`, `sex`, `race` |\n\n### 1. Cohort Identification\n\n#### Define code lists\n\n```python\n# ICD-10-CM codes for type 2 diabetes (example)\nT2D_ICD10 = [\"E11.0\", \"E11.1\", \"E11.2\", \"E11.3\", \"E11.4\",\n             \"E11.5\", \"E11.6\", \"E11.8\", \"E11.9\"]\n\n# NDC codes for Drug A (user supplies actual NDCs)\nDRUG_A_NDC = [\"12345678901\", \"12345678902\"]\nDRUG_B_NDC = [\"98765432101\", \"98765432102\"]\n\n# CPT codes for outcome procedure (e.g., amputation)\nOUTCOME_CPT = [\"27590\", \"27591\", \"27592\"]\n```\n\n#### Identify new users with washout\n\n```python\nimport pandas as pd\n\ndef identify_new_users(pharmacy, ndc_list, washout_days=180):\n    \"\"\"Identify incident users: first fill after washout with no prior fills.\"\"\"\n    drug_fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    drug_fills = drug_fills.sort_values([\"patient_id\", \"fill_date\"])\n\n    # First-ever fill\n    first_fill = drug_fills.groupby(\"patient_id\")[\"fill_date\"].min().reset_index()\n    first_fill.columns = [\"patient_id\", \"index_date\"]\n\n    # Require continuous enrollment for washout_days before index\n    cohort = first_fill.merge(enrollment, on=\"patient_id\")\n    cohort[\"washout_start\"] = cohort[\"index_date\"] - pd.Timedelta(days=washout_days)\n    cohort = cohort[cohort[\"enroll_start\"] \u003C= cohort[\"washout_start\"]]\n\n    # Exclude patients with any fill of the drug during washout\n    cohort = cohort.merge(drug_fills, on=\"patient_id\", suffixes=(\"\", \"_rx\"))\n    prior_fills = cohort[\n        (cohort[\"fill_date\"] >= cohort[\"washout_start\"]) &\n        (cohort[\"fill_date\"] \u003C cohort[\"index_date\"])\n    ]\n    prevalent_users = prior_fills[\"patient_id\"].unique()\n    cohort = first_fill[~first_fill[\"patient_id\"].isin(prevalent_users)]\n    return cohort  # columns: patient_id, index_date\n```\n\n#### Apply inclusion\u002Fexclusion criteria\n\n```python\ndef apply_criteria(cohort, diagnoses, enrollment, min_age=18,\n                   required_dx_codes=None, excluded_dx_codes=None,\n                   pre_index_window=365, min_followup=30):\n    \"\"\"Filter cohort by age, required\u002Fexcluded diagnoses, enrollment.\"\"\"\n    # Age at index\n    cohort = cohort.merge(demographics, on=\"patient_id\")\n    cohort[\"age\"] = (cohort[\"index_date\"] - cohort[\"birth_date\"]).dt.days \u002F 365.25\n    cohort = cohort[cohort[\"age\"] >= min_age]\n\n    # Required diagnosis in pre-index window\n    if required_dx_codes:\n        pre_dx = diagnoses[diagnoses[\"icd10_code\"].isin(required_dx_codes)]\n        pre_dx = pre_dx.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n        pre_dx = pre_dx[\n            (pre_dx[\"dx_date\"] >= pre_dx[\"index_date\"] - pd.Timedelta(days=pre_index_window)) &\n            (pre_dx[\"dx_date\"] \u003C pre_dx[\"index_date\"])\n        ]\n        cohort = cohort[cohort[\"patient_id\"].isin(pre_dx[\"patient_id\"].unique())]\n\n    # Exclude patients with certain diagnoses\n    if excluded_dx_codes:\n        excl = diagnoses[diagnoses[\"icd10_code\"].isin(excluded_dx_codes)]\n        excl = excl.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n        excl = excl[excl[\"dx_date\"] \u003C excl[\"index_date\"]]\n        cohort = cohort[~cohort[\"patient_id\"].isin(excl[\"patient_id\"].unique())]\n\n    # Minimum follow-up enrollment\n    cohort = cohort.merge(enrollment, on=\"patient_id\")\n    cohort[\"followup_end\"] = cohort[[\"enroll_end\", cohort.columns[-1]]].min(axis=1)\n    cohort = cohort[\n        (cohort[\"enroll_end\"] - cohort[\"index_date\"]).dt.days >= min_followup\n    ]\n    return cohort\n```\n\n### 2. Medication Adherence\n\n#### PDC (Proportion of Days Covered)\n\nPDC is the preferred measure (endorsed by PQA). It counts the number of days\nin the measurement period covered by at least one fill, capped at 1.0.\n\n```python\ndef calculate_pdc(pharmacy, cohort, ndc_list, period_days=365):\n    \"\"\"Calculate PDC for each patient over a fixed measurement period.\"\"\"\n    fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    fills = fills.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n    fills[\"period_end\"] = fills[\"index_date\"] + pd.Timedelta(days=period_days)\n\n    # Keep fills within measurement period\n    fills = fills[fills[\"fill_date\"] \u003C fills[\"period_end\"]]\n    fills[\"cover_start\"] = fills[\"fill_date\"]\n    fills[\"cover_end\"] = fills[\"fill_date\"] + pd.to_timedelta(fills[\"days_supply\"], unit=\"D\")\n    fills[\"cover_end\"] = fills[[\"cover_end\", \"period_end\"]].min(axis=1)\n    fills[\"cover_start\"] = fills[[\"cover_start\", \"index_date\"]].max(axis=1)\n\n    # Build day-level coverage (vectorized per patient)\n    results = []\n    for pid, grp in fills.groupby(\"patient_id\"):\n        days_covered = set()\n        for _, row in grp.iterrows():\n            days_covered.update(pd.date_range(row[\"cover_start\"], row[\"cover_end\"] - pd.Timedelta(days=1)))\n        results.append({\"patient_id\": pid, \"pdc\": min(len(days_covered) \u002F period_days, 1.0)})\n    return pd.DataFrame(results)\n```\n\n#### MPR (Medication Possession Ratio)\n\n```python\ndef calculate_mpr(pharmacy, cohort, ndc_list, period_days=365):\n    \"\"\"MPR = total days supply dispensed \u002F days in period. Can exceed 1.0.\"\"\"\n    fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    fills = fills.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n    fills[\"period_end\"] = fills[\"index_date\"] + pd.Timedelta(days=period_days)\n    fills = fills[(fills[\"fill_date\"] >= fills[\"index_date\"]) &\n                  (fills[\"fill_date\"] \u003C fills[\"period_end\"])]\n    mpr = fills.groupby(\"patient_id\")[\"days_supply\"].sum().reset_index()\n    mpr.columns = [\"patient_id\", \"total_supply\"]\n    mpr[\"mpr\"] = mpr[\"total_supply\"] \u002F period_days\n    return mpr\n```\n\n| Metric | Formula | Range | Notes |\n|---|---|---|---|\n| **PDC** | Days covered \u002F days in period | 0–1.0 | Preferred; no double-counting overlapping fills |\n| **MPR** | Total days supply \u002F days in period | 0–∞ | Can exceed 1.0 with early refills; less preferred |\n\nAdherence threshold: PDC ≥ 0.80 is the standard cutoff for \"adherent.\"\n\n### 3. Propensity Score Estimation\n\n#### Python (scikit-learn)\n\n```python\nfrom sklearn.linear_model import LogisticRegression\nimport numpy as np\n\ndef estimate_ps(cohort, covariate_cols, treatment_col=\"treatment\"):\n    \"\"\"Fit logistic regression PS model. Returns cohort with ps column.\"\"\"\n    X = cohort[covariate_cols].fillna(0)\n    y = cohort[treatment_col]\n    model = LogisticRegression(max_iter=1000, solver=\"lbfgs\", C=1e6)\n    model.fit(X, y)\n    cohort = cohort.copy()\n    cohort[\"ps\"] = model.predict_proba(X)[:, 1]\n    return cohort, model\n```\n\n#### R (MatchIt + cobalt)\n\n```r\nlibrary(MatchIt)\nlibrary(cobalt)\n\n# Estimate PS and perform 1:1 nearest-neighbor matching\nm \u003C- matchit(treatment ~ age + sex + charlson + prior_hosp + prior_rx_count,\n             data = cohort, method = \"nearest\", distance = \"glm\",\n             caliper = 0.2, ratio = 1)\nmatched_data \u003C- match.data(m)\n\n# Balance diagnostics\nbal.tab(m, thresholds = c(m = 0.1))  # SMD threshold\nlove.plot(m, threshold = 0.1)\n```\n\n### 4. Balance Diagnostics\n\n#### SMD calculation (Python)\n\n```python\ndef compute_smd(cohort, covariate_cols, treatment_col=\"treatment\"):\n    \"\"\"Compute standardized mean differences for all covariates.\"\"\"\n    treated = cohort[cohort[treatment_col] == 1]\n    comparator = cohort[cohort[treatment_col] == 0]\n    results = []\n    for col in covariate_cols:\n        mean_t = treated[col].mean()\n        mean_c = comparator[col].mean()\n        var_t = treated[col].var()\n        var_c = comparator[col].var()\n        pooled_sd = np.sqrt((var_t + var_c) \u002F 2)\n        smd = abs(mean_t - mean_c) \u002F pooled_sd if pooled_sd > 0 else 0\n        results.append({\"covariate\": col, \"smd\": round(smd, 4),\n                        \"balanced\": smd \u003C 0.1})\n    return pd.DataFrame(results)\n```\n\n| Diagnostic | Target | Action if failed |\n|---|---|---|\n| SMD | \u003C 0.1 for all covariates | Add interactions\u002Fnon-linear terms to PS model |\n| Variance ratio | 0.5–2.0 | Re-specify PS model or trim extremes |\n| PS overlap | Substantial overlap in both groups | Trim non-overlapping regions; report trimmed N |\n| Weight distribution (IPTW) | Mean ≈ 1.0; max \u003C 10 | Truncate at 1st\u002F99th percentile |\n\n### 5. IPTW (Inverse Probability of Treatment Weighting)\n\n```python\ndef compute_iptw(cohort, treatment_col=\"treatment\", ps_col=\"ps\",\n                 stabilized=True, truncate_pct=(1, 99)):\n    \"\"\"Compute stabilized IPTW weights with truncation.\"\"\"\n    cohort = cohort.copy()\n    t = cohort[treatment_col]\n    ps = cohort[ps_col]\n\n    if stabilized:\n        p_treat = t.mean()\n        cohort[\"iptw\"] = np.where(t == 1, p_treat \u002F ps, (1 - p_treat) \u002F (1 - ps))\n    else:\n        cohort[\"iptw\"] = np.where(t == 1, 1 \u002F ps, 1 \u002F (1 - ps))\n\n    # Truncate extreme weights\n    lo, hi = np.percentile(cohort[\"iptw\"], truncate_pct)\n    cohort[\"iptw\"] = cohort[\"iptw\"].clip(lo, hi)\n    return cohort\n```\n\n### 6. Kaplan-Meier Survival Analysis\n\n#### Python (lifelines)\n\n```python\nfrom lifelines import KaplanMeierFitter\nimport matplotlib.pyplot as plt\n\ndef plot_km(cohort, duration_col=\"followup_days\", event_col=\"event\",\n            group_col=\"treatment\", labels=(\"Drug B\", \"Drug A\")):\n    \"\"\"Plot Kaplan-Meier curves by treatment group.\"\"\"\n    fig, ax = plt.subplots(figsize=(8, 6))\n    kmf = KaplanMeierFitter()\n    for grp, label in zip([0, 1], labels):\n        mask = cohort[group_col] == grp\n        kmf.fit(cohort.loc[mask, duration_col], cohort.loc[mask, event_col],\n                label=label)\n        kmf.plot_survival_function(ax=ax, ci_show=True)\n    ax.set_xlabel(\"Days from index date\")\n    ax.set_ylabel(\"Survival probability\")\n    ax.set_title(\"Kaplan-Meier Survival Curves\")\n    plt.tight_layout()\n    return fig\n```\n\n#### R (survival + survminer)\n\n```r\nlibrary(survival)\nlibrary(survminer)\n\nfit \u003C- survfit(Surv(followup_days, event) ~ treatment, data = cohort)\nggsurvplot(fit, data = cohort, pval = TRUE, risk.table = TRUE,\n           xlab = \"Days from index date\", ylab = \"Survival probability\",\n           legend.labs = c(\"Drug B\", \"Drug A\"))\n```\n\n### 7. Cox Proportional Hazards Model\n\n#### Python (lifelines)\n\n```python\nfrom lifelines import CoxPHFitter\n\ndef fit_cox(cohort, duration_col=\"followup_days\", event_col=\"event\",\n            covariates=None, weight_col=None):\n    \"\"\"Fit Cox PH model, optionally weighted (IPTW).\"\"\"\n    cols = [duration_col, event_col] + (covariates or [])\n    if weight_col:\n        cols.append(weight_col)\n    df = cohort[cols].dropna()\n    cph = CoxPHFitter()\n    cph.fit(df, duration_col=duration_col, event_col=event_col,\n            weights_col=weight_col, robust=True if weight_col else False)\n    cph.print_summary()\n    return cph\n```\n\n#### R (survival)\n\n```r\n# Unweighted (matched cohort)\ncox_fit \u003C- coxph(Surv(followup_days, event) ~ treatment + age + sex + charlson,\n                 data = matched_data)\nsummary(cox_fit)\n\n# IPTW-weighted\ncox_iptw \u003C- coxph(Surv(followup_days, event) ~ treatment,\n                  data = cohort, weights = iptw, robust = TRUE)\nsummary(cox_iptw)\n```\n\n### 8. Proportional Hazards Assumption Check\n\n#### Schoenfeld residuals (Python)\n\n```python\ndef check_ph_assumption(cph):\n    \"\"\"Test and plot Schoenfeld residuals for PH assumption.\"\"\"\n    cph.check_assumptions(cohort, p_value_threshold=0.05, show_plots=True)\n```\n\n#### R\n\n```r\nph_test \u003C- cox.zph(cox_fit)\nprint(ph_test)       # p \u003C 0.05 → PH assumption violated for that covariate\nplot(ph_test)         # visual: residuals should show no trend over time\n```\n\n| PH test result | Action |\n|---|---|\n| All p > 0.05 | PH assumption holds; proceed |\n| Covariate p \u003C 0.05 | Stratify on that covariate or add time-interaction term |\n| Global p \u003C 0.05 | Consider restricted mean survival time (RMST) or parametric AFT model |\n\n### Pipeline summary\n\n```\n1. Define code lists (ICD-10, NDC, CPT)\n2. Identify new users with washout          → cohort table\n3. Apply inclusion\u002Fexclusion criteria       → filtered cohort\n4. Build baseline covariate matrix          → pre-index features\n5. Estimate propensity scores               → ps column\n6. Match or weight (IPTW)                   → balanced cohort\n7. Check balance (SMD \u003C 0.1)               → Love plot \u002F SMD table\n8. Calculate adherence (PDC\u002FMPR)            → adherence metrics\n9. Define outcome + follow-up               → duration + event columns\n10. Kaplan-Meier curves                     → survival plot\n11. Cox PH model (adjusted or weighted)     → hazard ratios + CIs\n12. Check PH assumption (Schoenfeld)        → assumption diagnostics\n```\n\n## Common Mistakes\n\n- **Wrong:** Using MPR instead of PDC for medication adherence\n  **Right:** Use PDC (Proportion of Days Covered) as the adherence metric\n  **Why:** MPR double-counts overlapping fills, can exceed 1.0, and is not endorsed by PQA\n\n- **Wrong:** Not requiring continuous enrollment during the study period\n  **Right:** Verify continuous enrollment for both washout and follow-up windows\n  **Why:** Gaps in enrollment create unobservable periods where events and fills are missed\n\n- **Wrong:** Omitting a washout period before the index date\n  **Right:** Require a clean washout (typically 180 days) with no prior fills of the study drug\n  **Why:** Without washout, prevalent users contaminate the new-user cohort and bias results\n\n- **Wrong:** Including post-index covariates in the propensity score model\n  **Right:** Only use pre-index (baseline) variables in the PS model\n  **Why:** Post-index variables may be affected by treatment, introducing collider bias\n\n- **Wrong:** Using p-values to assess covariate balance after matching\u002Fweighting\n  **Right:** Use standardized mean differences (SMD \u003C 0.1) for all covariates\n  **Why:** P-values conflate balance with sample size and are uninformative for this purpose\n\n- **Wrong:** Not checking propensity score overlap between treatment groups\n  **Right:** Plot PS distributions for both groups and trim non-overlapping regions\n  **Why:** Estimates in non-overlapping regions are extrapolated and unreliable\n\n- **Wrong:** Reporting Cox model results without testing the proportional hazards assumption\n  **Right:** Always run Schoenfeld residual tests and inspect plots for time trends\n  **Why:** Violated PH produces misleading hazard ratios; consider RMST or stratification instead\n\n- **Wrong:** Using naive standard errors with IPTW-weighted analyses\n  **Right:** Use robust (sandwich) variance estimators when fitting weighted models\n  **Why:** Naive SEs underestimate uncertainty because they ignore the weight estimation step\n\n- **Wrong:** Truncating IPTW weights too aggressively (e.g., at 5th\u002F95th percentile)\n  **Right:** Truncate conservatively (1st\u002F99th) and report sensitivity across thresholds\n  **Why:** Over-truncation reintroduces confounding by effectively unweighting key observations\n\n- **Wrong:** Defining follow-up end as a fixed calendar date for all patients\n  **Right:** End follow-up at the earliest of: event, disenrollment, end of study, or death\n  **Why:** Incorrect censoring biases survival estimates and violates non-informative censoring assumptions\n\n## References\n\n- Hernán MA, Robins JM. Causal Inference: What If. Chapman & Hall\u002FCRC 2020, https:\u002F\u002Fwww.hsph.harvard.edu\u002Fmiguel-hernan\u002Fcausal-inference-book\u002F\n- Austin PC. An introduction to propensity score methods. Multivariate Behav Res 2011, https:\u002F\u002Fdoi.org\u002F10.1080\u002F00273171.2011.568786\n- Pharmacy Quality Alliance. PDC measure specifications, https:\u002F\u002Fwww.pqaalliance.org\u002F\n- Davidson-Pilon C. lifelines documentation, https:\u002F\u002Flifelines.readthedocs.io\u002F\n- Ho DE et al. MatchIt: nonparametric preprocessing for parametric causal inference. J Stat Softw 2011, https:\u002F\u002Fdoi.org\u002F10.18637\u002Fjss.v042.i08\n- Therneau TM, Grambsch PM. Modeling Survival Data. Springer 2000\n",{"data":53,"body":71},{"name":4,"description":6,"usage":54,"version":55,"validated_against":56,"tags":62},"Invoke when building cohort analysis pipelines from claims or EHR data with adherence metrics, propensity scores, and survival analysis.","1.0.0",{"date":57,"packages":58},"2025-01-15",{"lifelines":59,"statsmodels":60,"pandas":61},"0.28","0.14","2.2",[63,64,65,66,67,68,69,70],"skill","category:pipeline","real-world-data","claims","survival-analysis","propensity-score","cohort","hcls",{"type":72,"children":73},"root",[74,83,90,96,121,126,131,137,142,171,176,182,210,216,223,228,457,463,470,572,578,796,802,1077,1083,1089,1094,1267,1273,1365,1450,1455,1461,1467,1569,1575,1678,1684,1690,1815,1914,1920,2059,2065,2071,2221,2227,2289,2295,2300,2418,2424,2502,2508,2514,2545,2550,2581,2642,2648,2658,2664,2880,2886,2951],{"type":75,"tag":76,"props":77,"children":79},"element","h1",{"id":78},"real-world-data-cohort-analysis-pipeline",[80],{"type":81,"value":82},"text","Real-World Data Cohort Analysis Pipeline",{"type":75,"tag":84,"props":85,"children":87},"h2",{"id":86},"overview",[88],{"type":81,"value":89},"Overview",{"type":75,"tag":91,"props":92,"children":93},"p",{},[94],{"type":81,"value":95},"This skill encodes a reproducible pipeline for analyzing administrative claims\nor EHR data to estimate comparative treatment effects. It covers the full\nworkflow from raw claims tables to adjusted survival estimates:",{"type":75,"tag":97,"props":98,"children":99},"ol",{},[100,106,111,116],{"type":75,"tag":101,"props":102,"children":103},"li",{},[104],{"type":81,"value":105},"Cohort identification using diagnosis (ICD-10-CM), procedure (CPT\u002FHCPCS),\nand drug (NDC) code lists",{"type":75,"tag":101,"props":107,"children":108},{},[109],{"type":81,"value":110},"Medication adherence measurement (PDC and MPR)",{"type":75,"tag":101,"props":112,"children":113},{},[114],{"type":81,"value":115},"Propensity score estimation and balance diagnostics",{"type":75,"tag":101,"props":117,"children":118},{},[119],{"type":81,"value":120},"Kaplan-Meier curves and Cox proportional hazards regression",{"type":75,"tag":91,"props":122,"children":123},{},[124],{"type":81,"value":125},"Code snippets use Python (pandas, scikit-learn, lifelines) and R\n(MatchIt, cobalt, survival, survminer). The user is responsible for data\naccess, IRB\u002Fprivacy compliance, and clinical code-list validation.",{"type":75,"tag":91,"props":127,"children":128},{},[129],{"type":81,"value":130},"Out of scope: data extraction from source systems, natural language processing\nof clinical notes, interrupted time series, instrumental variable analyses.",{"type":75,"tag":84,"props":132,"children":134},{"id":133},"usage",[135],{"type":81,"value":136},"Usage",{"type":75,"tag":91,"props":138,"children":139},{},[140],{"type":81,"value":141},"Invoke when the user asks to:",{"type":75,"tag":143,"props":144,"children":145},"ul",{},[146,151,156,161,166],{"type":75,"tag":101,"props":147,"children":148},{},[149],{"type":81,"value":150},"Build a cohort from claims or EHR data using diagnosis\u002Fprocedure\u002Fdrug codes",{"type":75,"tag":101,"props":152,"children":153},{},[154],{"type":81,"value":155},"Calculate PDC or MPR for medication adherence",{"type":75,"tag":101,"props":157,"children":158},{},[159],{"type":81,"value":160},"Estimate propensity scores and check covariate balance",{"type":75,"tag":101,"props":162,"children":163},{},[164],{"type":81,"value":165},"Run Kaplan-Meier or Cox regression on a claims-derived cohort",{"type":75,"tag":101,"props":167,"children":168},{},[169],{"type":81,"value":170},"Assess the proportional hazards assumption",{"type":75,"tag":91,"props":172,"children":173},{},[174],{"type":81,"value":175},"The skill emits exact code. The user supplies the data and code lists.",{"type":75,"tag":84,"props":177,"children":179},{"id":178},"response-format",[180],{"type":81,"value":181},"Response Format",{"type":75,"tag":143,"props":183,"children":184},{},[185,190,195,200,205],{"type":75,"tag":101,"props":186,"children":187},{},[188],{"type":81,"value":189},"Lead with the command or code the user needs — explain after",{"type":75,"tag":101,"props":191,"children":192},{},[193],{"type":81,"value":194},"Structure as: confirm inputs → working code → key parameters explained → gotchas",{"type":75,"tag":101,"props":196,"children":197},{},[198],{"type":81,"value":199},"One complete working example per task; do not show every alternative",{"type":75,"tag":101,"props":201,"children":202},{},[203],{"type":81,"value":204},"Keep code comments minimal and functional (what, not why-it-exists)",{"type":75,"tag":101,"props":206,"children":207},{},[208],{"type":81,"value":209},"Target: 50-100 lines of code with brief surrounding explanation",{"type":75,"tag":84,"props":211,"children":213},{"id":212},"core-concepts",[214],{"type":81,"value":215},"Core Concepts",{"type":75,"tag":217,"props":218,"children":220},"h3",{"id":219},"data-model-assumptions",[221],{"type":81,"value":222},"Data model assumptions",{"type":75,"tag":91,"props":224,"children":225},{},[226],{"type":81,"value":227},"The pipeline expects these tables (column names are illustrative):",{"type":75,"tag":229,"props":230,"children":231},"table",{},[232,251],{"type":75,"tag":233,"props":234,"children":235},"thead",{},[236],{"type":75,"tag":237,"props":238,"children":239},"tr",{},[240,246],{"type":75,"tag":241,"props":242,"children":243},"th",{},[244],{"type":81,"value":245},"Table",{"type":75,"tag":241,"props":247,"children":248},{},[249],{"type":81,"value":250},"Key columns",{"type":75,"tag":252,"props":253,"children":254},"tbody",{},[255,293,334,368,416],{"type":75,"tag":237,"props":256,"children":257},{},[258,269],{"type":75,"tag":259,"props":260,"children":261},"td",{},[262],{"type":75,"tag":263,"props":264,"children":266},"code",{"className":265},[],[267],{"type":81,"value":268},"enrollment",{"type":75,"tag":259,"props":270,"children":271},{},[272,278,280,286,287],{"type":75,"tag":263,"props":273,"children":275},{"className":274},[],[276],{"type":81,"value":277},"patient_id",{"type":81,"value":279},", ",{"type":75,"tag":263,"props":281,"children":283},{"className":282},[],[284],{"type":81,"value":285},"enroll_start",{"type":81,"value":279},{"type":75,"tag":263,"props":288,"children":290},{"className":289},[],[291],{"type":81,"value":292},"enroll_end",{"type":75,"tag":237,"props":294,"children":295},{},[296,305],{"type":75,"tag":259,"props":297,"children":298},{},[299],{"type":75,"tag":263,"props":300,"children":302},{"className":301},[],[303],{"type":81,"value":304},"diagnoses",{"type":75,"tag":259,"props":306,"children":307},{},[308,313,314,320,321,327,328],{"type":75,"tag":263,"props":309,"children":311},{"className":310},[],[312],{"type":81,"value":277},{"type":81,"value":279},{"type":75,"tag":263,"props":315,"children":317},{"className":316},[],[318],{"type":81,"value":319},"dx_date",{"type":81,"value":279},{"type":75,"tag":263,"props":322,"children":324},{"className":323},[],[325],{"type":81,"value":326},"icd10_code",{"type":81,"value":279},{"type":75,"tag":263,"props":329,"children":331},{"className":330},[],[332],{"type":81,"value":333},"dx_position",{"type":75,"tag":237,"props":335,"children":336},{},[337,346],{"type":75,"tag":259,"props":338,"children":339},{},[340],{"type":75,"tag":263,"props":341,"children":343},{"className":342},[],[344],{"type":81,"value":345},"procedures",{"type":75,"tag":259,"props":347,"children":348},{},[349,354,355,361,362],{"type":75,"tag":263,"props":350,"children":352},{"className":351},[],[353],{"type":81,"value":277},{"type":81,"value":279},{"type":75,"tag":263,"props":356,"children":358},{"className":357},[],[359],{"type":81,"value":360},"proc_date",{"type":81,"value":279},{"type":75,"tag":263,"props":363,"children":365},{"className":364},[],[366],{"type":81,"value":367},"cpt_code",{"type":75,"tag":237,"props":369,"children":370},{},[371,380],{"type":75,"tag":259,"props":372,"children":373},{},[374],{"type":75,"tag":263,"props":375,"children":377},{"className":376},[],[378],{"type":81,"value":379},"pharmacy",{"type":75,"tag":259,"props":381,"children":382},{},[383,388,389,395,396,402,403,409,410],{"type":75,"tag":263,"props":384,"children":386},{"className":385},[],[387],{"type":81,"value":277},{"type":81,"value":279},{"type":75,"tag":263,"props":390,"children":392},{"className":391},[],[393],{"type":81,"value":394},"fill_date",{"type":81,"value":279},{"type":75,"tag":263,"props":397,"children":399},{"className":398},[],[400],{"type":81,"value":401},"ndc_code",{"type":81,"value":279},{"type":75,"tag":263,"props":404,"children":406},{"className":405},[],[407],{"type":81,"value":408},"days_supply",{"type":81,"value":279},{"type":75,"tag":263,"props":411,"children":413},{"className":412},[],[414],{"type":81,"value":415},"quantity",{"type":75,"tag":237,"props":417,"children":418},{},[419,428],{"type":75,"tag":259,"props":420,"children":421},{},[422],{"type":75,"tag":263,"props":423,"children":425},{"className":424},[],[426],{"type":81,"value":427},"demographics",{"type":75,"tag":259,"props":429,"children":430},{},[431,436,437,443,444,450,451],{"type":75,"tag":263,"props":432,"children":434},{"className":433},[],[435],{"type":81,"value":277},{"type":81,"value":279},{"type":75,"tag":263,"props":438,"children":440},{"className":439},[],[441],{"type":81,"value":442},"birth_date",{"type":81,"value":279},{"type":75,"tag":263,"props":445,"children":447},{"className":446},[],[448],{"type":81,"value":449},"sex",{"type":81,"value":279},{"type":75,"tag":263,"props":452,"children":454},{"className":453},[],[455],{"type":81,"value":456},"race",{"type":75,"tag":217,"props":458,"children":460},{"id":459},"_1-cohort-identification",[461],{"type":81,"value":462},"1. Cohort Identification",{"type":75,"tag":464,"props":465,"children":467},"h4",{"id":466},"define-code-lists",[468],{"type":81,"value":469},"Define code lists",{"type":75,"tag":471,"props":472,"children":477},"pre",{"className":473,"code":474,"language":475,"meta":476,"style":476},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# ICD-10-CM codes for type 2 diabetes (example)\nT2D_ICD10 = [\"E11.0\", \"E11.1\", \"E11.2\", \"E11.3\", \"E11.4\",\n             \"E11.5\", \"E11.6\", \"E11.8\", \"E11.9\"]\n\n# NDC codes for Drug A (user supplies actual NDCs)\nDRUG_A_NDC = [\"12345678901\", \"12345678902\"]\nDRUG_B_NDC = [\"98765432101\", \"98765432102\"]\n\n# CPT codes for outcome procedure (e.g., amputation)\nOUTCOME_CPT = [\"27590\", \"27591\", \"27592\"]\n","python","",[478],{"type":75,"tag":263,"props":479,"children":480},{"__ignoreMap":476},[481,492,501,510,519,528,537,546,554,563],{"type":75,"tag":482,"props":483,"children":486},"span",{"class":484,"line":485},"line",1,[487],{"type":75,"tag":482,"props":488,"children":489},{},[490],{"type":81,"value":491},"# ICD-10-CM codes for type 2 diabetes (example)\n",{"type":75,"tag":482,"props":493,"children":495},{"class":484,"line":494},2,[496],{"type":75,"tag":482,"props":497,"children":498},{},[499],{"type":81,"value":500},"T2D_ICD10 = [\"E11.0\", \"E11.1\", \"E11.2\", \"E11.3\", \"E11.4\",\n",{"type":75,"tag":482,"props":502,"children":504},{"class":484,"line":503},3,[505],{"type":75,"tag":482,"props":506,"children":507},{},[508],{"type":81,"value":509},"             \"E11.5\", \"E11.6\", \"E11.8\", \"E11.9\"]\n",{"type":75,"tag":482,"props":511,"children":512},{"class":484,"line":29},[513],{"type":75,"tag":482,"props":514,"children":516},{"emptyLinePlaceholder":515},true,[517],{"type":81,"value":518},"\n",{"type":75,"tag":482,"props":520,"children":522},{"class":484,"line":521},5,[523],{"type":75,"tag":482,"props":524,"children":525},{},[526],{"type":81,"value":527},"# NDC codes for Drug A (user supplies actual NDCs)\n",{"type":75,"tag":482,"props":529,"children":531},{"class":484,"line":530},6,[532],{"type":75,"tag":482,"props":533,"children":534},{},[535],{"type":81,"value":536},"DRUG_A_NDC = [\"12345678901\", \"12345678902\"]\n",{"type":75,"tag":482,"props":538,"children":540},{"class":484,"line":539},7,[541],{"type":75,"tag":482,"props":542,"children":543},{},[544],{"type":81,"value":545},"DRUG_B_NDC = [\"98765432101\", \"98765432102\"]\n",{"type":75,"tag":482,"props":547,"children":549},{"class":484,"line":548},8,[550],{"type":75,"tag":482,"props":551,"children":552},{"emptyLinePlaceholder":515},[553],{"type":81,"value":518},{"type":75,"tag":482,"props":555,"children":557},{"class":484,"line":556},9,[558],{"type":75,"tag":482,"props":559,"children":560},{},[561],{"type":81,"value":562},"# CPT codes for outcome procedure (e.g., amputation)\n",{"type":75,"tag":482,"props":564,"children":566},{"class":484,"line":565},10,[567],{"type":75,"tag":482,"props":568,"children":569},{},[570],{"type":81,"value":571},"OUTCOME_CPT = [\"27590\", \"27591\", \"27592\"]\n",{"type":75,"tag":464,"props":573,"children":575},{"id":574},"identify-new-users-with-washout",[576],{"type":81,"value":577},"Identify new users with washout",{"type":75,"tag":471,"props":579,"children":581},{"className":473,"code":580,"language":475,"meta":476,"style":476},"import pandas as pd\n\ndef identify_new_users(pharmacy, ndc_list, washout_days=180):\n    \"\"\"Identify incident users: first fill after washout with no prior fills.\"\"\"\n    drug_fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    drug_fills = drug_fills.sort_values([\"patient_id\", \"fill_date\"])\n\n    # First-ever fill\n    first_fill = drug_fills.groupby(\"patient_id\")[\"fill_date\"].min().reset_index()\n    first_fill.columns = [\"patient_id\", \"index_date\"]\n\n    # Require continuous enrollment for washout_days before index\n    cohort = first_fill.merge(enrollment, on=\"patient_id\")\n    cohort[\"washout_start\"] = cohort[\"index_date\"] - pd.Timedelta(days=washout_days)\n    cohort = cohort[cohort[\"enroll_start\"] \u003C= cohort[\"washout_start\"]]\n\n    # Exclude patients with any fill of the drug during washout\n    cohort = cohort.merge(drug_fills, on=\"patient_id\", suffixes=(\"\", \"_rx\"))\n    prior_fills = cohort[\n        (cohort[\"fill_date\"] >= cohort[\"washout_start\"]) &\n        (cohort[\"fill_date\"] \u003C cohort[\"index_date\"])\n    ]\n    prevalent_users = prior_fills[\"patient_id\"].unique()\n    cohort = first_fill[~first_fill[\"patient_id\"].isin(prevalent_users)]\n    return cohort  # columns: patient_id, index_date\n",[582],{"type":75,"tag":263,"props":583,"children":584},{"__ignoreMap":476},[585,593,600,608,616,624,632,639,647,655,663,671,680,689,698,707,715,724,733,742,751,760,769,778,787],{"type":75,"tag":482,"props":586,"children":587},{"class":484,"line":485},[588],{"type":75,"tag":482,"props":589,"children":590},{},[591],{"type":81,"value":592},"import pandas as pd\n",{"type":75,"tag":482,"props":594,"children":595},{"class":484,"line":494},[596],{"type":75,"tag":482,"props":597,"children":598},{"emptyLinePlaceholder":515},[599],{"type":81,"value":518},{"type":75,"tag":482,"props":601,"children":602},{"class":484,"line":503},[603],{"type":75,"tag":482,"props":604,"children":605},{},[606],{"type":81,"value":607},"def identify_new_users(pharmacy, ndc_list, washout_days=180):\n",{"type":75,"tag":482,"props":609,"children":610},{"class":484,"line":29},[611],{"type":75,"tag":482,"props":612,"children":613},{},[614],{"type":81,"value":615},"    \"\"\"Identify incident users: first fill after washout with no prior fills.\"\"\"\n",{"type":75,"tag":482,"props":617,"children":618},{"class":484,"line":521},[619],{"type":75,"tag":482,"props":620,"children":621},{},[622],{"type":81,"value":623},"    drug_fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n",{"type":75,"tag":482,"props":625,"children":626},{"class":484,"line":530},[627],{"type":75,"tag":482,"props":628,"children":629},{},[630],{"type":81,"value":631},"    drug_fills = drug_fills.sort_values([\"patient_id\", \"fill_date\"])\n",{"type":75,"tag":482,"props":633,"children":634},{"class":484,"line":539},[635],{"type":75,"tag":482,"props":636,"children":637},{"emptyLinePlaceholder":515},[638],{"type":81,"value":518},{"type":75,"tag":482,"props":640,"children":641},{"class":484,"line":548},[642],{"type":75,"tag":482,"props":643,"children":644},{},[645],{"type":81,"value":646},"    # First-ever fill\n",{"type":75,"tag":482,"props":648,"children":649},{"class":484,"line":556},[650],{"type":75,"tag":482,"props":651,"children":652},{},[653],{"type":81,"value":654},"    first_fill = drug_fills.groupby(\"patient_id\")[\"fill_date\"].min().reset_index()\n",{"type":75,"tag":482,"props":656,"children":657},{"class":484,"line":565},[658],{"type":75,"tag":482,"props":659,"children":660},{},[661],{"type":81,"value":662},"    first_fill.columns = [\"patient_id\", \"index_date\"]\n",{"type":75,"tag":482,"props":664,"children":666},{"class":484,"line":665},11,[667],{"type":75,"tag":482,"props":668,"children":669},{"emptyLinePlaceholder":515},[670],{"type":81,"value":518},{"type":75,"tag":482,"props":672,"children":674},{"class":484,"line":673},12,[675],{"type":75,"tag":482,"props":676,"children":677},{},[678],{"type":81,"value":679},"    # Require continuous enrollment for washout_days before index\n",{"type":75,"tag":482,"props":681,"children":683},{"class":484,"line":682},13,[684],{"type":75,"tag":482,"props":685,"children":686},{},[687],{"type":81,"value":688},"    cohort = first_fill.merge(enrollment, on=\"patient_id\")\n",{"type":75,"tag":482,"props":690,"children":692},{"class":484,"line":691},14,[693],{"type":75,"tag":482,"props":694,"children":695},{},[696],{"type":81,"value":697},"    cohort[\"washout_start\"] = cohort[\"index_date\"] - pd.Timedelta(days=washout_days)\n",{"type":75,"tag":482,"props":699,"children":701},{"class":484,"line":700},15,[702],{"type":75,"tag":482,"props":703,"children":704},{},[705],{"type":81,"value":706},"    cohort = cohort[cohort[\"enroll_start\"] \u003C= cohort[\"washout_start\"]]\n",{"type":75,"tag":482,"props":708,"children":710},{"class":484,"line":709},16,[711],{"type":75,"tag":482,"props":712,"children":713},{"emptyLinePlaceholder":515},[714],{"type":81,"value":518},{"type":75,"tag":482,"props":716,"children":718},{"class":484,"line":717},17,[719],{"type":75,"tag":482,"props":720,"children":721},{},[722],{"type":81,"value":723},"    # Exclude patients with any fill of the drug during washout\n",{"type":75,"tag":482,"props":725,"children":727},{"class":484,"line":726},18,[728],{"type":75,"tag":482,"props":729,"children":730},{},[731],{"type":81,"value":732},"    cohort = cohort.merge(drug_fills, on=\"patient_id\", suffixes=(\"\", \"_rx\"))\n",{"type":75,"tag":482,"props":734,"children":736},{"class":484,"line":735},19,[737],{"type":75,"tag":482,"props":738,"children":739},{},[740],{"type":81,"value":741},"    prior_fills = cohort[\n",{"type":75,"tag":482,"props":743,"children":745},{"class":484,"line":744},20,[746],{"type":75,"tag":482,"props":747,"children":748},{},[749],{"type":81,"value":750},"        (cohort[\"fill_date\"] >= cohort[\"washout_start\"]) &\n",{"type":75,"tag":482,"props":752,"children":754},{"class":484,"line":753},21,[755],{"type":75,"tag":482,"props":756,"children":757},{},[758],{"type":81,"value":759},"        (cohort[\"fill_date\"] \u003C cohort[\"index_date\"])\n",{"type":75,"tag":482,"props":761,"children":763},{"class":484,"line":762},22,[764],{"type":75,"tag":482,"props":765,"children":766},{},[767],{"type":81,"value":768},"    ]\n",{"type":75,"tag":482,"props":770,"children":772},{"class":484,"line":771},23,[773],{"type":75,"tag":482,"props":774,"children":775},{},[776],{"type":81,"value":777},"    prevalent_users = prior_fills[\"patient_id\"].unique()\n",{"type":75,"tag":482,"props":779,"children":781},{"class":484,"line":780},24,[782],{"type":75,"tag":482,"props":783,"children":784},{},[785],{"type":81,"value":786},"    cohort = first_fill[~first_fill[\"patient_id\"].isin(prevalent_users)]\n",{"type":75,"tag":482,"props":788,"children":790},{"class":484,"line":789},25,[791],{"type":75,"tag":482,"props":792,"children":793},{},[794],{"type":81,"value":795},"    return cohort  # columns: patient_id, index_date\n",{"type":75,"tag":464,"props":797,"children":799},{"id":798},"apply-inclusionexclusion-criteria",[800],{"type":81,"value":801},"Apply inclusion\u002Fexclusion criteria",{"type":75,"tag":471,"props":803,"children":805},{"className":473,"code":804,"language":475,"meta":476,"style":476},"def apply_criteria(cohort, diagnoses, enrollment, min_age=18,\n                   required_dx_codes=None, excluded_dx_codes=None,\n                   pre_index_window=365, min_followup=30):\n    \"\"\"Filter cohort by age, required\u002Fexcluded diagnoses, enrollment.\"\"\"\n    # Age at index\n    cohort = cohort.merge(demographics, on=\"patient_id\")\n    cohort[\"age\"] = (cohort[\"index_date\"] - cohort[\"birth_date\"]).dt.days \u002F 365.25\n    cohort = cohort[cohort[\"age\"] >= min_age]\n\n    # Required diagnosis in pre-index window\n    if required_dx_codes:\n        pre_dx = diagnoses[diagnoses[\"icd10_code\"].isin(required_dx_codes)]\n        pre_dx = pre_dx.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n        pre_dx = pre_dx[\n            (pre_dx[\"dx_date\"] >= pre_dx[\"index_date\"] - pd.Timedelta(days=pre_index_window)) &\n            (pre_dx[\"dx_date\"] \u003C pre_dx[\"index_date\"])\n        ]\n        cohort = cohort[cohort[\"patient_id\"].isin(pre_dx[\"patient_id\"].unique())]\n\n    # Exclude patients with certain diagnoses\n    if excluded_dx_codes:\n        excl = diagnoses[diagnoses[\"icd10_code\"].isin(excluded_dx_codes)]\n        excl = excl.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n        excl = excl[excl[\"dx_date\"] \u003C excl[\"index_date\"]]\n        cohort = cohort[~cohort[\"patient_id\"].isin(excl[\"patient_id\"].unique())]\n\n    # Minimum follow-up enrollment\n    cohort = cohort.merge(enrollment, on=\"patient_id\")\n    cohort[\"followup_end\"] = cohort[[\"enroll_end\", cohort.columns[-1]]].min(axis=1)\n    cohort = cohort[\n        (cohort[\"enroll_end\"] - cohort[\"index_date\"]).dt.days >= min_followup\n    ]\n    return cohort\n",[806],{"type":75,"tag":263,"props":807,"children":808},{"__ignoreMap":476},[809,817,825,833,841,849,857,865,873,880,888,896,904,912,920,928,936,944,952,959,967,975,983,991,999,1007,1015,1024,1033,1042,1051,1060,1068],{"type":75,"tag":482,"props":810,"children":811},{"class":484,"line":485},[812],{"type":75,"tag":482,"props":813,"children":814},{},[815],{"type":81,"value":816},"def apply_criteria(cohort, diagnoses, enrollment, min_age=18,\n",{"type":75,"tag":482,"props":818,"children":819},{"class":484,"line":494},[820],{"type":75,"tag":482,"props":821,"children":822},{},[823],{"type":81,"value":824},"                   required_dx_codes=None, excluded_dx_codes=None,\n",{"type":75,"tag":482,"props":826,"children":827},{"class":484,"line":503},[828],{"type":75,"tag":482,"props":829,"children":830},{},[831],{"type":81,"value":832},"                   pre_index_window=365, min_followup=30):\n",{"type":75,"tag":482,"props":834,"children":835},{"class":484,"line":29},[836],{"type":75,"tag":482,"props":837,"children":838},{},[839],{"type":81,"value":840},"    \"\"\"Filter cohort by age, required\u002Fexcluded diagnoses, enrollment.\"\"\"\n",{"type":75,"tag":482,"props":842,"children":843},{"class":484,"line":521},[844],{"type":75,"tag":482,"props":845,"children":846},{},[847],{"type":81,"value":848},"    # Age at index\n",{"type":75,"tag":482,"props":850,"children":851},{"class":484,"line":530},[852],{"type":75,"tag":482,"props":853,"children":854},{},[855],{"type":81,"value":856},"    cohort = cohort.merge(demographics, on=\"patient_id\")\n",{"type":75,"tag":482,"props":858,"children":859},{"class":484,"line":539},[860],{"type":75,"tag":482,"props":861,"children":862},{},[863],{"type":81,"value":864},"    cohort[\"age\"] = (cohort[\"index_date\"] - cohort[\"birth_date\"]).dt.days \u002F 365.25\n",{"type":75,"tag":482,"props":866,"children":867},{"class":484,"line":548},[868],{"type":75,"tag":482,"props":869,"children":870},{},[871],{"type":81,"value":872},"    cohort = cohort[cohort[\"age\"] >= min_age]\n",{"type":75,"tag":482,"props":874,"children":875},{"class":484,"line":556},[876],{"type":75,"tag":482,"props":877,"children":878},{"emptyLinePlaceholder":515},[879],{"type":81,"value":518},{"type":75,"tag":482,"props":881,"children":882},{"class":484,"line":565},[883],{"type":75,"tag":482,"props":884,"children":885},{},[886],{"type":81,"value":887},"    # Required diagnosis in pre-index window\n",{"type":75,"tag":482,"props":889,"children":890},{"class":484,"line":665},[891],{"type":75,"tag":482,"props":892,"children":893},{},[894],{"type":81,"value":895},"    if required_dx_codes:\n",{"type":75,"tag":482,"props":897,"children":898},{"class":484,"line":673},[899],{"type":75,"tag":482,"props":900,"children":901},{},[902],{"type":81,"value":903},"        pre_dx = diagnoses[diagnoses[\"icd10_code\"].isin(required_dx_codes)]\n",{"type":75,"tag":482,"props":905,"children":906},{"class":484,"line":682},[907],{"type":75,"tag":482,"props":908,"children":909},{},[910],{"type":81,"value":911},"        pre_dx = pre_dx.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n",{"type":75,"tag":482,"props":913,"children":914},{"class":484,"line":691},[915],{"type":75,"tag":482,"props":916,"children":917},{},[918],{"type":81,"value":919},"        pre_dx = pre_dx[\n",{"type":75,"tag":482,"props":921,"children":922},{"class":484,"line":700},[923],{"type":75,"tag":482,"props":924,"children":925},{},[926],{"type":81,"value":927},"            (pre_dx[\"dx_date\"] >= pre_dx[\"index_date\"] - pd.Timedelta(days=pre_index_window)) &\n",{"type":75,"tag":482,"props":929,"children":930},{"class":484,"line":709},[931],{"type":75,"tag":482,"props":932,"children":933},{},[934],{"type":81,"value":935},"            (pre_dx[\"dx_date\"] \u003C pre_dx[\"index_date\"])\n",{"type":75,"tag":482,"props":937,"children":938},{"class":484,"line":717},[939],{"type":75,"tag":482,"props":940,"children":941},{},[942],{"type":81,"value":943},"        ]\n",{"type":75,"tag":482,"props":945,"children":946},{"class":484,"line":726},[947],{"type":75,"tag":482,"props":948,"children":949},{},[950],{"type":81,"value":951},"        cohort = cohort[cohort[\"patient_id\"].isin(pre_dx[\"patient_id\"].unique())]\n",{"type":75,"tag":482,"props":953,"children":954},{"class":484,"line":735},[955],{"type":75,"tag":482,"props":956,"children":957},{"emptyLinePlaceholder":515},[958],{"type":81,"value":518},{"type":75,"tag":482,"props":960,"children":961},{"class":484,"line":744},[962],{"type":75,"tag":482,"props":963,"children":964},{},[965],{"type":81,"value":966},"    # Exclude patients with certain diagnoses\n",{"type":75,"tag":482,"props":968,"children":969},{"class":484,"line":753},[970],{"type":75,"tag":482,"props":971,"children":972},{},[973],{"type":81,"value":974},"    if excluded_dx_codes:\n",{"type":75,"tag":482,"props":976,"children":977},{"class":484,"line":762},[978],{"type":75,"tag":482,"props":979,"children":980},{},[981],{"type":81,"value":982},"        excl = diagnoses[diagnoses[\"icd10_code\"].isin(excluded_dx_codes)]\n",{"type":75,"tag":482,"props":984,"children":985},{"class":484,"line":771},[986],{"type":75,"tag":482,"props":987,"children":988},{},[989],{"type":81,"value":990},"        excl = excl.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n",{"type":75,"tag":482,"props":992,"children":993},{"class":484,"line":780},[994],{"type":75,"tag":482,"props":995,"children":996},{},[997],{"type":81,"value":998},"        excl = excl[excl[\"dx_date\"] \u003C excl[\"index_date\"]]\n",{"type":75,"tag":482,"props":1000,"children":1001},{"class":484,"line":789},[1002],{"type":75,"tag":482,"props":1003,"children":1004},{},[1005],{"type":81,"value":1006},"        cohort = cohort[~cohort[\"patient_id\"].isin(excl[\"patient_id\"].unique())]\n",{"type":75,"tag":482,"props":1008,"children":1010},{"class":484,"line":1009},26,[1011],{"type":75,"tag":482,"props":1012,"children":1013},{"emptyLinePlaceholder":515},[1014],{"type":81,"value":518},{"type":75,"tag":482,"props":1016,"children":1018},{"class":484,"line":1017},27,[1019],{"type":75,"tag":482,"props":1020,"children":1021},{},[1022],{"type":81,"value":1023},"    # Minimum follow-up enrollment\n",{"type":75,"tag":482,"props":1025,"children":1027},{"class":484,"line":1026},28,[1028],{"type":75,"tag":482,"props":1029,"children":1030},{},[1031],{"type":81,"value":1032},"    cohort = cohort.merge(enrollment, on=\"patient_id\")\n",{"type":75,"tag":482,"props":1034,"children":1036},{"class":484,"line":1035},29,[1037],{"type":75,"tag":482,"props":1038,"children":1039},{},[1040],{"type":81,"value":1041},"    cohort[\"followup_end\"] = cohort[[\"enroll_end\", cohort.columns[-1]]].min(axis=1)\n",{"type":75,"tag":482,"props":1043,"children":1045},{"class":484,"line":1044},30,[1046],{"type":75,"tag":482,"props":1047,"children":1048},{},[1049],{"type":81,"value":1050},"    cohort = cohort[\n",{"type":75,"tag":482,"props":1052,"children":1054},{"class":484,"line":1053},31,[1055],{"type":75,"tag":482,"props":1056,"children":1057},{},[1058],{"type":81,"value":1059},"        (cohort[\"enroll_end\"] - cohort[\"index_date\"]).dt.days >= min_followup\n",{"type":75,"tag":482,"props":1061,"children":1063},{"class":484,"line":1062},32,[1064],{"type":75,"tag":482,"props":1065,"children":1066},{},[1067],{"type":81,"value":768},{"type":75,"tag":482,"props":1069,"children":1071},{"class":484,"line":1070},33,[1072],{"type":75,"tag":482,"props":1073,"children":1074},{},[1075],{"type":81,"value":1076},"    return cohort\n",{"type":75,"tag":217,"props":1078,"children":1080},{"id":1079},"_2-medication-adherence",[1081],{"type":81,"value":1082},"2. Medication Adherence",{"type":75,"tag":464,"props":1084,"children":1086},{"id":1085},"pdc-proportion-of-days-covered",[1087],{"type":81,"value":1088},"PDC (Proportion of Days Covered)",{"type":75,"tag":91,"props":1090,"children":1091},{},[1092],{"type":81,"value":1093},"PDC is the preferred measure (endorsed by PQA). It counts the number of days\nin the measurement period covered by at least one fill, capped at 1.0.",{"type":75,"tag":471,"props":1095,"children":1097},{"className":473,"code":1096,"language":475,"meta":476,"style":476},"def calculate_pdc(pharmacy, cohort, ndc_list, period_days=365):\n    \"\"\"Calculate PDC for each patient over a fixed measurement period.\"\"\"\n    fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    fills = fills.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n    fills[\"period_end\"] = fills[\"index_date\"] + pd.Timedelta(days=period_days)\n\n    # Keep fills within measurement period\n    fills = fills[fills[\"fill_date\"] \u003C fills[\"period_end\"]]\n    fills[\"cover_start\"] = fills[\"fill_date\"]\n    fills[\"cover_end\"] = fills[\"fill_date\"] + pd.to_timedelta(fills[\"days_supply\"], unit=\"D\")\n    fills[\"cover_end\"] = fills[[\"cover_end\", \"period_end\"]].min(axis=1)\n    fills[\"cover_start\"] = fills[[\"cover_start\", \"index_date\"]].max(axis=1)\n\n    # Build day-level coverage (vectorized per patient)\n    results = []\n    for pid, grp in fills.groupby(\"patient_id\"):\n        days_covered = set()\n        for _, row in grp.iterrows():\n            days_covered.update(pd.date_range(row[\"cover_start\"], row[\"cover_end\"] - pd.Timedelta(days=1)))\n        results.append({\"patient_id\": pid, \"pdc\": min(len(days_covered) \u002F period_days, 1.0)})\n    return pd.DataFrame(results)\n",[1098],{"type":75,"tag":263,"props":1099,"children":1100},{"__ignoreMap":476},[1101,1109,1117,1125,1133,1141,1148,1156,1164,1172,1180,1188,1196,1203,1211,1219,1227,1235,1243,1251,1259],{"type":75,"tag":482,"props":1102,"children":1103},{"class":484,"line":485},[1104],{"type":75,"tag":482,"props":1105,"children":1106},{},[1107],{"type":81,"value":1108},"def calculate_pdc(pharmacy, cohort, ndc_list, period_days=365):\n",{"type":75,"tag":482,"props":1110,"children":1111},{"class":484,"line":494},[1112],{"type":75,"tag":482,"props":1113,"children":1114},{},[1115],{"type":81,"value":1116},"    \"\"\"Calculate PDC for each patient over a fixed measurement period.\"\"\"\n",{"type":75,"tag":482,"props":1118,"children":1119},{"class":484,"line":503},[1120],{"type":75,"tag":482,"props":1121,"children":1122},{},[1123],{"type":81,"value":1124},"    fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n",{"type":75,"tag":482,"props":1126,"children":1127},{"class":484,"line":29},[1128],{"type":75,"tag":482,"props":1129,"children":1130},{},[1131],{"type":81,"value":1132},"    fills = fills.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n",{"type":75,"tag":482,"props":1134,"children":1135},{"class":484,"line":521},[1136],{"type":75,"tag":482,"props":1137,"children":1138},{},[1139],{"type":81,"value":1140},"    fills[\"period_end\"] = fills[\"index_date\"] + pd.Timedelta(days=period_days)\n",{"type":75,"tag":482,"props":1142,"children":1143},{"class":484,"line":530},[1144],{"type":75,"tag":482,"props":1145,"children":1146},{"emptyLinePlaceholder":515},[1147],{"type":81,"value":518},{"type":75,"tag":482,"props":1149,"children":1150},{"class":484,"line":539},[1151],{"type":75,"tag":482,"props":1152,"children":1153},{},[1154],{"type":81,"value":1155},"    # Keep fills within measurement period\n",{"type":75,"tag":482,"props":1157,"children":1158},{"class":484,"line":548},[1159],{"type":75,"tag":482,"props":1160,"children":1161},{},[1162],{"type":81,"value":1163},"    fills = fills[fills[\"fill_date\"] \u003C fills[\"period_end\"]]\n",{"type":75,"tag":482,"props":1165,"children":1166},{"class":484,"line":556},[1167],{"type":75,"tag":482,"props":1168,"children":1169},{},[1170],{"type":81,"value":1171},"    fills[\"cover_start\"] = fills[\"fill_date\"]\n",{"type":75,"tag":482,"props":1173,"children":1174},{"class":484,"line":565},[1175],{"type":75,"tag":482,"props":1176,"children":1177},{},[1178],{"type":81,"value":1179},"    fills[\"cover_end\"] = fills[\"fill_date\"] + pd.to_timedelta(fills[\"days_supply\"], unit=\"D\")\n",{"type":75,"tag":482,"props":1181,"children":1182},{"class":484,"line":665},[1183],{"type":75,"tag":482,"props":1184,"children":1185},{},[1186],{"type":81,"value":1187},"    fills[\"cover_end\"] = fills[[\"cover_end\", \"period_end\"]].min(axis=1)\n",{"type":75,"tag":482,"props":1189,"children":1190},{"class":484,"line":673},[1191],{"type":75,"tag":482,"props":1192,"children":1193},{},[1194],{"type":81,"value":1195},"    fills[\"cover_start\"] = fills[[\"cover_start\", \"index_date\"]].max(axis=1)\n",{"type":75,"tag":482,"props":1197,"children":1198},{"class":484,"line":682},[1199],{"type":75,"tag":482,"props":1200,"children":1201},{"emptyLinePlaceholder":515},[1202],{"type":81,"value":518},{"type":75,"tag":482,"props":1204,"children":1205},{"class":484,"line":691},[1206],{"type":75,"tag":482,"props":1207,"children":1208},{},[1209],{"type":81,"value":1210},"    # Build day-level coverage (vectorized per patient)\n",{"type":75,"tag":482,"props":1212,"children":1213},{"class":484,"line":700},[1214],{"type":75,"tag":482,"props":1215,"children":1216},{},[1217],{"type":81,"value":1218},"    results = []\n",{"type":75,"tag":482,"props":1220,"children":1221},{"class":484,"line":709},[1222],{"type":75,"tag":482,"props":1223,"children":1224},{},[1225],{"type":81,"value":1226},"    for pid, grp in fills.groupby(\"patient_id\"):\n",{"type":75,"tag":482,"props":1228,"children":1229},{"class":484,"line":717},[1230],{"type":75,"tag":482,"props":1231,"children":1232},{},[1233],{"type":81,"value":1234},"        days_covered = set()\n",{"type":75,"tag":482,"props":1236,"children":1237},{"class":484,"line":726},[1238],{"type":75,"tag":482,"props":1239,"children":1240},{},[1241],{"type":81,"value":1242},"        for _, row in grp.iterrows():\n",{"type":75,"tag":482,"props":1244,"children":1245},{"class":484,"line":735},[1246],{"type":75,"tag":482,"props":1247,"children":1248},{},[1249],{"type":81,"value":1250},"            days_covered.update(pd.date_range(row[\"cover_start\"], row[\"cover_end\"] - pd.Timedelta(days=1)))\n",{"type":75,"tag":482,"props":1252,"children":1253},{"class":484,"line":744},[1254],{"type":75,"tag":482,"props":1255,"children":1256},{},[1257],{"type":81,"value":1258},"        results.append({\"patient_id\": pid, \"pdc\": min(len(days_covered) \u002F period_days, 1.0)})\n",{"type":75,"tag":482,"props":1260,"children":1261},{"class":484,"line":753},[1262],{"type":75,"tag":482,"props":1263,"children":1264},{},[1265],{"type":81,"value":1266},"    return pd.DataFrame(results)\n",{"type":75,"tag":464,"props":1268,"children":1270},{"id":1269},"mpr-medication-possession-ratio",[1271],{"type":81,"value":1272},"MPR (Medication Possession Ratio)",{"type":75,"tag":471,"props":1274,"children":1276},{"className":473,"code":1275,"language":475,"meta":476,"style":476},"def calculate_mpr(pharmacy, cohort, ndc_list, period_days=365):\n    \"\"\"MPR = total days supply dispensed \u002F days in period. Can exceed 1.0.\"\"\"\n    fills = pharmacy[pharmacy[\"ndc_code\"].isin(ndc_list)].copy()\n    fills = fills.merge(cohort[[\"patient_id\", \"index_date\"]], on=\"patient_id\")\n    fills[\"period_end\"] = fills[\"index_date\"] + pd.Timedelta(days=period_days)\n    fills = fills[(fills[\"fill_date\"] >= fills[\"index_date\"]) &\n                  (fills[\"fill_date\"] \u003C fills[\"period_end\"])]\n    mpr = fills.groupby(\"patient_id\")[\"days_supply\"].sum().reset_index()\n    mpr.columns = [\"patient_id\", \"total_supply\"]\n    mpr[\"mpr\"] = mpr[\"total_supply\"] \u002F period_days\n    return mpr\n",[1277],{"type":75,"tag":263,"props":1278,"children":1279},{"__ignoreMap":476},[1280,1288,1296,1303,1310,1317,1325,1333,1341,1349,1357],{"type":75,"tag":482,"props":1281,"children":1282},{"class":484,"line":485},[1283],{"type":75,"tag":482,"props":1284,"children":1285},{},[1286],{"type":81,"value":1287},"def calculate_mpr(pharmacy, cohort, ndc_list, period_days=365):\n",{"type":75,"tag":482,"props":1289,"children":1290},{"class":484,"line":494},[1291],{"type":75,"tag":482,"props":1292,"children":1293},{},[1294],{"type":81,"value":1295},"    \"\"\"MPR = total days supply dispensed \u002F days in period. Can exceed 1.0.\"\"\"\n",{"type":75,"tag":482,"props":1297,"children":1298},{"class":484,"line":503},[1299],{"type":75,"tag":482,"props":1300,"children":1301},{},[1302],{"type":81,"value":1124},{"type":75,"tag":482,"props":1304,"children":1305},{"class":484,"line":29},[1306],{"type":75,"tag":482,"props":1307,"children":1308},{},[1309],{"type":81,"value":1132},{"type":75,"tag":482,"props":1311,"children":1312},{"class":484,"line":521},[1313],{"type":75,"tag":482,"props":1314,"children":1315},{},[1316],{"type":81,"value":1140},{"type":75,"tag":482,"props":1318,"children":1319},{"class":484,"line":530},[1320],{"type":75,"tag":482,"props":1321,"children":1322},{},[1323],{"type":81,"value":1324},"    fills = fills[(fills[\"fill_date\"] >= fills[\"index_date\"]) &\n",{"type":75,"tag":482,"props":1326,"children":1327},{"class":484,"line":539},[1328],{"type":75,"tag":482,"props":1329,"children":1330},{},[1331],{"type":81,"value":1332},"                  (fills[\"fill_date\"] \u003C fills[\"period_end\"])]\n",{"type":75,"tag":482,"props":1334,"children":1335},{"class":484,"line":548},[1336],{"type":75,"tag":482,"props":1337,"children":1338},{},[1339],{"type":81,"value":1340},"    mpr = fills.groupby(\"patient_id\")[\"days_supply\"].sum().reset_index()\n",{"type":75,"tag":482,"props":1342,"children":1343},{"class":484,"line":556},[1344],{"type":75,"tag":482,"props":1345,"children":1346},{},[1347],{"type":81,"value":1348},"    mpr.columns = [\"patient_id\", \"total_supply\"]\n",{"type":75,"tag":482,"props":1350,"children":1351},{"class":484,"line":565},[1352],{"type":75,"tag":482,"props":1353,"children":1354},{},[1355],{"type":81,"value":1356},"    mpr[\"mpr\"] = mpr[\"total_supply\"] \u002F period_days\n",{"type":75,"tag":482,"props":1358,"children":1359},{"class":484,"line":665},[1360],{"type":75,"tag":482,"props":1361,"children":1362},{},[1363],{"type":81,"value":1364},"    return mpr\n",{"type":75,"tag":229,"props":1366,"children":1367},{},[1368,1394],{"type":75,"tag":233,"props":1369,"children":1370},{},[1371],{"type":75,"tag":237,"props":1372,"children":1373},{},[1374,1379,1384,1389],{"type":75,"tag":241,"props":1375,"children":1376},{},[1377],{"type":81,"value":1378},"Metric",{"type":75,"tag":241,"props":1380,"children":1381},{},[1382],{"type":81,"value":1383},"Formula",{"type":75,"tag":241,"props":1385,"children":1386},{},[1387],{"type":81,"value":1388},"Range",{"type":75,"tag":241,"props":1390,"children":1391},{},[1392],{"type":81,"value":1393},"Notes",{"type":75,"tag":252,"props":1395,"children":1396},{},[1397,1424],{"type":75,"tag":237,"props":1398,"children":1399},{},[1400,1409,1414,1419],{"type":75,"tag":259,"props":1401,"children":1402},{},[1403],{"type":75,"tag":1404,"props":1405,"children":1406},"strong",{},[1407],{"type":81,"value":1408},"PDC",{"type":75,"tag":259,"props":1410,"children":1411},{},[1412],{"type":81,"value":1413},"Days covered \u002F days in period",{"type":75,"tag":259,"props":1415,"children":1416},{},[1417],{"type":81,"value":1418},"0–1.0",{"type":75,"tag":259,"props":1420,"children":1421},{},[1422],{"type":81,"value":1423},"Preferred; no double-counting overlapping fills",{"type":75,"tag":237,"props":1425,"children":1426},{},[1427,1435,1440,1445],{"type":75,"tag":259,"props":1428,"children":1429},{},[1430],{"type":75,"tag":1404,"props":1431,"children":1432},{},[1433],{"type":81,"value":1434},"MPR",{"type":75,"tag":259,"props":1436,"children":1437},{},[1438],{"type":81,"value":1439},"Total days supply \u002F days in period",{"type":75,"tag":259,"props":1441,"children":1442},{},[1443],{"type":81,"value":1444},"0–∞",{"type":75,"tag":259,"props":1446,"children":1447},{},[1448],{"type":81,"value":1449},"Can exceed 1.0 with early refills; less preferred",{"type":75,"tag":91,"props":1451,"children":1452},{},[1453],{"type":81,"value":1454},"Adherence threshold: PDC ≥ 0.80 is the standard cutoff for \"adherent.\"",{"type":75,"tag":217,"props":1456,"children":1458},{"id":1457},"_3-propensity-score-estimation",[1459],{"type":81,"value":1460},"3. Propensity Score Estimation",{"type":75,"tag":464,"props":1462,"children":1464},{"id":1463},"python-scikit-learn",[1465],{"type":81,"value":1466},"Python (scikit-learn)",{"type":75,"tag":471,"props":1468,"children":1470},{"className":473,"code":1469,"language":475,"meta":476,"style":476},"from sklearn.linear_model import LogisticRegression\nimport numpy as np\n\ndef estimate_ps(cohort, covariate_cols, treatment_col=\"treatment\"):\n    \"\"\"Fit logistic regression PS model. Returns cohort with ps column.\"\"\"\n    X = cohort[covariate_cols].fillna(0)\n    y = cohort[treatment_col]\n    model = LogisticRegression(max_iter=1000, solver=\"lbfgs\", C=1e6)\n    model.fit(X, y)\n    cohort = cohort.copy()\n    cohort[\"ps\"] = model.predict_proba(X)[:, 1]\n    return cohort, model\n",[1471],{"type":75,"tag":263,"props":1472,"children":1473},{"__ignoreMap":476},[1474,1482,1490,1497,1505,1513,1521,1529,1537,1545,1553,1561],{"type":75,"tag":482,"props":1475,"children":1476},{"class":484,"line":485},[1477],{"type":75,"tag":482,"props":1478,"children":1479},{},[1480],{"type":81,"value":1481},"from sklearn.linear_model import LogisticRegression\n",{"type":75,"tag":482,"props":1483,"children":1484},{"class":484,"line":494},[1485],{"type":75,"tag":482,"props":1486,"children":1487},{},[1488],{"type":81,"value":1489},"import numpy as np\n",{"type":75,"tag":482,"props":1491,"children":1492},{"class":484,"line":503},[1493],{"type":75,"tag":482,"props":1494,"children":1495},{"emptyLinePlaceholder":515},[1496],{"type":81,"value":518},{"type":75,"tag":482,"props":1498,"children":1499},{"class":484,"line":29},[1500],{"type":75,"tag":482,"props":1501,"children":1502},{},[1503],{"type":81,"value":1504},"def estimate_ps(cohort, covariate_cols, treatment_col=\"treatment\"):\n",{"type":75,"tag":482,"props":1506,"children":1507},{"class":484,"line":521},[1508],{"type":75,"tag":482,"props":1509,"children":1510},{},[1511],{"type":81,"value":1512},"    \"\"\"Fit logistic regression PS model. Returns cohort with ps column.\"\"\"\n",{"type":75,"tag":482,"props":1514,"children":1515},{"class":484,"line":530},[1516],{"type":75,"tag":482,"props":1517,"children":1518},{},[1519],{"type":81,"value":1520},"    X = cohort[covariate_cols].fillna(0)\n",{"type":75,"tag":482,"props":1522,"children":1523},{"class":484,"line":539},[1524],{"type":75,"tag":482,"props":1525,"children":1526},{},[1527],{"type":81,"value":1528},"    y = cohort[treatment_col]\n",{"type":75,"tag":482,"props":1530,"children":1531},{"class":484,"line":548},[1532],{"type":75,"tag":482,"props":1533,"children":1534},{},[1535],{"type":81,"value":1536},"    model = LogisticRegression(max_iter=1000, solver=\"lbfgs\", C=1e6)\n",{"type":75,"tag":482,"props":1538,"children":1539},{"class":484,"line":556},[1540],{"type":75,"tag":482,"props":1541,"children":1542},{},[1543],{"type":81,"value":1544},"    model.fit(X, y)\n",{"type":75,"tag":482,"props":1546,"children":1547},{"class":484,"line":565},[1548],{"type":75,"tag":482,"props":1549,"children":1550},{},[1551],{"type":81,"value":1552},"    cohort = cohort.copy()\n",{"type":75,"tag":482,"props":1554,"children":1555},{"class":484,"line":665},[1556],{"type":75,"tag":482,"props":1557,"children":1558},{},[1559],{"type":81,"value":1560},"    cohort[\"ps\"] = model.predict_proba(X)[:, 1]\n",{"type":75,"tag":482,"props":1562,"children":1563},{"class":484,"line":673},[1564],{"type":75,"tag":482,"props":1565,"children":1566},{},[1567],{"type":81,"value":1568},"    return cohort, model\n",{"type":75,"tag":464,"props":1570,"children":1572},{"id":1571},"r-matchit-cobalt",[1573],{"type":81,"value":1574},"R (MatchIt + cobalt)",{"type":75,"tag":471,"props":1576,"children":1580},{"className":1577,"code":1578,"language":1579,"meta":476,"style":476},"language-r shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","library(MatchIt)\nlibrary(cobalt)\n\n# Estimate PS and perform 1:1 nearest-neighbor matching\nm \u003C- matchit(treatment ~ age + sex + charlson + prior_hosp + prior_rx_count,\n             data = cohort, method = \"nearest\", distance = \"glm\",\n             caliper = 0.2, ratio = 1)\nmatched_data \u003C- match.data(m)\n\n# Balance diagnostics\nbal.tab(m, thresholds = c(m = 0.1))  # SMD threshold\nlove.plot(m, threshold = 0.1)\n","r",[1581],{"type":75,"tag":263,"props":1582,"children":1583},{"__ignoreMap":476},[1584,1592,1600,1607,1615,1623,1631,1639,1647,1654,1662,1670],{"type":75,"tag":482,"props":1585,"children":1586},{"class":484,"line":485},[1587],{"type":75,"tag":482,"props":1588,"children":1589},{},[1590],{"type":81,"value":1591},"library(MatchIt)\n",{"type":75,"tag":482,"props":1593,"children":1594},{"class":484,"line":494},[1595],{"type":75,"tag":482,"props":1596,"children":1597},{},[1598],{"type":81,"value":1599},"library(cobalt)\n",{"type":75,"tag":482,"props":1601,"children":1602},{"class":484,"line":503},[1603],{"type":75,"tag":482,"props":1604,"children":1605},{"emptyLinePlaceholder":515},[1606],{"type":81,"value":518},{"type":75,"tag":482,"props":1608,"children":1609},{"class":484,"line":29},[1610],{"type":75,"tag":482,"props":1611,"children":1612},{},[1613],{"type":81,"value":1614},"# Estimate PS and perform 1:1 nearest-neighbor matching\n",{"type":75,"tag":482,"props":1616,"children":1617},{"class":484,"line":521},[1618],{"type":75,"tag":482,"props":1619,"children":1620},{},[1621],{"type":81,"value":1622},"m \u003C- matchit(treatment ~ age + sex + charlson + prior_hosp + prior_rx_count,\n",{"type":75,"tag":482,"props":1624,"children":1625},{"class":484,"line":530},[1626],{"type":75,"tag":482,"props":1627,"children":1628},{},[1629],{"type":81,"value":1630},"             data = cohort, method = \"nearest\", distance = \"glm\",\n",{"type":75,"tag":482,"props":1632,"children":1633},{"class":484,"line":539},[1634],{"type":75,"tag":482,"props":1635,"children":1636},{},[1637],{"type":81,"value":1638},"             caliper = 0.2, ratio = 1)\n",{"type":75,"tag":482,"props":1640,"children":1641},{"class":484,"line":548},[1642],{"type":75,"tag":482,"props":1643,"children":1644},{},[1645],{"type":81,"value":1646},"matched_data \u003C- match.data(m)\n",{"type":75,"tag":482,"props":1648,"children":1649},{"class":484,"line":556},[1650],{"type":75,"tag":482,"props":1651,"children":1652},{"emptyLinePlaceholder":515},[1653],{"type":81,"value":518},{"type":75,"tag":482,"props":1655,"children":1656},{"class":484,"line":565},[1657],{"type":75,"tag":482,"props":1658,"children":1659},{},[1660],{"type":81,"value":1661},"# Balance diagnostics\n",{"type":75,"tag":482,"props":1663,"children":1664},{"class":484,"line":665},[1665],{"type":75,"tag":482,"props":1666,"children":1667},{},[1668],{"type":81,"value":1669},"bal.tab(m, thresholds = c(m = 0.1))  # SMD threshold\n",{"type":75,"tag":482,"props":1671,"children":1672},{"class":484,"line":673},[1673],{"type":75,"tag":482,"props":1674,"children":1675},{},[1676],{"type":81,"value":1677},"love.plot(m, threshold = 0.1)\n",{"type":75,"tag":217,"props":1679,"children":1681},{"id":1680},"_4-balance-diagnostics",[1682],{"type":81,"value":1683},"4. Balance Diagnostics",{"type":75,"tag":464,"props":1685,"children":1687},{"id":1686},"smd-calculation-python",[1688],{"type":81,"value":1689},"SMD calculation (Python)",{"type":75,"tag":471,"props":1691,"children":1693},{"className":473,"code":1692,"language":475,"meta":476,"style":476},"def compute_smd(cohort, covariate_cols, treatment_col=\"treatment\"):\n    \"\"\"Compute standardized mean differences for all covariates.\"\"\"\n    treated = cohort[cohort[treatment_col] == 1]\n    comparator = cohort[cohort[treatment_col] == 0]\n    results = []\n    for col in covariate_cols:\n        mean_t = treated[col].mean()\n        mean_c = comparator[col].mean()\n        var_t = treated[col].var()\n        var_c = comparator[col].var()\n        pooled_sd = np.sqrt((var_t + var_c) \u002F 2)\n        smd = abs(mean_t - mean_c) \u002F pooled_sd if pooled_sd > 0 else 0\n        results.append({\"covariate\": col, \"smd\": round(smd, 4),\n                        \"balanced\": smd \u003C 0.1})\n    return pd.DataFrame(results)\n",[1694],{"type":75,"tag":263,"props":1695,"children":1696},{"__ignoreMap":476},[1697,1705,1713,1721,1729,1736,1744,1752,1760,1768,1776,1784,1792,1800,1808],{"type":75,"tag":482,"props":1698,"children":1699},{"class":484,"line":485},[1700],{"type":75,"tag":482,"props":1701,"children":1702},{},[1703],{"type":81,"value":1704},"def compute_smd(cohort, covariate_cols, treatment_col=\"treatment\"):\n",{"type":75,"tag":482,"props":1706,"children":1707},{"class":484,"line":494},[1708],{"type":75,"tag":482,"props":1709,"children":1710},{},[1711],{"type":81,"value":1712},"    \"\"\"Compute standardized mean differences for all covariates.\"\"\"\n",{"type":75,"tag":482,"props":1714,"children":1715},{"class":484,"line":503},[1716],{"type":75,"tag":482,"props":1717,"children":1718},{},[1719],{"type":81,"value":1720},"    treated = cohort[cohort[treatment_col] == 1]\n",{"type":75,"tag":482,"props":1722,"children":1723},{"class":484,"line":29},[1724],{"type":75,"tag":482,"props":1725,"children":1726},{},[1727],{"type":81,"value":1728},"    comparator = cohort[cohort[treatment_col] == 0]\n",{"type":75,"tag":482,"props":1730,"children":1731},{"class":484,"line":521},[1732],{"type":75,"tag":482,"props":1733,"children":1734},{},[1735],{"type":81,"value":1218},{"type":75,"tag":482,"props":1737,"children":1738},{"class":484,"line":530},[1739],{"type":75,"tag":482,"props":1740,"children":1741},{},[1742],{"type":81,"value":1743},"    for col in covariate_cols:\n",{"type":75,"tag":482,"props":1745,"children":1746},{"class":484,"line":539},[1747],{"type":75,"tag":482,"props":1748,"children":1749},{},[1750],{"type":81,"value":1751},"        mean_t = treated[col].mean()\n",{"type":75,"tag":482,"props":1753,"children":1754},{"class":484,"line":548},[1755],{"type":75,"tag":482,"props":1756,"children":1757},{},[1758],{"type":81,"value":1759},"        mean_c = comparator[col].mean()\n",{"type":75,"tag":482,"props":1761,"children":1762},{"class":484,"line":556},[1763],{"type":75,"tag":482,"props":1764,"children":1765},{},[1766],{"type":81,"value":1767},"        var_t = treated[col].var()\n",{"type":75,"tag":482,"props":1769,"children":1770},{"class":484,"line":565},[1771],{"type":75,"tag":482,"props":1772,"children":1773},{},[1774],{"type":81,"value":1775},"        var_c = comparator[col].var()\n",{"type":75,"tag":482,"props":1777,"children":1778},{"class":484,"line":665},[1779],{"type":75,"tag":482,"props":1780,"children":1781},{},[1782],{"type":81,"value":1783},"        pooled_sd = np.sqrt((var_t + var_c) \u002F 2)\n",{"type":75,"tag":482,"props":1785,"children":1786},{"class":484,"line":673},[1787],{"type":75,"tag":482,"props":1788,"children":1789},{},[1790],{"type":81,"value":1791},"        smd = abs(mean_t - mean_c) \u002F pooled_sd if pooled_sd > 0 else 0\n",{"type":75,"tag":482,"props":1793,"children":1794},{"class":484,"line":682},[1795],{"type":75,"tag":482,"props":1796,"children":1797},{},[1798],{"type":81,"value":1799},"        results.append({\"covariate\": col, \"smd\": round(smd, 4),\n",{"type":75,"tag":482,"props":1801,"children":1802},{"class":484,"line":691},[1803],{"type":75,"tag":482,"props":1804,"children":1805},{},[1806],{"type":81,"value":1807},"                        \"balanced\": smd \u003C 0.1})\n",{"type":75,"tag":482,"props":1809,"children":1810},{"class":484,"line":700},[1811],{"type":75,"tag":482,"props":1812,"children":1813},{},[1814],{"type":81,"value":1266},{"type":75,"tag":229,"props":1816,"children":1817},{},[1818,1839],{"type":75,"tag":233,"props":1819,"children":1820},{},[1821],{"type":75,"tag":237,"props":1822,"children":1823},{},[1824,1829,1834],{"type":75,"tag":241,"props":1825,"children":1826},{},[1827],{"type":81,"value":1828},"Diagnostic",{"type":75,"tag":241,"props":1830,"children":1831},{},[1832],{"type":81,"value":1833},"Target",{"type":75,"tag":241,"props":1835,"children":1836},{},[1837],{"type":81,"value":1838},"Action if failed",{"type":75,"tag":252,"props":1840,"children":1841},{},[1842,1860,1878,1896],{"type":75,"tag":237,"props":1843,"children":1844},{},[1845,1850,1855],{"type":75,"tag":259,"props":1846,"children":1847},{},[1848],{"type":81,"value":1849},"SMD",{"type":75,"tag":259,"props":1851,"children":1852},{},[1853],{"type":81,"value":1854},"\u003C 0.1 for all covariates",{"type":75,"tag":259,"props":1856,"children":1857},{},[1858],{"type":81,"value":1859},"Add interactions\u002Fnon-linear terms to PS model",{"type":75,"tag":237,"props":1861,"children":1862},{},[1863,1868,1873],{"type":75,"tag":259,"props":1864,"children":1865},{},[1866],{"type":81,"value":1867},"Variance ratio",{"type":75,"tag":259,"props":1869,"children":1870},{},[1871],{"type":81,"value":1872},"0.5–2.0",{"type":75,"tag":259,"props":1874,"children":1875},{},[1876],{"type":81,"value":1877},"Re-specify PS model or trim extremes",{"type":75,"tag":237,"props":1879,"children":1880},{},[1881,1886,1891],{"type":75,"tag":259,"props":1882,"children":1883},{},[1884],{"type":81,"value":1885},"PS overlap",{"type":75,"tag":259,"props":1887,"children":1888},{},[1889],{"type":81,"value":1890},"Substantial overlap in both groups",{"type":75,"tag":259,"props":1892,"children":1893},{},[1894],{"type":81,"value":1895},"Trim non-overlapping regions; report trimmed N",{"type":75,"tag":237,"props":1897,"children":1898},{},[1899,1904,1909],{"type":75,"tag":259,"props":1900,"children":1901},{},[1902],{"type":81,"value":1903},"Weight distribution (IPTW)",{"type":75,"tag":259,"props":1905,"children":1906},{},[1907],{"type":81,"value":1908},"Mean ≈ 1.0; max \u003C 10",{"type":75,"tag":259,"props":1910,"children":1911},{},[1912],{"type":81,"value":1913},"Truncate at 1st\u002F99th percentile",{"type":75,"tag":217,"props":1915,"children":1917},{"id":1916},"_5-iptw-inverse-probability-of-treatment-weighting",[1918],{"type":81,"value":1919},"5. IPTW (Inverse Probability of Treatment Weighting)",{"type":75,"tag":471,"props":1921,"children":1923},{"className":473,"code":1922,"language":475,"meta":476,"style":476},"def compute_iptw(cohort, treatment_col=\"treatment\", ps_col=\"ps\",\n                 stabilized=True, truncate_pct=(1, 99)):\n    \"\"\"Compute stabilized IPTW weights with truncation.\"\"\"\n    cohort = cohort.copy()\n    t = cohort[treatment_col]\n    ps = cohort[ps_col]\n\n    if stabilized:\n        p_treat = t.mean()\n        cohort[\"iptw\"] = np.where(t == 1, p_treat \u002F ps, (1 - p_treat) \u002F (1 - ps))\n    else:\n        cohort[\"iptw\"] = np.where(t == 1, 1 \u002F ps, 1 \u002F (1 - ps))\n\n    # Truncate extreme weights\n    lo, hi = np.percentile(cohort[\"iptw\"], truncate_pct)\n    cohort[\"iptw\"] = cohort[\"iptw\"].clip(lo, hi)\n    return cohort\n",[1924],{"type":75,"tag":263,"props":1925,"children":1926},{"__ignoreMap":476},[1927,1935,1943,1951,1958,1966,1974,1981,1989,1997,2005,2013,2021,2028,2036,2044,2052],{"type":75,"tag":482,"props":1928,"children":1929},{"class":484,"line":485},[1930],{"type":75,"tag":482,"props":1931,"children":1932},{},[1933],{"type":81,"value":1934},"def compute_iptw(cohort, treatment_col=\"treatment\", ps_col=\"ps\",\n",{"type":75,"tag":482,"props":1936,"children":1937},{"class":484,"line":494},[1938],{"type":75,"tag":482,"props":1939,"children":1940},{},[1941],{"type":81,"value":1942},"                 stabilized=True, truncate_pct=(1, 99)):\n",{"type":75,"tag":482,"props":1944,"children":1945},{"class":484,"line":503},[1946],{"type":75,"tag":482,"props":1947,"children":1948},{},[1949],{"type":81,"value":1950},"    \"\"\"Compute stabilized IPTW weights with truncation.\"\"\"\n",{"type":75,"tag":482,"props":1952,"children":1953},{"class":484,"line":29},[1954],{"type":75,"tag":482,"props":1955,"children":1956},{},[1957],{"type":81,"value":1552},{"type":75,"tag":482,"props":1959,"children":1960},{"class":484,"line":521},[1961],{"type":75,"tag":482,"props":1962,"children":1963},{},[1964],{"type":81,"value":1965},"    t = cohort[treatment_col]\n",{"type":75,"tag":482,"props":1967,"children":1968},{"class":484,"line":530},[1969],{"type":75,"tag":482,"props":1970,"children":1971},{},[1972],{"type":81,"value":1973},"    ps = cohort[ps_col]\n",{"type":75,"tag":482,"props":1975,"children":1976},{"class":484,"line":539},[1977],{"type":75,"tag":482,"props":1978,"children":1979},{"emptyLinePlaceholder":515},[1980],{"type":81,"value":518},{"type":75,"tag":482,"props":1982,"children":1983},{"class":484,"line":548},[1984],{"type":75,"tag":482,"props":1985,"children":1986},{},[1987],{"type":81,"value":1988},"    if stabilized:\n",{"type":75,"tag":482,"props":1990,"children":1991},{"class":484,"line":556},[1992],{"type":75,"tag":482,"props":1993,"children":1994},{},[1995],{"type":81,"value":1996},"        p_treat = t.mean()\n",{"type":75,"tag":482,"props":1998,"children":1999},{"class":484,"line":565},[2000],{"type":75,"tag":482,"props":2001,"children":2002},{},[2003],{"type":81,"value":2004},"        cohort[\"iptw\"] = np.where(t == 1, p_treat \u002F ps, (1 - p_treat) \u002F (1 - ps))\n",{"type":75,"tag":482,"props":2006,"children":2007},{"class":484,"line":665},[2008],{"type":75,"tag":482,"props":2009,"children":2010},{},[2011],{"type":81,"value":2012},"    else:\n",{"type":75,"tag":482,"props":2014,"children":2015},{"class":484,"line":673},[2016],{"type":75,"tag":482,"props":2017,"children":2018},{},[2019],{"type":81,"value":2020},"        cohort[\"iptw\"] = np.where(t == 1, 1 \u002F ps, 1 \u002F (1 - ps))\n",{"type":75,"tag":482,"props":2022,"children":2023},{"class":484,"line":682},[2024],{"type":75,"tag":482,"props":2025,"children":2026},{"emptyLinePlaceholder":515},[2027],{"type":81,"value":518},{"type":75,"tag":482,"props":2029,"children":2030},{"class":484,"line":691},[2031],{"type":75,"tag":482,"props":2032,"children":2033},{},[2034],{"type":81,"value":2035},"    # Truncate extreme weights\n",{"type":75,"tag":482,"props":2037,"children":2038},{"class":484,"line":700},[2039],{"type":75,"tag":482,"props":2040,"children":2041},{},[2042],{"type":81,"value":2043},"    lo, hi = np.percentile(cohort[\"iptw\"], truncate_pct)\n",{"type":75,"tag":482,"props":2045,"children":2046},{"class":484,"line":709},[2047],{"type":75,"tag":482,"props":2048,"children":2049},{},[2050],{"type":81,"value":2051},"    cohort[\"iptw\"] = cohort[\"iptw\"].clip(lo, hi)\n",{"type":75,"tag":482,"props":2053,"children":2054},{"class":484,"line":717},[2055],{"type":75,"tag":482,"props":2056,"children":2057},{},[2058],{"type":81,"value":1076},{"type":75,"tag":217,"props":2060,"children":2062},{"id":2061},"_6-kaplan-meier-survival-analysis",[2063],{"type":81,"value":2064},"6. Kaplan-Meier Survival Analysis",{"type":75,"tag":464,"props":2066,"children":2068},{"id":2067},"python-lifelines",[2069],{"type":81,"value":2070},"Python (lifelines)",{"type":75,"tag":471,"props":2072,"children":2074},{"className":473,"code":2073,"language":475,"meta":476,"style":476},"from lifelines import KaplanMeierFitter\nimport matplotlib.pyplot as plt\n\ndef plot_km(cohort, duration_col=\"followup_days\", event_col=\"event\",\n            group_col=\"treatment\", labels=(\"Drug B\", \"Drug A\")):\n    \"\"\"Plot Kaplan-Meier curves by treatment group.\"\"\"\n    fig, ax = plt.subplots(figsize=(8, 6))\n    kmf = KaplanMeierFitter()\n    for grp, label in zip([0, 1], labels):\n        mask = cohort[group_col] == grp\n        kmf.fit(cohort.loc[mask, duration_col], cohort.loc[mask, event_col],\n                label=label)\n        kmf.plot_survival_function(ax=ax, ci_show=True)\n    ax.set_xlabel(\"Days from index date\")\n    ax.set_ylabel(\"Survival probability\")\n    ax.set_title(\"Kaplan-Meier Survival Curves\")\n    plt.tight_layout()\n    return fig\n",[2075],{"type":75,"tag":263,"props":2076,"children":2077},{"__ignoreMap":476},[2078,2086,2094,2101,2109,2117,2125,2133,2141,2149,2157,2165,2173,2181,2189,2197,2205,2213],{"type":75,"tag":482,"props":2079,"children":2080},{"class":484,"line":485},[2081],{"type":75,"tag":482,"props":2082,"children":2083},{},[2084],{"type":81,"value":2085},"from lifelines import KaplanMeierFitter\n",{"type":75,"tag":482,"props":2087,"children":2088},{"class":484,"line":494},[2089],{"type":75,"tag":482,"props":2090,"children":2091},{},[2092],{"type":81,"value":2093},"import matplotlib.pyplot as plt\n",{"type":75,"tag":482,"props":2095,"children":2096},{"class":484,"line":503},[2097],{"type":75,"tag":482,"props":2098,"children":2099},{"emptyLinePlaceholder":515},[2100],{"type":81,"value":518},{"type":75,"tag":482,"props":2102,"children":2103},{"class":484,"line":29},[2104],{"type":75,"tag":482,"props":2105,"children":2106},{},[2107],{"type":81,"value":2108},"def plot_km(cohort, duration_col=\"followup_days\", event_col=\"event\",\n",{"type":75,"tag":482,"props":2110,"children":2111},{"class":484,"line":521},[2112],{"type":75,"tag":482,"props":2113,"children":2114},{},[2115],{"type":81,"value":2116},"            group_col=\"treatment\", labels=(\"Drug B\", \"Drug A\")):\n",{"type":75,"tag":482,"props":2118,"children":2119},{"class":484,"line":530},[2120],{"type":75,"tag":482,"props":2121,"children":2122},{},[2123],{"type":81,"value":2124},"    \"\"\"Plot Kaplan-Meier curves by treatment group.\"\"\"\n",{"type":75,"tag":482,"props":2126,"children":2127},{"class":484,"line":539},[2128],{"type":75,"tag":482,"props":2129,"children":2130},{},[2131],{"type":81,"value":2132},"    fig, ax = plt.subplots(figsize=(8, 6))\n",{"type":75,"tag":482,"props":2134,"children":2135},{"class":484,"line":548},[2136],{"type":75,"tag":482,"props":2137,"children":2138},{},[2139],{"type":81,"value":2140},"    kmf = KaplanMeierFitter()\n",{"type":75,"tag":482,"props":2142,"children":2143},{"class":484,"line":556},[2144],{"type":75,"tag":482,"props":2145,"children":2146},{},[2147],{"type":81,"value":2148},"    for grp, label in zip([0, 1], labels):\n",{"type":75,"tag":482,"props":2150,"children":2151},{"class":484,"line":565},[2152],{"type":75,"tag":482,"props":2153,"children":2154},{},[2155],{"type":81,"value":2156},"        mask = cohort[group_col] == grp\n",{"type":75,"tag":482,"props":2158,"children":2159},{"class":484,"line":665},[2160],{"type":75,"tag":482,"props":2161,"children":2162},{},[2163],{"type":81,"value":2164},"        kmf.fit(cohort.loc[mask, duration_col], cohort.loc[mask, event_col],\n",{"type":75,"tag":482,"props":2166,"children":2167},{"class":484,"line":673},[2168],{"type":75,"tag":482,"props":2169,"children":2170},{},[2171],{"type":81,"value":2172},"                label=label)\n",{"type":75,"tag":482,"props":2174,"children":2175},{"class":484,"line":682},[2176],{"type":75,"tag":482,"props":2177,"children":2178},{},[2179],{"type":81,"value":2180},"        kmf.plot_survival_function(ax=ax, ci_show=True)\n",{"type":75,"tag":482,"props":2182,"children":2183},{"class":484,"line":691},[2184],{"type":75,"tag":482,"props":2185,"children":2186},{},[2187],{"type":81,"value":2188},"    ax.set_xlabel(\"Days from index date\")\n",{"type":75,"tag":482,"props":2190,"children":2191},{"class":484,"line":700},[2192],{"type":75,"tag":482,"props":2193,"children":2194},{},[2195],{"type":81,"value":2196},"    ax.set_ylabel(\"Survival probability\")\n",{"type":75,"tag":482,"props":2198,"children":2199},{"class":484,"line":709},[2200],{"type":75,"tag":482,"props":2201,"children":2202},{},[2203],{"type":81,"value":2204},"    ax.set_title(\"Kaplan-Meier Survival Curves\")\n",{"type":75,"tag":482,"props":2206,"children":2207},{"class":484,"line":717},[2208],{"type":75,"tag":482,"props":2209,"children":2210},{},[2211],{"type":81,"value":2212},"    plt.tight_layout()\n",{"type":75,"tag":482,"props":2214,"children":2215},{"class":484,"line":726},[2216],{"type":75,"tag":482,"props":2217,"children":2218},{},[2219],{"type":81,"value":2220},"    return fig\n",{"type":75,"tag":464,"props":2222,"children":2224},{"id":2223},"r-survival-survminer",[2225],{"type":81,"value":2226},"R (survival + survminer)",{"type":75,"tag":471,"props":2228,"children":2230},{"className":1577,"code":2229,"language":1579,"meta":476,"style":476},"library(survival)\nlibrary(survminer)\n\nfit \u003C- survfit(Surv(followup_days, event) ~ treatment, data = cohort)\nggsurvplot(fit, data = cohort, pval = TRUE, risk.table = TRUE,\n           xlab = \"Days from index date\", ylab = \"Survival probability\",\n           legend.labs = c(\"Drug B\", \"Drug A\"))\n",[2231],{"type":75,"tag":263,"props":2232,"children":2233},{"__ignoreMap":476},[2234,2242,2250,2257,2265,2273,2281],{"type":75,"tag":482,"props":2235,"children":2236},{"class":484,"line":485},[2237],{"type":75,"tag":482,"props":2238,"children":2239},{},[2240],{"type":81,"value":2241},"library(survival)\n",{"type":75,"tag":482,"props":2243,"children":2244},{"class":484,"line":494},[2245],{"type":75,"tag":482,"props":2246,"children":2247},{},[2248],{"type":81,"value":2249},"library(survminer)\n",{"type":75,"tag":482,"props":2251,"children":2252},{"class":484,"line":503},[2253],{"type":75,"tag":482,"props":2254,"children":2255},{"emptyLinePlaceholder":515},[2256],{"type":81,"value":518},{"type":75,"tag":482,"props":2258,"children":2259},{"class":484,"line":29},[2260],{"type":75,"tag":482,"props":2261,"children":2262},{},[2263],{"type":81,"value":2264},"fit \u003C- survfit(Surv(followup_days, event) ~ treatment, data = cohort)\n",{"type":75,"tag":482,"props":2266,"children":2267},{"class":484,"line":521},[2268],{"type":75,"tag":482,"props":2269,"children":2270},{},[2271],{"type":81,"value":2272},"ggsurvplot(fit, data = cohort, pval = TRUE, risk.table = TRUE,\n",{"type":75,"tag":482,"props":2274,"children":2275},{"class":484,"line":530},[2276],{"type":75,"tag":482,"props":2277,"children":2278},{},[2279],{"type":81,"value":2280},"           xlab = \"Days from index date\", ylab = \"Survival probability\",\n",{"type":75,"tag":482,"props":2282,"children":2283},{"class":484,"line":539},[2284],{"type":75,"tag":482,"props":2285,"children":2286},{},[2287],{"type":81,"value":2288},"           legend.labs = c(\"Drug B\", \"Drug A\"))\n",{"type":75,"tag":217,"props":2290,"children":2292},{"id":2291},"_7-cox-proportional-hazards-model",[2293],{"type":81,"value":2294},"7. Cox Proportional Hazards Model",{"type":75,"tag":464,"props":2296,"children":2298},{"id":2297},"python-lifelines-1",[2299],{"type":81,"value":2070},{"type":75,"tag":471,"props":2301,"children":2303},{"className":473,"code":2302,"language":475,"meta":476,"style":476},"from lifelines import CoxPHFitter\n\ndef fit_cox(cohort, duration_col=\"followup_days\", event_col=\"event\",\n            covariates=None, weight_col=None):\n    \"\"\"Fit Cox PH model, optionally weighted (IPTW).\"\"\"\n    cols = [duration_col, event_col] + (covariates or [])\n    if weight_col:\n        cols.append(weight_col)\n    df = cohort[cols].dropna()\n    cph = CoxPHFitter()\n    cph.fit(df, duration_col=duration_col, event_col=event_col,\n            weights_col=weight_col, robust=True if weight_col else False)\n    cph.print_summary()\n    return cph\n",[2304],{"type":75,"tag":263,"props":2305,"children":2306},{"__ignoreMap":476},[2307,2315,2322,2330,2338,2346,2354,2362,2370,2378,2386,2394,2402,2410],{"type":75,"tag":482,"props":2308,"children":2309},{"class":484,"line":485},[2310],{"type":75,"tag":482,"props":2311,"children":2312},{},[2313],{"type":81,"value":2314},"from lifelines import CoxPHFitter\n",{"type":75,"tag":482,"props":2316,"children":2317},{"class":484,"line":494},[2318],{"type":75,"tag":482,"props":2319,"children":2320},{"emptyLinePlaceholder":515},[2321],{"type":81,"value":518},{"type":75,"tag":482,"props":2323,"children":2324},{"class":484,"line":503},[2325],{"type":75,"tag":482,"props":2326,"children":2327},{},[2328],{"type":81,"value":2329},"def fit_cox(cohort, duration_col=\"followup_days\", event_col=\"event\",\n",{"type":75,"tag":482,"props":2331,"children":2332},{"class":484,"line":29},[2333],{"type":75,"tag":482,"props":2334,"children":2335},{},[2336],{"type":81,"value":2337},"            covariates=None, weight_col=None):\n",{"type":75,"tag":482,"props":2339,"children":2340},{"class":484,"line":521},[2341],{"type":75,"tag":482,"props":2342,"children":2343},{},[2344],{"type":81,"value":2345},"    \"\"\"Fit Cox PH model, optionally weighted (IPTW).\"\"\"\n",{"type":75,"tag":482,"props":2347,"children":2348},{"class":484,"line":530},[2349],{"type":75,"tag":482,"props":2350,"children":2351},{},[2352],{"type":81,"value":2353},"    cols = [duration_col, event_col] + (covariates or [])\n",{"type":75,"tag":482,"props":2355,"children":2356},{"class":484,"line":539},[2357],{"type":75,"tag":482,"props":2358,"children":2359},{},[2360],{"type":81,"value":2361},"    if weight_col:\n",{"type":75,"tag":482,"props":2363,"children":2364},{"class":484,"line":548},[2365],{"type":75,"tag":482,"props":2366,"children":2367},{},[2368],{"type":81,"value":2369},"        cols.append(weight_col)\n",{"type":75,"tag":482,"props":2371,"children":2372},{"class":484,"line":556},[2373],{"type":75,"tag":482,"props":2374,"children":2375},{},[2376],{"type":81,"value":2377},"    df = cohort[cols].dropna()\n",{"type":75,"tag":482,"props":2379,"children":2380},{"class":484,"line":565},[2381],{"type":75,"tag":482,"props":2382,"children":2383},{},[2384],{"type":81,"value":2385},"    cph = CoxPHFitter()\n",{"type":75,"tag":482,"props":2387,"children":2388},{"class":484,"line":665},[2389],{"type":75,"tag":482,"props":2390,"children":2391},{},[2392],{"type":81,"value":2393},"    cph.fit(df, duration_col=duration_col, event_col=event_col,\n",{"type":75,"tag":482,"props":2395,"children":2396},{"class":484,"line":673},[2397],{"type":75,"tag":482,"props":2398,"children":2399},{},[2400],{"type":81,"value":2401},"            weights_col=weight_col, robust=True if weight_col else False)\n",{"type":75,"tag":482,"props":2403,"children":2404},{"class":484,"line":682},[2405],{"type":75,"tag":482,"props":2406,"children":2407},{},[2408],{"type":81,"value":2409},"    cph.print_summary()\n",{"type":75,"tag":482,"props":2411,"children":2412},{"class":484,"line":691},[2413],{"type":75,"tag":482,"props":2414,"children":2415},{},[2416],{"type":81,"value":2417},"    return cph\n",{"type":75,"tag":464,"props":2419,"children":2421},{"id":2420},"r-survival",[2422],{"type":81,"value":2423},"R (survival)",{"type":75,"tag":471,"props":2425,"children":2427},{"className":1577,"code":2426,"language":1579,"meta":476,"style":476},"# Unweighted (matched cohort)\ncox_fit \u003C- coxph(Surv(followup_days, event) ~ treatment + age + sex + charlson,\n                 data = matched_data)\nsummary(cox_fit)\n\n# IPTW-weighted\ncox_iptw \u003C- coxph(Surv(followup_days, event) ~ treatment,\n                  data = cohort, weights = iptw, robust = TRUE)\nsummary(cox_iptw)\n",[2428],{"type":75,"tag":263,"props":2429,"children":2430},{"__ignoreMap":476},[2431,2439,2447,2455,2463,2470,2478,2486,2494],{"type":75,"tag":482,"props":2432,"children":2433},{"class":484,"line":485},[2434],{"type":75,"tag":482,"props":2435,"children":2436},{},[2437],{"type":81,"value":2438},"# Unweighted (matched cohort)\n",{"type":75,"tag":482,"props":2440,"children":2441},{"class":484,"line":494},[2442],{"type":75,"tag":482,"props":2443,"children":2444},{},[2445],{"type":81,"value":2446},"cox_fit \u003C- coxph(Surv(followup_days, event) ~ treatment + age + sex + charlson,\n",{"type":75,"tag":482,"props":2448,"children":2449},{"class":484,"line":503},[2450],{"type":75,"tag":482,"props":2451,"children":2452},{},[2453],{"type":81,"value":2454},"                 data = matched_data)\n",{"type":75,"tag":482,"props":2456,"children":2457},{"class":484,"line":29},[2458],{"type":75,"tag":482,"props":2459,"children":2460},{},[2461],{"type":81,"value":2462},"summary(cox_fit)\n",{"type":75,"tag":482,"props":2464,"children":2465},{"class":484,"line":521},[2466],{"type":75,"tag":482,"props":2467,"children":2468},{"emptyLinePlaceholder":515},[2469],{"type":81,"value":518},{"type":75,"tag":482,"props":2471,"children":2472},{"class":484,"line":530},[2473],{"type":75,"tag":482,"props":2474,"children":2475},{},[2476],{"type":81,"value":2477},"# IPTW-weighted\n",{"type":75,"tag":482,"props":2479,"children":2480},{"class":484,"line":539},[2481],{"type":75,"tag":482,"props":2482,"children":2483},{},[2484],{"type":81,"value":2485},"cox_iptw \u003C- coxph(Surv(followup_days, event) ~ treatment,\n",{"type":75,"tag":482,"props":2487,"children":2488},{"class":484,"line":548},[2489],{"type":75,"tag":482,"props":2490,"children":2491},{},[2492],{"type":81,"value":2493},"                  data = cohort, weights = iptw, robust = TRUE)\n",{"type":75,"tag":482,"props":2495,"children":2496},{"class":484,"line":556},[2497],{"type":75,"tag":482,"props":2498,"children":2499},{},[2500],{"type":81,"value":2501},"summary(cox_iptw)\n",{"type":75,"tag":217,"props":2503,"children":2505},{"id":2504},"_8-proportional-hazards-assumption-check",[2506],{"type":81,"value":2507},"8. Proportional Hazards Assumption Check",{"type":75,"tag":464,"props":2509,"children":2511},{"id":2510},"schoenfeld-residuals-python",[2512],{"type":81,"value":2513},"Schoenfeld residuals (Python)",{"type":75,"tag":471,"props":2515,"children":2517},{"className":473,"code":2516,"language":475,"meta":476,"style":476},"def check_ph_assumption(cph):\n    \"\"\"Test and plot Schoenfeld residuals for PH assumption.\"\"\"\n    cph.check_assumptions(cohort, p_value_threshold=0.05, show_plots=True)\n",[2518],{"type":75,"tag":263,"props":2519,"children":2520},{"__ignoreMap":476},[2521,2529,2537],{"type":75,"tag":482,"props":2522,"children":2523},{"class":484,"line":485},[2524],{"type":75,"tag":482,"props":2525,"children":2526},{},[2527],{"type":81,"value":2528},"def check_ph_assumption(cph):\n",{"type":75,"tag":482,"props":2530,"children":2531},{"class":484,"line":494},[2532],{"type":75,"tag":482,"props":2533,"children":2534},{},[2535],{"type":81,"value":2536},"    \"\"\"Test and plot Schoenfeld residuals for PH assumption.\"\"\"\n",{"type":75,"tag":482,"props":2538,"children":2539},{"class":484,"line":503},[2540],{"type":75,"tag":482,"props":2541,"children":2542},{},[2543],{"type":81,"value":2544},"    cph.check_assumptions(cohort, p_value_threshold=0.05, show_plots=True)\n",{"type":75,"tag":464,"props":2546,"children":2547},{"id":1579},[2548],{"type":81,"value":2549},"R",{"type":75,"tag":471,"props":2551,"children":2553},{"className":1577,"code":2552,"language":1579,"meta":476,"style":476},"ph_test \u003C- cox.zph(cox_fit)\nprint(ph_test)       # p \u003C 0.05 → PH assumption violated for that covariate\nplot(ph_test)         # visual: residuals should show no trend over time\n",[2554],{"type":75,"tag":263,"props":2555,"children":2556},{"__ignoreMap":476},[2557,2565,2573],{"type":75,"tag":482,"props":2558,"children":2559},{"class":484,"line":485},[2560],{"type":75,"tag":482,"props":2561,"children":2562},{},[2563],{"type":81,"value":2564},"ph_test \u003C- cox.zph(cox_fit)\n",{"type":75,"tag":482,"props":2566,"children":2567},{"class":484,"line":494},[2568],{"type":75,"tag":482,"props":2569,"children":2570},{},[2571],{"type":81,"value":2572},"print(ph_test)       # p \u003C 0.05 → PH assumption violated for that covariate\n",{"type":75,"tag":482,"props":2574,"children":2575},{"class":484,"line":503},[2576],{"type":75,"tag":482,"props":2577,"children":2578},{},[2579],{"type":81,"value":2580},"plot(ph_test)         # visual: residuals should show no trend over time\n",{"type":75,"tag":229,"props":2582,"children":2583},{},[2584,2600],{"type":75,"tag":233,"props":2585,"children":2586},{},[2587],{"type":75,"tag":237,"props":2588,"children":2589},{},[2590,2595],{"type":75,"tag":241,"props":2591,"children":2592},{},[2593],{"type":81,"value":2594},"PH test result",{"type":75,"tag":241,"props":2596,"children":2597},{},[2598],{"type":81,"value":2599},"Action",{"type":75,"tag":252,"props":2601,"children":2602},{},[2603,2616,2629],{"type":75,"tag":237,"props":2604,"children":2605},{},[2606,2611],{"type":75,"tag":259,"props":2607,"children":2608},{},[2609],{"type":81,"value":2610},"All p > 0.05",{"type":75,"tag":259,"props":2612,"children":2613},{},[2614],{"type":81,"value":2615},"PH assumption holds; proceed",{"type":75,"tag":237,"props":2617,"children":2618},{},[2619,2624],{"type":75,"tag":259,"props":2620,"children":2621},{},[2622],{"type":81,"value":2623},"Covariate p \u003C 0.05",{"type":75,"tag":259,"props":2625,"children":2626},{},[2627],{"type":81,"value":2628},"Stratify on that covariate or add time-interaction term",{"type":75,"tag":237,"props":2630,"children":2631},{},[2632,2637],{"type":75,"tag":259,"props":2633,"children":2634},{},[2635],{"type":81,"value":2636},"Global p \u003C 0.05",{"type":75,"tag":259,"props":2638,"children":2639},{},[2640],{"type":81,"value":2641},"Consider restricted mean survival time (RMST) or parametric AFT model",{"type":75,"tag":217,"props":2643,"children":2645},{"id":2644},"pipeline-summary",[2646],{"type":81,"value":2647},"Pipeline summary",{"type":75,"tag":471,"props":2649,"children":2653},{"className":2650,"code":2652,"language":81},[2651],"language-text","1. Define code lists (ICD-10, NDC, CPT)\n2. Identify new users with washout          → cohort table\n3. Apply inclusion\u002Fexclusion criteria       → filtered cohort\n4. Build baseline covariate matrix          → pre-index features\n5. Estimate propensity scores               → ps column\n6. Match or weight (IPTW)                   → balanced cohort\n7. Check balance (SMD \u003C 0.1)               → Love plot \u002F SMD table\n8. Calculate adherence (PDC\u002FMPR)            → adherence metrics\n9. Define outcome + follow-up               → duration + event columns\n10. Kaplan-Meier curves                     → survival plot\n11. Cox PH model (adjusted or weighted)     → hazard ratios + CIs\n12. Check PH assumption (Schoenfeld)        → assumption diagnostics\n",[2654],{"type":75,"tag":263,"props":2655,"children":2656},{"__ignoreMap":476},[2657],{"type":81,"value":2652},{"type":75,"tag":84,"props":2659,"children":2661},{"id":2660},"common-mistakes",[2662],{"type":81,"value":2663},"Common Mistakes",{"type":75,"tag":143,"props":2665,"children":2666},{},[2667,2691,2712,2733,2754,2775,2796,2817,2838,2859],{"type":75,"tag":101,"props":2668,"children":2669},{},[2670,2675,2677,2682,2684,2689],{"type":75,"tag":1404,"props":2671,"children":2672},{},[2673],{"type":81,"value":2674},"Wrong:",{"type":81,"value":2676}," Using MPR instead of PDC for medication adherence\n",{"type":75,"tag":1404,"props":2678,"children":2679},{},[2680],{"type":81,"value":2681},"Right:",{"type":81,"value":2683}," Use PDC (Proportion of Days Covered) as the adherence metric\n",{"type":75,"tag":1404,"props":2685,"children":2686},{},[2687],{"type":81,"value":2688},"Why:",{"type":81,"value":2690}," MPR double-counts overlapping fills, can exceed 1.0, and is not endorsed by PQA",{"type":75,"tag":101,"props":2692,"children":2693},{},[2694,2698,2700,2704,2706,2710],{"type":75,"tag":1404,"props":2695,"children":2696},{},[2697],{"type":81,"value":2674},{"type":81,"value":2699}," Not requiring continuous enrollment during the study period\n",{"type":75,"tag":1404,"props":2701,"children":2702},{},[2703],{"type":81,"value":2681},{"type":81,"value":2705}," Verify continuous enrollment for both washout and follow-up windows\n",{"type":75,"tag":1404,"props":2707,"children":2708},{},[2709],{"type":81,"value":2688},{"type":81,"value":2711}," Gaps in enrollment create unobservable periods where events and fills are missed",{"type":75,"tag":101,"props":2713,"children":2714},{},[2715,2719,2721,2725,2727,2731],{"type":75,"tag":1404,"props":2716,"children":2717},{},[2718],{"type":81,"value":2674},{"type":81,"value":2720}," Omitting a washout period before the index date\n",{"type":75,"tag":1404,"props":2722,"children":2723},{},[2724],{"type":81,"value":2681},{"type":81,"value":2726}," Require a clean washout (typically 180 days) with no prior fills of the study drug\n",{"type":75,"tag":1404,"props":2728,"children":2729},{},[2730],{"type":81,"value":2688},{"type":81,"value":2732}," Without washout, prevalent users contaminate the new-user cohort and bias results",{"type":75,"tag":101,"props":2734,"children":2735},{},[2736,2740,2742,2746,2748,2752],{"type":75,"tag":1404,"props":2737,"children":2738},{},[2739],{"type":81,"value":2674},{"type":81,"value":2741}," Including post-index covariates in the propensity score model\n",{"type":75,"tag":1404,"props":2743,"children":2744},{},[2745],{"type":81,"value":2681},{"type":81,"value":2747}," Only use pre-index (baseline) variables in the PS model\n",{"type":75,"tag":1404,"props":2749,"children":2750},{},[2751],{"type":81,"value":2688},{"type":81,"value":2753}," Post-index variables may be affected by treatment, introducing collider bias",{"type":75,"tag":101,"props":2755,"children":2756},{},[2757,2761,2763,2767,2769,2773],{"type":75,"tag":1404,"props":2758,"children":2759},{},[2760],{"type":81,"value":2674},{"type":81,"value":2762}," Using p-values to assess covariate balance after matching\u002Fweighting\n",{"type":75,"tag":1404,"props":2764,"children":2765},{},[2766],{"type":81,"value":2681},{"type":81,"value":2768}," Use standardized mean differences (SMD \u003C 0.1) for all covariates\n",{"type":75,"tag":1404,"props":2770,"children":2771},{},[2772],{"type":81,"value":2688},{"type":81,"value":2774}," P-values conflate balance with sample size and are uninformative for this purpose",{"type":75,"tag":101,"props":2776,"children":2777},{},[2778,2782,2784,2788,2790,2794],{"type":75,"tag":1404,"props":2779,"children":2780},{},[2781],{"type":81,"value":2674},{"type":81,"value":2783}," Not checking propensity score overlap between treatment groups\n",{"type":75,"tag":1404,"props":2785,"children":2786},{},[2787],{"type":81,"value":2681},{"type":81,"value":2789}," Plot PS distributions for both groups and trim non-overlapping regions\n",{"type":75,"tag":1404,"props":2791,"children":2792},{},[2793],{"type":81,"value":2688},{"type":81,"value":2795}," Estimates in non-overlapping regions are extrapolated and unreliable",{"type":75,"tag":101,"props":2797,"children":2798},{},[2799,2803,2805,2809,2811,2815],{"type":75,"tag":1404,"props":2800,"children":2801},{},[2802],{"type":81,"value":2674},{"type":81,"value":2804}," Reporting Cox model results without testing the proportional hazards assumption\n",{"type":75,"tag":1404,"props":2806,"children":2807},{},[2808],{"type":81,"value":2681},{"type":81,"value":2810}," Always run Schoenfeld residual tests and inspect plots for time trends\n",{"type":75,"tag":1404,"props":2812,"children":2813},{},[2814],{"type":81,"value":2688},{"type":81,"value":2816}," Violated PH produces misleading hazard ratios; consider RMST or stratification instead",{"type":75,"tag":101,"props":2818,"children":2819},{},[2820,2824,2826,2830,2832,2836],{"type":75,"tag":1404,"props":2821,"children":2822},{},[2823],{"type":81,"value":2674},{"type":81,"value":2825}," Using naive standard errors with IPTW-weighted analyses\n",{"type":75,"tag":1404,"props":2827,"children":2828},{},[2829],{"type":81,"value":2681},{"type":81,"value":2831}," Use robust (sandwich) variance estimators when fitting weighted models\n",{"type":75,"tag":1404,"props":2833,"children":2834},{},[2835],{"type":81,"value":2688},{"type":81,"value":2837}," Naive SEs underestimate uncertainty because they ignore the weight estimation step",{"type":75,"tag":101,"props":2839,"children":2840},{},[2841,2845,2847,2851,2853,2857],{"type":75,"tag":1404,"props":2842,"children":2843},{},[2844],{"type":81,"value":2674},{"type":81,"value":2846}," Truncating IPTW weights too aggressively (e.g., at 5th\u002F95th percentile)\n",{"type":75,"tag":1404,"props":2848,"children":2849},{},[2850],{"type":81,"value":2681},{"type":81,"value":2852}," Truncate conservatively (1st\u002F99th) and report sensitivity across thresholds\n",{"type":75,"tag":1404,"props":2854,"children":2855},{},[2856],{"type":81,"value":2688},{"type":81,"value":2858}," Over-truncation reintroduces confounding by effectively unweighting key observations",{"type":75,"tag":101,"props":2860,"children":2861},{},[2862,2866,2868,2872,2874,2878],{"type":75,"tag":1404,"props":2863,"children":2864},{},[2865],{"type":81,"value":2674},{"type":81,"value":2867}," Defining follow-up end as a fixed calendar date for all patients\n",{"type":75,"tag":1404,"props":2869,"children":2870},{},[2871],{"type":81,"value":2681},{"type":81,"value":2873}," End follow-up at the earliest of: event, disenrollment, end of study, or death\n",{"type":75,"tag":1404,"props":2875,"children":2876},{},[2877],{"type":81,"value":2688},{"type":81,"value":2879}," Incorrect censoring biases survival estimates and violates non-informative censoring assumptions",{"type":75,"tag":84,"props":2881,"children":2883},{"id":2882},"references",[2884],{"type":81,"value":2885},"References",{"type":75,"tag":143,"props":2887,"children":2888},{},[2889,2902,2913,2924,2935,2946],{"type":75,"tag":101,"props":2890,"children":2891},{},[2892,2894],{"type":81,"value":2893},"Hernán MA, Robins JM. Causal Inference: What If. Chapman & Hall\u002FCRC 2020, ",{"type":75,"tag":2895,"props":2896,"children":2900},"a",{"href":2897,"rel":2898},"https:\u002F\u002Fwww.hsph.harvard.edu\u002Fmiguel-hernan\u002Fcausal-inference-book\u002F",[2899],"nofollow",[2901],{"type":81,"value":2897},{"type":75,"tag":101,"props":2903,"children":2904},{},[2905,2907],{"type":81,"value":2906},"Austin PC. An introduction to propensity score methods. Multivariate Behav Res 2011, ",{"type":75,"tag":2895,"props":2908,"children":2911},{"href":2909,"rel":2910},"https:\u002F\u002Fdoi.org\u002F10.1080\u002F00273171.2011.568786",[2899],[2912],{"type":81,"value":2909},{"type":75,"tag":101,"props":2914,"children":2915},{},[2916,2918],{"type":81,"value":2917},"Pharmacy Quality Alliance. PDC measure specifications, ",{"type":75,"tag":2895,"props":2919,"children":2922},{"href":2920,"rel":2921},"https:\u002F\u002Fwww.pqaalliance.org\u002F",[2899],[2923],{"type":81,"value":2920},{"type":75,"tag":101,"props":2925,"children":2926},{},[2927,2929],{"type":81,"value":2928},"Davidson-Pilon C. lifelines documentation, ",{"type":75,"tag":2895,"props":2930,"children":2933},{"href":2931,"rel":2932},"https:\u002F\u002Flifelines.readthedocs.io\u002F",[2899],[2934],{"type":81,"value":2931},{"type":75,"tag":101,"props":2936,"children":2937},{},[2938,2940],{"type":81,"value":2939},"Ho DE et al. MatchIt: nonparametric preprocessing for parametric causal inference. J Stat Softw 2011, ",{"type":75,"tag":2895,"props":2941,"children":2944},{"href":2942,"rel":2943},"https:\u002F\u002Fdoi.org\u002F10.18637\u002Fjss.v042.i08",[2899],[2945],{"type":81,"value":2942},{"type":75,"tag":101,"props":2947,"children":2948},{},[2949],{"type":81,"value":2950},"Therneau TM, Grambsch PM. Modeling Survival Data. Springer 2000",{"type":75,"tag":2952,"props":2953,"children":2954},"style",{},[2955],{"type":81,"value":2956},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2958,"total":3134},[2959,2978,2999,3009,3022,3035,3045,3055,3076,3091,3106,3121],{"slug":2960,"name":2960,"fn":2961,"description":2962,"org":2963,"tags":2964,"stars":2975,"repoUrl":2976,"updatedAt":2977},"agentcore-investigation","investigate Bedrock AgentCore runtime sessions","Investigate Bedrock AgentCore runtime sessions via CloudWatch Logs Insights — resolve session\u002Ftrace IDs, query OTEL spans, filter noise, build timelines. Use when debugging AgentCore agent sessions, tracing tool calls, or analyzing latency.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2965,2966,2969,2972],{"name":27,"slug":28,"type":16},{"name":2967,"slug":2968,"type":16},"Debugging","debugging",{"name":2970,"slug":2971,"type":16},"Logs","logs",{"name":2973,"slug":2974,"type":16},"Observability","observability",9427,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fmcp","2026-07-12T08:37:22.601527",{"slug":2979,"name":2980,"fn":2981,"description":2982,"org":2983,"tags":2984,"stars":2975,"repoUrl":2976,"updatedAt":2998},"amazon-aurora-dsql","amazon aurora dsql","build applications with Aurora DSQL","Build with Aurora DSQL — manage schemas, execute queries, handle migrations, diagnose query plans, load data, and develop applications with a serverless, distributed SQL database. Covers IAM auth, multi-tenant patterns, MySQL-to-DSQL and PostgreSQL-to-DSQL schema conversion, FK replacement code generation, OCC retry patterns, ORM migration (Django\u002FHibernate\u002FRails), DDL operations, query plan explainability, SQL compatibility validation, and bulk data loading. Triggers on phrases like: DSQL, Aurora DSQL, create DSQL table, DSQL schema, migrate to DSQL, distributed SQL database, serverless PostgreSQL-compatible database, DSQL query plan, DSQL EXPLAIN ANALYZE, why is my DSQL query slow, DSQL foreign key, DSQL OCC retry, DSQL multi-region, load into DSQL, load CSV into DSQL, bulk load DSQL, aurora-dsql-loader.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2985,2988,2989,2992,2995],{"name":2986,"slug":2987,"type":16},"Aurora","aurora",{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},"Database","database",{"name":2993,"slug":2994,"type":16},"Serverless","serverless",{"name":2996,"slug":2997,"type":16},"SQL","sql","2026-07-12T08:36:45.053393",{"slug":3000,"name":3001,"fn":2981,"description":2982,"org":3002,"tags":3003,"stars":2975,"repoUrl":2976,"updatedAt":3008},"aurora-dsql","aurora dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3004,3005,3006,3007],{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},{"name":2993,"slug":2994,"type":16},{"name":2996,"slug":2997,"type":16},"2026-07-12T08:36:42.694299",{"slug":3010,"name":3011,"fn":2981,"description":2982,"org":3012,"tags":3013,"stars":2975,"repoUrl":2976,"updatedAt":3021},"aws-dsql","aws dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3014,3015,3016,3019,3020],{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},{"name":3017,"slug":3018,"type":16},"Migration","migration",{"name":2993,"slug":2994,"type":16},{"name":2996,"slug":2997,"type":16},"2026-07-12T08:36:38.584057",{"slug":3023,"name":3024,"fn":2981,"description":2982,"org":3025,"tags":3026,"stars":2975,"repoUrl":2976,"updatedAt":3034},"distributed-postgres","distributed postgres",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3027,3028,3029,3032,3033],{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},{"name":3030,"slug":3031,"type":16},"PostgreSQL","postgresql",{"name":2993,"slug":2994,"type":16},{"name":2996,"slug":2997,"type":16},"2026-07-12T08:36:46.530743",{"slug":3036,"name":3037,"fn":2981,"description":2982,"org":3038,"tags":3039,"stars":2975,"repoUrl":2976,"updatedAt":3044},"distributed-sql","distributed sql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3040,3041,3042,3043],{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},{"name":2993,"slug":2994,"type":16},{"name":2996,"slug":2997,"type":16},"2026-07-12T08:36:48.104182",{"slug":3046,"name":3046,"fn":2981,"description":2982,"org":3047,"tags":3048,"stars":2975,"repoUrl":2976,"updatedAt":3054},"dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3049,3050,3051,3052,3053],{"name":27,"slug":28,"type":16},{"name":2990,"slug":2991,"type":16},{"name":3017,"slug":3018,"type":16},{"name":2993,"slug":2994,"type":16},{"name":2996,"slug":2997,"type":16},"2026-07-12T08:36:36.374512",{"slug":3056,"name":3056,"fn":3057,"description":3058,"org":3059,"tags":3060,"stars":3073,"repoUrl":3074,"updatedAt":3075},"cost-efficiency-analyzer","analyze cost efficiency and expenses","Analyzes cost structure, cost efficiency, and expense management from P&L data. Use when the user asks about costs, expenses, COGS, operating expenses, cost ratios, cost control, spending efficiency, margin compression from cost side, or wants to understand where money is going. Also use for \"are we spending too much\", \"cost breakdown\", \"expense analysis\", or \"how efficient are our operations\". NOT for revenue or top-line analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3061,3064,3067,3070],{"name":3062,"slug":3063,"type":16},"Accounting","accounting",{"name":3065,"slug":3066,"type":16},"Analytics","analytics",{"name":3068,"slug":3069,"type":16},"Cost Optimization","cost-optimization",{"name":3071,"slug":3072,"type":16},"Finance","finance",3176,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fagentcore-samples","2026-07-12T08:40:03.29555",{"slug":3077,"name":3077,"fn":3078,"description":3079,"org":3080,"tags":3081,"stars":3073,"repoUrl":3074,"updatedAt":3090},"executive-financial-briefing","generate executive financial briefings","Generates a concise executive-level financial briefing or summary suitable for a CEO, CFO, or board presentation. Use when the user asks for a summary, briefing, executive summary, board update, financial overview, financial health check, or \"how is the business doing\". Covers the full P&L picture in one page. Also use for \"give me the highlights\", \"what do I need to know\", or \"quick financial update\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3082,3083,3084,3087],{"name":27,"slug":28,"type":16},{"name":3071,"slug":3072,"type":16},{"name":3085,"slug":3086,"type":16},"Management","management",{"name":3088,"slug":3089,"type":16},"Reporting","reporting","2026-07-12T08:40:02.066471",{"slug":3092,"name":3092,"fn":3093,"description":3094,"org":3095,"tags":3096,"stars":3073,"repoUrl":3074,"updatedAt":3105},"multi-quarter-trend-analysis","analyze multi-quarter financial trends","Analyzes financial trends across multiple quarters by comparing P&L metrics over time. Use when the user wants to see trends, patterns, trajectories, or directional movement across 3 or more quarters. Also use for \"how are we trending\", \"show me the trend\", \"track performance over time\", \"quarter over quarter comparison across all quarters\", or any multi-period longitudinal analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3097,3098,3099,3102],{"name":3065,"slug":3066,"type":16},{"name":3071,"slug":3072,"type":16},{"name":3100,"slug":3101,"type":16},"Financial Statements","financial-statements",{"name":3103,"slug":3104,"type":16},"Variance Analysis","variance-analysis","2026-07-12T08:40:00.79141",{"slug":3107,"name":3107,"fn":3108,"description":3109,"org":3110,"tags":3111,"stars":3073,"repoUrl":3074,"updatedAt":3120},"pdf","process and manipulate PDF documents","Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text\u002Ftables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting\u002Fdecrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3112,3115,3118],{"name":3113,"slug":3114,"type":16},"Automation","automation",{"name":3116,"slug":3117,"type":16},"Documents","documents",{"name":3119,"slug":3107,"type":16},"PDF","2026-07-12T08:41:44.135656",{"slug":3122,"name":3122,"fn":3123,"description":3124,"org":3125,"tags":3126,"stars":3073,"repoUrl":3074,"updatedAt":3133},"quarterly-kpi-calculator","calculate quarterly financial KPIs","Calculates quarterly financial KPIs from P&L data. P&L figures can be provided directly by the user or fetched from the financial data MCP server. Use when the user wants KPI calculations such as Gross Margin %, EBITDA Margin %, Operating Expense Ratio, or Revenue Growth % QoQ. Also use for quarterly performance review, P&L analysis, or interpreting financial ratios against benchmarks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3127,3128,3129,3130],{"name":3062,"slug":3063,"type":16},{"name":21,"slug":22,"type":16},{"name":3071,"slug":3072,"type":16},{"name":3131,"slug":3132,"type":16},"KPI","kpi","2026-07-12T08:39:59.54971",150,{"items":3136,"total":3229},[3137,3153,3166,3180,3193,3206,3219],{"slug":3138,"name":3138,"fn":3139,"description":3140,"org":3141,"tags":3142,"stars":29,"repoUrl":30,"updatedAt":3152},"aws-genai-ml-architect","design AWS GenAI and ML architectures","Reasoning skill for designing AWS GenAI and ML architectures for healthcare and life sciences workloads. Use when the user asks to choose between SageMaker and Bedrock, design a RAG system over medical literature, architect clinical NLP or medical imaging inference, plan genomics or drug discovery pipelines on AWS, address HIPAA\u002FPHI compliance in ML systems, design MLOps for regulated clinical models, or optimize cost for HCLS ML workloads. Triggers include \"AWS architecture\", \"SageMaker vs Bedrock\", \"HIPAA ML\", \"clinical RAG\", \"medical imaging inference\", \"genomics on AWS\", \"PHI training\", \"MLOps healthcare\", \"Bedrock guardrails\", \"HealthLake\", \"HCLS cloud architecture\", \"BAA compliance\", \"SageMaker endpoint\", \"Bedrock knowledge base\", \"clinical NLP on AWS\", \"FDA SaMD on AWS\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3143,3146,3147,3148,3149],{"name":3144,"slug":3145,"type":16},"Architecture","architecture",{"name":27,"slug":28,"type":16},{"name":18,"slug":19,"type":16},{"name":24,"slug":25,"type":16},{"name":3150,"slug":3151,"type":16},"LLM","llm","2026-07-12T08:38:07.975937",{"slug":3154,"name":3154,"fn":3155,"description":3156,"org":3157,"tags":3158,"stars":29,"repoUrl":30,"updatedAt":3165},"biomarker-discovery","guide biomarker discovery and validation","Reason about biomarker discovery and validation in HCLS — classifying biomarker intent, choosing feature-selection and cross-validation strategies, avoiding leakage, and planning external replication. Use when the user asks to discover, develop, or validate a biomarker; select features from high-dimensional omics or clinical data; design a validation study; choose evaluation metrics; justify sample size; combine multi-omics signals; or assess clinical utility. Triggers include \"discover a biomarker\", \"validate biomarker\", \"prognostic vs predictive\", \"feature selection\", \"LASSO vs elastic net\", \"nested cross-validation\", \"data leakage\", \"C-index\", \"time-dependent AUC\", \"decision curve analysis\", \"external validation cohort\", \"events per variable\", \"optimism-corrected\", \"multi-omics integration\", \"clinical utility of a biomarker\", \"is this biomarker ready\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3159,3160,3163,3164],{"name":27,"slug":28,"type":16},{"name":3161,"slug":3162,"type":16},"Bioinformatics","bioinformatics",{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},"2026-07-12T08:37:49.295301",{"slug":3167,"name":3167,"fn":3168,"description":3169,"org":3170,"tags":3171,"stars":29,"repoUrl":30,"updatedAt":3179},"cdisc-compliance","reason about CDISC SDTM and ADaM implementation","Reason about CDISC SDTM and ADaM implementation for regulatory submissions. Use when the user asks about SDTM domain mapping, ADaM dataset design, controlled terminology versioning, define.xml completeness, FDA or PMDA submission requirements, query prioritization by clinical impact, SUPPQUAL usage, or CDISC compliance review. Triggers include \"SDTM mapping\", \"ADaM dataset\", \"CDISC compliance\", \"controlled terminology\", \"define.xml\", \"FDA submission data\", \"PMDA submission\", \"SDTM domain\", \"ADSL\", \"ADAE\", \"ADLB\", \"BDS structure\", \"SUPPQUAL\", \"RELREC\", \"value-level metadata\", \"CDISC CT\", \"regulatory submission data standards\", \"eCTD datasets\", \"SDTM 3.3\", \"ADaM 1.1\", \"query prioritization\", \"clinical data review\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3172,3175,3176],{"name":3173,"slug":3174,"type":16},"Clinical Trials","clinical-trials",{"name":24,"slug":25,"type":16},{"name":3177,"slug":3178,"type":16},"Regulatory Compliance","regulatory-compliance","2026-07-12T08:37:33.35594",{"slug":3181,"name":3181,"fn":3182,"description":3183,"org":3184,"tags":3185,"stars":29,"repoUrl":30,"updatedAt":3192},"cell-type-annotation","annotate single-cell RNA-seq clusters","Generate code to assign cell type labels to single-cell RNA-seq clusters using CellTypist, SingleR, marker-based annotation, or reference label transfer (scANVI\u002Fingest). Triggers on requests to \"annotate cell types\", \"label clusters\", \"run CellTypist\", \"SingleR annotation\", \"marker gene dotplot\", \"transfer labels from reference atlas\", \"cell identity\", \"automated annotation\", \"reference mapping\", \"scANVI label transfer\", \"canonical markers\", \"immune cell types\", \"hierarchical annotation\", \"majority voting CellTypist\", \"over-clustering annotation\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3186,3187,3188,3189],{"name":3161,"slug":3162,"type":16},{"name":21,"slug":22,"type":16},{"name":24,"slug":25,"type":16},{"name":3190,"slug":3191,"type":16},"RNA-seq","rna-seq","2026-07-12T08:38:05.443454",{"slug":3194,"name":3194,"fn":3195,"description":3196,"org":3197,"tags":3198,"stars":29,"repoUrl":30,"updatedAt":3205},"cheminformatics","calculate molecular properties with RDKit","Cheminformatics pipeline for small-molecule property calculation, filtering, and similarity analysis using RDKit. Use when the user asks to compute molecular descriptors, filter compounds by Lipinski or Veber rules, detect PAINS, calculate fingerprint similarity, run matched molecular pair analysis, generate ADMET descriptors, or process SMILES. Triggers include \"RDKit\", \"molecular descriptors\", \"Lipinski\", \"rule of five\", \"Veber\", \"PAINS\", \"pan-assay interference\", \"Morgan fingerprint\", \"Tanimoto\", \"fingerprint similarity\", \"matched molecular pair\", \"MMP\", \"mmpdb\", \"ADMET\", \"druglikeness\", \"SMILES\", \"cheminformatics\", \"compound filtering\", \"chemical similarity\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3199,3200,3203,3204],{"name":3161,"slug":3162,"type":16},{"name":3201,"slug":3202,"type":16},"Chemistry","chemistry",{"name":21,"slug":22,"type":16},{"name":14,"slug":15,"type":16},"2026-07-12T08:37:28.334619",{"slug":3207,"name":3207,"fn":3208,"description":3209,"org":3210,"tags":3211,"stars":29,"repoUrl":30,"updatedAt":3218},"claims-analytics","analyze and parse healthcare claims data","Pipeline skill for healthcare claims data parsing, analysis, and fraud detection. Use when the user asks to parse X12 837 or 835 claim files, manipulate ICD-10 CPT or HCPCS codes, detect billing pattern anomalies, profile providers against specialty peers, identify outlier billing behavior, validate NCCI edits programmatically, detect duplicate claims, run Benford's law analysis on charges, build claims data pipelines, or analyze E&M code distributions. Triggers include \"parse X12 837\", \"parse 835\", \"claims SQL\", \"ICD-10 manipulation\", \"CPT code analysis\", \"provider profiling\", \"billing outlier\", \"NCCI validation code\", \"duplicate claim detection\", \"Benford's law charges\", \"claims ETL\", \"E&M distribution analysis\", \"claims analytics pipeline\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3212,3213,3214,3217],{"name":21,"slug":22,"type":16},{"name":18,"slug":19,"type":16},{"name":3215,"slug":3216,"type":16},"Insurance","insurance",{"name":24,"slug":25,"type":16},"2026-07-12T08:37:34.815088",{"slug":3220,"name":3220,"fn":3221,"description":3222,"org":3223,"tags":3224,"stars":29,"repoUrl":30,"updatedAt":3228},"claims-billing-rules","analyze healthcare claims billing rules","Reasoning skill for healthcare claims billing rules and fraud detection logic. Use when the user asks about CMS billing rules, place of service codes, global surgery periods, modifier usage (25 59 76 77), NCCI edit logic, column 1 column 2 code pairs, mutually exclusive procedures, modifier indicators, fraud waste and abuse patterns, E&M upcoding, unbundling, phantom billing, impossible day detection, coding error versus fraud distinction, FWA investigation methodology, or claims audit logic. Triggers include \"CMS billing rules\", \"NCCI edits\", \"modifier 25\", \"modifier 59\", \"global surgery period\", \"upcoding\", \"unbundling\", \"phantom billing\", \"impossible day\", \"FWA\", \"fraud waste abuse\", \"coding error vs fraud\", \"claims audit\", \"billing compliance\", \"E&M level selection\".\n",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[3225,3226,3227],{"name":18,"slug":19,"type":16},{"name":3215,"slug":3216,"type":16},{"name":3177,"slug":3178,"type":16},"2026-07-12T08:38:28.210856",40]