Skip to content

Health

A clinical and patient-analytics warehouse. Patients visit providers, receive diagnoses, undergo lab tests, and are monitored for readmission. Risk stratification is tracked with SCD2 versioning; diagnoses accumulate via a many-to-many bridge; encounters produce child lab results (parent/child); prescriptions reference encounters via a cross-fact FK. A per-metric treatment cohort isolates a clinical intervention's lift on medication adherence specifically. Holdout reserves the last quarter for readmission prediction.

What it produces

Table Type Grain Rows (default) Notes
dim_date dim per_period 24 24 monthly periods, Jan 2023 – Dec 2024
dim_patient dim per_entity (+ SCD2 + geo) 102–408 102 patients, expanded by SCD2 risk-stratification
dim_diagnosis dim per_reference 8 hypertension, type2_diabetes, asthma, ...
dim_medication dim per_reference 10 analgesic, antibiotic, statin, ...
fct_clinical_activity fact per_entity_per_period 2,448 seven metrics + encounter narrative notes
fct_encounters fact variable (proportional, CDC) ~encounter_vol one row per encounter; CDC for chart amendments
fct_lab_results fact per_parent_row 1–4× parent child labs per encounter
fct_prescriptions fact variable (proportional) ~0.6× enc_vol cross-fact FK to fct_encounters
evt_lab_order event variable (proportional) ~7,000 scaled 3× lab_volume
evt_readmission event variable (proportional) ~150–400 scaled 1.5× readmission_risk
bridge_patient_diagnosis bridge static M:N 100–400 each patient accumulates 1–4 diagnoses
<fct>_train, <fct>_holdout fact holdout split one pair per per_entity_per_period fact (3-period split)

102 entities across six segments: chronic_progressive (18), recovering (16, half in a per-metric treatment cohort), acute_episodic (20), well_managed (22), pediatric_routine (14), high_risk_post_surgical (12).

Features exercised

  • Heteroscedastic noisescale_with_trajectory: true. Patients with higher risk show larger absolute vital-sign jitter.
  • Seasonality — flu/respiratory season Oct–Feb (+0.20), summer dip in acute visits Jun–Aug (-0.15).
  • Causal lagreadmission_risk follows bp_systolic with delay 1.
  • SCD2risk_stratification on dim_patient tracking readmission_risk with thresholds [0.25, 0.55, 0.80] (low / moderate / high / critical).
  • Per-metric treatmentrecovering segment 50/50 split. Treatment arm gets lift_log_odds: 0.6 at period 6, but only medication_adherence shifts. Other vitals (BP, A1c) stay byte-identical to control draws.
  • Parent / child + cross-fact FKfct_encounters drives child fct_lab_results; fct_prescriptions references encounters via ref.fct_encounters (a cross-fact FK).
  • CDCfct_encounters carries audit columns for chart amendments after coding/billing review.
  • Bridgebridge_patient_diagnosis (M:N), trajectory-driven by readmission_risk so high-risk patients accumulate more diagnoses.
  • Geo bundlehome_country, home_country_code, home_region, home_city on dim_patient.
  • Narrative columnencounter_notes on fct_clinical_activity with per-segment lexicons.
  • Holdout — last three months reserved for readmission-prediction validation.
  • Lifecycle stageswell / watch / high_risk / critical on readmission_risk.

Quality injection is omitted to keep the readmission-prediction teaching surface clean. holdout and quality combine cleanly if you want to layer corruption on top — corrupted rows are sliced by date_key, so both train and holdout splits carry their share of issues.

Use it

from plotsim import load_template, generate_tables, write_tables

config = load_template("health")
tables = generate_tables(config)
write_tables(tables, config, output_dir="./output")
plotsim template health -o health.yaml
plotsim run health.yaml -o ./output

Common customizations

Drop the per-metric narrowing on the treatment

Default treatment narrows to medication_adherence. To let the lift shift every metric (trajectory-wide application):

# Re-author with create() — segments are immutable on the loaded config.
segments=[
    # ... other segments unchanged ...
    {
        "name":      "recovering",
        "count":     16,
        "archetype": "decline",
        "baseline":  {"readmission_risk": "high", "medication_adherence": "mid"},
        "treatment": {
            "fraction":      0.5,
            "lift_log_odds": 0.6,
            "start_period":  6,
            # target_metric omitted → trajectory-wide
        },
    },
]
segments:
  - name: recovering
    count: 16
    archetype: decline
    baseline: { readmission_risk: high, medication_adherence: mid }
    treatment:
      fraction:      0.5
      lift_log_odds: 0.6
      start_period:  6
      # target_metric omitted → trajectory-wide

Bias the diagnosis bridge by a different metric

Default driver is readmission_risk. To bias diagnosis count by A1c:

bridges=[
    {
        "name":        "bridge_patient_diagnosis",
        "left":        "dim_patient",
        "right":       "dim_diagnosis",
        "cardinality": (1, 4),
        "driver":      "a1c",     # changed from readmission_risk
    },
]
bridges:
  - name: bridge_patient_diagnosis
    left: dim_patient
    right: dim_diagnosis
    cardinality: [1, 4]
    driver: a1c

Switch to daily granularity for a year

Encounters and labs are more naturally daily than monthly:

config = create(
    # ... same other fields ...
    window=("2024-01-01", "2024-12-31", "daily"),
)
window:
  start: 2024-01-01
  end:   2024-12-31
  every: daily

The cell-budget gate (default 2,000,000 cells) may need raising for daily windows; see Output and scaling.

Where it shines

  • Readmission-prediction training pipelines — readmission_risk as target, the holdout split for validation, the manifest's treatment_cohorts and archetype_assignments as the answer key.
  • Per-metric ATE recovery exercises — the recovering cohort's per-metric treatment lets students verify they can identify which metric was actually shifted versus which metrics moved through correlation leakage.
  • Clinical-note classification — encounter_notes carries archetype-driven text suitable for narrative classifier teaching.