Tim Bartolo commited on
Commit
14a7a09
1 Parent(s): c9f2118

Add application file

Browse files
Files changed (1) hide show
  1. app.py +490 -0
app.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Gradio application to predict ESI triag level based on observation
2
+ # author: tbartolo
3
+ import math
4
+ import joblib
5
+ import gradio as gr
6
+
7
+ # START models
8
+ symptom_code_map = joblib.load('resources/etc/symptom_code_map.joblib')
9
+ age_min_max_scaler = joblib.load('resources/normalizers/AGE_min_max_scaler.joblib')
10
+ bpdias_min_max_scaler = joblib.load('resources/normalizers/BPDIAS_min_max_scaler.joblib')
11
+ bpsys_min_max_scaler = joblib.load('resources/normalizers/BPSYS_min_max_scaler.joblib')
12
+ lov_min_max_scaler = joblib.load('resources/normalizers/LOV_min_max_scaler.joblib')
13
+ painscale_min_max_scaler = joblib.load('resources/normalizers/PAINSCALE_min_max_scaler.joblib')
14
+ respr_min_max_scaler = joblib.load('resources/normalizers/RESPR_min_max_scaler.joblib')
15
+ tempf_discretizer_model = joblib.load('resources/normalizers/TEMPF_discretizer_model.joblib')
16
+ waittime_min_max_scaler = joblib.load('resources/normalizers/WAITTIME_min_max_scaler.joblib')
17
+ pulse_min_max_scaler = joblib.load('resources/normalizers/PULSE_min_max_scaler.joblib')
18
+ popct_min_max_scaler = joblib.load('resources/normalizers/POPCT_min_max_scaler.joblib')
19
+ gb_classifier_model = joblib.load('resources/algorithms/gb_classifier_model.joblib')
20
+ # END models
21
+
22
+ waittime_mean = 30.86
23
+ lov_mean = 236.02
24
+ age_mean = 37.71
25
+ tempf_mean = 94.95
26
+ pulse_mean = 89.86
27
+ respr_mean = 19.89
28
+ bpsys_mean = 119.74
29
+ bpdias_mean = 69.47
30
+ popct_mean = 94.52
31
+
32
+
33
+ def master_fn(waittime, lov, age, newborn, private_residnce, sex, arrems, ambtransfer, pulse, respr, bpsys, bpdias,
34
+ popct, painscale, seen72, initial_visit, injury, injury72, etohab, alzhd, asthma, cancer, cebvd, ckd,
35
+ copd, chf, cad, deprn, diabtyp1, diabtyp2, diabtyp0, esrd, hpe, edhiv, hyplipid, htn, obesity, osa,
36
+ ostprsis, substab, racer, tempf, rfv1, rfv2, rfv3, rfv4, rfv5, trauma, overdose_poison, medical_surgical):
37
+ def waittime_normalizer(value):
38
+ if not value or value < 0:
39
+ value = waittime_mean
40
+
41
+ log10_value = math.log10(value)
42
+ return waittime_min_max_scaler.transform([[log10_value]])[0][0]
43
+
44
+ def lov_normalizer(value):
45
+ if not value or value <= 0:
46
+ value = lov_mean
47
+
48
+ log10_value = math.log10(value)
49
+ return lov_min_max_scaler.transform([[log10_value]])[0][0]
50
+
51
+ def age_normalizer(value):
52
+ if not value or value < 0:
53
+ value = age_mean
54
+
55
+ return age_min_max_scaler.transform([[value]])[0][0]
56
+
57
+ def newborn_normalizer(value):
58
+ if not value:
59
+ value = "no"
60
+
61
+ return yes_no_to_binary(value)
62
+
63
+ def private_residnce_normalizer(value):
64
+ if not value:
65
+ value = "yes"
66
+
67
+ return yes_no_to_binary(value)
68
+
69
+ def sex_normalizer(value):
70
+ if not value:
71
+ value = "male"
72
+
73
+ if value.lower() == "female":
74
+ return 1
75
+ elif value.lower() == "male":
76
+ return 0
77
+ else:
78
+ raise ValueError("Illegal argument: SEX must \"female\" or \"male\"")
79
+
80
+ def arrems_normalizer(value):
81
+ if not value:
82
+ value = "no"
83
+
84
+ return yes_no_to_binary(value)
85
+
86
+ def ambtransfer_normalizer(value):
87
+ if not value:
88
+ value = "no"
89
+
90
+ return yes_no_to_binary(value)
91
+
92
+ def pulse_normalizer(value):
93
+ if not value or value <= 0:
94
+ value = pulse_mean
95
+
96
+ return pulse_min_max_scaler.transform([[value]])[0][0]
97
+
98
+ def respr_normalizer(value):
99
+ if not value or value <= 0:
100
+ value = respr_mean
101
+
102
+ if value > 35:
103
+ value = 35 # clip
104
+
105
+ return respr_min_max_scaler.transform([[value]])[0][0]
106
+
107
+ def bpsys_normalizer(value):
108
+ if not value or value <= 0:
109
+ value = bpsys_mean
110
+
111
+ return bpsys_min_max_scaler.transform([[value]])[0][0]
112
+
113
+ def bpdias_normalizer(value):
114
+ if not value or value <= 0:
115
+ value = bpdias_mean
116
+
117
+ return bpdias_min_max_scaler.transform([[value]])[0][0]
118
+
119
+ def popct_normalizer(value):
120
+ if not value or value <= 0:
121
+ value = popct_mean
122
+
123
+ log10_value = math.log10(value)
124
+ return popct_min_max_scaler.transform([[log10_value]])[0][0]
125
+
126
+ def painscale_normalizer(value):
127
+ if not value:
128
+ value = 0
129
+
130
+ return painscale_min_max_scaler.transform([[value]])[0][0]
131
+
132
+ def seen72_normalizer(value):
133
+ if not value:
134
+ value = "no"
135
+
136
+ return yes_no_to_binary(value)
137
+
138
+ def initial_visit_normalizer(value):
139
+ if not value:
140
+ value = "yes"
141
+
142
+ return yes_no_to_binary(value)
143
+
144
+ def injury_normalizer(value):
145
+ if not value:
146
+ value = "no"
147
+
148
+ return yes_no_to_binary(value)
149
+
150
+ def injury72_normalizer(value):
151
+ if not value:
152
+ value = "no"
153
+
154
+ return yes_no_to_binary(value)
155
+
156
+ def etohab_normalizer(value):
157
+ if not value:
158
+ value = "no"
159
+
160
+ return yes_no_to_binary(value)
161
+
162
+ def alzhd_normalizer(value):
163
+ if not value:
164
+ value = "no"
165
+
166
+ return yes_no_to_binary(value)
167
+
168
+ def asthma_normalizer(value):
169
+ if not value:
170
+ value = "no"
171
+
172
+ return yes_no_to_binary(value)
173
+
174
+ def cancer_normalizer(value):
175
+ if not value:
176
+ value = "no"
177
+
178
+ return yes_no_to_binary(value)
179
+
180
+ def cebvd_normalizer(value):
181
+ if not value:
182
+ value = "no"
183
+
184
+ return yes_no_to_binary(value)
185
+
186
+ def ckd_normalizer(value):
187
+ if not value:
188
+ value = "no"
189
+
190
+ return yes_no_to_binary(value)
191
+
192
+ def copd_normalizer(value):
193
+ if not value:
194
+ value = "no"
195
+
196
+ return yes_no_to_binary(value)
197
+
198
+ def chf_normalizer(value):
199
+ if not value:
200
+ value = "no"
201
+
202
+ return yes_no_to_binary(value)
203
+
204
+ def cad_normalizer(value):
205
+ if not value:
206
+ value = "no"
207
+
208
+ return yes_no_to_binary(value)
209
+
210
+ def deprn_normalizer(value):
211
+ if not value:
212
+ value = "no"
213
+
214
+ return yes_no_to_binary(value)
215
+
216
+ def diabtyp2_normalizer(value):
217
+ if not value:
218
+ value = "no"
219
+
220
+ return yes_no_to_binary(value)
221
+
222
+ def diabtyp1_normalizer(value):
223
+ if not value:
224
+ value = "no"
225
+
226
+ return yes_no_to_binary(value)
227
+
228
+ def diabtyp0_normalizer(value):
229
+ if not value:
230
+ value = "no"
231
+
232
+ return yes_no_to_binary(value)
233
+
234
+ def esrd_normalizer(value):
235
+ if not value:
236
+ value = "no"
237
+
238
+ return yes_no_to_binary(value)
239
+
240
+ def hpe_normalizer(value):
241
+ if not value:
242
+ value = "no"
243
+
244
+ return yes_no_to_binary(value)
245
+
246
+ def edhiv_normalizer(value):
247
+ if not value:
248
+ value = "no"
249
+
250
+ return yes_no_to_binary(value)
251
+
252
+ def hyplipid_normalizer(value):
253
+ if not value:
254
+ value = "no"
255
+
256
+ return yes_no_to_binary(value)
257
+
258
+ def htn_normalizer(value):
259
+ if not value:
260
+ value = "no"
261
+
262
+ return yes_no_to_binary(value)
263
+
264
+ def obesity_normalizer(value):
265
+ if not value:
266
+ value = "no"
267
+
268
+ return yes_no_to_binary(value)
269
+
270
+ def osa_normalizer(value):
271
+ if not value:
272
+ value = "no"
273
+
274
+ return yes_no_to_binary(value)
275
+
276
+ def ostprsis_normalizer(value):
277
+ if not value:
278
+ value = "no"
279
+
280
+ return yes_no_to_binary(value)
281
+
282
+ def substab_normalizer(value):
283
+ if not value:
284
+ value = "no"
285
+
286
+ return yes_no_to_binary(value)
287
+
288
+ def racer_normalizer(value):
289
+ if not value:
290
+ value = "other"
291
+
292
+ if value.lower() == "white":
293
+ return [0, 1]
294
+ elif value.lower() == "black":
295
+ return [1, 0]
296
+ elif value.lower() == "other":
297
+ return [0, 0]
298
+ else:
299
+ raise ValueError("Illegal argument: RACER must \"white\" or \"black\" or \"other\"")
300
+
301
+ def tempf_normalizer(value):
302
+ if not value or value < 70:
303
+ value = tempf_mean
304
+
305
+ return tempf_discretizer_model.transform([[value * 10]]).flatten()
306
+
307
+ def rfv1_normalizer(value):
308
+ if not value:
309
+ value = 0
310
+
311
+ return rfv_to_mapping(value)
312
+
313
+ def rfv2_normalizer(value):
314
+ if not value:
315
+ value = 0
316
+
317
+ return rfv_to_mapping(value)
318
+
319
+ def rfv3_normalizer(value):
320
+ if not value:
321
+ value = 0
322
+
323
+ return rfv_to_mapping(value)
324
+
325
+ def rfv4_normalizer(value):
326
+ if not value:
327
+ value = 0
328
+
329
+ return rfv_to_mapping(value)
330
+
331
+ def rfv5_normalizer(value):
332
+ if not value:
333
+ value = 0
334
+
335
+ return rfv_to_mapping(value)
336
+
337
+ def trauma_normalizer(value):
338
+ if not value:
339
+ value = "no"
340
+
341
+ return yes_no_to_binary(value)
342
+
343
+ def overdose_poison_normalizer(value):
344
+ if not value:
345
+ value = "no"
346
+
347
+ return yes_no_to_binary(value)
348
+
349
+ def medical_surgical_normalizer(value):
350
+ if not value:
351
+ value = "no"
352
+
353
+ return yes_no_to_binary(value)
354
+
355
+ def yes_no_to_binary(value):
356
+ if value.lower() == "yes":
357
+ return 1
358
+ elif value.lower() == "no":
359
+ return 0
360
+ else:
361
+ raise ValueError("Illegal argument: Value must \"yes\" or \"no\"")
362
+
363
+ def rfv_to_mapping(value):
364
+ value = symptom_code_map.get(value * 10)
365
+
366
+ if value or value == 0:
367
+ return decimal_to_binary_array(value, 13)
368
+ else:
369
+ raise ValueError("Illegal argument: Value must be valid symptom code")
370
+
371
+ def decimal_to_binary_array(number, array_size):
372
+ binary_representation = bin(number)[2:] # Convert to binary and remove '0b' prefix
373
+ binary_array = [int(bit) for bit in binary_representation.zfill(array_size)[-array_size:]]
374
+ return binary_array
375
+
376
+ observation = []
377
+
378
+ observation.append(waittime_normalizer(waittime))
379
+ observation.append(lov_normalizer(lov))
380
+ observation.append(age_normalizer(age))
381
+ observation.append(newborn_normalizer(newborn))
382
+ observation.append(private_residnce_normalizer(private_residnce))
383
+ observation.append(sex_normalizer(sex))
384
+ observation.append((arrems_normalizer(arrems)))
385
+ observation.append((ambtransfer_normalizer(ambtransfer)))
386
+ observation.append(pulse_normalizer(pulse))
387
+ observation.append(respr_normalizer(respr))
388
+ observation.append(bpsys_normalizer(bpsys))
389
+ observation.append(bpdias_normalizer(bpdias))
390
+ observation.append(popct_normalizer(popct))
391
+ observation.append(painscale_normalizer(painscale))
392
+ observation.append(seen72_normalizer(seen72))
393
+ observation.append(initial_visit_normalizer(initial_visit))
394
+ observation.append(injury_normalizer(injury))
395
+ observation.append(injury72_normalizer(injury72))
396
+ observation.append(etohab_normalizer(etohab))
397
+ observation.append(alzhd_normalizer(alzhd))
398
+ observation.append(asthma_normalizer(asthma))
399
+ observation.append(cancer_normalizer(cancer))
400
+ observation.append(cebvd_normalizer(cebvd))
401
+ observation.append(ckd_normalizer(ckd))
402
+ observation.append(copd_normalizer(copd))
403
+ observation.append(chf_normalizer(chf))
404
+ observation.append(cad_normalizer(cad))
405
+ observation.append(deprn_normalizer(deprn))
406
+ observation.append(diabtyp1_normalizer(diabtyp1))
407
+ observation.append(diabtyp2_normalizer(diabtyp2))
408
+ observation.append(diabtyp0_normalizer(diabtyp0))
409
+ observation.append(esrd_normalizer(esrd))
410
+ observation.append(hpe_normalizer(hpe))
411
+ observation.append(edhiv_normalizer(edhiv))
412
+ observation.append(hyplipid_normalizer(hyplipid))
413
+ observation.append(htn_normalizer(htn))
414
+ observation.append(obesity_normalizer(obesity))
415
+ observation.append(osa_normalizer(osa))
416
+ observation.append(ostprsis_normalizer(ostprsis))
417
+ observation.append(substab_normalizer(substab))
418
+
419
+ observation = (observation + racer_normalizer(racer) + tempf_normalizer(tempf).tolist() + rfv1_normalizer(rfv1)
420
+ + rfv2_normalizer(rfv2)) + rfv3_normalizer(rfv3) + rfv4_normalizer(rfv4) + rfv5_normalizer(rfv5)
421
+
422
+ observation.append(trauma_normalizer(trauma))
423
+ observation.append(overdose_poison_normalizer(overdose_poison))
424
+ observation.append(medical_surgical_normalizer(medical_surgical))
425
+
426
+ return gb_classifier_model.predict([observation])[0]
427
+
428
+
429
+ demo = gr.Interface(
430
+ master_fn,
431
+ [
432
+ "number", # waittime
433
+ "number", # lov
434
+ "number", # age
435
+ gr.Radio(["yes", "no"]), # newborn
436
+ gr.Radio(["yes", "no"]), # private_residnce
437
+ gr.Radio(["male", "female"]), # sex
438
+ gr.Radio(["yes", "no"]), # arrems
439
+ gr.Radio(["yes", "no"]), # ambtransfer
440
+ "number", # pulse
441
+ "number", # respr
442
+ "number", # bpsys
443
+ "number", # bpdias
444
+ "number", # popct
445
+ "number", # painscale
446
+ gr.Radio(["yes", "no"]), # seen72
447
+ gr.Radio(["yes", "no"]), # initial_visit
448
+ gr.Radio(["yes", "no"]), # injury
449
+ gr.Radio(["yes", "no"]), # injury72
450
+ gr.Radio(["yes", "no"]), # etohab
451
+ gr.Radio(["yes", "no"]), # alzhd
452
+ gr.Radio(["yes", "no"]), # asthma
453
+ gr.Radio(["yes", "no"]), # cancer
454
+ gr.Radio(["yes", "no"]), # cebvd
455
+ gr.Radio(["yes", "no"]), # ckd
456
+ gr.Radio(["yes", "no"]), # copd
457
+ gr.Radio(["yes", "no"]), # chf
458
+ gr.Radio(["yes", "no"]), # cad
459
+ gr.Radio(["yes", "no"]), # deprn
460
+ gr.Radio(["yes", "no"]), # diabtyp1
461
+ gr.Radio(["yes", "no"]), # diabtyp2
462
+ gr.Radio(["yes", "no"]), # diabtyp0
463
+ gr.Radio(["yes", "no"]), # esrd
464
+ gr.Radio(["yes", "no"]), # hpe
465
+ gr.Radio(["yes", "no"]), # edhiv
466
+ gr.Radio(["yes", "no"]), # hyplipid
467
+ gr.Radio(["yes", "no"]), # htn
468
+ gr.Radio(["yes", "no"]), # obesity
469
+ gr.Radio(["yes", "no"]), # osa
470
+ gr.Radio(["yes", "no"]), # ostprsis
471
+ gr.Radio(["yes", "no"]), # substab
472
+ gr.Radio(["white", "black", "other"]), # racer
473
+ "number", # tempf
474
+ "number", # rfv1
475
+ "number", # rfv2
476
+ "number", # rfv3
477
+ "number", # rfv4
478
+ "number", # rfv5
479
+ gr.Radio(["yes", "no"]), # trauma
480
+ gr.Radio(["yes", "no"]), # overdose_poison
481
+ gr.Radio(["yes", "no"]) # medical_surgical
482
+ ],
483
+ "number",
484
+ title="ESI Triage Level Predictor",
485
+ description="Enter the patient attributes and symptoms to perform the prediction. Note this application is a POC "
486
+ "and by no means claims any sort of accuracy in its predictions.",
487
+ )
488
+
489
+ if __name__ == "__main__":
490
+ demo.launch(show_api=False)