Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/api/tasks/pyhealth.tasks.dka.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,7 @@ pyhealth.tasks.dka
:undoc-members:
:show-inheritance:

.. autoclass:: pyhealth.tasks.dka.T1DDKAPredictionMIMIC4
:members:
:undoc-members:
:show-inheritance:
3 changes: 1 addition & 2 deletions examples/clinical_tasks/t1dka_mimic4.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

Target Population:
- Patients with Type 1 Diabetes Mellitus (T1DM) ONLY
- Predicts DKA occurrence within 90 days of T1DM diagnosis
- Predicts DKA from the T1DM diagnosis through the next 90 days
- Smaller, focused patient cohort
"""

Expand Down Expand Up @@ -179,4 +179,3 @@ def main():
if __name__ == "__main__":
main()


25 changes: 21 additions & 4 deletions pyhealth/tasks/dka.py
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ class T1DDKAPredictionMIMIC4(BaseTask):
Target Population:
- Patients with Type 1 Diabetes (ICD-9 or ICD-10 codes)
- Excludes patients without any T1DM diagnosis codes
- Excludes DKA events recorded before a separate T1DM diagnosis

Label Definition:
- Positive (1): Patient has DKA code within 90 days of T1DM diagnosis
Expand Down Expand Up @@ -459,20 +460,34 @@ def __call__(self, patient: Any) -> List[Dict[str, Any]]:

has_t1dm = False
t1dm_times: List[datetime] = []
t1dm_diagnosis_times: list[datetime] = []
dka_times: list[datetime] = []

for diag in all_diagnoses:
code = getattr(diag, "icd_code", None)
version = getattr(diag, "icd_version", None)
diag_time = getattr(diag, "timestamp", None)
is_dka = self._is_dka_code(code, version)
if self._is_t1dm_code(code, version):
has_t1dm = True
diag_time = getattr(diag, "timestamp", None)
if diag_time:
t1dm_times.append(diag_time)
if not is_dka:
t1dm_diagnosis_times.append(diag_time)
if is_dka and diag_time:
dka_times.append(diag_time)

# Skip patients without T1DM diagnosis (early exit before sorting)
if not has_t1dm:
return []

if (
t1dm_diagnosis_times
and dka_times
and min(dka_times) < min(t1dm_diagnosis_times)
):
return []

# Get admissions and sort by timestamp
admissions = patient.get_events(event_type="admissions")
if not admissions:
Expand Down Expand Up @@ -609,9 +624,11 @@ def __call__(self, patient: Any) -> List[Dict[str, Any]]:
# Determine label based on temporal relationship
has_dka_within_window = False
if has_dka and t1dm_times and dka_time:
for t1dm_time in t1dm_times:
delta = abs((dka_time - t1dm_time).days)
if delta <= self.dka_window_days:
diagnosis_times = t1dm_diagnosis_times or t1dm_times
for t1dm_time in diagnosis_times:
if t1dm_time <= dka_time <= t1dm_time + timedelta(
days=self.dka_window_days
):
has_dka_within_window = True
break
elif has_dka and not t1dm_times:
Expand Down
56 changes: 55 additions & 1 deletion tests/core/test_t1d_dka_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_events(self, event_type: str, filters=None, return_df: bool = False):

def _build_patient(t0: datetime, admission_days: list[int], diag_specs: list[tuple[str, int | str, int, str]]) -> DummyPatient:
admissions = [DummyAdmission(f"a{i+1}", t0 + timedelta(days=day)) for i, day in enumerate(admission_days)]
base_diag = DummyDiagnosis("E10.10", 10, t0, "t1dm")
base_diag = DummyDiagnosis("E10.9", 10, t0, "t1dm")
diagnoses = [base_diag] + [
DummyDiagnosis(code, version, t0 + timedelta(days=day), hadm_id)
for code, version, day, hadm_id in diag_specs
Expand Down Expand Up @@ -101,6 +101,60 @@ def test_sequences_do_not_exceed_window(self):
self._assert_within_window(sample_long, expected_visits=2)
self.assertEqual(sample_long["label"], 0)

def test_dka_before_t1dm_diagnosis_is_excluded(self):
patient = _build_patient(
self.t0,
admission_days=[-60, -30],
diag_specs=[
("I10", 10, -60, "a1"),
("E1011", 10, -30, "a2"),
],
)

self.assertEqual(self.task(patient), [])

def test_dka_at_t1dm_diagnosis_is_positive(self):
patient = _build_patient(
self.t0,
admission_days=[-10, 0],
diag_specs=[
("I10", 10, -10, "a1"),
("E1011", 10, 0, "a2"),
],
)

samples = self.task(patient)
self.assertEqual(len(samples), 1)
self.assertEqual(samples[0]["label"], 1)

def test_dka_inside_window_is_positive(self):
patient = _build_patient(
self.t0,
admission_days=[10, 30],
diag_specs=[
("I10", 10, 10, "a1"),
("E1011", 10, 30, "a2"),
],
)

samples = self.task(patient)
self.assertEqual(len(samples), 1)
self.assertEqual(samples[0]["label"], 1)

def test_dka_at_end_of_window_is_positive(self):
patient = _build_patient(
self.t0,
admission_days=[30, 90],
diag_specs=[
("I10", 10, 30, "a1"),
("E1011", 10, 90, "a2"),
],
)

samples = self.task(patient)
self.assertEqual(len(samples), 1)
self.assertEqual(samples[0]["label"], 1)


if __name__ == "__main__":
unittest.main()
Loading