1+ from __future__ import annotations
2+
3+ import os
4+
5+ import numpy as np
6+ import torch
7+ from sklearn .ensemble import RandomForestClassifier
8+ from sklearn .metrics import brier_score_loss , roc_auc_score
9+
10+ from pyhealth .datasets import (
11+ MIMIC4EHRDataset ,
12+ create_sample_dataset ,
13+ get_dataloader ,
14+ )
15+ from pyhealth .models import CaliForest
16+ from pyhealth .tasks import InHospitalMortalityMIMIC4
17+
18+
19+ # Set your MIMIC-IV dataset path via environment variable before running:
20+ # export MIMIC4_ROOT=/your/path/to/mimiciv/3.1
21+ ROOT = os .getenv ("MIMIC4_ROOT" )
22+
23+
24+ def evaluate (y_true : np .ndarray , y_prob : np .ndarray ) -> dict [str , float ]:
25+ """Compute AUROC and Brier score."""
26+ y_true = np .asarray (y_true ).reshape (- 1 )
27+ y_prob = np .asarray (y_prob ).reshape (- 1 )
28+ return {
29+ "auroc" : float (roc_auc_score (y_true , y_prob )),
30+ "brier" : float (brier_score_loss (y_true , y_prob )),
31+ }
32+
33+
34+ def run_califorest (
35+ X_train : np .ndarray ,
36+ y_train : np .ndarray ,
37+ X_test : np .ndarray ,
38+ y_test : np .ndarray ,
39+ calibration : str ,
40+ ) -> dict [str , float ]:
41+ """Train and evaluate CaliForest on tabularized features."""
42+ train_samples = []
43+ for i in range (len (X_train )):
44+ train_samples .append (
45+ {
46+ "patient_id" : f"train-{ i } " ,
47+ "visit_id" : f"train-{ i } " ,
48+ "features" : X_train [i ].tolist (),
49+ "label" : int (y_train [i ]),
50+ }
51+ )
52+
53+ test_samples = []
54+ for i in range (len (X_test )):
55+ test_samples .append (
56+ {
57+ "patient_id" : f"test-{ i } " ,
58+ "visit_id" : f"test-{ i } " ,
59+ "features" : X_test [i ].tolist (),
60+ "label" : int (y_test [i ]),
61+ }
62+ )
63+
64+ train_dataset = create_sample_dataset (
65+ samples = train_samples ,
66+ input_schema = {"features" : "tensor" },
67+ output_schema = {"label" : "binary" },
68+ dataset_name = f"mimic4_train_tabular_{ calibration } " ,
69+ )
70+ test_dataset = create_sample_dataset (
71+ samples = test_samples ,
72+ input_schema = {"features" : "tensor" },
73+ output_schema = {"label" : "binary" },
74+ dataset_name = f"mimic4_test_tabular_{ calibration } " ,
75+ )
76+
77+ train_loader = get_dataloader (
78+ train_dataset , batch_size = len (train_dataset ), shuffle = False
79+ )
80+ test_loader = get_dataloader (
81+ test_dataset , batch_size = len (test_dataset ), shuffle = False
82+ )
83+
84+ test_batch = next (iter (test_loader ))
85+
86+ model = CaliForest (
87+ dataset = train_dataset ,
88+ n_estimators = 100 ,
89+ calibration = calibration ,
90+ random_state = 42 ,
91+ )
92+ model .fit (train_loader )
93+
94+ with torch .no_grad ():
95+ ret = model (** test_batch )
96+
97+ cali_probs = ret ["y_prob" ].detach ().cpu ().numpy ().reshape (- 1 )
98+ return evaluate (y_test , cali_probs )
99+
100+
101+ def main ():
102+ if not ROOT :
103+ raise ValueError (
104+ "MIMIC4_ROOT is not set. Example:\n "
105+ "export MIMIC4_ROOT=/your/path/to/mimiciv/3.1"
106+ )
107+
108+ print ("=" * 80 )
109+ print ("Loading MIMIC-IV EHR dataset" )
110+ print ("=" * 80 )
111+
112+ dataset = MIMIC4EHRDataset (
113+ root = ROOT ,
114+ tables = ["diagnoses_icd" , "procedures_icd" , "labevents" ],
115+ )
116+
117+ task = InHospitalMortalityMIMIC4 ()
118+ sample_dataset = dataset .set_task (task )
119+
120+ print (f"Total samples: { len (sample_dataset )} " )
121+
122+ subset_size = 2000
123+ raw_subset_samples = [sample_dataset [i ] for i in range (subset_size )]
124+
125+ clean_subset_samples = []
126+ for sample in raw_subset_samples :
127+ clean_subset_samples .append (
128+ {
129+ "patient_id" : str (sample ["patient_id" ]),
130+ "visit_id" : str (sample ["admission_id" ]),
131+ "labs" : sample ["labs" ].tolist (),
132+ "mortality" : int (sample ["mortality" ].item ()),
133+ }
134+ )
135+
136+ subset_dataset = create_sample_dataset (
137+ samples = clean_subset_samples ,
138+ input_schema = {"labs" : "tensor" },
139+ output_schema = {"mortality" : "binary" },
140+ dataset_name = "mimic4_mortality_subset" ,
141+ )
142+
143+ loader = get_dataloader (subset_dataset , batch_size = subset_size , shuffle = False )
144+ batch = next (iter (loader ))
145+
146+ X = batch ["labs" ].detach ().cpu ().numpy ()
147+ y = batch ["mortality" ].detach ().cpu ().numpy ().reshape (- 1 )
148+
149+ X = X .reshape (X .shape [0 ], - 1 )
150+
151+ print ("Flattened feature matrix:" , X .shape )
152+ print ("Labels:" , y .shape )
153+
154+ split = int (0.8 * len (X ))
155+ X_train , X_test = X [:split ], X [split :]
156+ y_train , y_test = y [:split ], y [split :]
157+
158+ print ("=" * 80 )
159+ print ("Baseline Random Forest" )
160+ print ("=" * 80 )
161+
162+ rf = RandomForestClassifier (
163+ n_estimators = 100 ,
164+ random_state = 42 ,
165+ bootstrap = True ,
166+ )
167+ rf .fit (X_train , y_train )
168+ rf_probs = rf .predict_proba (X_test )[:, 1 ]
169+ rf_metrics = evaluate (y_test , rf_probs )
170+ print ("RF metrics:" , rf_metrics )
171+
172+ print ("=" * 80 )
173+ print ("CaliForest (isotonic calibration)" )
174+ print ("=" * 80 )
175+ isotonic_metrics = run_califorest (
176+ X_train , y_train , X_test , y_test , calibration = "isotonic"
177+ )
178+ print ("CaliForest isotonic metrics:" , isotonic_metrics )
179+
180+ print ("=" * 80 )
181+ print ("CaliForest (logistic calibration)" )
182+ print ("=" * 80 )
183+ logistic_metrics = run_califorest (
184+ X_train , y_train , X_test , y_test , calibration = "logistic"
185+ )
186+ print ("CaliForest logistic metrics:" , logistic_metrics )
187+
188+
189+ if __name__ == "__main__" :
190+ main ()
0 commit comments