-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. DROP TABLE IF EXISTS weight_durations; CREATE TABLE weight_durations AS -- This query extracts weights for adult ICU patients with start/stop times -- if an admission weight is given, then this is assigned from intime to outtime -- This query extracts weights for adult ICU patients with start/stop times -- if an admission weight is given, then this is assigned from intime to outtime WITH wt_neonate AS ( SELECT c.icustay_id, c.charttime , MAX(CASE WHEN c.itemid = 3580 THEN c.valuenum END) as wt_kg , MAX(CASE WHEN c.itemid = 3581 THEN c.valuenum END) as wt_lb , MAX(CASE WHEN c.itemid = 3582 THEN c.valuenum END) as wt_oz FROM chartevents c WHERE c.itemid in (3580, 3581, 3582) AND c.icustay_id IS NOT NULL AND COALESCE(c.error, 0) = 0 -- wt_oz/wt_lb/wt_kg are only 0 erroneously, so drop these rows AND c.valuenum > 0 -- a separate query was run to manually verify only 1 value exists per -- icustay_id/charttime/itemid grouping -- therefore, we can use max() across itemid to collapse these values to 1 row per group GROUP BY c.icustay_id, c.charttime ) , birth_wt AS ( SELECT c.icustay_id, c.charttime , MAX( CASE WHEN c.itemid = 4183 THEN -- clean free-text birth weight data CASE -- ignore value if there are any non-numeric characters WHEN REGEXP_CONTAINS(c.value, '[^0-9\\.]') THEN NULL -- convert grams to kd WHEN CAST(c.value AS NUMERIC) > 100 THEN CAST(c.value AS NUMERIC)/1000 -- keep kg as is, filtering bad values (largest baby ever born was conveniently 9.98kg) WHEN CAST(c.value AS NUMERIC) < 10 THEN CAST(c.value AS NUMERIC) -- ignore other values (those between 10-100) - junk data ELSE NULL END -- itemid 3723 happily has all numeric data - also doesn't store any grams data WHEN c.itemid = 3723 AND c.valuenum < 10 THEN c.valuenum ELSE NULL END) as wt_kg FROM chartevents c WHERE c.itemid in (3723, 4183) AND c.icustay_id IS NOT NULL AND COALESCE(c.error, 0) = 0 -- a separate query was run to manually verify only 1 value exists per -- icustay_id/charttime/itemid grouping -- therefore, we can use max() across itemid to collapse these values to 1 row per group GROUP BY c.icustay_id, c.charttime ) , wt_stg as ( SELECT c.icustay_id , c.charttime , case when c.itemid in (762,226512) then 'admit' else 'daily' end as weight_type -- TODO: eliminate obvious outliers if there is a reasonable weight , c.valuenum as weight FROM chartevents c WHERE c.valuenum IS NOT NULL AND c.itemid in ( 762,226512 -- Admit Wt , 763,224639 -- Daily Weight ) AND c.icustay_id IS NOT NULL AND c.valuenum > 0 -- exclude rows marked as error AND COALESCE(c.error, 0) = 0 UNION ALL SELECT n.icustay_id , n.charttime , 'daily' AS weight_type , CASE WHEN wt_kg IS NOT NULL THEN wt_kg WHEN wt_lb IS NOT NULL THEN wt_lb*0.45359237 + wt_oz*0.0283495231 ELSE NULL END AS weight FROM wt_neonate n UNION ALL SELECT b.icustay_id , b.charttime -- birth weight of neonates is treated as admission weight , 'admit' AS weight_type , wt_kg as weight FROM birth_wt b ) -- get more weights from echo - completes data for ~2500 patients -- we only use echo data if there is *no* charted data -- we impute the median echo weight for their entire ICU stay , echo as ( select ie.icustay_id , ec.charttime , 'echo' AS weight_type , 0.453592*ec.weight as weight from icustays ie inner join echo_data ec on ie.hadm_id = ec.hadm_id where ec.weight is not null and ie.icustay_id not in (select distinct icustay_id from wt_stg) ) , wt_stg0 AS ( SELECT icustay_id, charttime, weight_type, weight FROM wt_stg UNION ALL SELECT icustay_id, charttime, weight_type, weight FROM echo ) -- assign ascending row number , wt_stg1 as ( select icustay_id , charttime , weight_type , weight , ROW_NUMBER() OVER (partition by icustay_id, weight_type order by charttime) as rn from wt_stg0 WHERE weight IS NOT NULL ) -- change charttime to intime for the first admission weight recorded , wt_stg2 AS ( SELECT wt_stg1.icustay_id , ie.intime, ie.outtime , case when wt_stg1.weight_type = 'admit' and wt_stg1.rn = 1 then DATETIME_SUB(ie.intime, INTERVAL '2' HOUR) else wt_stg1.charttime end as starttime , wt_stg1.weight from wt_stg1 INNER JOIN icustays ie on ie.icustay_id = wt_stg1.icustay_id ) , wt_stg3 as ( select icustay_id , intime, outtime , starttime , coalesce( LEAD(starttime) OVER (PARTITION BY icustay_id ORDER BY starttime), DATETIME_ADD(GREATEST(outtime, starttime), INTERVAL '2' HOUR) ) as endtime , weight from wt_stg2 ) -- this table is the start/stop times from admit/daily weight in charted data , wt1 as ( select icustay_id , starttime , coalesce(endtime, LEAD(starttime) OVER (partition by icustay_id order by starttime), -- impute ICU discharge as the end of the final weight measurement -- plus a 2 hour "fuzziness" window DATETIME_ADD(outtime, INTERVAL '2' HOUR) ) as endtime , weight from wt_stg3 ) -- if the intime for the patient is < the first charted daily weight -- then we will have a "gap" at the start of their stay -- to prevent this, we look for these gaps and backfill the first weight -- this adds (153255-149657)=3598 rows, meaning this fix helps for up to 3598 icustay_id , wt_fix as ( select ie.icustay_id -- we add a 2 hour "fuzziness" window , DATETIME_SUB(ie.intime, INTERVAL '2' HOUR) as starttime , wt.starttime as endtime , wt.weight from icustays ie inner join -- the below subquery returns one row for each unique icustay_id -- the row contains: the first starttime and the corresponding weight ( SELECT wt1.icustay_id, wt1.starttime, wt1.weight , ROW_NUMBER() OVER (PARTITION BY wt1.icustay_id ORDER BY wt1.starttime) as rn FROM wt1 ) wt ON ie.icustay_id = wt.icustay_id AND wt.rn = 1 and ie.intime < wt.starttime ) -- add the backfill rows to the main weight table select wt1.icustay_id , wt1.starttime , wt1.endtime , wt1.weight from wt1 UNION ALL SELECT wt_fix.icustay_id , wt_fix.starttime , wt_fix.endtime , wt_fix.weight from wt_fix