Analyze a patient cohort: $ARGUMENTS
Use your fhir-basics skill to query FHIR endpoints. Use your clinical-knowledge skill to identify care gaps and apply correct thresholds. Use your analysis-methods skill to write correct Python analysis code.
Execution Rules
- Do NOT explore the workspace or list files. Begin the analysis immediately.
- Write ONE Python script that does everything: FHIR queries, analysis, chart, and summary.
- Write the script correctly the first time. Do NOT write a draft and then edit it.
- Run it with
python (NOT python3).
- All HTTP calls must use
subprocess.run(["curl", "-sf", "--max-time", "30", url], capture_output=True, text=True) -- the requests library does NOT work through the sandbox proxy. See the fhir-basics skill for the fhir_get helper pattern.
- Save the script to
/tmp/<name>.py, run it once, interpret the output.
Steps
Identify the cohort -- Query GET /Condition?code={snomed_code}&_count=200 and follow pagination links to get all matching Condition resources. Extract unique patient IDs from entry[].resource.subject.reference. Report the cohort size.
Pull clinical data in BATCHED queries (do NOT loop per-patient):
- Lab values: Use
get_latest_labs_batch(loinc_code, patient_ids) to fetch ALL observations for the LOINC code in one call and filter client-side. This queries GET /Observation?code={loinc_code}&_count=500&_sort=-date without a patient filter, then builds a dict keyed by patient ID. Handle both valueQuantity (numeric) and valueString (text) formats. For blood pressure, query the BP panel code 85354-9 in batch and parse components.
- Medications: Use
get_all_medications_batch(patient_ids) to fetch GET /MedicationRequest?status=active&_count=500 in one call, then filter to cohort patients client-side.
- NEVER write a
for pid in patient_ids: loop that makes FHIR HTTP calls inside the loop. The sandbox proxy adds 1-3s latency per call. With 24 patients x 4 LOINC codes = 96 calls = 5+ minutes. Batching brings this to 4-6 total calls = 30 seconds.
Build a pandas DataFrame with one row per patient:
patient_id (string)
{lab_name} (float or None)
lab_date (string)
on_target_med (boolean -- True if the patient is on the specified medication class)
medications (comma-separated string of all active med names)
med_count (int)
Data quality check:
- Report how many patients have the lab recorded vs. missing
- If > 30% missing, flag as a data quality issue but continue analysis
- If < 5 patients have data, warn that the sample is too small for meaningful statistics
Identify care gap patients: Apply the threshold and medication check:
- Gap = lab value exceeds threshold AND patient is NOT on the specified medication class
- Report: total with condition, total with lab data, total above threshold, total in care gap
- Compute gap rate as percentage of patients with lab data (not total cohort)
Generate visualization:
- Histogram of the lab value distribution with threshold line
- Use NVIDIA dark theme:
plt.style.use('dark_background'), primary color #76B900, background #1a1a1a
- Annotate with sample size (N = ...) and gap count
- Save as PNG with
dpi=150
Write a plain-English summary including:
- Cohort size and data completeness
- Distribution statistics (mean, median, range)
- Gap patient count and percentage with absolute numbers
- Comparison to relevant CMS quality measure if applicable
- Any notable patterns (e.g., "All gap patients had no medications recorded at all")
Disclaimer: "This analysis is for research and operational purposes. Clinical decisions should be made by qualified clinicians."
Example: Diabetes Gap Analysis (CMS122)
Condition: Type 2 Diabetes (SNOMED 44054006)
Lab: HbA1c (LOINC 4548-4)
Threshold: > 9.0%
Gap medication: insulin or GLP-1 agonist
Quality measure: CMS122v12 (poor glycemic control)
INSULIN_AND_GLP1 = ["insulin", "liraglutide", "semaglutide", "dulaglutide",
"exenatide", "tirzepatide", "victoza", "ozempic",
"trulicity", "byetta", "mounjaro", "rybelsus"]
def is_on_insulin_or_glp1(med_list):
med_lower = [m.lower() for m in med_list]
return any(drug in med_text for drug in INSULIN_AND_GLP1 for med_text in med_lower)
Example: Hypertension Gap Analysis (CMS165)
Condition: Essential Hypertension (SNOMED 38341003)
Lab: Systolic BP (LOINC 8480-6) -- use component Observation pattern
Threshold: >= 140 mmHg
Gap medication: any antihypertensive
Quality measure: CMS165v12 (controlling high blood pressure)
Note: Use get_latest_bp() from the analysis-methods skill to handle both BP panel (85354-9) and standalone systolic Observations.
ANTIHYPERTENSIVES = ["lisinopril", "enalapril", "ramipril", "benazepril",
"losartan", "valsartan", "irbesartan", "olmesartan", "telmisartan",
"amlodipine", "nifedipine", "diltiazem",
"metoprolol", "atenolol", "carvedilol", "bisoprolol",
"hydrochlorothiazide", "hctz", "chlorthalidone",
"furosemide", "spironolactone"]
def is_on_antihypertensive(med_list):
med_lower = [m.lower() for m in med_list]
return any(drug in med_text for drug in ANTIHYPERTENSIVES for med_text in med_lower)
1---2name: cohort-compare3description: Analyze a cohort of patients from FHIR endpoints to find care gaps and patterns. Use when asked to compare patients, find quality gaps, or analyze a population.4---56Analyze a patient cohort: $ARGUMENTS78Use your fhir-basics skill to query FHIR endpoints. Use your clinical-knowledge skill to identify care gaps and apply correct thresholds. Use your analysis-methods skill to write correct Python analysis code.910## Execution Rules1112- Do NOT explore the workspace or list files. Begin the analysis immediately.13- Write ONE Python script that does everything: FHIR queries, analysis, chart, and summary.14- Write the script correctly the first time. Do NOT write a draft and then edit it.15- Run it with `python` (NOT `python3`).16- All HTTP calls must use `subprocess.run(["curl", "-sf", "--max-time", "30", url], capture_output=True, text=True)` -- the `requests` library does NOT work through the sandbox proxy. See the fhir-basics skill for the `fhir_get` helper pattern.17- Save the script to `/tmp/<name>.py`, run it once, interpret the output.1819## Steps20211. **Identify the cohort** -- Query `GET /Condition?code={snomed_code}&_count=200` and follow pagination links to get all matching Condition resources. Extract unique patient IDs from `entry[].resource.subject.reference`. Report the cohort size.22232. **Pull clinical data in BATCHED queries (do NOT loop per-patient)**:24 - **Lab values**: Use `get_latest_labs_batch(loinc_code, patient_ids)` to fetch ALL observations for the LOINC code in one call and filter client-side. This queries `GET /Observation?code={loinc_code}&_count=500&_sort=-date` without a patient filter, then builds a dict keyed by patient ID. Handle both `valueQuantity` (numeric) and `valueString` (text) formats. For blood pressure, query the BP panel code `85354-9` in batch and parse components.25 - **Medications**: Use `get_all_medications_batch(patient_ids)` to fetch `GET /MedicationRequest?status=active&_count=500` in one call, then filter to cohort patients client-side.26 - **NEVER write a `for pid in patient_ids:` loop that makes FHIR HTTP calls inside the loop.** The sandbox proxy adds 1-3s latency per call. With 24 patients x 4 LOINC codes = 96 calls = 5+ minutes. Batching brings this to 4-6 total calls = 30 seconds.27283. **Build a pandas DataFrame** with one row per patient:29 - `patient_id` (string)30 - `{lab_name}` (float or None)31 - `lab_date` (string)32 - `on_target_med` (boolean -- True if the patient is on the specified medication class)33 - `medications` (comma-separated string of all active med names)34 - `med_count` (int)35364. **Data quality check**:37 - Report how many patients have the lab recorded vs. missing38 - If > 30% missing, flag as a data quality issue but continue analysis39 - If < 5 patients have data, warn that the sample is too small for meaningful statistics40415. **Identify care gap patients**: Apply the threshold and medication check:42 - Gap = lab value exceeds threshold AND patient is NOT on the specified medication class43 - Report: total with condition, total with lab data, total above threshold, total in care gap44 - Compute gap rate as percentage of patients with lab data (not total cohort)45466. **Generate visualization**:47 - Histogram of the lab value distribution with threshold line48 - Use NVIDIA dark theme: `plt.style.use('dark_background')`, primary color `#76B900`, background `#1a1a1a`49 - Annotate with sample size (N = ...) and gap count50 - Save as PNG with `dpi=150`51527. **Write a plain-English summary** including:53 - Cohort size and data completeness54 - Distribution statistics (mean, median, range)55 - Gap patient count and percentage with absolute numbers56 - Comparison to relevant CMS quality measure if applicable57 - Any notable patterns (e.g., "All gap patients had no medications recorded at all")58598. **Disclaimer**: "This analysis is for research and operational purposes. Clinical decisions should be made by qualified clinicians."6061## Example: Diabetes Gap Analysis (CMS122)6263Condition: Type 2 Diabetes (SNOMED 44054006)64Lab: HbA1c (LOINC 4548-4)65Threshold: > 9.0%66Gap medication: insulin or GLP-1 agonist67Quality measure: CMS122v12 (poor glycemic control)6869```python70INSULIN_AND_GLP1 = ["insulin", "liraglutide", "semaglutide", "dulaglutide",71 "exenatide", "tirzepatide", "victoza", "ozempic",72 "trulicity", "byetta", "mounjaro", "rybelsus"]7374def is_on_insulin_or_glp1(med_list):75 med_lower = [m.lower() for m in med_list]76 return any(drug in med_text for drug in INSULIN_AND_GLP1 for med_text in med_lower)77```7879## Example: Hypertension Gap Analysis (CMS165)8081Condition: Essential Hypertension (SNOMED 38341003)82Lab: Systolic BP (LOINC 8480-6) -- **use component Observation pattern**83Threshold: >= 140 mmHg84Gap medication: any antihypertensive85Quality measure: CMS165v12 (controlling high blood pressure)8687Note: Use `get_latest_bp()` from the analysis-methods skill to handle both BP panel (85354-9) and standalone systolic Observations.8889```python90ANTIHYPERTENSIVES = ["lisinopril", "enalapril", "ramipril", "benazepril",91 "losartan", "valsartan", "irbesartan", "olmesartan", "telmisartan",92 "amlodipine", "nifedipine", "diltiazem",93 "metoprolol", "atenolol", "carvedilol", "bisoprolol",94 "hydrochlorothiazide", "hctz", "chlorthalidone",95 "furosemide", "spironolactone"]9697def is_on_antihypertensive(med_list):98 med_lower = [m.lower() for m in med_list]99 return any(drug in med_text for drug in ANTIHYPERTENSIVES for med_text in med_lower)100```