Repository: Medical-Event-Data-Standard/MEDS-DEV
Pull request #171: Refactor abnormal lab predicates by separating code and its range part so that users don't need to specify the same code twice.
Author: login: Jwoo5
Created: 2025-03-08T00:16:01Z | Merged: 2025-03-11T17:05:16Z

Closes #75 

Linked issue #75: Abnormal lab predicates should use a revised specification format for cods vs. value ranges.
Right now, many of the abnormal lab tasks (e.g., https://github.com/mmcdermott/MEDS-DEV/blob/main/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml#L20) use a pattern like this:

```yaml
  # Plain predicates
  creatinine:
    code: ???
  abnormally_high_creatinine:
    code: ???
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
```

This (1) requires users to specify two predicates in their dataset file and (2) does not have unit-specifying names for predicates possibly enabling range-unit confusion in dataset owners. I'd recommend we move to a predicate pattern like this:

```yaml
  # Plain predicates
  creatinine_mgdl:
    code: ???
  abnormally_high_creatinine_mgdl_range:
    code: null
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
  abnormally_high_creatinine:
    expr: or(and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range), ...[other units])
```
Then, dataset owners just need to specify the codes for `creatinine_mgdl` (and any other relevant units), and the ranges are pre-defined. 

We could also simplify this slightly later if ACES could be expanded to allow this syntax by doing:
```yaml
  # Plain predicates
  creatinine_mgdl:
    code: ???
  abnormally_high_creatinine_mgdl:
    code: creatinine_mgdl.code
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
  abnormally_high_creatinine:
    expr: or(abnormally_high_creatinine_mgdl, ...[other units])
```
but that is very much not essential.

Tagging @justin13601 @mandiehyewon and @Jwoo5 for thoughts

Linked issue comments: user: login: justin13601
created_at: 2025-03-05T18:08:13Z
body: Agreed regarding having unit-specific predicates, and only requiring the user to define one makes more sense.

For:
`or(and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range), ...[other units])`,
I can't actually remember if this syntax is parseable in ACES right now (specifically the nesting of or(and()) in one line)...
author_association: MEMBER
user: login: Jwoo5
created_at: 2025-03-05T18:29:50Z
body: I agreed as well, but we should check first if ACES supports such a syntax like `or(and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range), ...[other units])` as @justin13601 mentioned.
If we should update ACES to support this syntax, I think we can use this for the time being:
```yaml
 # Plain predicates
  creatinine_mgdl:
    code: ???
  abnormally_high_creatinine_mgdl_range:
    code: ???
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
  abnormally_high_creatinine:
    expr: or(abnormally_high_creatinine_mgdl_range, ..., [other units])
```
This still requires users to specify two duplicated predicates for each unit, but it seems not that critical in my opinion.
author_association: COLLABORATOR
user: login: mmcdermott
created_at: 2025-03-05T19:03:31Z
body: You can just add two derived predicates as well--one with the `and` and one with the `or` to avoid the need for two predicates. In general, I'd prioritize minimizing the number of predicates users have to specify. But we should verify the ACES config limitations as well.
author_association: COLLABORATOR
user: login: Jwoo5
created_at: 2025-03-05T19:14:04Z
body: Sounds good. @justin13601 could you check if ACES accepts the nesting of logical operations (e.g., `or(and(),...,and())`) for derived predicates?
If it doesn't, we can move to this:
```yaml
 # Plain predicates
  creatinine_mgdl:
    code: ???
  abnormally_high_creatinine_mgdl_range:
    code: null
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
  abnormally_high_creatinine_mgdl:
    expr: and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range)
  abnormally_high_creatinine:
    expr: or(abnormally_high_creatinine_mgdl, ..., [other units])
```
author_association: COLLABORATOR
user: login: justin13601
created_at: 2025-03-05T19:36:15Z
body: > Sounds good. [@justin13601](https://github.com/justin13601) could you check if ACES accepts the nesting of logical operations (e.g., `or(and(),...,and())`) for derived predicates?

Yeah doesn't look like ACES supports that kind of nesting right now, so will need to expand it manually. Looks like this functionality is already being tracked in #66.
author_association: MEMBER
user: login: Jwoo5
created_at: 2025-03-05T19:51:49Z
body: Thanks, then I will update configs for abnormal lab prediction tasks accordingly soon!

p.s. Unfortunately, my workspace server is currently down due to an unknown reason, so I cannot update it for right now :(
author_association: COLLABORATOR

Diff:
diff --git a/src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml b/src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
index ca695570..981901d6 100644
--- a/src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
+++ b/src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
@@ -14,149 +14,25 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
-    code: LAB//50912//mg/dL
-    value_min: 1.3 # mg/dL
-    value_min_inclusive: False
-    value_max: null
-  abnormally_high_creatinine_2:
-    code: LAB//52546//mg/dL
-    value_min: 1.3 # mg/dL
-    value_min_inclusive: False
-    value_max: null
-  creatinine:
-    expr: or(creatinine_1, creatinine_2)
-  abnormally_high_creatinine:
-    expr: or(abnormally_high_creatinine_1, abnormally_high_creatinine_2)
+  creatinine_mgdl:
+    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
 
-  sodium_1:
-    code: LAB//220645//mEq/L
-  sodium_2:
-    code: LAB//50983//mEq/L
-  sodium_3:
-    code: LAB//52623//mEq/L
-  abnormally_low_sodium_1:
-    code: LAB//220645//mEq/L
-    value_min: null
-    value_max: 135 # mEq/L
-    value_max_inclusive: False
-  abnormally_low_sodium_2:
-    code: LAB//50983//mEq/L
-    value_min: null
-    value_max: 135 # mEq/L
-    value_max_inclusive: False
-  abnormally_low_sodium_3:
-    code: LAB//52623//mEq/L
-    value_min: null
-    value_max: 135 # mEq/L
-    value_max_inclusive: False
-  sodium:
-    expr: or(sodium_1, sodium_2, sodium_3)
-  abnormally_low_sodium:
-    expr: or(abnormally_low_sodium_1, abnormally_low_sodium_2, abnormally_low_sodium_3)
+  sodium_meql:
+    code:
+      { any: ["LAB//220645//mEq/L", "LAB//50983//mEq/L", "LAB//52623//mEq/L"] }
 
-  bicarbonate_1:
-    code: LAB//227443//mEq/L
-  bicarbonate_2:
-    code: LAB//50882//mEq/L
-  abnormally_low_bicarbonate_1:
-    code: LAB//227443//mEq/L
-    value_min: null
-    value_max: 22 # mEq/L
-    value_max_inclusive: False
-  abnormally_low_bicarbonate_2:
-    code: LAB//50882//mEq/L
-    value_min: null
-    value_max: 22 # mEq/L
-    value_max_inclusive: False
-  bicarbonate:
-    expr: or(bicarbonate_1, bicarbonate_2)
-  abnormally_low_bicarbonate:
-    expr: or(abnormally_low_bicarbonate_1, abnormally_low_bicarbonate_2)
+  bicarbonate_meql:
+    code: { any: ["LAB//227443//mEq/L", "LAB//50882//mEq/L"] }
 
-  hemoglobin_1:
-    code: LAB//220228//g/dl
-  hemoglobin_2:
-    code: LAB//50811//g/dL
-  abnormally_low_hemoglobin_1:
-    code: LAB//220228//g/dl
-    value_min: null
-    value_max: 13 # g/dL
-    value_max_inclusive: False
-  abnormally_low_hemoglobin_2:
-    code: LAB//50811//g/dL
-    value_min: null
-    value_max: 13 # g/dL
-    value_max_inclusive: False
-  hemoglobin:
-    expr: or(hemoglobin_1, hemoglobin_2)
-  abnormally_low_hemoglobin:
-    expr: or(abnormally_low_hemoglobin_1, abnormally_low_hemoglobin_2)
+  hemoglobin_gdl:
+    code: { any: ["LAB//220228//g/dl", "LAB//50811//g/dL"] }
 
-  wbc_1:
-    code: LAB//220546//K/uL
-  wbc_2:
-    code: LAB//51300//K/uL
-  abnormally_high_wbc_1:
-    code: LAB//220546//K/uL
-    value_min: 11 # K/uL
-    value_min_inclusive: False
-    value_max: null
-  abnormally_high_wbc_2:
-    code: LAB//51300//K/uL
-    value_min: 11 # K/uL
-    value_min_inclusive: False
-    value_max: null
-  wbc:
-    expr: or(wbc_1, wbc_2)
-  abnormally_high_wbc:
-    expr: or(abnormally_high_wbc_1, abnormally_high_wbc_2)
+  wbc_kul:
+    code: { any: ["LAB//220546//K/uL", "LAB//51300//K/uL"] }
 
-  platelets_1:
-    code: LAB//227457//K/uL
-  platelets_2:
-    code: LAB//51265//K/uL
-  abnormally_low_platelets_1:
-    code: LAB//227457//K/uL
-    value_min: null
-    value_max: 150 # K/uL
-    value_max_inclusive: False
-  abnormally_low_platelets_2:
-    code: LAB//51265//K/uL
-    value_min: null
-    value_max: 150 # K/uL
-    value_max_inclusive: False
-  platelets:
-    expr: or(platelets_1, platelets_2)
-  abnormally_low_platelets:
-    expr: or(abnormally_low_platelets_1, abnormally_low_platelets_2)
+  platelets_kul:
+    code: { any: ["LAB//227457//K/uL", "LAB//51265//K/uL"] }
 
-  map_1:
-    code: LAB//220052//mmHg
-  map_2:
-    code: LAB//220181//mmHg
-  map_3:
-    code: LAB//225312//mmHg
-  abnormally_low_map_1:
-    code: LAB//220052//mmHg
-    value_min: null
-    value_max: 65 # mmHg
-    value_max_inclusive: False
-  abnormally_low_map_2:
-    code: LAB//220181//mmHg
-    value_min: null
-    value_max: 65 # mmHg
-    value_max_inclusive: False
-  abnormally_low_map_3:
-    code: LAB//225312//mmHg
-    value_min: null
-    value_max: 65 # mmHg
-    value_max_inclusive: False
-  map:
-    expr: or(map_1, map_2, map_3)
-  abnormally_low_map:
-    expr: or(abnormally_low_map_1, abnormally_low_map_2, abnormally_low_map_3)
+  map_mmhg:
+    code:
+      { any: ["LAB//220052//mmHg", "LAB//220181//mmHg", "LAB//225312//mmHg"] }
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml
index 27c58e03..da70c44d 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  creatinine:
-    code: ???
-  abnormally_high_creatinine:
+  creatinine_mgdl:
     code: ???
+  abnormally_high_creatinine_mgdl_range:
+    code: null
     value_min: 1.3 # mg/dL
     value_min_inclusive: False
     value_max: null
+  abnormally_high_creatinine_mgdl:
+    expr: and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range)
+
+  # creatinine:
+  #   expr: or(creatinine_mgdl, ..., creatinine_other_unit)
+  # abnormally_high_creatinine:
+  #   expr: or(abnormally_high_creatinine_mgdl, ..., abnormally_high_creatinine_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown elevated creatinine values previously
     has:
-      abnormally_high_creatinine: (None, 0)
+      # abnormally_high_creatinine: (None, 0)
+      abnormally_high_creatinine_mgdl: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      creatinine: (1, None)
-    label: abnormally_high_creatinine
+      # creatinine: (1, None)
+      creatinine_mgdl: (1, None)
+    # label: abnormally_high_creatinine
+    label: abnormally_high_creatinine_mgdl
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/hyponatremia/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/hyponatremia/first_24h.yaml
index 3f31e4a9..2e80d59b 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/hyponatremia/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/hyponatremia/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  sodium:
-    code: ???
-  abnormally_low_sodium:
+  sodium_meql:
     code: ???
+  abnormally_low_sodium_meql_range:
+    code: null
     value_min: null
     value_max: 135 # mEq/L
     value_max_inclusive: False
+  abnormally_low_sodium_meql:
+    expr: and(sodium_meql, abnormally_low_sodium_meql_range)
+
+  # sodium:
+  #   expr: or(sodium_meql, ..., sodium_other_unit)
+  # abnormally_low_sodium:
+  #   expr: or(abnormally_low_sodium_meql, ..., abnormally_low_sodium_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown low sodium values previously
     has:
-      abnormally_low_sodium: (None, 0)
+      # abnormally_low_sodium: (None, 0)
+      abnormally_low_sodium_meql: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      sodium: (1, None)
-    label: abnormally_low_sodium
+      # sodium: (1, None)
+      sodium_meql: (1, None)
+    # label: abnormally_low_sodium
+    label: abnormally_low_sodium_meql
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/metabolic_acidosis/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/metabolic_acidosis/first_24h.yaml
index 0df87809..0f30dc8d 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/metabolic_acidosis/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/blood_chemistry/metabolic_acidosis/first_24h.yaml
@@ -24,13 +24,20 @@ predicates:
     expr: or(icu_discharge, death, hospital_discharge)
 
   # Plain predicates
-  bicarbonate:
-    code: ???
-  abnormally_low_bicarbonate:
+  bicarbonate_meql:
     code: ???
+  abnormally_low_bicarbonate_meql_range:
+    code: null
     value_min: null
     value_max: 22 # mEq/L
     value_max_inclusive: False
+  abnormally_low_bicarbonate_meql:
+    expr: and(bicarbonate_meql, abnormally_low_bicarbonate_meql_range)
+
+  # bicarbonate:
+  #   expr: or(bicarbonate_meql, ..., bicarbonate_other_unit)
+  # abnormally_low_bicarbonate:
+  #   expr: or(abnormally_low_bicarbonate_meql, ..., abnormally_low_bicarbonate_other_unit)
 
 trigger: hospital_admission
 
@@ -43,7 +50,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown low bicarbonate values previously
     has:
-      abnormally_low_bicarbonate: (None, 0)
+      # abnormally_low_bicarbonate: (None, 0)
+      abnormally_low_bicarbonate_meql: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -67,5 +75,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      bicarbonate: (1, None)
-    label: abnormally_low_bicarbonate
+      # bicarbonate: (1, None)
+      bicarbonate_meql: (1, None)
+    # label: abnormally_low_bicarbonate
+    label: abnormally_low_bicarbonate_meql
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/cbc/anemia/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/cbc/anemia/first_24h.yaml
index 6a09f7a6..e56cb3ba 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/cbc/anemia/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/cbc/anemia/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  hemoglobin:
-    code: ???
-  abnormally_low_hemoglobin:
+  hemoglobin_gdl:
     code: ???
+  abnormally_low_hemoglobin_gdl_range:
+    code: null
     value_min: null
     value_max: 13 # g/dL
     value_max_inclusive: False
+  abnormally_low_hemoglobin_gdl:
+    expr: and(hemoglobin_gdl, abnormally_low_hemoglobin_gdl_range)
+
+  # hemoglobin:
+  #   expr: or(hemoglobin_gdl, ..., hemoglobin_other_unit)
+  # abnormally_low_hemoglobin:
+  #   expr: or(abnormally_low_hemoglobin_gdl, ..., abnormally_low_hemoglobin_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown low hemoglobin values previously
     has:
-      abnormally_low_hemoglobin: (None, 0)
+      # abnormally_low_hemoglobin: (None, 0)
+      abnormally_low_hemoglobin_gdl: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      hemoglobin: (1, None)
-    label: abnormally_low_hemoglobin
+      # hemoglobin: (1, None)
+      hemoglobin_gdl: (1, None)
+    # label: abnormally_low_hemoglobin
+    label: abnormally_low_hemoglobin_gdl
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/cbc/leukocytosis/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/cbc/leukocytosis/first_24h.yaml
index 1ca9198a..40b914e6 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/cbc/leukocytosis/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/cbc/leukocytosis/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  wbc:
-    code: ???
-  abnormally_high_wbc:
+  wbc_kul:
     code: ???
+  abnormally_high_wbc_kul_range:
+    code: null
     value_min: 11 # K/uL
     value_min_inclusive: False
     value_max: null
+  abnormally_high_wbc_kul:
+    expr: and(wbc_kul, abnormally_high_wbc_kul_range)
+
+  # wbc:
+  #   expr: or(wbc_kul, ..., wbc_other_unit)
+  # abnormally_high_wbc:
+  #   expr: or(abnormally_high_wbc_kul, ..., abnormally_high_wbc_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown high WBC values previously
     has:
-      abnormally_high_wbc: (None, 0)
+      # abnormally_high_wbc: (None, 0)
+      abnormally_high_wbc_kul: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      wbc: (1, None)
-    label: abnormally_high_wbc
+      # wbc: (1, None)
+      wbc_kul: (1, None)
+    # label: abnormally_high_wbc
+    label: abnormally_high_wbc_kul
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/cbc/thrombocytopenia/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/cbc/thrombocytopenia/first_24h.yaml
index 4f6366a5..93f8ff40 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/cbc/thrombocytopenia/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/cbc/thrombocytopenia/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  platelets:
-    code: ???
-  abnormally_low_platelets:
+  platelets_kul:
     code: ???
+  abnormally_low_platelets_kul_range:
+    code: null
     value_min: null
     value_max: 150 # K/uL
     value_max_inclusive: False
+  abnormally_low_platelets_kul:
+    expr: and(platelets_kul, abnormally_low_platelets_kul_range)
+
+  # platelets:
+  #   expr: or(platelets_kul, ..., platelets_other_unit)
+  # abnormally_low_platelets:
+  #   expr: or(abnormally_low_platelets_kul, ..., abnormally_low_platelets_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown low platelets values previously
     has:
-      abnormally_low_platelets: (None, 0)
+      # abnormally_low_platelets: (None, 0)
+      abnormally_low_platelets_kul_range: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      platelets: (1, None)
-    label: abnormally_low_platelets
+      # platelets: (1, None)
+      platelets_kul: (1, None)
+    # label: abnormally_low_platelets
+    label: abnormally_low_platelets_kul
diff --git a/src/MEDS_DEV/tasks/abnormal_lab/vital/hypotension/first_24h.yaml b/src/MEDS_DEV/tasks/abnormal_lab/vital/hypotension/first_24h.yaml
index d7769bbf..937b0f27 100644
--- a/src/MEDS_DEV/tasks/abnormal_lab/vital/hypotension/first_24h.yaml
+++ b/src/MEDS_DEV/tasks/abnormal_lab/vital/hypotension/first_24h.yaml
@@ -23,13 +23,20 @@ predicates:
     expr: or(death, hospital_discharge)
 
   # Plain predicates
-  map:
-    code: ???
-  abnormally_low_map:
+  map_mmhg:
     code: ???
+  abnormally_low_map_mmhg_range:
+    code: null
     value_min: null
     value_max: 65 # mmHg
     value_max_inclusive: False
+  abnormally_low_map_mmhg:
+    expr: and(map_mmhg, abnormally_low_map_mmhg_range)
+
+  # map:
+  #   expr: or(map_mmhg, ..., map_other_unit)
+  # abnormally_low_map:
+  #   expr: or(abnormally_low_map_mmhg, ..., abnormally_low_map_other_unit)
 
 trigger: hospital_admission
 
@@ -42,7 +49,8 @@ windows:
     index_timestamp: end
     # do not allow to have shown low MAP values previously
     has:
-      abnormally_low_map: (None, 0)
+      # abnormally_low_map: (None, 0)
+      abnormally_low_map_mmhg: (None, 0)
   # do not allow hospital discharge to happen within the input + target window
   no_discharge:
     start: trigger
@@ -66,5 +74,7 @@ windows:
     start_inclusive: False
     end_inclusive: True
     has:
-      map: (1, None)
-    label: abnormally_low_map
+      # map: (1, None)
+      map_mmhg: (1, None)
+    # label: abnormally_low_map
+    label: abnormally_low_map_mmhg


Reviews:
user: login: mmcdermott
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T03:35:21Z
user: login: Jwoo5
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-08T07:57:04Z
user: login: mmcdermott
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T01:42:48Z
user: login: Jwoo5
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T19:19:06Z
user: login: mmcdermott
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-10T19:32:59Z
user: login: mmcdermott
body: LGTM
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-11T16:22:41Z

Review comments:
diff_hunk: @@ -14,149 +14,181 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
+  creatinine_mgdl_1:
     code: LAB//50912//mg/dL
+  abnormally_high_creatinine_mgdl_1_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: mmcdermott
body: This is the same as `abnormally_high_creatinine_mgdl_2_range`. Why not just do:

```
creatinine_mgdl:
  code: {any: ["LAB//50912//mg/dL", "LAB//5246//mg/dL"]}
```

Then I think you can actually skip all the other high-creatinine related specifiers, no?
created_at: 2025-03-08T03:33:21Z
author_association: COLLABORATOR
diff_hunk: @@ -14,149 +14,181 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
+  creatinine_mgdl_1:
     code: LAB//50912//mg/dL
+  abnormally_high_creatinine_mgdl_1_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: mmcdermott
body: I think you can actually apply this paradigm to all the labs.
created_at: 2025-03-08T03:35:19Z
author_association: COLLABORATOR
diff_hunk: @@ -14,149 +14,181 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
+  creatinine_mgdl_1:
     code: LAB//50912//mg/dL
+  abnormally_high_creatinine_mgdl_1_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: Jwoo5
body: Sounds good. I was not so familiar with that kind of syntax in ACES, but it looks good to move to that style.
I will update this PR accordingly for all the labs tonight!
created_at: 2025-03-08T07:57:04Z
author_association: COLLABORATOR
diff_hunk: @@ -14,149 +14,102 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
-    code: LAB//50912//mg/dL
+  creatinine_mgdl:
+    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
+  abnormally_high_creatinine_mgdl_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: mmcdermott
body: you should only need to define `creatinine_mgdl` here, right? The rest should be defined in the task criteria file, no?
created_at: 2025-03-10T01:42:45Z
author_association: COLLABORATOR
diff_hunk: @@ -14,149 +14,102 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
-    code: LAB//50912//mg/dL
+  creatinine_mgdl:
+    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
+  abnormally_high_creatinine_mgdl_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: Jwoo5
body: So, in `datasets/MIMIC-IV/predicates.yaml`:
```yaml
predicates:
  creatinine_mgdl:
    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
```
and, in `tasks/abnormal_lab/blood_chemistry/elevated_creatinine/first_24h.yaml`:
```yaml
predicates:
  # Plain predicates
  abnormally_high_creatinine_mgdl_range:
    code: null
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null

  abnormally_high_creatinine_mgdl:
    expr: and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range)
```
, right?

If I understand correctly, I will proceed accordingly.
created_at: 2025-03-10T19:19:06Z
author_association: COLLABORATOR
diff_hunk: @@ -14,149 +14,102 @@ predicates:
   icu_discharge:
     code: { regex: "^ICU_DISCHARGE//.*" }
 
-  creatinine_1:
-    code: LAB//50912//mg/dL
-  creatinine_2:
-    code: LAB//52546//mg/dL
-  abnormally_high_creatinine_1:
-    code: LAB//50912//mg/dL
+  creatinine_mgdl:
+    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
+  abnormally_high_creatinine_mgdl_range:
path: src/MEDS_DEV/datasets/MIMIC-IV/predicates.yaml
user: login: mmcdermott
body: For `datasets/MIMIC-IV/predicates.yaml`, yes, but for `tasks/...` I would put 
```
creatinine_mgdl:
  code: ???
```

in there too just for clarity that it needs to be overwritten.
@justin13601 that should work, right?
created_at: 2025-03-10T19:32:59Z
author_association: COLLABORATOR

PR comments:
user: login: codecov[bot]
created_at: 2025-03-08T00:29:26Z
body: ## [Codecov](https://app.codecov.io/gh/Medical-Event-Data-Standard/MEDS-DEV/pull/171?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Medical-Event-Data-Standard) Report
All modified and coverable lines are covered by tests :white_check_mark:

:white_check_mark: All tests successful. No failed tests found.

author_association: NONE
user: login: Jwoo5
created_at: 2025-03-08T16:44:41Z
body: @mmcdermott @justin13601 Is there any way to define derived predicate expression with only one participant (like just a placeholder)? For example:
```yaml
  creatinine_mgdl:
    code: { any: ["LAB//50912//mg/dL", "LAB//52546//mg/dL"] }
  abnormally_high_creatinine_mgdl_range:
    code: null
    value_min: 1.3 # mg/dL
    value_min_inclusive: False
    value_max: null
  abnormally_high_creatinine_mgdl:
    expr: and(creatinine_mgdl, abnormally_high_creatinine_mgdl_range)
  creatinine:
    # add other units by `or(creatinine_mgdl, ..., creatinine_other_unit)`
    expr: creatinine_mgdl
  abnormally_high_creatinine:
    # add other units by `or(abnormally_high_creatinine_mgdl, ..., abnormally_high_creatinine_other_unit)`
    expr: abnormally_high_creatinine_mgdl
```
Here, we may want to reserve general predicates without ranges like `creatinine` and `abnormally_high_creatinine` as a combination of different ranges for general usage in the future, but I am not sure how we can do it for now given we defined only one range for this lab item.
author_association: COLLABORATOR
user: login: justin13601
created_at: 2025-03-08T21:22:42Z
body: > @mmcdermott @justin13601 Is there any way to define derived predicate expression with only one participant (like just a placeholder)?

I don't think there is a way to define a derived predicate with a placeholder since a definition only makes sense with 2+ constituent predicates. Maybe we can do something like:
```  
abnormally_high_creatinine:
    expr: or(abnormally_high_creatinine_mgdl, ???)
```

to show you're meant to replace the placeholder?


author_association: MEMBER
user: login: mmcdermott
created_at: 2025-03-09T02:26:59Z
body: > @mmcdermott @justin13601 Is there any way to define derived predicate expression with only one participant (like just a placeholder)? For example:

The right way to do this for now @Jwoo5 is to just make the rest of the task config depend on the predicate name that has the unit suffix. That is the easiest and is a fully valid solution, and when ACES adds what is functionally renaming or arbitrary arg list syntax support for `and` or `or`, we can change it further. 

author_association: COLLABORATOR
user: login: Jwoo5
created_at: 2025-03-09T22:21:14Z
body: Agreed; will update it accordingly.
author_association: COLLABORATOR
user: login: mmcdermott
created_at: 2025-03-11T16:20:11Z
body: @Jwoo5 I checked and the syntax I proposed works, so I made those modifications. Let me know if it looks ok.
author_association: COLLABORATOR
user: login: Jwoo5
created_at: 2025-03-11T16:33:42Z
body: > @Jwoo5 I checked and the syntax I proposed works, so I made those modifications. Let me know if it looks ok.

Thanks for taking this; looks good to me! 
author_association: COLLABORATOR