Skip to content

Commit d027604

Browse files
authored
Fix the MIMIC4 LoS and DKA task unit tests (#813)
* Fix MIMIC4 LoS prediction unit tests * Fix MIMIC4 DKA prediction unit tests * Remove icustays.csv to avoid merge conflict
1 parent 1550c33 commit d027604

4 files changed

Lines changed: 154 additions & 78 deletions

File tree

test-resources/core/mimic4demo/hosp/admissions.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
subject_id,hadm_id,admittime,dischtime,admission_type,admission_location,insurance,language,marital_status,race,discharge_location,hospital_expire_flag
2+
10001,19999,2150-02-15 08:00:00,2150-02-18 14:00:00,ELECTIVE,PHYSICIAN REFERRAL,Medicare,ENGLISH,MARRIED,WHITE,HOME,0
3+
10002,20000,2151-01-01 07:00:00,2151-01-02 11:00:00,URGENT,TRANSFER FROM HOSPITAL,Medicaid,ENGLISH,SINGLE,BLACK,HOME,0
24
10001,20001,2150-03-15 08:00:00,2150-03-18 14:00:00,ELECTIVE,PHYSICIAN REFERRAL,Medicare,ENGLISH,MARRIED,WHITE,HOME,0
35
10001,20002,2150-06-20 10:30:00,2150-06-25 16:00:00,EMERGENCY,EMERGENCY ROOM,Medicare,ENGLISH,MARRIED,WHITE,HOME,0
46
10002,20003,2151-01-10 07:00:00,2151-01-12 11:00:00,URGENT,TRANSFER FROM HOSPITAL,Medicaid,ENGLISH,SINGLE,BLACK,HOME,0

test-resources/core/mimic4demo/hosp/diagnoses_icd.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
subject_id,hadm_id,seq_num,icd_code,icd_version
2+
10001,19999,1,E1065,10
3+
10002,20000,1,E1065,10
24
10001,20001,1,E1010,10
35
10001,20001,2,E1165,10
46
10001,20001,3,I10,10

tests/core/test_mimic4_dka.py

Lines changed: 143 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from pathlib import Path
44

55
from pyhealth.datasets import MIMIC4Dataset
6-
from pyhealth.tasks.dka import DKAPredictionMIMIC4
6+
from pyhealth.tasks.dka import DKAPredictionMIMIC4, T1DDKAPredictionMIMIC4
77

88

99
class TestMIMIC4DKAPrediction(unittest.TestCase):
@@ -17,7 +17,7 @@ def setUp(self):
1717
def _setup_dataset_path(self):
1818
"""Get path to local MIMIC-IV demo dataset in test resources."""
1919
# Get the path to the test-resources/core/mimic4demo directory
20-
test_dir = Path(__file__).parent.parent
20+
test_dir = Path(__file__).parent.parent.parent
2121
self.demo_dataset_path = str(
2222
test_dir / "test-resources" / "core" / "mimic4demo"
2323
)
@@ -26,12 +26,6 @@ def _setup_dataset_path(self):
2626
print(f"Setting up MIMIC-IV demo dataset for DKA prediction")
2727
print(f"Dataset path: {self.demo_dataset_path}")
2828

29-
# Verify the dataset exists
30-
if not os.path.exists(self.demo_dataset_path):
31-
raise unittest.SkipTest(
32-
f"MIMIC-IV demo dataset not found at {self.demo_dataset_path}"
33-
)
34-
3529
# List files in the hosp directory
3630
hosp_path = os.path.join(self.demo_dataset_path, "hosp")
3731
if os.path.exists(hosp_path):
@@ -47,9 +41,9 @@ def _load_dataset(self):
4741
"""Load the dataset for testing."""
4842
tables = ["diagnoses_icd", "procedures_icd", "prescriptions", "labevents"]
4943
print(f"Loading MIMIC4Dataset with tables: {tables}")
50-
self.dataset = MIMIC4Dataset(root=self.demo_dataset_path, tables=tables)
44+
self.dataset = MIMIC4Dataset(ehr_root=self.demo_dataset_path, ehr_tables=tables)
5145
print(f"✓ Dataset loaded successfully")
52-
print(f" Total patients: {len(self.dataset.patients)}")
46+
print(f" Total patients: {len(self.dataset.unique_patient_ids)}")
5347
print()
5448

5549
def test_dataset_stats(self):
@@ -75,9 +69,33 @@ def test_dka_prediction_task_initialization(self):
7569
print("Testing default initialization...")
7670
task = DKAPredictionMIMIC4()
7771
self.assertEqual(task.task_name, "DKAPredictionMIMIC4")
72+
self.assertEqual(task.padding, 0)
73+
self.assertIn("icd_codes", task.input_schema)
74+
self.assertIn("labs", task.input_schema)
75+
self.assertIn("label", task.output_schema)
76+
print(f"✓ Default task initialized: {task.task_name}")
77+
print(f" Input schema: {list(task.input_schema.keys())}")
78+
print(f" Output schema: {list(task.output_schema.keys())}")
79+
80+
# Test custom initialization
81+
print("\nTesting custom initialization...")
82+
custom_task = DKAPredictionMIMIC4(padding=5)
83+
self.assertEqual(custom_task.padding, 5)
84+
print(f"✓ Custom task initialized with padding={custom_task.padding}")
85+
86+
def test_t1ddka_prediction_task_initialization(self):
87+
"""Test T1DDKAPredictionMIMIC4 task initialization."""
88+
print(f"\n{'='*60}")
89+
print("TEST: test_t1ddka_prediction_task_initialization()")
90+
print(f"{'='*60}")
91+
92+
# Test default initialization
93+
print("Testing default initialization...")
94+
task = T1DDKAPredictionMIMIC4()
95+
self.assertEqual(task.task_name, "T1DDKAPredictionMIMIC4")
7896
self.assertEqual(task.dka_window_days, 90)
7997
self.assertEqual(task.padding, 0)
80-
self.assertIn("diagnoses", task.input_schema)
98+
self.assertIn("icd_codes", task.input_schema)
8199
self.assertIn("labs", task.input_schema)
82100
self.assertIn("label", task.output_schema)
83101
print(f"✓ Default task initialized: {task.task_name}")
@@ -87,7 +105,7 @@ def test_dka_prediction_task_initialization(self):
87105

88106
# Test custom initialization
89107
print("\nTesting custom initialization...")
90-
custom_task = DKAPredictionMIMIC4(dka_window_days=30, padding=5)
108+
custom_task = T1DDKAPredictionMIMIC4(dka_window_days=30, padding=5)
91109
self.assertEqual(custom_task.dka_window_days, 30)
92110
self.assertEqual(custom_task.padding, 5)
93111
print(f"✓ Custom task initialized with window={custom_task.dka_window_days}, padding={custom_task.padding}")
@@ -98,24 +116,37 @@ def test_dka_prediction_class_variables(self):
98116
print("TEST: test_dka_prediction_class_variables()")
99117
print(f"{'='*60}")
100118

101-
# Test T1DM codes
102-
self.assertEqual(DKAPredictionMIMIC4.T1DM_ICD10_PREFIX, "E10")
103-
self.assertIn("25001", DKAPredictionMIMIC4.T1DM_ICD9_CODES)
104-
print(f"✓ T1DM ICD-10 prefix: {DKAPredictionMIMIC4.T1DM_ICD10_PREFIX}")
105-
print(f"✓ T1DM ICD-9 codes: {len(DKAPredictionMIMIC4.T1DM_ICD9_CODES)} codes")
106-
107119
# Test DKA codes
108-
self.assertEqual(DKAPredictionMIMIC4.DKA_ICD10_PREFIX, "E101")
120+
self.assertIn("E101", DKAPredictionMIMIC4.DKA_ICD10_PREFIXES)
109121
self.assertIn("25011", DKAPredictionMIMIC4.DKA_ICD9_CODES)
110-
print(f"✓ DKA ICD-10 prefix: {DKAPredictionMIMIC4.DKA_ICD10_PREFIX}")
122+
print(f"✓ DKA ICD-10 prefix: {DKAPredictionMIMIC4.DKA_ICD10_PREFIXES}")
111123
print(f"✓ DKA ICD-9 codes: {len(DKAPredictionMIMIC4.DKA_ICD9_CODES)} codes")
112124

113125
# Test lab categories
114-
self.assertEqual(len(DKAPredictionMIMIC4.LAB_CATEGORY_ORDER), 6)
115-
expected_categories = ["glucose", "bicarbonate", "anion_gap", "potassium", "sodium", "chloride"]
126+
self.assertEqual(len(DKAPredictionMIMIC4.LAB_CATEGORY_ORDER), 10)
127+
expected_categories = ["Sodium", "Potassium", "Chloride", "Bicarbonate", "Glucose", "Calcium", "Magnesium", "Anion Gap", "Osmolality", "Phosphate"]
116128
self.assertEqual(DKAPredictionMIMIC4.LAB_CATEGORY_ORDER, expected_categories)
117129
print(f"✓ Lab categories: {DKAPredictionMIMIC4.LAB_CATEGORY_ORDER}")
118-
print(f"✓ Total lab item IDs: {len(DKAPredictionMIMIC4.ALL_LAB_ITEMIDS)}")
130+
print(f"✓ Total lab item IDs: {len(DKAPredictionMIMIC4.LABITEMS)}")
131+
132+
def test_t1ddka_prediction_class_variables(self):
133+
"""Test that class variables are properly defined."""
134+
print(f"\n{'='*60}")
135+
print("TEST: test_t1ddka_prediction_class_variables()")
136+
print(f"{'='*60}")
137+
138+
# Test T1DM codes
139+
self.assertEqual(T1DDKAPredictionMIMIC4.T1DM_ICD10_PREFIX, "E10")
140+
self.assertIn("25001", T1DDKAPredictionMIMIC4.T1DM_ICD9_CODES)
141+
print(f"✓ T1DM ICD-10 prefix: {T1DDKAPredictionMIMIC4.T1DM_ICD10_PREFIX}")
142+
print(f"✓ T1DM ICD-9 codes: {len(T1DDKAPredictionMIMIC4.T1DM_ICD9_CODES)} codes")
143+
144+
# Test lab categories
145+
self.assertEqual(len(T1DDKAPredictionMIMIC4.LAB_CATEGORY_ORDER), 10)
146+
expected_categories = ["Sodium", "Potassium", "Chloride", "Bicarbonate", "Glucose", "Calcium", "Magnesium", "Anion Gap", "Osmolality", "Phosphate"]
147+
self.assertEqual(T1DDKAPredictionMIMIC4.LAB_CATEGORY_ORDER, expected_categories)
148+
print(f"✓ Lab categories: {T1DDKAPredictionMIMIC4.LAB_CATEGORY_ORDER}")
149+
print(f"✓ Total lab item IDs: {len(T1DDKAPredictionMIMIC4.LABITEMS)}")
119150

120151
def test_dka_prediction_set_task(self):
121152
"""Test DKAPredictionMIMIC4 task with set_task() method."""
@@ -131,18 +162,15 @@ def test_dka_prediction_set_task(self):
131162
print("\nCalling dataset.set_task()...")
132163
sample_dataset = self.dataset.set_task(task)
133164
self.assertIsNotNone(sample_dataset, "set_task should return a dataset")
134-
self.assertTrue(
135-
hasattr(sample_dataset, "samples"), "Sample dataset should have samples"
136-
)
137165
print(f"✓ set_task() completed")
138166

139167
# Check sample count
140-
num_samples = len(sample_dataset.samples)
168+
num_samples = len(sample_dataset)
141169
print(f"✓ Generated {num_samples} DKA prediction samples")
142170

143171
if num_samples > 0:
144-
sample = sample_dataset.samples[0]
145-
required_keys = ["patient_id", "record_id", "diagnoses", "labs", "label"]
172+
sample = sample_dataset[0]
173+
required_keys = ["patient_id", "record_id", "icd_codes", "labs", "label"]
146174

147175
print(f"\nFirst sample structure:")
148176
print(f" Sample keys: {list(sample.keys())}")
@@ -151,7 +179,7 @@ def test_dka_prediction_set_task(self):
151179
self.assertIn(key, sample, f"Sample should contain key: {key}")
152180

153181
# Verify diagnoses format (tuple of times and sequences)
154-
diagnoses = sample["diagnoses"]
182+
diagnoses = sample["icd_codes"]
155183
self.assertIsInstance(diagnoses, tuple, "diagnoses should be a tuple")
156184
self.assertEqual(len(diagnoses), 2, "diagnoses tuple should have 2 elements")
157185
print(f" - diagnoses: {len(diagnoses[1])} admission(s)")
@@ -168,8 +196,8 @@ def test_dka_prediction_set_task(self):
168196

169197
# Count label distribution
170198
label_counts = {0: 0, 1: 0}
171-
for s in sample_dataset.samples:
172-
label_counts[s["label"]] += 1
199+
for s in sample_dataset:
200+
label_counts[int(s["label"].item())] += 1
173201

174202
print(f"\nLabel distribution:")
175203
print(f" - No DKA (0): {label_counts[0]} ({label_counts[0]/num_samples*100:.1f}%)")
@@ -183,6 +211,69 @@ def test_dka_prediction_set_task(self):
183211
traceback.print_exc()
184212
self.fail(f"Failed to use set_task with DKAPredictionMIMIC4: {e}")
185213

214+
def test_t1ddka_prediction_set_task(self):
215+
"""Test T1DDKAPredictionMIMIC4 task with set_task() method."""
216+
print(f"\n{'='*60}")
217+
print("TEST: test_t1ddka_prediction_set_task()")
218+
print(f"{'='*60}")
219+
220+
print("Initializing T1DDKAPredictionMIMIC4 task...")
221+
task = T1DDKAPredictionMIMIC4()
222+
223+
# Test using set_task method
224+
try:
225+
print("\nCalling dataset.set_task()...")
226+
sample_dataset = self.dataset.set_task(task)
227+
self.assertIsNotNone(sample_dataset, "set_task should return a dataset")
228+
print(f"✓ set_task() completed")
229+
230+
# Check sample count
231+
num_samples = len(sample_dataset)
232+
print(f"✓ Generated {num_samples} DKA prediction samples")
233+
234+
if num_samples > 0:
235+
sample = sample_dataset[0]
236+
required_keys = ["patient_id", "record_id", "icd_codes", "labs", "label"]
237+
238+
print(f"\nFirst sample structure:")
239+
print(f" Sample keys: {list(sample.keys())}")
240+
241+
for key in required_keys:
242+
self.assertIn(key, sample, f"Sample should contain key: {key}")
243+
244+
# Verify diagnoses format (tuple of times and sequences)
245+
diagnoses = sample["icd_codes"]
246+
self.assertIsInstance(diagnoses, tuple, "diagnoses should be a tuple")
247+
self.assertEqual(len(diagnoses), 2, "diagnoses tuple should have 2 elements")
248+
print(f" - diagnoses: {len(diagnoses[1])} admission(s)")
249+
250+
# Verify labs format (tuple of times and sequences)
251+
labs = sample["labs"]
252+
self.assertIsInstance(labs, tuple, "labs should be a tuple")
253+
self.assertEqual(len(labs), 2, "labs tuple should have 2 elements")
254+
print(f" - labs: {len(labs[1])} lab vector(s)")
255+
256+
# Verify label is binary
257+
self.assertIn(sample["label"], [0, 1], "Label should be binary (0 or 1)")
258+
print(f" - label: {sample['label']}")
259+
260+
# Count label distribution
261+
label_counts = {0: 0, 1: 0}
262+
for s in sample_dataset:
263+
label_counts[int(s["label"].item())] += 1
264+
265+
print(f"\nLabel distribution:")
266+
print(f" - No DKA (0): {label_counts[0]} ({label_counts[0]/num_samples*100:.1f}%)")
267+
print(f" - Has DKA (1): {label_counts[1]} ({label_counts[1]/num_samples*100:.1f}%)")
268+
269+
print(f"\n✓ test_t1ddka_prediction_set_task() passed successfully")
270+
271+
except Exception as e:
272+
print(f"✗ Failed with error: {e}")
273+
import traceback
274+
traceback.print_exc()
275+
self.fail(f"Failed to use set_task with T1DDKAPredictionMIMIC4: {e}")
276+
186277
def test_dka_prediction_helper_methods(self):
187278
"""Test helper methods of DKAPredictionMIMIC4."""
188279
print(f"\n{'='*60}")
@@ -191,13 +282,25 @@ def test_dka_prediction_helper_methods(self):
191282

192283
task = DKAPredictionMIMIC4()
193284

194-
# Test _normalize_icd
195-
print("Testing _normalize_icd()...")
196-
self.assertEqual(task._normalize_icd("E10.10"), "E1010")
197-
self.assertEqual(task._normalize_icd("e10.10"), "E1010")
198-
self.assertEqual(task._normalize_icd(None), "")
199-
self.assertEqual(task._normalize_icd(" 25001 "), "25001")
200-
print("✓ _normalize_icd() works correctly")
285+
# Test _is_dka_code
286+
print("\nTesting _is_dka_code()...")
287+
self.assertTrue(task._is_dka_code("E10.10", 10))
288+
self.assertTrue(task._is_dka_code("E1011", "10"))
289+
self.assertTrue(task._is_dka_code("25011", 9))
290+
self.assertTrue(task._is_dka_code("25013", "9"))
291+
self.assertFalse(task._is_dka_code("E10.65", 10)) # Not DKA
292+
self.assertFalse(task._is_dka_code(None, 10))
293+
print("✓ _is_dka_code() works correctly")
294+
295+
print(f"\n✓ test_dka_prediction_helper_methods() passed successfully")
296+
297+
def test_t1ddka_prediction_helper_methods(self):
298+
"""Test helper methods of DKAPredictionMIMIC4."""
299+
print(f"\n{'='*60}")
300+
print("TEST: test_t1ddka_prediction_helper_methods()")
301+
print(f"{'='*60}")
302+
303+
task = T1DDKAPredictionMIMIC4()
201304

202305
# Test _is_t1dm_code
203306
print("\nTesting _is_t1dm_code()...")
@@ -220,19 +323,7 @@ def test_dka_prediction_helper_methods(self):
220323
self.assertFalse(task._is_dka_code(None, 10))
221324
print("✓ _is_dka_code() works correctly")
222325

223-
# Test _deduplicate_preserve_order
224-
print("\nTesting _deduplicate_preserve_order()...")
225-
self.assertEqual(
226-
task._deduplicate_preserve_order(["A", "B", "A", "C", "B"]),
227-
["A", "B", "C"]
228-
)
229-
self.assertEqual(
230-
task._deduplicate_preserve_order([]),
231-
[]
232-
)
233-
print("✓ _deduplicate_preserve_order() works correctly")
234-
235-
print(f"\n✓ test_dka_prediction_helper_methods() passed successfully")
326+
print(f"\n✓ test_t1ddka_prediction_helper_methods() passed successfully")
236327

237328

238329
if __name__ == "__main__":

tests/core/test_mimic4_los.py

Lines changed: 7 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def setUp(self):
1717
def _setup_dataset_path(self):
1818
"""Get path to local MIMIC-IV demo dataset in test resources."""
1919
# Get the path to the test-resources/core/mimic4demo directory
20-
test_dir = Path(__file__).parent.parent
20+
test_dir = Path(__file__).parent.parent.parent
2121
self.demo_dataset_path = str(
2222
test_dir / "test-resources" / "core" / "mimic4demo"
2323
)
@@ -26,12 +26,6 @@ def _setup_dataset_path(self):
2626
print(f"Setting up MIMIC-IV demo dataset for length of stay prediction")
2727
print(f"Dataset path: {self.demo_dataset_path}")
2828

29-
# Verify the dataset exists
30-
if not os.path.exists(self.demo_dataset_path):
31-
raise unittest.SkipTest(
32-
f"MIMIC-IV demo dataset not found at {self.demo_dataset_path}"
33-
)
34-
3529
# List files in the hosp directory
3630
hosp_path = os.path.join(self.demo_dataset_path, "hosp")
3731
if os.path.exists(hosp_path):
@@ -47,9 +41,9 @@ def _load_dataset(self):
4741
"""Load the dataset for testing."""
4842
tables = ["diagnoses_icd", "procedures_icd", "prescriptions"]
4943
print(f"Loading MIMIC4Dataset with tables: {tables}")
50-
self.dataset = MIMIC4Dataset(root=self.demo_dataset_path, tables=tables)
44+
self.dataset = MIMIC4Dataset(ehr_root=self.demo_dataset_path, ehr_tables=tables)
5145
print(f"✓ Dataset loaded successfully")
52-
print(f" Total patients: {len(self.dataset.patients)}")
46+
print(f" Total patients: {len(self.dataset.unique_patient_ids)}")
5347
print()
5448

5549
def test_dataset_stats(self):
@@ -89,19 +83,16 @@ def test_length_of_stay_prediction_mimic4_set_task(self):
8983
print("\nCalling dataset.set_task()...")
9084
sample_dataset = self.dataset.set_task(task)
9185
self.assertIsNotNone(sample_dataset, "set_task should return a dataset")
92-
self.assertTrue(
93-
hasattr(sample_dataset, "samples"), "Sample dataset should have samples"
94-
)
9586
print(f"✓ set_task() completed")
9687

9788
# Verify we got some samples
98-
num_samples = len(sample_dataset.samples)
89+
num_samples = len(sample_dataset)
9990
self.assertGreater(num_samples, 0, "Should generate at least one sample")
10091
print(f"✓ Generated {num_samples} length of stay prediction samples")
10192

10293
# Test sample structure
10394
if num_samples > 0:
104-
sample = sample_dataset.samples[0]
95+
sample = sample_dataset[0]
10596
required_keys = [
10697
"visit_id",
10798
"patient_id",
@@ -128,20 +119,10 @@ def test_length_of_stay_prediction_mimic4_set_task(self):
128119
"Length of stay category should be 0-9",
129120
)
130121

131-
# Verify conditions include icd_version prefix (MIMIC-4 specific)
132-
if sample["conditions"]:
133-
first_condition = sample["conditions"][0]
134-
self.assertIn(
135-
"_",
136-
first_condition,
137-
"MIMIC-4 conditions should include version prefix (e.g., '10_E1010')",
138-
)
139-
print(f" Sample condition format: {first_condition}")
140-
141122
# Count LOS distribution
142123
los_counts = {i: 0 for i in range(10)}
143-
for s in sample_dataset.samples:
144-
los_counts[s["los"]] += 1
124+
for s in sample_dataset:
125+
los_counts[int(s["los"].item())] += 1
145126

146127
print(f"\nLength of stay category distribution:")
147128
category_labels = [

0 commit comments

Comments
 (0)