-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSOFA queries.sql
1270 lines (1132 loc) · 47.7 KB
/
SOFA queries.sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
DROP MATERIALIZED VIEW IF EXISTS labsfirstday CASCADE;
CREATE materialized VIEW labsfirstday AS
SELECT
pvt.subject_id, pvt.hadm_id, pvt.icustay_id
, min(CASE WHEN label = 'ANION GAP' THEN valuenum ELSE null END) as ANIONGAP_min
, max(CASE WHEN label = 'ANION GAP' THEN valuenum ELSE null END) as ANIONGAP_max
, min(CASE WHEN label = 'ALBUMIN' THEN valuenum ELSE null END) as ALBUMIN_min
, max(CASE WHEN label = 'ALBUMIN' THEN valuenum ELSE null END) as ALBUMIN_max
, min(CASE WHEN label = 'BANDS' THEN valuenum ELSE null END) as BANDS_min
, max(CASE WHEN label = 'BANDS' THEN valuenum ELSE null END) as BANDS_max
, min(CASE WHEN label = 'BICARBONATE' THEN valuenum ELSE null END) as BICARBONATE_min
, max(CASE WHEN label = 'BICARBONATE' THEN valuenum ELSE null END) as BICARBONATE_max
, min(CASE WHEN label = 'BILIRUBIN' THEN valuenum ELSE null END) as BILIRUBIN_min
, max(CASE WHEN label = 'BILIRUBIN' THEN valuenum ELSE null END) as BILIRUBIN_max
, min(CASE WHEN label = 'CREATININE' THEN valuenum ELSE null END) as CREATININE_min
, max(CASE WHEN label = 'CREATININE' THEN valuenum ELSE null END) as CREATININE_max
, min(CASE WHEN label = 'CHLORIDE' THEN valuenum ELSE null END) as CHLORIDE_min
, max(CASE WHEN label = 'CHLORIDE' THEN valuenum ELSE null END) as CHLORIDE_max
, min(CASE WHEN label = 'GLUCOSE' THEN valuenum ELSE null END) as GLUCOSE_min
, max(CASE WHEN label = 'GLUCOSE' THEN valuenum ELSE null END) as GLUCOSE_max
, min(CASE WHEN label = 'HEMATOCRIT' THEN valuenum ELSE null END) as HEMATOCRIT_min
, max(CASE WHEN label = 'HEMATOCRIT' THEN valuenum ELSE null END) as HEMATOCRIT_max
, min(CASE WHEN label = 'HEMOGLOBIN' THEN valuenum ELSE null END) as HEMOGLOBIN_min
, max(CASE WHEN label = 'HEMOGLOBIN' THEN valuenum ELSE null END) as HEMOGLOBIN_max
, min(CASE WHEN label = 'LACTATE' THEN valuenum ELSE null END) as LACTATE_min
, max(CASE WHEN label = 'LACTATE' THEN valuenum ELSE null END) as LACTATE_max
, min(CASE WHEN label = 'PLATELET' THEN valuenum ELSE null END) as PLATELET_min
, max(CASE WHEN label = 'PLATELET' THEN valuenum ELSE null END) as PLATELET_max
, min(CASE WHEN label = 'POTASSIUM' THEN valuenum ELSE null END) as POTASSIUM_min
, max(CASE WHEN label = 'POTASSIUM' THEN valuenum ELSE null END) as POTASSIUM_max
, min(CASE WHEN label = 'PTT' THEN valuenum ELSE null END) as PTT_min
, max(CASE WHEN label = 'PTT' THEN valuenum ELSE null END) as PTT_max
, min(CASE WHEN label = 'INR' THEN valuenum ELSE null END) as INR_min
, max(CASE WHEN label = 'INR' THEN valuenum ELSE null END) as INR_max
, min(CASE WHEN label = 'PT' THEN valuenum ELSE null END) as PT_min
, max(CASE WHEN label = 'PT' THEN valuenum ELSE null END) as PT_max
, min(CASE WHEN label = 'SODIUM' THEN valuenum ELSE null END) as SODIUM_min
, max(CASE WHEN label = 'SODIUM' THEN valuenum ELSE null end) as SODIUM_max
, min(CASE WHEN label = 'BUN' THEN valuenum ELSE null end) as BUN_min
, max(CASE WHEN label = 'BUN' THEN valuenum ELSE null end) as BUN_max
, min(CASE WHEN label = 'WBC' THEN valuenum ELSE null end) as WBC_min
, max(CASE WHEN label = 'WBC' THEN valuenum ELSE null end) as WBC_max
FROM
( -- begin query that extracts the data
SELECT ie.subject_id, ie.hadm_id, ie.icustay_id
-- here we assign labels to ITEMIDs
-- this also fuses together multiple ITEMIDs containing the same data
, CASE
WHEN itemid = 50868 THEN 'ANION GAP'
WHEN itemid = 50862 THEN 'ALBUMIN'
WHEN itemid = 51144 THEN 'BANDS'
WHEN itemid = 50882 THEN 'BICARBONATE'
WHEN itemid = 50885 THEN 'BILIRUBIN'
WHEN itemid = 50912 THEN 'CREATININE'
WHEN itemid = 50806 THEN 'CHLORIDE'
WHEN itemid = 50902 THEN 'CHLORIDE'
WHEN itemid = 50809 THEN 'GLUCOSE'
WHEN itemid = 50931 THEN 'GLUCOSE'
WHEN itemid = 50810 THEN 'HEMATOCRIT'
WHEN itemid = 51221 THEN 'HEMATOCRIT'
WHEN itemid = 50811 THEN 'HEMOGLOBIN'
WHEN itemid = 51222 THEN 'HEMOGLOBIN'
WHEN itemid = 50813 THEN 'LACTATE'
WHEN itemid = 51265 THEN 'PLATELET'
WHEN itemid = 50822 THEN 'POTASSIUM'
WHEN itemid = 50971 THEN 'POTASSIUM'
WHEN itemid = 51275 THEN 'PTT'
WHEN itemid = 51237 THEN 'INR'
WHEN itemid = 51274 THEN 'PT'
WHEN itemid = 50824 THEN 'SODIUM'
WHEN itemid = 50983 THEN 'SODIUM'
WHEN itemid = 51006 THEN 'BUN'
WHEN itemid = 51300 THEN 'WBC'
WHEN itemid = 51301 THEN 'WBC'
ELSE null
END AS label
, -- add in some sanity checks on the values
-- the where clause below requires all valuenum to be > 0, so these are only upper limit checks
CASE
WHEN itemid = 50862 and valuenum > 10 THEN null -- g/dL 'ALBUMIN'
WHEN itemid = 50868 and valuenum > 10000 THEN null -- mEq/L 'ANION GAP'
WHEN itemid = 51144 and valuenum < 0 THEN null -- immature band forms, %
WHEN itemid = 51144 and valuenum > 100 THEN null -- immature band forms, %
WHEN itemid = 50882 and valuenum > 10000 THEN null -- mEq/L 'BICARBONATE'
WHEN itemid = 50885 and valuenum > 150 THEN null -- mg/dL 'BILIRUBIN'
WHEN itemid = 50806 and valuenum > 10000 THEN null -- mEq/L 'CHLORIDE'
WHEN itemid = 50902 and valuenum > 10000 THEN null -- mEq/L 'CHLORIDE'
WHEN itemid = 50912 and valuenum > 150 THEN null -- mg/dL 'CREATININE'
WHEN itemid = 50809 and valuenum > 10000 THEN null -- mg/dL 'GLUCOSE'
WHEN itemid = 50931 and valuenum > 10000 THEN null -- mg/dL 'GLUCOSE'
WHEN itemid = 50810 and valuenum > 100 THEN null -- % 'HEMATOCRIT'
WHEN itemid = 51221 and valuenum > 100 THEN null -- % 'HEMATOCRIT'
WHEN itemid = 50811 and valuenum > 50 THEN null -- g/dL 'HEMOGLOBIN'
WHEN itemid = 51222 and valuenum > 50 THEN null -- g/dL 'HEMOGLOBIN'
WHEN itemid = 50813 and valuenum > 50 THEN null -- mmol/L 'LACTATE'
WHEN itemid = 51265 and valuenum > 10000 THEN null -- K/uL 'PLATELET'
WHEN itemid = 50822 and valuenum > 30 THEN null -- mEq/L 'POTASSIUM'
WHEN itemid = 50971 and valuenum > 30 THEN null -- mEq/L 'POTASSIUM'
WHEN itemid = 51275 and valuenum > 150 THEN null -- sec 'PTT'
WHEN itemid = 51237 and valuenum > 50 THEN null -- 'INR'
WHEN itemid = 51274 and valuenum > 150 THEN null -- sec 'PT'
WHEN itemid = 50824 and valuenum > 200 THEN null -- mEq/L == mmol/L 'SODIUM'
WHEN itemid = 50983 and valuenum > 200 THEN null -- mEq/L == mmol/L 'SODIUM'
WHEN itemid = 51006 and valuenum > 300 THEN null -- 'BUN'
WHEN itemid = 51300 and valuenum > 1000 THEN null -- 'WBC'
WHEN itemid = 51301 and valuenum > 1000 THEN null -- 'WBC'
ELSE le.valuenum
END AS valuenum
FROM mimiciii.icustays ie
LEFT JOIN mimiciii.labevents le
ON le.subject_id = ie.subject_id AND le.hadm_id = ie.hadm_id
AND le.charttime BETWEEN (ie.intime - interval '6' hour) AND (ie.intime + interval '1' day)
AND le.ITEMID in
(
-- comment is: LABEL | CATEGORY | FLUID | NUMBER OF ROWS IN LABEVENTS
50868, -- ANION GAP | CHEMISTRY | BLOOD | 769895
50862, -- ALBUMIN | CHEMISTRY | BLOOD | 146697
51144, -- BANDS - hematology
50882, -- BICARBONATE | CHEMISTRY | BLOOD | 780733
50885, -- BILIRUBIN, TOTAL | CHEMISTRY | BLOOD | 238277
50912, -- CREATININE | CHEMISTRY | BLOOD | 797476
50902, -- CHLORIDE | CHEMISTRY | BLOOD | 795568
50806, -- CHLORIDE, WHOLE BLOOD | BLOOD GAS | BLOOD | 48187
50931, -- GLUCOSE | CHEMISTRY | BLOOD | 748981
50809, -- GLUCOSE | BLOOD GAS | BLOOD | 196734
51221, -- HEMATOCRIT | HEMATOLOGY | BLOOD | 881846
50810, -- HEMATOCRIT, CALCULATED | BLOOD GAS | BLOOD | 89715
51222, -- HEMOGLOBIN | HEMATOLOGY | BLOOD | 752523
50811, -- HEMOGLOBIN | BLOOD GAS | BLOOD | 89712
50813, -- LACTATE | BLOOD GAS | BLOOD | 187124
51265, -- PLATELET COUNT | HEMATOLOGY | BLOOD | 778444
50971, -- POTASSIUM | CHEMISTRY | BLOOD | 845825
50822, -- POTASSIUM, WHOLE BLOOD | BLOOD GAS | BLOOD | 192946
51275, -- PTT | HEMATOLOGY | BLOOD | 474937
51237, -- INR(PT) | HEMATOLOGY | BLOOD | 471183
51274, -- PT | HEMATOLOGY | BLOOD | 469090
50983, -- SODIUM | CHEMISTRY | BLOOD | 808489
50824, -- SODIUM, WHOLE BLOOD | BLOOD GAS | BLOOD | 71503
51006, -- UREA NITROGEN | CHEMISTRY | BLOOD | 791925
51301, -- WHITE BLOOD CELLS | HEMATOLOGY | BLOOD | 753301
51300 -- WBC COUNT | HEMATOLOGY | BLOOD | 2371
)
AND valuenum IS NOT null AND valuenum > 0 -- lab values cannot be 0 and cannot be negative
) pvt
GROUP BY pvt.subject_id, pvt.hadm_id, pvt.icustay_id
ORDER BY pvt.subject_id, pvt.hadm_id, pvt.icustay_id;
--bloodgast firstday
DROP MATERIALIZED VIEW IF EXISTS bloodgasfirstday CASCADE;
create materialized view bloodgasfirstday as
with pvt as
( -- begin query that extracts the data
select ie.subject_id, ie.hadm_id, ie.icustay_id
-- here we assign labels to ITEMIDs
-- this also fuses together multiple ITEMIDs containing the same data
, case
when itemid = 50800 then 'SPECIMEN'
when itemid = 50801 then 'AADO2'
when itemid = 50802 then 'BASEEXCESS'
when itemid = 50803 then 'BICARBONATE'
when itemid = 50804 then 'TOTALCO2'
when itemid = 50805 then 'CARBOXYHEMOGLOBIN'
when itemid = 50806 then 'CHLORIDE'
when itemid = 50808 then 'CALCIUM'
when itemid = 50809 then 'GLUCOSE'
when itemid = 50810 then 'HEMATOCRIT'
when itemid = 50811 then 'HEMOGLOBIN'
when itemid = 50812 then 'INTUBATED'
when itemid = 50813 then 'LACTATE'
when itemid = 50814 then 'METHEMOGLOBIN'
when itemid = 50815 then 'O2FLOW'
when itemid = 50816 then 'FIO2'
when itemid = 50817 then 'SO2' -- OXYGENSATURATION
when itemid = 50818 then 'PCO2'
when itemid = 50819 then 'PEEP'
when itemid = 50820 then 'PH'
when itemid = 50821 then 'PO2'
when itemid = 50822 then 'POTASSIUM'
when itemid = 50823 then 'REQUIREDO2'
when itemid = 50824 then 'SODIUM'
when itemid = 50825 then 'TEMPERATURE'
when itemid = 50826 then 'TIDALVOLUME'
when itemid = 50827 then 'VENTILATIONRATE'
when itemid = 50828 then 'VENTILATOR'
else null
end as label
, charttime
, value
-- add in some sanity checks on the values
, case
when valuenum <= 0 then null
when itemid = 50810 and valuenum > 100 then null -- hematocrit
-- ensure FiO2 is a valid number between 21-100
-- mistakes are rare (<100 obs out of ~100,000)
-- there are 862 obs of valuenum == 20 - some people round down!
-- rather than risk imputing garbage data for FiO2, we simply NULL invalid values
when itemid = 50816 and valuenum < 20 then null
when itemid = 50816 and valuenum > 100 then null
when itemid = 50817 and valuenum > 100 then null -- O2 sat
when itemid = 50815 and valuenum > 70 then null -- O2 flow
when itemid = 50821 and valuenum > 800 then null -- PO2
-- conservative upper limit
else valuenum
end as valuenum
from mimiciii.icustays ie
left join mimiciii.labevents le
on le.subject_id = ie.subject_id and le.hadm_id = ie.hadm_id
and le.charttime between (ie.intime - interval '6' hour) and (ie.intime + interval '1' day)
and le.ITEMID in
-- blood gases
(
50800, 50801, 50802, 50803, 50804, 50805, 50806, 50807, 50808, 50809
, 50810, 50811, 50812, 50813, 50814, 50815, 50816, 50817, 50818, 50819
, 50820, 50821, 50822, 50823, 50824, 50825, 50826, 50827, 50828
, 51545
)
)
select pvt.SUBJECT_ID, pvt.HADM_ID, pvt.ICUSTAY_ID, pvt.CHARTTIME
, max(case when label = 'SPECIMEN' then value else null end) as SPECIMEN
, max(case when label = 'AADO2' then valuenum else null end) as AADO2
, max(case when label = 'BASEEXCESS' then valuenum else null end) as BASEEXCESS
, max(case when label = 'BICARBONATE' then valuenum else null end) as BICARBONATE
, max(case when label = 'TOTALCO2' then valuenum else null end) as TOTALCO2
, max(case when label = 'CARBOXYHEMOGLOBIN' then valuenum else null end) as CARBOXYHEMOGLOBIN
, max(case when label = 'CHLORIDE' then valuenum else null end) as CHLORIDE
, max(case when label = 'CALCIUM' then valuenum else null end) as CALCIUM
, max(case when label = 'GLUCOSE' then valuenum else null end) as GLUCOSE
, max(case when label = 'HEMATOCRIT' then valuenum else null end) as HEMATOCRIT
, max(case when label = 'HEMOGLOBIN' then valuenum else null end) as HEMOGLOBIN
, max(case when label = 'INTUBATED' then valuenum else null end) as INTUBATED
, max(case when label = 'LACTATE' then valuenum else null end) as LACTATE
, max(case when label = 'METHEMOGLOBIN' then valuenum else null end) as METHEMOGLOBIN
, max(case when label = 'O2FLOW' then valuenum else null end) as O2FLOW
, max(case when label = 'FIO2' then valuenum else null end) as FIO2
, max(case when label = 'SO2' then valuenum else null end) as SO2 -- OXYGENSATURATION
, max(case when label = 'PCO2' then valuenum else null end) as PCO2
, max(case when label = 'PEEP' then valuenum else null end) as PEEP
, max(case when label = 'PH' then valuenum else null end) as PH
, max(case when label = 'PO2' then valuenum else null end) as PO2
, max(case when label = 'POTASSIUM' then valuenum else null end) as POTASSIUM
, max(case when label = 'REQUIREDO2' then valuenum else null end) as REQUIREDO2
, max(case when label = 'SODIUM' then valuenum else null end) as SODIUM
, max(case when label = 'TEMPERATURE' then valuenum else null end) as TEMPERATURE
, max(case when label = 'TIDALVOLUME' then valuenum else null end) as TIDALVOLUME
, max(case when label = 'VENTILATIONRATE' then valuenum else null end) as VENTILATIONRATE
, max(case when label = 'VENTILATOR' then valuenum else null end) as VENTILATOR
from pvt
group by pvt.subject_id, pvt.hadm_id, pvt.icustay_id, pvt.CHARTTIME
order by pvt.subject_id, pvt.hadm_id, pvt.icustay_id, pvt.CHARTTIME;
DROP MATERIALIZED VIEW IF EXISTS bloodgasfirstdayarterial CASCADE;
CREATE MATERIALIZED VIEW bloodgasfirstdayarterial AS
with stg_spo2 as
(
select SUBJECT_ID, HADM_ID, ICUSTAY_ID, CHARTTIME
-- max here is just used to group SpO2 by charttime
, max(case when valuenum <= 0 or valuenum > 100 then null else valuenum end) as SpO2
from mimiciii.CHARTEVENTS
-- o2 sat
where ITEMID in
(
646 -- SpO2
, 220277 -- O2 saturation pulseoxymetry
)
group by SUBJECT_ID, HADM_ID, ICUSTAY_ID, CHARTTIME
)
, stg_fio2 as
(
select SUBJECT_ID, HADM_ID, ICUSTAY_ID, CHARTTIME
-- pre-process the FiO2s to ensure they are between 21-100%
, max(
case
when itemid = 223835
then case
when valuenum > 0 and valuenum <= 1
then valuenum * 100
-- improperly input data - looks like O2 flow in litres
when valuenum > 1 and valuenum < 21
then null
when valuenum >= 21 and valuenum <= 100
then valuenum
else null end -- unphysiological
when itemid in (3420, 3422)
-- all these values are well formatted
then valuenum
when itemid = 190 and valuenum > 0.20 and valuenum < 1
-- well formatted but not in %
then valuenum * 100
else null end
) as fio2_chartevents
from mimiciii.CHARTEVENTS
where ITEMID in
(
3420 -- FiO2
, 190 -- FiO2 set
, 223835 -- Inspired O2 Fraction (FiO2)
, 3422 -- FiO2 [measured]
)
-- exclude rows marked as error
and error IS DISTINCT FROM 1
group by SUBJECT_ID, HADM_ID, ICUSTAY_ID, CHARTTIME
)
, stg2 as
(
select bg.*
, ROW_NUMBER() OVER (partition by bg.icustay_id, bg.charttime order by s1.charttime DESC) as lastRowSpO2
, s1.spo2
from bloodgasfirstday bg
left join stg_spo2 s1
-- same patient
on bg.icustay_id = s1.icustay_id
-- spo2 occurred at most 2 hours before this blood gas
and s1.charttime between bg.charttime - interval '2' hour and bg.charttime
where bg.po2 is not null
)
, stg3 as
(
select bg.*
, ROW_NUMBER() OVER (partition by bg.icustay_id, bg.charttime order by s2.charttime DESC) as lastRowFiO2
, s2.fio2_chartevents
-- create our specimen prediction
, 1/(1+exp(-(-0.02544
+ 0.04598 * po2
+ coalesce(-0.15356 * spo2 , -0.15356 * 97.49420 + 0.13429)
+ coalesce( 0.00621 * fio2_chartevents , 0.00621 * 51.49550 + -0.24958)
+ coalesce( 0.10559 * hemoglobin , 0.10559 * 10.32307 + 0.05954)
+ coalesce( 0.13251 * so2 , 0.13251 * 93.66539 + -0.23172)
+ coalesce(-0.01511 * pco2 , -0.01511 * 42.08866 + -0.01630)
+ coalesce( 0.01480 * fio2 , 0.01480 * 63.97836 + -0.31142)
+ coalesce(-0.00200 * aado2 , -0.00200 * 442.21186 + -0.01328)
+ coalesce(-0.03220 * bicarbonate , -0.03220 * 22.96894 + -0.06535)
+ coalesce( 0.05384 * totalco2 , 0.05384 * 24.72632 + -0.01405)
+ coalesce( 0.08202 * lactate , 0.08202 * 3.06436 + 0.06038)
+ coalesce( 0.10956 * ph , 0.10956 * 7.36233 + -0.00617)
+ coalesce( 0.00848 * o2flow , 0.00848 * 7.59362 + -0.35803)
))) as SPECIMEN_PROB
from stg2 bg
left join stg_fio2 s2
-- same patient
on bg.icustay_id = s2.icustay_id
-- fio2 occurred at most 4 hours before this blood gas
and s2.charttime between bg.charttime - interval '4' hour and bg.charttime
where bg.lastRowSpO2 = 1 -- only the row with the most recent SpO2 (if no SpO2 found lastRowSpO2 = 1)
)
select subject_id, hadm_id,
icustay_id, charttime
, SPECIMEN -- raw data indicating sample type, only present 80% of the time
-- prediction of specimen for missing data
, case
when SPECIMEN is not null then SPECIMEN
when SPECIMEN_PROB > 0.75 then 'ART'
else null end as SPECIMEN_PRED
, SPECIMEN_PROB
-- oxygen related parameters
, SO2, spo2 -- note spo2 is from chartevents
, PO2, PCO2
, fio2_chartevents, FIO2
, AADO2
-- also calculate AADO2
, case
when PO2 is not null
and pco2 is not null
and coalesce(FIO2, fio2_chartevents) is not null
-- multiple by 100 because FiO2 is in a % but should be a fraction
then (coalesce(FIO2, fio2_chartevents)/100) * (760 - 47) - (pco2/0.8) - po2
else null
end as AADO2_calc
, case
when PO2 is not null and coalesce(FIO2, fio2_chartevents) is not null
-- multiply by 100 because FiO2 is in a % but should be a fraction
then 100*PO2/(coalesce(FIO2, fio2_chartevents))
else null
end as PaO2FiO2
-- acid-base parameters
, PH, BASEEXCESS
, BICARBONATE, TOTALCO2
-- blood count parameters
, HEMATOCRIT
, HEMOGLOBIN
, CARBOXYHEMOGLOBIN
, METHEMOGLOBIN
-- chemistry
, CHLORIDE, CALCIUM
, TEMPERATURE
, POTASSIUM, SODIUM
, LACTATE
, GLUCOSE
-- ventilation stuff that's sometimes input
, INTUBATED, TIDALVOLUME, VENTILATIONRATE, VENTILATOR
, PEEP, O2Flow
, REQUIREDO2
from stg3
where lastRowFiO2 = 1 -- only the most recent FiO2
-- restrict it to *only* arterial samples
and (SPECIMEN = 'ART' or SPECIMEN_PROB > 0.75)
order by icustay_id, charttime;
--urine output
DROP MATERIALIZED VIEW IF EXISTS uofirstday CASCADE;
create materialized view uofirstday as
select
-- patient identifiers
ie.subject_id, ie.hadm_id, ie.icustay_id
-- volumes associated with urine output ITEMIDs
, sum(
-- we consider input of GU irrigant as a negative volume
case when oe.itemid = 227488 then -1*VALUE
else VALUE end
) as UrineOutput
from mimiciii.icustays ie
-- Join to the outputevents table to get urine output
left join mimiciii.outputevents oe
-- join on all patient identifiers
on ie.subject_id = oe.subject_id and ie.hadm_id = oe.hadm_id and ie.icustay_id = oe.icustay_id
-- and ensure the data occurs during the first day
and oe.charttime between ie.intime and (ie.intime + interval '1' day) -- first ICU day
where itemid in
(
-- these are the most frequently occurring urine output observations in CareVue
40055, -- "Urine Out Foley"
43175, -- "Urine ."
40069, -- "Urine Out Void"
40094, -- "Urine Out Condom Cath"
40715, -- "Urine Out Suprapubic"
40473, -- "Urine Out IleoConduit"
40085, -- "Urine Out Incontinent"
40057, -- "Urine Out Rt Nephrostomy"
40056, -- "Urine Out Lt Nephrostomy"
40405, -- "Urine Out Other"
40428, -- "Urine Out Straight Cath"
40086,-- Urine Out Incontinent
40096, -- "Urine Out Ureteral Stent #1"
40651, -- "Urine Out Ureteral Stent #2"
-- these are the most frequently occurring urine output observations in MetaVision
226559, -- "Foley"
226560, -- "Void"
226561, -- "Condom Cath"
226584, -- "Ileoconduit"
226563, -- "Suprapubic"
226564, -- "R Nephrostomy"
226565, -- "L Nephrostomy"
226567, -- Straight Cath
226557, -- R Ureteral Stent
226558, -- L Ureteral Stent
227488, -- GU Irrigant Volume In
227489 -- GU Irrigant/Urine Volume Out
)
group by ie.subject_id, ie.hadm_id, ie.icustay_id
order by ie.subject_id, ie.hadm_id, ie.icustay_id;
--ventilation duration
DROP MATERIALIZED VIEW IF EXISTS ventsettings CASCADE;
CREATE MATERIALIZED VIEW ventsettings AS
select
icustay_id, charttime
-- case statement determining whether it is an instance of mech vent
, max(
case
when itemid is null or value is null then 0 -- can't have null values
when itemid = 720 and value != 'Other/Remarks' THEN 1 -- VentTypeRecorded
when itemid = 223848 and value != 'Other' THEN 1
when itemid = 223849 then 1 -- ventilator mode
when itemid = 467 and value = 'Ventilator' THEN 1 -- O2 delivery device == ventilator
when itemid in
(
445, 448, 449, 450, 1340, 1486, 1600, 224687 -- minute volume
, 639, 654, 681, 682, 683, 684,224685,224684,224686 -- tidal volume
, 218,436,535,444,459,224697,224695,224696,224746,224747 -- High/Low/Peak/Mean/Neg insp force ("RespPressure")
, 221,1,1211,1655,2000,226873,224738,224419,224750,227187 -- Insp pressure
, 543 -- PlateauPressure
, 5865,5866,224707,224709,224705,224706 -- APRV pressure
, 60,437,505,506,686,220339,224700 -- PEEP
, 3459 -- high pressure relief
, 501,502,503,224702 -- PCV
, 223,667,668,669,670,671,672 -- TCPCV
, 224701 -- PSVlevel
)
THEN 1
else 0
end
) as MechVent
, max(
case
-- initiation of oxygen therapy indicates the ventilation has ended
when itemid = 226732 and value in
(
'Nasal cannula', -- 153714 observations
'Face tent', -- 24601 observations
'Aerosol-cool', -- 24560 observations
'Trach mask ', -- 16435 observations
'High flow neb', -- 10785 observations
'Non-rebreather', -- 5182 observations
'Venti mask ', -- 1947 observations
'Medium conc mask ', -- 1888 observations
'T-piece', -- 1135 observations
'High flow nasal cannula', -- 925 observations
'Ultrasonic neb', -- 9 observations
'Vapomist' -- 3 observations
) then 1
when itemid = 467 and value in
(
'Cannula', -- 278252 observations
'Nasal Cannula', -- 248299 observations
'None', -- 95498 observations
'Face Tent', -- 35766 observations
'Aerosol-Cool', -- 33919 observations
'Trach Mask', -- 32655 observations
'Hi Flow Neb', -- 14070 observations
'Non-Rebreather', -- 10856 observations
'Venti Mask', -- 4279 observations
'Medium Conc Mask', -- 2114 observations
'Vapotherm', -- 1655 observations
'T-Piece', -- 779 observations
'Hood', -- 670 observations
'Hut', -- 150 observations
'TranstrachealCat', -- 78 observations
'Heated Neb', -- 37 observations
'Ultrasonic Neb' -- 2 observations
) then 1
else 0
end
) as OxygenTherapy
, max(
case when itemid is null or value is null then 0
-- extubated indicates ventilation event has ended
when itemid = 640 and value = 'Extubated' then 1
when itemid = 640 and value = 'Self Extubation' then 1
else 0
end
)
as Extubated
, max(
case when itemid is null or value is null then 0
when itemid = 640 and value = 'Self Extubation' then 1
else 0
end
)
as SelfExtubated
from mimiciii.chartevents ce
where ce.value is not null
-- exclude rows marked as error
and ce.error IS DISTINCT FROM 1
and itemid in
(
-- the below are settings used to indicate ventilation
720, 223849 -- vent mode
, 223848 -- vent type
, 445, 448, 449, 450, 1340, 1486, 1600, 224687 -- minute volume
, 639, 654, 681, 682, 683, 684,224685,224684,224686 -- tidal volume
, 218,436,535,444,224697,224695,224696,224746,224747 -- High/Low/Peak/Mean ("RespPressure")
, 221,1,1211,1655,2000,226873,224738,224419,224750,227187 -- Insp pressure
, 543 -- PlateauPressure
, 5865,5866,224707,224709,224705,224706 -- APRV pressure
, 60,437,505,506,686,220339,224700 -- PEEP
, 3459 -- high pressure relief
, 501,502,503,224702 -- PCV
, 223,667,668,669,670,671,672 -- TCPCV
, 224701 -- PSVlevel
-- the below are settings used to indicate extubation
, 640 -- extubated
-- the below indicate oxygen/NIV, i.e. the end of a mechanical vent event
, 468 -- O2 Delivery Device#2
, 469 -- O2 Delivery Mode
, 470 -- O2 Flow (lpm)
, 471 -- O2 Flow (lpm) #2
, 227287 -- O2 Flow (additional cannula)
, 226732 -- O2 Delivery Device(s)
, 223834 -- O2 Flow
-- used in both oxygen + vent calculation
, 467 -- O2 Delivery Device
)
group by icustay_id, charttime
UNION
-- add in the extubation flags from procedureevents_mv
-- note that we only need the start time for the extubation
-- (extubation is always charted as ending 1 minute after it started)
select
icustay_id, starttime as charttime
, 0 as MechVent
, 0 as OxygenTherapy
, 1 as Extubated
, case when itemid = 225468 then 1 else 0 end as SelfExtubated
from mimiciii.procedureevents_mv
where itemid in
(
227194 -- "Extubation"
, 225468 -- "Unplanned Extubation (patient-initiated)"
, 225477 -- "Unplanned Extubation (non-patient initiated)"
);
--DROP MATERIALIZED VIEW IF EXISTS VENTDURATIONS CASCADE;
DROP MATERIALIZED VIEW IF EXISTS VENTDURATIONS CASCADE;
create MATERIALIZED VIEW ventdurations as
with vd0 as
(
select
icustay_id
-- this carries over the previous charttime which had a mechanical ventilation event
, case
when MechVent=1 then
LAG(CHARTTIME, 1) OVER (partition by icustay_id, MechVent order by charttime)
else null
end as charttime_lag
, charttime
, MechVent
, OxygenTherapy
, Extubated
, SelfExtubated
from ventsettings
)
, vd1 as
(
select
icustay_id
, charttime_lag
, charttime
, MechVent
, OxygenTherapy
, Extubated
, SelfExtubated
-- if this is a mechanical ventilation event, we calculate the time since the last event
, case
-- if the current observation indicates mechanical ventilation is present
-- calculate the time since the last vent event
when MechVent=1 then
CHARTTIME - charttime_lag
else null
end as ventduration
, LAG(Extubated,1)
OVER
(
partition by icustay_id, case when MechVent=1 or Extubated=1 then 1 else 0 end
order by charttime
) as ExtubatedLag
-- now we determine if the current mech vent event is a "new", i.e. they've just been intubated
, case
-- if there is an extubation flag, we mark any subsequent ventilation as a new ventilation event
--when Extubated = 1 then 0 -- extubation is *not* a new ventilation event, the *subsequent* row is
when
LAG(Extubated,1)
OVER
(
partition by icustay_id, case when MechVent=1 or Extubated=1 then 1 else 0 end
order by charttime
)
= 1 then 1
-- if patient has initiated oxygen therapy, and is not currently vented, start a newvent
when MechVent = 0 and OxygenTherapy = 1 then 1
-- if there is less than 8 hours between vent settings, we do not treat this as a new ventilation event
when (CHARTTIME - charttime_lag) > interval '8' hour
then 1
else 0
end as newvent
-- use the staging table with only vent settings from chart events
FROM vd0 ventsettings
)
, vd2 as
(
select vd1.*
-- create a cumulative sum of the instances of new ventilation
-- this results in a monotonic integer assigned to each instance of ventilation
, case when MechVent=1 or Extubated = 1 then
SUM( newvent )
OVER ( partition by icustay_id order by charttime )
else null end
as ventnum
--- now we convert CHARTTIME of ventilator settings into durations
from vd1
)
-- create the durations for each mechanical ventilation instance
select icustay_id
-- regenerate ventnum so it's sequential
, ROW_NUMBER() over (partition by icustay_id order by ventnum) as ventnum
, min(charttime) as starttime
, max(charttime) as endtime
, extract(epoch from max(charttime)-min(charttime))/60/60 AS duration_hours
from vd2
group by icustay_id, ventnum
having min(charttime) != max(charttime)
-- patient had to be mechanically ventilated at least once
-- i.e. max(mechvent) should be 1
-- this excludes a frequent situation of NIV/oxygen before intub
-- in these cases, ventnum=0 and max(mechvent)=0, so they are ignored
and max(mechvent) = 1
order by icustay_id, ventnum;
-- ventilation first day
DROP MATERIALIZED VIEW IF EXISTS ventfirstday CASCADE;
CREATE MATERIALIZED VIEW ventfirstday AS
select
ie.subject_id, ie.hadm_id, ie.icustay_id
-- if vd.icustay_id is not null, then they have a valid ventilation event
-- in this case, we say they are ventilated
-- otherwise, they are not
, max(case
when vd.icustay_id is not null then 1
else 0 end) as vent
from mimiciii.icustays ie
left join ventdurations vd
on ie.icustay_id = vd.icustay_id
and
(
-- ventilation duration overlaps with ICU admission -> vented on admission
(vd.starttime <= ie.intime and vd.endtime >= ie.intime)
-- ventilation started during the first day
OR (vd.starttime >= ie.intime and vd.starttime <= ie.intime + interval '1' day)
)
group by ie.subject_id, ie.hadm_id, ie.icustay_id
order by ie.subject_id, ie.hadm_id, ie.icustay_id;
-- vitals first day
DROP MATERIALIZED VIEW IF EXISTS vitalsfirstday CASCADE;
create materialized view vitalsfirstday as
SELECT pvt.subject_id, pvt.hadm_id, pvt.icustay_id
-- Easier names
, min(case when VitalID = 1 then valuenum else null end) as HeartRate_Min
, max(case when VitalID = 1 then valuenum else null end) as HeartRate_Max
, avg(case when VitalID = 1 then valuenum else null end) as HeartRate_Mean
, min(case when VitalID = 2 then valuenum else null end) as SysBP_Min
, max(case when VitalID = 2 then valuenum else null end) as SysBP_Max
, avg(case when VitalID = 2 then valuenum else null end) as SysBP_Mean
, min(case when VitalID = 3 then valuenum else null end) as DiasBP_Min
, max(case when VitalID = 3 then valuenum else null end) as DiasBP_Max
, avg(case when VitalID = 3 then valuenum else null end) as DiasBP_Mean
, min(case when VitalID = 4 then valuenum else null end) as MeanBP_Min
, max(case when VitalID = 4 then valuenum else null end) as MeanBP_Max
, avg(case when VitalID = 4 then valuenum else null end) as MeanBP_Mean
, min(case when VitalID = 5 then valuenum else null end) as RespRate_Min
, max(case when VitalID = 5 then valuenum else null end) as RespRate_Max
, avg(case when VitalID = 5 then valuenum else null end) as RespRate_Mean
, min(case when VitalID = 6 then valuenum else null end) as TempC_Min
, max(case when VitalID = 6 then valuenum else null end) as TempC_Max
, avg(case when VitalID = 6 then valuenum else null end) as TempC_Mean
, min(case when VitalID = 7 then valuenum else null end) as SpO2_Min
, max(case when VitalID = 7 then valuenum else null end) as SpO2_Max
, avg(case when VitalID = 7 then valuenum else null end) as SpO2_Mean
, min(case when VitalID = 8 then valuenum else null end) as Glucose_Min
, max(case when VitalID = 8 then valuenum else null end) as Glucose_Max
, avg(case when VitalID = 8 then valuenum else null end) as Glucose_Mean
FROM (
select ie.subject_id, ie.hadm_id, ie.icustay_id
, case
when itemid in (211,220045) and valuenum > 0 and valuenum < 300 then 1 -- HeartRate
when itemid in (51,442,455,6701,220179,220050) and valuenum > 0 and valuenum < 400 then 2 -- SysBP
when itemid in (8368,8440,8441,8555,220180,220051) and valuenum > 0 and valuenum < 300 then 3 -- DiasBP
when itemid in (456,52,6702,443,220052,220181,225312) and valuenum > 0 and valuenum < 300 then 4 -- MeanBP
when itemid in (615,618,220210,224690) and valuenum > 0 and valuenum < 70 then 5 -- RespRate
when itemid in (223761,678) and valuenum > 70 and valuenum < 120 then 6 -- TempF, converted to degC in valuenum call
when itemid in (223762,676) and valuenum > 10 and valuenum < 50 then 6 -- TempC
when itemid in (646,220277) and valuenum > 0 and valuenum <= 100 then 7 -- SpO2
when itemid in (807,811,1529,3745,3744,225664,220621,226537) and valuenum > 0 then 8 -- Glucose
else null end as VitalID
-- convert F to C
, case when itemid in (223761,678) then (valuenum-32)/1.8 else valuenum end as valuenum
from mimiciii.icustays ie
left join mimiciii.chartevents ce
on ie.subject_id = ce.subject_id and ie.hadm_id = ce.hadm_id and ie.icustay_id = ce.icustay_id
and ce.charttime between ie.intime and ie.intime + interval '1' day
-- exclude rows marked as error
and ce.error IS DISTINCT FROM 1
where ce.itemid in
(
-- HEART RATE
211, --"Heart Rate"
220045, --"Heart Rate"
-- Systolic/diastolic
51, -- Arterial BP [Systolic]
442, -- Manual BP [Systolic]
455, -- NBP [Systolic]
6701, -- Arterial BP #2 [Systolic]
220179, -- Non Invasive Blood Pressure systolic
220050, -- Arterial Blood Pressure systolic
8368, -- Arterial BP [Diastolic]
8440, -- Manual BP [Diastolic]
8441, -- NBP [Diastolic]
8555, -- Arterial BP #2 [Diastolic]
220180, -- Non Invasive Blood Pressure diastolic
220051, -- Arterial Blood Pressure diastolic
-- MEAN ARTERIAL PRESSURE
456, --"NBP Mean"
52, --"Arterial BP Mean"
6702, -- Arterial BP Mean #2
443, -- Manual BP Mean(calc)
220052, --"Arterial Blood Pressure mean"
220181, --"Non Invasive Blood Pressure mean"
225312, --"ART BP mean"
-- RESPIRATORY RATE
618,-- Respiratory Rate
615,-- Resp Rate (Total)
220210,-- Respiratory Rate
224690, -- Respiratory Rate (Total)
-- SPO2, peripheral
646, 220277,
-- GLUCOSE, both lab and fingerstick
807,-- Fingerstick Glucose
811,-- Glucose (70-105)
1529,-- Glucose
3745,-- BloodGlucose
3744,-- Blood Glucose
225664,-- Glucose finger stick
220621,-- Glucose (serum)
226537,-- Glucose (whole blood)
-- TEMPERATURE
223762, -- "Temperature Celsius"
676, -- "Temperature C"
223761, -- "Temperature Fahrenheit"
678 -- "Temperature F"
)
) pvt
group by pvt.subject_id, pvt.hadm_id, pvt.icustay_id
order by pvt.subject_id, pvt.hadm_id, pvt.icustay_id;
-- gcs first day
DROP MATERIALIZED VIEW IF EXISTS gcsfirstday CASCADE;
create materialized view gcsfirstday as
with base as
(
SELECT pvt.ICUSTAY_ID
, pvt.charttime
-- Easier names - note we coalesced Metavision and CareVue IDs below
, max(case when pvt.itemid = 454 then pvt.valuenum else null end) as GCSMotor
, max(case when pvt.itemid = 723 then pvt.valuenum else null end) as GCSVerbal
, max(case when pvt.itemid = 184 then pvt.valuenum else null end) as GCSEyes
-- If verbal was set to 0 in the below select, then this is an intubated patient
, case
when max(case when pvt.itemid = 723 then pvt.valuenum else null end) = 0
then 1
else 0
end as EndoTrachFlag
, ROW_NUMBER ()
OVER (PARTITION BY pvt.ICUSTAY_ID ORDER BY pvt.charttime ASC) as rn
FROM (
select l.ICUSTAY_ID
-- merge the ITEMIDs so that the pivot applies to both metavision/carevue data
, case
when l.ITEMID in (723,223900) then 723
when l.ITEMID in (454,223901) then 454
when l.ITEMID in (184,220739) then 184
else l.ITEMID end
as ITEMID
-- convert the data into a number, reserving a value of 0 for ET/Trach
, case
-- endotrach/vent is assigned a value of 0, later parsed specially
when l.ITEMID = 723 and l.VALUE = '1.0 ET/Trach' then 0 -- carevue
when l.ITEMID = 223900 and l.VALUE = 'No Response-ETT' then 0 -- metavision
else VALUENUM
end
as VALUENUM
, l.CHARTTIME
from mimiciii.CHARTEVENTS l
-- get intime for charttime subselection
inner join mimiciii.icustays b
on l.icustay_id = b.icustay_id
-- Isolate the desired GCS variables
where l.ITEMID in
(
-- 198 -- GCS
-- GCS components, CareVue
184, 454, 723
-- GCS components, Metavision
, 223900, 223901, 220739
)
-- Only get data for the first 24 hours
and l.charttime between b.intime and b.intime + interval '1' day
-- exclude rows marked as error
and l.error IS DISTINCT FROM 1
) pvt
group by pvt.ICUSTAY_ID, pvt.charttime
)
, gcs as (
select b.*
, b2.GCSVerbal as GCSVerbalPrev
, b2.GCSMotor as GCSMotorPrev
, b2.GCSEyes as GCSEyesPrev
-- Calculate GCS, factoring in special case when they are intubated and prev vals
-- note that the coalesce are used to implement the following if:
-- if current value exists, use it
-- if previous value exists, use it
-- otherwise, default to normal
, case
-- replace GCS during sedation with 15
when b.GCSVerbal = 0
then 15
when b.GCSVerbal is null and b2.GCSVerbal = 0
then 15
-- if previously they were intub, but they aren't now, do not use previous GCS values
when b2.GCSVerbal = 0
then
coalesce(b.GCSMotor,6)
+ coalesce(b.GCSVerbal,5)
+ coalesce(b.GCSEyes,4)
-- otherwise, add up score normally, imputing previous value if none available at current time
else
coalesce(b.GCSMotor,coalesce(b2.GCSMotor,6))
+ coalesce(b.GCSVerbal,coalesce(b2.GCSVerbal,5))
+ coalesce(b.GCSEyes,coalesce(b2.GCSEyes,4))
end as GCS
from base b
-- join to itself within 6 hours to get previous value
left join base b2
on b.ICUSTAY_ID = b2.ICUSTAY_ID and b.rn = b2.rn+1 and b2.charttime > b.charttime - interval '6' hour
)
, gcs_final as (
select gcs.*
-- This sorts the data by GCS, so rn=1 is the the lowest GCS values to keep
, ROW_NUMBER ()