From 8d77a6598610d5f1e41f5da0e4fc6341f97724cc Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Thu, 8 Dec 2022 21:44:31 -0500 Subject: [PATCH 01/12] fix: update hard-coded postgres path to TARGET_PATH --- mimic-iv/concepts/convert_bigquery_to_postgres.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mimic-iv/concepts/convert_bigquery_to_postgres.sh b/mimic-iv/concepts/convert_bigquery_to_postgres.sh index d61fd3a25..ac6bd6a34 100755 --- a/mimic-iv/concepts/convert_bigquery_to_postgres.sh +++ b/mimic-iv/concepts/convert_bigquery_to_postgres.sh @@ -100,14 +100,14 @@ do continue fi echo -n " ${tbl} .." - echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "postgres/${d}/${tbl}.sql" - echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "postgres/${d}/${tbl}.sql" + echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "${TARGET_PATH}/${d}/${tbl}.sql" + echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "${TARGET_PATH}/${d}/${tbl}.sql" cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" >> "${TARGET_PATH}/${d}/${fn}" if [[ ! " ${TABLES_TO_SKIP[*]} " =~ " ${tbl} " ]]; then # this table is *not* in our skip array # therefore, we print it out to the make concepts script - echo "\i ${d}/${fn}" >> postgres/postgres-make-concepts.sql + echo "\i ${d}/${fn}" >> ${TARGET_PATH}/postgres-make-concepts.sql fi fi done @@ -115,7 +115,7 @@ do done # finally generate first_day_sofa which depends on concepts in firstday folder -echo "" >> postgres/postgres-make-concepts.sql +echo "" >> ${TARGET_PATH}/postgres-make-concepts.sql echo "-- final tables which were dependent on one or more prior tables" >> ${TARGET_PATH}/postgres-make-concepts.sql echo -n "final:" From 45faad04c502e4dd8d1c476908f5336fdfdc03f7 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Thu, 8 Dec 2022 21:50:53 -0500 Subject: [PATCH 02/12] tidy: remove if statement that was irrelevant --- mimic-iv/concepts/convert_bigquery_to_postgres.sh | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/mimic-iv/concepts/convert_bigquery_to_postgres.sh b/mimic-iv/concepts/convert_bigquery_to_postgres.sh index ac6bd6a34..6cc5ab9e4 100755 --- a/mimic-iv/concepts/convert_bigquery_to_postgres.sh +++ b/mimic-iv/concepts/convert_bigquery_to_postgres.sh @@ -49,12 +49,8 @@ do echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "$TARGET_PATH/${d}/${tbl}.sql" echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "$TARGET_PATH/${d}/${tbl}.sql" - # for two scripts, add a perl replace to cast rounded values as numeric - if [[ "${tbl}" == "icustay_times" ]] || [[ "${tbl}" == "urine_output" ]]; then - cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" | sed -r -e "${REGEX_SECONDS}" >> "$TARGET_PATH/${d}/${tbl}.sql" - else - cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" | sed -r -e "${REGEX_SECONDS}" >> "$TARGET_PATH/${d}/${tbl}.sql" - fi + # apply regex to map bigquery syntax to postgres syntax + cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" | sed -r -e "${REGEX_SECONDS}" >> "$TARGET_PATH/${d}/${tbl}.sql" # write out a call to this script in the make concepts file echo "\i ${d}/${tbl}.sql" >> $TARGET_PATH/postgres-make-concepts.sql From b3216181c04270deef9923bd8782c44dfb6ff933 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Thu, 8 Dec 2022 22:03:37 -0500 Subject: [PATCH 03/12] fix: correct schema name to use physionet-data identifier --- mimic-iii/concepts/fluid_balance/crystalloid_bolus.sql | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mimic-iii/concepts/fluid_balance/crystalloid_bolus.sql b/mimic-iii/concepts/fluid_balance/crystalloid_bolus.sql index c4d1ce5ca..71881f365 100644 --- a/mimic-iii/concepts/fluid_balance/crystalloid_bolus.sql +++ b/mimic-iii/concepts/fluid_balance/crystalloid_bolus.sql @@ -11,7 +11,7 @@ with t1 as when mv.amountuom = 'ml' then mv.amount else null end) as amount - from inputevents_mv mv + from `physionet-data.mimiciii_clinical.inputevents_mv` mv where mv.itemid in ( -- 225943 Solution @@ -47,7 +47,7 @@ with t1 as , cv.charttime -- carevue always has units in millilitres , round(cv.amount) as amount - from inputevents_cv cv + from `physionet-data.mimiciii_clinical.inputevents_cv` cv where cv.itemid in ( 30015 -- "D5/.45NS" -- mixed colloids and crystalloids @@ -155,4 +155,4 @@ select , sum(amount) as crystalloid_bolus from t2 group by t2.icustay_id, t2.charttime -order by icustay_id, charttime; +; \ No newline at end of file From 52589818f8ce3c49df3984f8e9ffbf8e7e9a36d6 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:30:36 -0500 Subject: [PATCH 04/12] fix: syntax updated for bq, drop create table statement --- mimic-iii/concepts/rrt.sql | 1 - 1 file changed, 1 deletion(-) diff --git a/mimic-iii/concepts/rrt.sql b/mimic-iii/concepts/rrt.sql index 9392fb91a..90da1fcf7 100644 --- a/mimic-iii/concepts/rrt.sql +++ b/mimic-iii/concepts/rrt.sql @@ -35,7 +35,6 @@ -- ) rrt -- where rn = 1; -CREATE TABLE `physionet-data.mimiciii_derived.rrt` as with cv_ce as ( select ie.icustay_id From 2b962cfd3de8dd17602cf2135f02ed47fbd1c469 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:32:39 -0500 Subject: [PATCH 05/12] fix: move psql specific ccs dx query/data into concepts_postgres --- .../diagnosis/README.md | 4 ++-- .../diagnosis/ccs_dx.sql} | 0 .../diagnosis/ccs_multi_dx.csv.gz | Bin 3 files changed, 2 insertions(+), 2 deletions(-) rename mimic-iii/{concepts => concepts_postgres}/diagnosis/README.md (74%) rename mimic-iii/{concepts/diagnosis/ccs_diagnosis_table_psql.sql => concepts_postgres/diagnosis/ccs_dx.sql} (100%) rename mimic-iii/{concepts => concepts_postgres}/diagnosis/ccs_multi_dx.csv.gz (100%) diff --git a/mimic-iii/concepts/diagnosis/README.md b/mimic-iii/concepts_postgres/diagnosis/README.md similarity index 74% rename from mimic-iii/concepts/diagnosis/README.md rename to mimic-iii/concepts_postgres/diagnosis/README.md index 086247d9d..b44edd505 100644 --- a/mimic-iii/concepts/diagnosis/README.md +++ b/mimic-iii/concepts_postgres/diagnosis/README.md @@ -2,7 +2,7 @@ The Clinical Classification Software (CCS) categorizes ICD-9 coded diagnoses int This folder contains: -* `ccs_diagnosis_table.sql` - Creates two tables: `ccs_single_level_dx` and `ccs_multi_level_dx`. These two tables are loaded from `ccs_single_level_dx.csv.gz` and `ccs_multi_level_dx.csv.gz`. Note that the script assumes you are using PostgreSQL v9.4 or later, and you must execute the script from this directory. +* `ccs_dx.sql` - Creates two tables: `ccs_single_level_dx` and `ccs_multi_level_dx`. These two tables are loaded from `ccs_single_level_dx.csv.gz` and `ccs_multi_level_dx.csv.gz`. Note that the script assumes you are using PostgreSQL v9.4 or later, and you must execute the script from this directory. ## Creation of the ccs_multi_level file @@ -45,4 +45,4 @@ df.to_csv('ccs_multi_dx.csv.gz', index=False, compression='gzip') (above run with Python 3.7 and pandas 0.23.2). -Now the SQL script can be run (`ccs_diagnosis_table.sql`). The `ccs_multi_dx.csv.gz` file generated by this process is available in the repo, so the above process is just for documentation, and does not necessarily have to be re-run. \ No newline at end of file +Now the SQL script can be run (`ccs_dx.sql`). The `ccs_multi_dx.csv.gz` file generated by this process is available in the repo, so the above process is just for documentation, and does not necessarily have to be re-run. \ No newline at end of file diff --git a/mimic-iii/concepts/diagnosis/ccs_diagnosis_table_psql.sql b/mimic-iii/concepts_postgres/diagnosis/ccs_dx.sql similarity index 100% rename from mimic-iii/concepts/diagnosis/ccs_diagnosis_table_psql.sql rename to mimic-iii/concepts_postgres/diagnosis/ccs_dx.sql diff --git a/mimic-iii/concepts/diagnosis/ccs_multi_dx.csv.gz b/mimic-iii/concepts_postgres/diagnosis/ccs_multi_dx.csv.gz similarity index 100% rename from mimic-iii/concepts/diagnosis/ccs_multi_dx.csv.gz rename to mimic-iii/concepts_postgres/diagnosis/ccs_multi_dx.csv.gz From 7787389b1f1f8f78a58349f63cc9f3e16a8b79ed Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:34:02 -0500 Subject: [PATCH 06/12] fix: correct bq table refs and column names --- mimic-iii/concepts/durations/neuroblock_dose.sql | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mimic-iii/concepts/durations/neuroblock_dose.sql b/mimic-iii/concepts/durations/neuroblock_dose.sql index 9590b0175..ac6e476c9 100644 --- a/mimic-iii/concepts/durations/neuroblock_dose.sql +++ b/mimic-iii/concepts/durations/neuroblock_dose.sql @@ -7,11 +7,11 @@ with drugmv as ( select icustay_id, orderid - , rate as vaso_rate - , amount as vaso_amount + , rate as drug_rate + , amount as drug_amount , starttime , endtime - from inputevents_mv + from `physionet-data.mimiciii_clinical.inputevents_mv` where itemid in ( 222062 -- Vecuronium (664 rows, 154 infusion rows) @@ -41,7 +41,7 @@ with drugmv as when itemid >= 40000 then coalesce(rate, amount) else rate end) as drug_rate , max(amount) as drug_amount - from inputevents_cv + from `physionet-data.mimiciii_clinical.inputevents_cv` where itemid in ( 30114 -- Cisatracurium (63994 rows) From 15ad099db724452ea9e4c46f8ad35c1acdb3a40a Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:34:49 -0500 Subject: [PATCH 07/12] fix: correct column name valueuom -> rateuom --- mimic-iii/concepts/durations/vasopressin_dose.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mimic-iii/concepts/durations/vasopressin_dose.sql b/mimic-iii/concepts/durations/vasopressin_dose.sql index ea3226d30..2c08c52d8 100644 --- a/mimic-iii/concepts/durations/vasopressin_dose.sql +++ b/mimic-iii/concepts/durations/vasopressin_dose.sql @@ -235,7 +235,7 @@ and ( select icustay_id, linkorderid - , CASE WHEN valueuom = 'units/min' THEN rate*60.0 ELSE rate END as vaso_rate + , CASE WHEN rateuom = 'units/min' THEN rate*60.0 ELSE rate END as vaso_rate , amount as vaso_amount , starttime , endtime From 72c0d2ae8c3487e8af52ef53ce02b7513387d30e Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:36:27 -0500 Subject: [PATCH 08/12] tidy: add readme about where ccs data/schema are --- mimic-iii/concepts/diagnosis/README.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 mimic-iii/concepts/diagnosis/README.md diff --git a/mimic-iii/concepts/diagnosis/README.md b/mimic-iii/concepts/diagnosis/README.md new file mode 100644 index 000000000..b5e093ff5 --- /dev/null +++ b/mimic-iii/concepts/diagnosis/README.md @@ -0,0 +1,4 @@ +# ccs_dx + +The `ccs_multi_dx.csv.gz` data file must be uploaded to `physionet-data.mimiciii_derived.ccs_multi_dx`. +The data file is available in [../concepts_postgres/diagnosis](../concepts_postgres/diagnosis). The BigQuery schema definition is available in this folder as [ccs_multi_dx.json](/ccs_multi_dx.json). \ No newline at end of file From bfb95798d8d66a70211db1f8e40d873de02bfb9f Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:37:01 -0500 Subject: [PATCH 09/12] fix: move psql specific query into concepts_postgres folder --- .../{concepts => concepts_postgres}/demographics/note_counts.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mimic-iii/{concepts => concepts_postgres}/demographics/note_counts.sql (100%) diff --git a/mimic-iii/concepts/demographics/note_counts.sql b/mimic-iii/concepts_postgres/demographics/note_counts.sql similarity index 100% rename from mimic-iii/concepts/demographics/note_counts.sql rename to mimic-iii/concepts_postgres/demographics/note_counts.sql From 95ac4232aa9b8d6bf4f8762dc62d743c1b34a768 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:42:12 -0500 Subject: [PATCH 10/12] refactor: bash script generates an sql file to create all psql concepts --- .../convert_mimiciii_concepts_bq_to_psql.sh | 158 ++++++++++++++++++ mimic-iii/concepts/postgres_make_concepts.sh | 106 ------------ .../postgres_make_concepts_windows.bat | 109 ------------ .../postgres-functions.sql | 26 +-- .../postgres-make-concepts.sql | 98 +++++++++++ 5 files changed, 272 insertions(+), 225 deletions(-) create mode 100644 mimic-iii/concepts/convert_mimiciii_concepts_bq_to_psql.sh delete mode 100644 mimic-iii/concepts/postgres_make_concepts.sh delete mode 100644 mimic-iii/concepts/postgres_make_concepts_windows.bat rename mimic-iii/{concepts => concepts_postgres}/postgres-functions.sql (91%) create mode 100644 mimic-iii/concepts_postgres/postgres-make-concepts.sql diff --git a/mimic-iii/concepts/convert_mimiciii_concepts_bq_to_psql.sh b/mimic-iii/concepts/convert_mimiciii_concepts_bq_to_psql.sh new file mode 100644 index 000000000..dfc44c675 --- /dev/null +++ b/mimic-iii/concepts/convert_mimiciii_concepts_bq_to_psql.sh @@ -0,0 +1,158 @@ +#!/bin/bash +# This shell script converts BigQuery .sql files into PostgreSQL .sql files. + +# path in which we create the postgres concepts +TARGET_PATH='../concepts_postgres' +mkdir -p $TARGET_PATH + +# String replacements are necessary for some queries. + +# Schema replacement: change `physionet-data..` to just
(with no backticks) +export REGEX_SCHEMA='s/`physionet-data.(mimiciii_clinical|mimiciii_derived|mimiciii_notes).([A-Za-z0-9_-]+)`/\2/g' +# Note that these queries are very senstive to changes, e.g. adding whitespaces after comma can already change the behavior. +export REGEX_DATETIME_DIFF="s/DATETIME_DIFF\(([^,]+), ?(.*), ?(DAY|MINUTE|SECOND|HOUR|YEAR)\)/DATETIME_DIFF(\1, \2, '\3')/g" +export REGEX_DATETIME_TRUNC="s/DATETIME_TRUNC\(([^,]+), ?(DAY|MINUTE|SECOND|HOUR|YEAR)\)/DATE_TRUNC('\2', \1)/g" +# Add necessary quotes to INTERVAL, e.g. "INTERVAL 5 hour" to "INTERVAL '5' hour" +export REGEX_INTERVAL="s/interval ([[:digit:]]+) (hour|day|month|year)/INTERVAL '\1' \2/gI" +# Specific queries for some problems that arose with some files. +export REGEX_INT="s/CAST\(hr AS INT64\)/CAST\(hr AS bigint\)/g" +export REGEX_ARRAY="s/GENERATE_ARRAY\(-24, CEIL\(DATETIME\_DIFF\(it\.outtime_hr, it\.intime_hr, HOUR\)\)\)/ARRAY\(SELECT \* FROM generate\_series\(-24, CEIL\(DATETIME\_DIFF\(it\.outtime_hr, it\.intime_hr, HOUR\)\)\)\)/g" +export REGEX_HOUR_INTERVAL="s/INTERVAL CAST\(hr AS INT64\) HOUR/interval \'1\' hour * CAST\(hr AS bigint\)/g" +export REGEX_SECONDS="s/SECOND\)/\'SECOND\'\)/g" + +# tables we want to run before all other concepts +# usually because they are used as dependencies +DIR_AND_TABLES_TO_PREBUILD='demographics.icustay_times demographics.icustay_hours .echo_data .code_status .rrt durations.weight_durations fluid_balance.urine_output organfailure.kdigo_uo' + +# tables which are written directly in postgresql and source code controlled +# this is usually because there is no trivial conversion between bq/psql syntax +DIR_AND_TABLES_ALREADY_IN_PSQL='demographics.icustay_times demographics.icustay_hours demographics.note_counts diagnosis.ccs_dx' + +# tables which we want to run after all other concepts +# usually because they depend on one or more other queries +DIR_AND_TABLES_TO_SKIP='' + +# First, we re-create the postgres-make-concepts.sql file. +echo "\echo ''" > $TARGET_PATH/postgres-make-concepts.sql + +# Now we add some preamble for the user running the script. +echo "\echo '==='" >> $TARGET_PATH/postgres-make-concepts.sql +echo "\echo 'Beginning to create materialized views for MIMIC database.'" >> $TARGET_PATH/postgres-make-concepts.sql +echo "\echo '"'Any notices of the form "NOTICE: materialized view "XXXXXX" does not exist" can be ignored.'"'" >> $TARGET_PATH/postgres-make-concepts.sql +echo "\echo 'The scripts drop views before creating them, and these notices indicate nothing existed prior to creating the view.'" >> $TARGET_PATH/postgres-make-concepts.sql +echo "\echo '==='" >> $TARGET_PATH/postgres-make-concepts.sql +echo "\echo ''" >> $TARGET_PATH/postgres-make-concepts.sql + +# ======================================== # +# === CONCEPTS WHICH WE MUST RUN FIRST === # +# ======================================== # +echo -n "Dependencies:" + +# output table creation calls to the make-concepts script +echo "" >> $TARGET_PATH/postgres-make-concepts.sql +echo "-- dependencies" >> $TARGET_PATH/postgres-make-concepts.sql + +for dir_and_table in $DIR_AND_TABLES_TO_PREBUILD; +do + d=`echo ${dir_and_table} | cut -d. -f1` + tbl=`echo ${dir_and_table} | cut -d. -f2` + + if [[ $d == '' ]]; then + d='.' + fi + + # make the sub-folder for postgres if it does not exist + mkdir -p "$TARGET_PATH/${d}" + + # convert the bigquery script to psql and output it to the appropriate subfolder + echo -n " ${d}.${tbl} .." + + # re-write the script into psql using regex + # the if statement ensures we do not overwrite tables which are already written in psql + if ! [[ "$DIR_AND_TABLES_ALREADY_IN_PSQL" =~ "$d.$tbl" ]]; then + echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "${TARGET_PATH}/${d}/${tbl}.sql" + echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "${TARGET_PATH}/${d}/${tbl}.sql" + cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" >> "${TARGET_PATH}/${d}/${tbl}.sql" + fi + + # write out a call to this script in the make concepts file + echo "\i ${d}/${tbl}.sql" >> $TARGET_PATH/postgres-make-concepts.sql +done +echo " done!" + +# ================================== # +# === MAIN LOOP FOR ALL CONCEPTS === # +# ================================== # + +# Iterate through each concept subfolder, and: +# (1) apply the above regular expressions to update the script +# (2) output to the postgres subfolder +# (3) add a line to the postgres-make-concepts.sql script to generate this table + +# organfailure.kdigo_stages firstday.first_day_sofa sepsis.sepsis3 medication.vasoactive_agent medication.norepinephrine_equivalent_dose + +# the order *only* matters during the conversion step because our loop is +# inserting table build commands into the postgres-make-concepts.sql file +for d in durations comorbidity demographics firstday fluid_balance sepsis diagnosis organfailure severityscores; +do + mkdir -p "$TARGET_PATH/${d}" + echo -n "${d}:" + echo "" >> $TARGET_PATH/postgres-make-concepts.sql + echo "-- ${d}" >> $TARGET_PATH/postgres-make-concepts.sql + for fn in `ls $d`; + do + # only run SQL queries + if [[ "${fn: -4}" == ".sql" ]]; then + # table name is file name minus extension + tbl="${fn%????}" + echo -n " ${tbl} " + + if [[ "$DIR_AND_TABLES_TO_PREBUILD" =~ "$d.$tbl" ]]; then + echo -n "(exists!) .." + continue + elif [[ "$DIR_AND_TABLES_TO_SKIP" =~ "$d.$tbl" ]]; then + echo -n "(skipping!) .." + continue + else + echo -n ".." + fi + + # re-write the script into psql using regex + # the if statement ensures we do not overwrite tables which are already written in psql + if ! [[ "$DIR_AND_TABLES_ALREADY_IN_PSQL" =~ "$d.$tbl" ]]; then + echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "${TARGET_PATH}/${d}/${tbl}.sql" + echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "${TARGET_PATH}/${d}/${tbl}.sql" + cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" >> "${TARGET_PATH}/${d}/${fn}" + fi + + # add statement to generate this table to make concepts script + echo "\i ${d}/${fn}" >> ${TARGET_PATH}/postgres-make-concepts.sql + fi + done + echo " done!" +done + +# finally generate first_day_sofa which depends on concepts in firstday folder +echo "" >> ${TARGET_PATH}/postgres-make-concepts.sql +echo "-- final tables which were dependent on one or more prior tables" >> ${TARGET_PATH}/postgres-make-concepts.sql + +echo -n "final:" +for dir_and_table in $DIR_AND_TABLES_TO_SKIP +do + d=`echo ${dir_and_table} | cut -d. -f1` + tbl=`echo ${dir_and_table} | cut -d. -f2` + + # make the sub-folder for postgres if it does not exist + mkdir -p "$TARGET_PATH/${d}" + + # convert the bigquery script to psql and output it to the appropriate subfolder + echo -n " ${d}.${tbl} .." + if ! [[ "$DIR_AND_TABLES_ALREADY_IN_PSQL" =~ "$d.$tbl" ]]; then + echo "-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY." > "${TARGET_PATH}/${d}/${tbl}.sql" + echo "DROP TABLE IF EXISTS ${tbl}; CREATE TABLE ${tbl} AS " >> "${TARGET_PATH}/${d}/${tbl}.sql" + cat "${d}/${tbl}.sql" | sed -r -e "${REGEX_ARRAY}" | sed -r -e "${REGEX_HOUR_INTERVAL}" | sed -r -e "${REGEX_INT}" | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_DATETIME_TRUNC}" | sed -r -e "${REGEX_SCHEMA}" | sed -r -e "${REGEX_INTERVAL}" >> "${TARGET_PATH}/${d}/${fn}" + fi + # write out a call to this script in the make concepts file + echo "\i ${d}/${tbl}.sql" >> $TARGET_PATH/postgres-make-concepts.sql +done +echo " done!" diff --git a/mimic-iii/concepts/postgres_make_concepts.sh b/mimic-iii/concepts/postgres_make_concepts.sh deleted file mode 100644 index 8973829ca..000000000 --- a/mimic-iii/concepts/postgres_make_concepts.sh +++ /dev/null @@ -1,106 +0,0 @@ -# This file makes tables for the concepts in this subfolder. -# Be sure to run postgres-functions.sql first, as the concepts rely on those function definitions. -# Note that this may take a large amount of time and hard drive space. -# -# Exporting DBCONNEXTRA before calling this script will add this to the -# connection string. For example, running: -# DBCONNEXTRA="user=mimic password=mimic" bash postgres_make_concepts.sh -# will add these settings to all of the psql calls. (Note that "dbname" -# and "search_path" do not need to be set.) - -# string replacements are necessary for some queries -REGEX_DATETIME_DIFF="s/DATETIME_DIFF\((.+?),\s?(.+?),\s?(DAY|MINUTE|SECOND|HOUR|YEAR)\)/DATETIME_DIFF(\1, \2, '\3')/g" -REGEX_SCHEMA='s/`physionet-data.(mimiciii_clinical|mimiciii_derived|mimiciii_notes).(.+?)`/\2/g' -CONNSTR="dbname=mimic $DBCONNEXTRA" - -# this is set as the search_path variable for psql -# a search path of "public,mimiciii" will search both public and mimiciii -# schemas for data, but will create tables on the public schema -PSQL_PREAMBLE='SET search_path TO public,mimiciii' - -echo '' -echo '===' -echo 'Beginning to create tables for MIMIC database.' -echo 'Any notices of the form "NOTICE: TABLE "XXXXXX" does not exist" can be ignored.' -echo 'The scripts drop views before creating them, and these notices indicate nothing existed prior to creating the view.' -echo '===' -echo '' - -echo 'Top level files..' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS code_status; CREATE TABLE code_status AS "; cat code_status.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS echo_data; CREATE TABLE echo_data AS "; cat echo_data.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -# Durations (usually of treatments) -echo 'Directory 1 of 9: durations' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS ventilation_classification; CREATE TABLE ventilation_classification AS "; cat durations/ventilation_classification.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS ventilation_durations; CREATE TABLE ventilation_durations AS "; cat durations/ventilation_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS crrt_durations; CREATE TABLE crrt_durations AS "; cat durations/crrt_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS adenosine_durations; CREATE TABLE adenosine_durations AS "; cat durations/adenosine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS dobutamine_durations; CREATE TABLE dobutamine_durations AS "; cat durations/dobutamine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS dopamine_durations; CREATE TABLE dopamine_durations AS "; cat durations/dopamine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS epinephrine_durations; CREATE TABLE epinephrine_durations AS "; cat durations/epinephrine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS isuprel_durations; CREATE TABLE isuprel_durations AS "; cat durations/isuprel_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS milrinone_durations; CREATE TABLE milrinone_durations AS "; cat durations/milrinone_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS norepinephrine_durations; CREATE TABLE norepinephrine_durations AS "; cat durations/norepinephrine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS phenylephrine_durations; CREATE TABLE phenylephrine_durations AS "; cat durations/phenylephrine_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS vasopressin_durations; CREATE TABLE vasopressin_durations AS "; cat durations/vasopressin_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS vasopressor_durations; CREATE TABLE vasopressor_durations AS "; cat durations/vasopressor_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS weight_durations; CREATE TABLE weight_durations AS "; cat durations/weight_durations.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Directory 2 of 9: comorbidity' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS elixhauser_ahrq_v37; CREATE TABLE elixhauser_ahrq_v37 AS "; cat comorbidity/elixhauser_ahrq_v37.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS elixhauser_ahrq_v37_no_drg; CREATE TABLE elixhauser_ahrq_v37_no_drg AS "; cat comorbidity/elixhauser_ahrq_v37_no_drg.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS elixhauser_quan; CREATE TABLE elixhauser_quan AS "; cat comorbidity/elixhauser_quan.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS elixhauser_score_ahrq; CREATE TABLE elixhauser_score_ahrq AS "; cat comorbidity/elixhauser_score_ahrq.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS elixhauser_score_quan; CREATE TABLE elixhauser_score_quan AS "; cat comorbidity/elixhauser_score_quan.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Directory 3 of 9: demographics' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS icustay_detail; CREATE TABLE icustay_detail AS "; cat demographics/icustay_detail.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Directory 4 of 9: firstday' -# data which is extracted from a patient's first ICU stay -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS blood_gas_first_day; CREATE TABLE blood_gas_first_day AS "; cat firstday/blood_gas_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS blood_gas_first_day_arterial; CREATE TABLE blood_gas_first_day_arterial AS "; cat firstday/blood_gas_first_day_arterial.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS gcs_first_day; CREATE TABLE gcs_first_day AS "; cat firstday/gcs_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS height_first_day; CREATE TABLE height_first_day AS "; cat firstday/height_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS labs_first_day; CREATE TABLE labs_first_day AS "; cat firstday/labs_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS rrt_first_day; CREATE TABLE rrt_first_day AS "; cat firstday/rrt_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS urine_output_first_day; CREATE TABLE urine_output_first_day AS "; cat firstday/urine_output_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS ventilation_first_day; CREATE TABLE ventilation_first_day AS "; cat firstday/ventilation_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS vitals_first_day; CREATE TABLE vitals_first_day AS "; cat firstday/vitals_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS weight_first_day; CREATE TABLE weight_first_day AS "; cat firstday/weight_first_day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Directory 5 of 9: fluid_balance' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS urine_output; CREATE TABLE urine_output AS "; cat fluid_balance/urine_output.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Directory 6 of 9: sepsis' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS angus; CREATE TABLE angus AS "; cat sepsis/angus.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS martin; CREATE TABLE martin AS "; cat sepsis/martin.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS explicit; CREATE TABLE explicit AS "; cat sepsis/explicit.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -# diagnosis mapping using CCS -echo 'Directory 7 of 9: diagnosis' -cd diagnosis -psql "${CONNSTR}" -f ccs_diagnosis_table_psql.sql -cd .. - -# Organ failure scores -echo 'Directory 8 of 9: organfailure' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS kdigo_creatinine; CREATE TABLE kdigo_creatinine AS "; cat organfailure/kdigo_creatinine.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS kdigo_uo; CREATE TABLE kdigo_uo AS "; cat organfailure/kdigo_uo.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS kdigo_stages; CREATE TABLE kdigo_stages AS "; cat organfailure/kdigo_stages.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS kdigo_stages_7day; CREATE TABLE kdigo_stages_7day AS "; cat organfailure/kdigo_stages_7day.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS kdigo_stages_48hr; CREATE TABLE kdigo_stages_48hr AS "; cat organfailure/kdigo_stages_48hr.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS meld; CREATE TABLE meld AS "; cat organfailure/meld.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -# Severity of illness scores (requires many views from above) -echo 'Directory 9 of 9: severityscores' -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS oasis; CREATE TABLE oasis AS "; cat severityscores/oasis.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS sofa; CREATE TABLE sofa AS "; cat severityscores/sofa.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS saps; CREATE TABLE saps AS "; cat severityscores/saps.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS sapsii; CREATE TABLE sapsii AS "; cat severityscores/sapsii.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS apsiii; CREATE TABLE apsiii AS "; cat severityscores/apsiii.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS lods; CREATE TABLE lods AS "; cat severityscores/lods.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" -{ echo "${PSQL_PREAMBLE}; DROP TABLE IF EXISTS sirs; CREATE TABLE sirs AS "; cat severityscores/sirs.sql; } | sed -r -e "${REGEX_DATETIME_DIFF}" | sed -r -e "${REGEX_SCHEMA}" | psql "${CONNSTR}" - -echo 'Finished creating tables.' diff --git a/mimic-iii/concepts/postgres_make_concepts_windows.bat b/mimic-iii/concepts/postgres_make_concepts_windows.bat deleted file mode 100644 index c9c65cc9d..000000000 --- a/mimic-iii/concepts/postgres_make_concepts_windows.bat +++ /dev/null @@ -1,109 +0,0 @@ -REM This file makes tables for the concepts in this subfolder. -REM Be sure to run postgres-functions.sql first, as the concepts rely on those function definitions. -REM Note that this may take a large amount of time and hard drive space. - -REM ** YOU MUST SET THE PASSWORD BELOW ** -SET "CONNSTR=postgresql://postgres:INSERT_PASSWORD_HERE@localhost:5432/mimic" -REM ** YOU MUST SET YOUR PSQL PATH BELOW ** -SET "PSQL_PATH=C:\Program Files\PostgreSQL\13\bin\psql.exe" - -REM string replacements are necessary for some queries -SET "REGEX_DATETIME_DIFF=s/DATETIME_DIFF\((.+?),\s?(.+?),\s?(DAY|MINUTE|SECOND|HOUR|YEAR)\)/DATETIME_DIFF(\1, \2, '\3')/g" - -REM Note we must escape ` characters in this pattern. -SET "REGEX_SCHEMA=s/\`physionet-data.(mimiciii_clinical|mimiciii_derived|mimiciii_notes).(.+?)\`/\2/g" - -REM this is set as the search_path variable for psql -REM a search path of "public,mimiciii" will search both public and mimiciii -REM schemas for data, but will create tables on the public schema -SET "PSQL_PREAMBLE=SET search_path TO public,mimiciii" - -ECHO === -ECHO Generating needed functions. -ECHO === -"%PSQL_PATH%" "%CONNSTR%" < postgres-functions.sql - -ECHO === -ECHO Beginning to create tables for MIMIC database. -ECHO Any notices of the form "NOTICE: TABLE "XXXXXX" does not exist" can be ignored. -ECHO The scripts drop views before creating them, and these notices indicate nothing existed prior to creating the view. -ECHO === - -ECHO Top level files.. -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS code_status; CREATE TABLE code_status AS "; cat code_status.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS echo_data; CREATE TABLE echo_data AS "; cat echo_data.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -REM Durations (usually of treatments) -echo 'Directory 1 of 9: durations' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS ventilation_classification; CREATE TABLE ventilation_classification AS "; cat durations/ventilation_classification.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS ventilation_durations; CREATE TABLE ventilation_durations AS "; cat durations/ventilation_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS crrt_durations; CREATE TABLE crrt_durations AS "; cat durations/crrt_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS adenosine_durations; CREATE TABLE adenosine_durations AS "; cat durations/adenosine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS dobutamine_durations; CREATE TABLE dobutamine_durations AS "; cat durations/dobutamine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS dopamine_durations; CREATE TABLE dopamine_durations AS "; cat durations/dopamine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS epinephrine_durations; CREATE TABLE epinephrine_durations AS "; cat durations/epinephrine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS isuprel_durations; CREATE TABLE isuprel_durations AS "; cat durations/isuprel_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS milrinone_durations; CREATE TABLE milrinone_durations AS "; cat durations/milrinone_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS norepinephrine_durations; CREATE TABLE norepinephrine_durations AS "; cat durations/norepinephrine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS phenylephrine_durations; CREATE TABLE phenylephrine_durations AS "; cat durations/phenylephrine_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS vasopressin_durations; CREATE TABLE vasopressin_durations AS "; cat durations/vasopressin_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS vasopressor_durations; CREATE TABLE vasopressor_durations AS "; cat durations/vasopressor_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS weight_durations; CREATE TABLE weight_durations AS "; cat durations/weight_durations.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -echo 'Directory 2 of 9: comorbidity' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS elixhauser_ahrq_v37; CREATE TABLE elixhauser_ahrq_v37 AS "; cat comorbidity/elixhauser_ahrq_v37.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS elixhauser_ahrq_v37_no_drg; CREATE TABLE elixhauser_ahrq_v37_no_drg AS "; cat comorbidity/elixhauser_ahrq_v37_no_drg.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS elixhauser_quan; CREATE TABLE elixhauser_quan AS "; cat comorbidity/elixhauser_quan.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS elixhauser_score_ahrq; CREATE TABLE elixhauser_score_ahrq AS "; cat comorbidity/elixhauser_score_ahrq.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS elixhauser_score_quan; CREATE TABLE elixhauser_score_quan AS "; cat comorbidity/elixhauser_score_quan.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -echo 'Directory 3 of 9: demographics' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS icustay_detail; CREATE TABLE icustay_detail AS "; cat demographics/icustay_detail.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -REM data which is extracted from a patient's first ICU stay -echo 'Directory 4 of 9: firstday' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS blood_gas_first_day; CREATE TABLE blood_gas_first_day AS "; cat firstday/blood_gas_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS blood_gas_first_day_arterial; CREATE TABLE blood_gas_first_day_arterial AS "; cat firstday/blood_gas_first_day_arterial.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS gcs_first_day; CREATE TABLE gcs_first_day AS "; cat firstday/gcs_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS height_first_day; CREATE TABLE height_first_day AS "; cat firstday/height_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS labs_first_day; CREATE TABLE labs_first_day AS "; cat firstday/labs_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS rrt_first_day; CREATE TABLE rrt_first_day AS "; cat firstday/rrt_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS urine_output_first_day; CREATE TABLE urine_output_first_day AS "; cat firstday/urine_output_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS ventilation_first_day; CREATE TABLE ventilation_first_day AS "; cat firstday/ventilation_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS vitals_first_day; CREATE TABLE vitals_first_day AS "; cat firstday/vitals_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS weight_first_day; CREATE TABLE weight_first_day AS "; cat firstday/weight_first_day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -echo 'Directory 5 of 9: fluid_balance' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS urine_output; CREATE TABLE urine_output AS "; cat fluid_balance/urine_output.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -echo 'Directory 6 of 9: sepsis' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS angus; CREATE TABLE angus AS "; cat sepsis/angus.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS martin; CREATE TABLE martin AS "; cat sepsis/martin.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS explicit; CREATE TABLE explicit AS "; cat sepsis/explicit.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -REM diagnosis mapping using CCS -echo 'Directory 7 of 9: diagnosis' -cd diagnosis -"%PSQL_PATH%" "%CONNSTR%" < ccs_diagnosis_table_psql.sql -cd .. - -REM Organ failure scores -echo 'Directory 8 of 9: organfailure' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS kdigo_creatinine; CREATE TABLE kdigo_creatinine AS "; cat organfailure/kdigo_creatinine.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS kdigo_uo; CREATE TABLE kdigo_uo AS "; cat organfailure/kdigo_uo.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS kdigo_stages; CREATE TABLE kdigo_stages AS "; cat organfailure/kdigo_stages.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS kdigo_stages_7day; CREATE TABLE kdigo_stages_7day AS "; cat organfailure/kdigo_stages_7day.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS kdigo_stages_48hr; CREATE TABLE kdigo_stages_48hr AS "; cat organfailure/kdigo_stages_48hr.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS meld; CREATE TABLE meld AS "; cat organfailure/meld.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -REM Severity of illness scores (requires many views from above) -echo 'Directory 9 of 9: severityscores' -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS oasis; CREATE TABLE oasis AS "; cat severityscores/oasis.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS sofa; CREATE TABLE sofa AS "; cat severityscores/sofa.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS saps; CREATE TABLE saps AS "; cat severityscores/saps.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS sapsii; CREATE TABLE sapsii AS "; cat severityscores/sapsii.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS apsiii; CREATE TABLE apsiii AS "; cat severityscores/apsiii.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS lods; CREATE TABLE lods AS "; cat severityscores/lods.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" -wsl.exe { cmd.exe /c echo %PSQL_PREAMBLE%";"; echo "DROP TABLE IF EXISTS sirs; CREATE TABLE sirs AS "; cat severityscores/sirs.sql; } | wsl.exe sed -r -e "%REGEX_SCHEMA%" | wsl.exe sed -r -e "%REGEX_DATETIME_DIFF%" | "%PSQL_PATH%" "%CONNSTR%" - -echo 'Finished creating tables.' diff --git a/mimic-iii/concepts/postgres-functions.sql b/mimic-iii/concepts_postgres/postgres-functions.sql similarity index 91% rename from mimic-iii/concepts/postgres-functions.sql rename to mimic-iii/concepts_postgres/postgres-functions.sql index be7fd5d57..e886b9a3c 100644 --- a/mimic-iii/concepts/postgres-functions.sql +++ b/mimic-iii/concepts_postgres/postgres-functions.sql @@ -1,8 +1,5 @@ --- Functions TODO: --- FROM table CROSS JOIN UNNEST(table.column) AS col -> ???? (see icustay-hours) --- ???(column) -> PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY column) (not sure how to do median in BQ) - -SET search_path TO mimiciii; +-- (Optional): set the search_path so all functions are generated on the mimiciii schema +-- SET search_path TO mimiciii; CREATE OR REPLACE FUNCTION REGEXP_EXTRACT(str TEXT, pattern TEXT) RETURNS TEXT AS $$ BEGIN @@ -38,17 +35,22 @@ RETURN TO_TIMESTAMP( END; $$ LANGUAGE PLPGSQL; --- overload allowing string input - --- DATETIME_ADD(datetime, INTERVAL 'n' DATEPART) -> datetime + INTERVAL 'n' DATEPART -- note: in bigquery, `INTERVAL 1 YEAR` is a valid interval -- but in postgres, it must be `INTERVAL '1' YEAR` + +-- DATETIME_ADD(datetime, INTERVAL 'n' DATEPART) -> datetime + INTERVAL 'n' DATEPART CREATE OR REPLACE FUNCTION DATETIME_ADD(datetime_val TIMESTAMP(3), intvl INTERVAL) RETURNS TIMESTAMP(3) AS $$ BEGIN RETURN datetime_val + intvl; END; $$ LANGUAGE PLPGSQL; +CREATE OR REPLACE FUNCTION DATE_ADD(dt DATE, intvl INTERVAL) RETURNS TIMESTAMP(3) AS $$ +BEGIN +RETURN CAST(dt AS TIMESTAMP(3)) + intvl; +END; $$ +LANGUAGE PLPGSQL; + -- DATETIME_SUB(datetime, INTERVAL 'n' DATEPART) -> datetime - INTERVAL 'n' DATEPART CREATE OR REPLACE FUNCTION DATETIME_SUB(datetime_val TIMESTAMP(3), intvl INTERVAL) RETURNS TIMESTAMP(3) AS $$ BEGIN @@ -56,6 +58,12 @@ RETURN datetime_val - intvl; END; $$ LANGUAGE PLPGSQL; +CREATE OR REPLACE FUNCTION DATE_SUB(dt DATE, intvl INTERVAL) RETURNS TIMESTAMP(3) AS $$ +BEGIN +RETURN CAST(dt AS TIMESTAMP(3)) - intvl; +END; $$ +LANGUAGE PLPGSQL; + -- TODO: -- DATETIME_TRUNC(datetime, PART) -> DATE_TRUNC('datepart', datetime) @@ -153,5 +161,3 @@ RETURN TO_TIMESTAMP( ); END; $$ LANGUAGE PLPGSQL; - - diff --git a/mimic-iii/concepts_postgres/postgres-make-concepts.sql b/mimic-iii/concepts_postgres/postgres-make-concepts.sql new file mode 100644 index 000000000..50b021029 --- /dev/null +++ b/mimic-iii/concepts_postgres/postgres-make-concepts.sql @@ -0,0 +1,98 @@ +\echo '' +\echo '===' +\echo 'Beginning to create materialized views for MIMIC database.' +\echo 'Any notices of the form "NOTICE: materialized view "XXXXXX" does not exist" can be ignored.' +\echo 'The scripts drop views before creating them, and these notices indicate nothing existed prior to creating the view.' +\echo '===' +\echo '' + +-- dependencies +\i demographics/icustay_times.sql +\i demographics/icustay_hours.sql +\i ./echo_data.sql +\i ./code_status.sql +\i ./rrt.sql +\i durations/weight_durations.sql +\i fluid_balance/urine_output.sql +\i organfailure/kdigo_uo.sql + +-- durations +\i durations/adenosine_durations.sql +\i durations/arterial_line_durations.sql +\i durations/central_line_durations.sql +\i durations/crrt_durations.sql +\i durations/dobutamine_dose.sql +\i durations/dobutamine_durations.sql +\i durations/dopamine_dose.sql +\i durations/dopamine_durations.sql +\i durations/epinephrine_dose.sql +\i durations/epinephrine_durations.sql +\i durations/isuprel_durations.sql +\i durations/milrinone_durations.sql +\i durations/neuroblock_dose.sql +\i durations/norepinephrine_dose.sql +\i durations/norepinephrine_durations.sql +\i durations/phenylephrine_dose.sql +\i durations/phenylephrine_durations.sql +\i durations/vasopressin_dose.sql +\i durations/vasopressin_durations.sql +\i durations/vasopressor_durations.sql +\i durations/ventilation_classification.sql +\i durations/ventilation_durations.sql + +-- comorbidity +\i comorbidity/elixhauser_ahrq_v37.sql +\i comorbidity/elixhauser_ahrq_v37_no_drg.sql +\i comorbidity/elixhauser_quan.sql +\i comorbidity/elixhauser_score_ahrq.sql +\i comorbidity/elixhauser_score_quan.sql + +-- demographics +\i demographics/heightweight.sql +\i demographics/icustay_detail.sql + +-- firstday +\i firstday/blood_gas_first_day.sql +\i firstday/blood_gas_first_day_arterial.sql +\i firstday/gcs_first_day.sql +\i firstday/height_first_day.sql +\i firstday/labs_first_day.sql +\i firstday/rrt_first_day.sql +\i firstday/urine_output_first_day.sql +\i firstday/ventilation_first_day.sql +\i firstday/vitals_first_day.sql +\i firstday/weight_first_day.sql + +-- fluid_balance +\i fluid_balance/colloid_bolus.sql +\i fluid_balance/crystalloid_bolus.sql +\i fluid_balance/ffp_transfusion.sql +\i fluid_balance/rbc_transfusion.sql + +-- sepsis +\i sepsis/angus.sql +\i sepsis/explicit.sql +\i sepsis/martin.sql + +-- diagnosis +\i diagnosis/ccs_dx.sql + +-- organfailure +\i organfailure/kdigo_creatinine.sql +\i organfailure/kdigo_stages.sql +\i organfailure/kdigo_stages_48hr.sql +\i organfailure/kdigo_stages_7day.sql +\i organfailure/meld.sql + +-- severityscores +\i severityscores/apsiii.sql +\i severityscores/lods.sql +\i severityscores/mlods.sql +\i severityscores/oasis.sql +\i severityscores/qsofa.sql +\i severityscores/saps.sql +\i severityscores/sapsii.sql +\i severityscores/sirs.sql +\i severityscores/sofa.sql + +-- final tables which were dependent on one or more prior tables From 8c0461fbdbd46e2e5eb4b7a93bd754eefeed3937 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:43:32 -0500 Subject: [PATCH 11/12] feat: commit the autogenerated psql version of concepts --- mimic-iii/concepts_postgres/code_status.sql | 75 ++ .../comorbidity/elixhauser_ahrq_v37.sql | 993 ++++++++++++++++++ .../elixhauser_ahrq_v37_no_drg.sql | 523 +++++++++ .../comorbidity/elixhauser_quan.sql | 272 +++++ .../comorbidity/elixhauser_score_ahrq.sql | 108 ++ .../comorbidity/elixhauser_score_quan.sql | 109 ++ .../demographics/heightweight.sql | 107 ++ .../demographics/icustay_detail.sql | 105 ++ .../demographics/icustay_hours.sql | 36 + .../demographics/icustay_times.sql | 55 + .../demographics/weight_durations.sql | 2 + .../durations/adenosine_durations.sql | 231 ++++ .../durations/arterial_line_durations.sql | 180 ++++ .../durations/central_line_durations.sql | 175 +++ .../durations/crrt_durations.sql | 197 ++++ .../durations/dobutamine_dose.sql | 259 +++++ .../durations/dobutamine_durations.sql | 228 ++++ .../durations/dopamine_dose.sql | 262 +++++ .../durations/dopamine_durations.sql | 231 ++++ .../durations/epinephrine_dose.sql | 273 +++++ .../durations/epinephrine_durations.sql | 231 ++++ .../durations/isuprel_durations.sql | 228 ++++ .../durations/milrinone_durations.sql | 228 ++++ .../durations/neuroblock_dose.sql | 318 ++++++ .../durations/norepinephrine_dose.sql | 270 +++++ .../durations/norepinephrine_durations.sql | 227 ++++ .../durations/phenylephrine_dose.sql | 259 +++++ .../durations/phenylephrine_durations.sql | 231 ++++ .../durations/vasopressin_dose.sql | 259 +++++ .../durations/vasopressin_durations.sql | 228 ++++ .../durations/vasopressor_durations.sql | 316 ++++++ .../durations/ventilation_classification.sql | 142 +++ .../durations/ventilation_durations.sql | 112 ++ .../durations/weight_durations.sql | 207 ++++ mimic-iii/concepts_postgres/echo_data.sql | 48 + .../firstday/blood_gas_first_day.sql | 108 ++ .../firstday/blood_gas_first_day_arterial.sql | 156 +++ .../firstday/first_day_sofa.sql | 2 + .../firstday/gcs_first_day.sql | 143 +++ .../firstday/height_first_day.sql | 76 ++ .../firstday/labs_first_day.sql | 155 +++ .../firstday/rrt_first_day.sql | 195 ++++ .../firstday/urine_output_first_day.sql | 58 + .../firstday/ventilation_first_day.sql | 26 + .../firstday/vitals_first_day.sql | 120 +++ .../firstday/weight_first_day.sql | 120 +++ .../fluid_balance/colloid_bolus.sql | 123 +++ .../fluid_balance/crystalloid_bolus.sql | 160 +++ .../fluid_balance/ffp_transfusion.sql | 96 ++ .../fluid_balance/rbc_transfusion.sql | 91 ++ .../fluid_balance/urine_output.sql | 45 + .../measurement/urine_output.sql | 2 + .../norepinephrine_equivalent_dose.sql | 2 + .../medication/vasoactive_agent.sql | 2 + .../organfailure/kdigo_creatinine.sql | 39 + .../organfailure/kdigo_stages.sql | 92 ++ .../organfailure/kdigo_stages_48hr.sql | 69 ++ .../organfailure/kdigo_stages_7day.sql | 69 ++ .../organfailure/kdigo_uo.sql | 84 ++ .../concepts_postgres/organfailure/meld.sql | 158 +++ mimic-iii/concepts_postgres/rrt.sql | 351 +++++++ mimic-iii/concepts_postgres/sepsis/angus.sql | 104 ++ .../concepts_postgres/sepsis/explicit.sql | 36 + mimic-iii/concepts_postgres/sepsis/martin.sql | 109 ++ .../concepts_postgres/sepsis/sepsis3.sql | 2 + .../severityscores/apsiii.sql | 836 +++++++++++++++ .../concepts_postgres/severityscores/lods.sql | 236 +++++ .../severityscores/mlods.sql | 224 ++++ .../severityscores/oasis.sql | 254 +++++ .../severityscores/qsofa.sql | 71 ++ .../concepts_postgres/severityscores/saps.sql | 336 ++++++ .../severityscores/sapsii.sql | 384 +++++++ .../concepts_postgres/severityscores/sirs.sql | 113 ++ .../concepts_postgres/severityscores/sofa.sql | 272 +++++ .../treatment/abx_prescriptions_list.sql | 175 +++ .../treatment/suspicion_of_infection.sql | 139 +++ 76 files changed, 13558 insertions(+) create mode 100644 mimic-iii/concepts_postgres/code_status.sql create mode 100644 mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37.sql create mode 100644 mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37_no_drg.sql create mode 100644 mimic-iii/concepts_postgres/comorbidity/elixhauser_quan.sql create mode 100644 mimic-iii/concepts_postgres/comorbidity/elixhauser_score_ahrq.sql create mode 100644 mimic-iii/concepts_postgres/comorbidity/elixhauser_score_quan.sql create mode 100644 mimic-iii/concepts_postgres/demographics/heightweight.sql create mode 100644 mimic-iii/concepts_postgres/demographics/icustay_detail.sql create mode 100644 mimic-iii/concepts_postgres/demographics/icustay_hours.sql create mode 100644 mimic-iii/concepts_postgres/demographics/icustay_times.sql create mode 100644 mimic-iii/concepts_postgres/demographics/weight_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/adenosine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/arterial_line_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/central_line_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/crrt_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/dobutamine_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/dobutamine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/dopamine_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/dopamine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/epinephrine_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/epinephrine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/isuprel_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/milrinone_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/neuroblock_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/norepinephrine_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/norepinephrine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/phenylephrine_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/phenylephrine_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/vasopressin_dose.sql create mode 100644 mimic-iii/concepts_postgres/durations/vasopressin_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/vasopressor_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/ventilation_classification.sql create mode 100644 mimic-iii/concepts_postgres/durations/ventilation_durations.sql create mode 100644 mimic-iii/concepts_postgres/durations/weight_durations.sql create mode 100644 mimic-iii/concepts_postgres/echo_data.sql create mode 100644 mimic-iii/concepts_postgres/firstday/blood_gas_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/blood_gas_first_day_arterial.sql create mode 100644 mimic-iii/concepts_postgres/firstday/first_day_sofa.sql create mode 100644 mimic-iii/concepts_postgres/firstday/gcs_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/height_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/labs_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/rrt_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/urine_output_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/ventilation_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/vitals_first_day.sql create mode 100644 mimic-iii/concepts_postgres/firstday/weight_first_day.sql create mode 100644 mimic-iii/concepts_postgres/fluid_balance/colloid_bolus.sql create mode 100644 mimic-iii/concepts_postgres/fluid_balance/crystalloid_bolus.sql create mode 100644 mimic-iii/concepts_postgres/fluid_balance/ffp_transfusion.sql create mode 100644 mimic-iii/concepts_postgres/fluid_balance/rbc_transfusion.sql create mode 100644 mimic-iii/concepts_postgres/fluid_balance/urine_output.sql create mode 100644 mimic-iii/concepts_postgres/measurement/urine_output.sql create mode 100644 mimic-iii/concepts_postgres/medication/norepinephrine_equivalent_dose.sql create mode 100644 mimic-iii/concepts_postgres/medication/vasoactive_agent.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/kdigo_creatinine.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/kdigo_stages.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/kdigo_stages_48hr.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/kdigo_stages_7day.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/kdigo_uo.sql create mode 100644 mimic-iii/concepts_postgres/organfailure/meld.sql create mode 100644 mimic-iii/concepts_postgres/rrt.sql create mode 100644 mimic-iii/concepts_postgres/sepsis/angus.sql create mode 100644 mimic-iii/concepts_postgres/sepsis/explicit.sql create mode 100644 mimic-iii/concepts_postgres/sepsis/martin.sql create mode 100644 mimic-iii/concepts_postgres/sepsis/sepsis3.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/apsiii.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/lods.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/mlods.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/oasis.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/qsofa.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/saps.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/sapsii.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/sirs.sql create mode 100644 mimic-iii/concepts_postgres/severityscores/sofa.sql create mode 100644 mimic-iii/concepts_postgres/treatment/abx_prescriptions_list.sql create mode 100644 mimic-iii/concepts_postgres/treatment/suspicion_of_infection.sql diff --git a/mimic-iii/concepts_postgres/code_status.sql b/mimic-iii/concepts_postgres/code_status.sql new file mode 100644 index 000000000..921c3aff0 --- /dev/null +++ b/mimic-iii/concepts_postgres/code_status.sql @@ -0,0 +1,75 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS code_status; CREATE TABLE code_status AS +-- This query extracts: +-- i) a patient's first code status +-- ii) a patient's last code status +-- iii) the time of the first entry of DNR or CMO + +with t1 as +( + select icustay_id, charttime, value + -- use row number to identify first and last code status + , ROW_NUMBER() over (PARTITION BY icustay_id order by charttime) as rnfirst + , ROW_NUMBER() over (PARTITION BY icustay_id order by charttime desc) as rnlast + + -- coalesce the values + , case + when value in ('Full Code','Full code') then 1 + else 0 end as fullcode + , case + when value in ('Comfort Measures','Comfort measures only') then 1 + else 0 end as cmo + , case + when value = 'CPR Not Indicate' then 1 + else 0 end as dncpr -- only in CareVue, i.e. only possible for ~60-70% of patients + , case + when value in ('Do Not Intubate','DNI (do not intubate)','DNR / DNI') then 1 + else 0 end as dni + , case + when value in ('Do Not Resuscita','DNR (do not resuscitate)','DNR / DNI') then 1 + else 0 end as dnr + FROM chartevents + where itemid in (128, 223758) + and value is not null + and value != 'Other/Remarks' + -- exclude rows marked as error + AND (error IS NULL OR error = 0) +) +select ie.subject_id, ie.hadm_id, ie.icustay_id + -- first recorded code status + , max(case when rnfirst = 1 then t1.fullcode else null end) as fullcode_first + , max(case when rnfirst = 1 then t1.cmo else null end) as cmo_first + , max(case when rnfirst = 1 then t1.dnr else null end) as dnr_first + , max(case when rnfirst = 1 then t1.dni else null end) as dni_first + , max(case when rnfirst = 1 then t1.dncpr else null end) as dncpr_first + + -- last recorded code status + , max(case when rnlast = 1 then t1.fullcode else null end) as fullcode_last + , max(case when rnlast = 1 then t1.cmo else null end) as cmo_last + , max(case when rnlast = 1 then t1.dnr else null end) as dnr_last + , max(case when rnlast = 1 then t1.dni else null end) as dni_last + , max(case when rnlast = 1 then t1.dncpr else null end) as DNCPR_last + + -- were they *at any time* given a certain code status + , max(t1.fullcode) as fullcode + , max(t1.cmo) as cmo + , max(t1.dnr) as dnr + , max(t1.dni) as dni + , max(t1.dncpr) as dncpr + + -- time until their first DNR + , min(case when t1.dnr = 1 then t1.charttime else null end) + as dnr_first_charttime + , min(case when t1.dni = 1 then t1.charttime else null end) + as dni_first_charttime + , min(case when t1.dncpr = 1 then t1.charttime else null end) + as dncpr_first_charttime + + -- first code status of CMO + , min(case when t1.cmo = 1 then t1.charttime else null end) + as timecmo_chart + +FROM icustays ie +left join t1 + on ie.icustay_id = t1.icustay_id +group by ie.subject_id, ie.hadm_id, ie.icustay_id, ie.intime; diff --git a/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37.sql b/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37.sql new file mode 100644 index 000000000..b0097f8e8 --- /dev/null +++ b/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37.sql @@ -0,0 +1,993 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS elixhauser_ahrq_v37; CREATE TABLE elixhauser_ahrq_v37 AS +-- This code uses the latest version of Elixhauser provided by AHRQ + +with eliflg as +( +select hadm_id, seq_num, icd9_code +-- note that these codes will seem incomplete at first +-- for example, CHF is missing a lot of codes referenced in the literature (402.11, 402.91, etc) +-- these codes are captured by hypertension flags instead +-- later there are some complicated rules which confirm/reject those codes as chf +, CASE + when icd9_code = '39891' then 1 + when icd9_code between '4280' and '4289' then 1 + end as chf /* Congestive heart failure */ + +-- cardiac arrhythmias is removed in up to date versions +, case + when icd9_code = '42610' then 1 + when icd9_code = '42611' then 1 + when icd9_code = '42613' then 1 + when icd9_code between '4262' and '42653' then 1 + when icd9_code between '4266' and '42689' then 1 + when icd9_code = '4270' then 1 + when icd9_code = '4272' then 1 + when icd9_code = '42731' then 1 + when icd9_code = '42760' then 1 + when icd9_code = '4279' then 1 + when icd9_code = '7850' then 1 + when icd9_code between 'V450' and 'V4509' then 1 + when icd9_code between 'V533' and 'V5339' then 1 + end as arythm /* Cardiac arrhythmias */ + +, CASE + when icd9_code between '09320' and '09324' then 1 + when icd9_code between '3940' and '3971' then 1 + when icd9_code = '3979' then 1 + when icd9_code between '4240' and '42499' then 1 + when icd9_code between '7463' and '7466' then 1 + when icd9_code = 'V422' then 1 + when icd9_code = 'V433' then 1 + end as valve /* Valvular disease */ + +, CASE + when icd9_code between '41511' and '41519' then 1 + when icd9_code between '4160' and '4169' then 1 + when icd9_code = '4179' then 1 + end as pulmcirc /* Pulmonary circulation disorder */ + +, CASE + when icd9_code between '4400' and '4409' then 1 + when icd9_code between '44100' and '4419' then 1 + when icd9_code between '4420' and '4429' then 1 + when icd9_code between '4431' and '4439' then 1 + when icd9_code between '44421' and '44422' then 1 + when icd9_code = '4471' then 1 + when icd9_code = '449' then 1 + when icd9_code = '5571' then 1 + when icd9_code = '5579' then 1 + when icd9_code = 'V434' then 1 + end as perivasc /* Peripheral vascular disorder */ + +, CASE + when icd9_code = '4011' then 1 + when icd9_code = '4019' then 1 + when icd9_code between '64200' and '64204' then 1 + end as htn /* Hypertension, uncomplicated */ + +, CASE + when icd9_code = '4010' then 1 + when icd9_code = '4372' then 1 + end as htncx /* Hypertension, complicated */ + + + /******************************************************************/ + /* The following are special, temporary formats used in the */ + /* creation of the hypertension complicated comorbidity when */ + /* overlapping with congestive heart failure or renal failure */ + /* occurs. These temporary formats are referenced in the program */ + /* called comoanaly2009.txt. */ + /******************************************************************/ +, CASE + when icd9_code between '64220' and '64224' then 1 + end as htnpreg /* Pre-existing hypertension complicating pregnancy */ + +, CASE + when icd9_code = '40200' then 1 + when icd9_code = '40210' then 1 + when icd9_code = '40290' then 1 + when icd9_code = '40509' then 1 + when icd9_code = '40519' then 1 + when icd9_code = '40599' then 1 + end as htnwochf /* Hypertensive heart disease without heart failure */ + +, CASE + when icd9_code = '40201' then 1 + when icd9_code = '40211' then 1 + when icd9_code = '40291' then 1 + end as htnwchf /* Hypertensive heart disease with heart failure */ + +, CASE + when icd9_code = '40300' then 1 + when icd9_code = '40310' then 1 + when icd9_code = '40390' then 1 + when icd9_code = '40501' then 1 + when icd9_code = '40511' then 1 + when icd9_code = '40591' then 1 + when icd9_code between '64210' and '64214' then 1 + end as hrenworf /* Hypertensive renal disease without renal failure */ + +, CASE + when icd9_code = '40301' then 1 + when icd9_code = '40311' then 1 + when icd9_code = '40391' then 1 + end as hrenwrf /* Hypertensive renal disease with renal failure */ + +, CASE + when icd9_code = '40400' then 1 + when icd9_code = '40410' then 1 + when icd9_code = '40490' then 1 + end as hhrwohrf /* Hypertensive heart and renal disease without heart or renal failure */ + +, CASE + when icd9_code = '40401' then 1 + when icd9_code = '40411' then 1 + when icd9_code = '40491' then 1 + end as hhrwchf /* Hypertensive heart and renal disease with heart failure */ + +, CASE + when icd9_code = '40402' then 1 + when icd9_code = '40412' then 1 + when icd9_code = '40492' then 1 + end as hhrwrf /* Hypertensive heart and renal disease with renal failure */ + +, CASE + when icd9_code = '40403' then 1 + when icd9_code = '40413' then 1 + when icd9_code = '40493' then 1 + end as hhrwhrf /* Hypertensive heart and renal disease with heart and renal failure */ + +, CASE + when icd9_code between '64270' and '64274' then 1 + when icd9_code between '64290' and '64294' then 1 + end as ohtnpreg /* Other hypertension in pregnancy */ + + /******************** End Temporary Formats ***********************/ + +, CASE + when icd9_code between '3420' and '3449' then 1 + when icd9_code between '43820' and '43853' then 1 + when icd9_code = '78072' then 1 + end as para /* Paralysis */ + +, CASE + when icd9_code between '3300' and '3319' then 1 + when icd9_code = '3320' then 1 + when icd9_code = '3334' then 1 + when icd9_code = '3335' then 1 + when icd9_code = '3337' then 1 + when icd9_code in ('33371','33372','33379','33385','33394') then 1 + when icd9_code between '3340' and '3359' then 1 + when icd9_code = '3380' then 1 + when icd9_code = '340' then 1 + when icd9_code between '3411' and '3419' then 1 + when icd9_code between '34500' and '34511' then 1 + when icd9_code between '3452' and '3453' then 1 + when icd9_code between '34540' and '34591' then 1 + when icd9_code between '34700' and '34701' then 1 + when icd9_code between '34710' and '34711' then 1 + when icd9_code = '3483' then 1 -- discontinued icd-9 + when icd9_code between '64940' and '64944' then 1 + when icd9_code = '7687' then 1 + when icd9_code between '76870' and '76873' then 1 + when icd9_code = '7803' then 1 + when icd9_code = '78031' then 1 + when icd9_code = '78032' then 1 + when icd9_code = '78033' then 1 + when icd9_code = '78039' then 1 + when icd9_code = '78097' then 1 + when icd9_code = '7843' then 1 + end as neuro /* Other neurological */ + +, CASE + when icd9_code between '490' and '4928' then 1 + when icd9_code between '49300' and '49392' then 1 + when icd9_code between '494' and '4941' then 1 + when icd9_code between '4950' and '505' then 1 + when icd9_code = '5064' then 1 + end as chrnlung /* Chronic pulmonary disease */ + +, CASE + when icd9_code between '25000' and '25033' then 1 + when icd9_code between '64800' and '64804' then 1 + when icd9_code between '24900' and '24931' then 1 + end as dm /* Diabetes w/o chronic complications*/ + +, CASE + when icd9_code between '25040' and '25093' then 1 + when icd9_code = '7751' then 1 + when icd9_code between '24940' and '24991' then 1 + end as dmcx /* Diabetes w/ chronic complications */ + +, CASE + when icd9_code between '243' and '2442' then 1 + when icd9_code = '2448' then 1 + when icd9_code = '2449' then 1 + end as hypothy /* Hypothyroidism */ + +, CASE + when icd9_code = '585' then 1 -- discontinued code + when icd9_code = '5853' then 1 + when icd9_code = '5854' then 1 + when icd9_code = '5855' then 1 + when icd9_code = '5856' then 1 + when icd9_code = '5859' then 1 + when icd9_code = '586' then 1 + when icd9_code = 'V420' then 1 + when icd9_code = 'V451' then 1 + when icd9_code between 'V560' and 'V5632' then 1 + when icd9_code = 'V568' then 1 + when icd9_code between 'V4511' and 'V4512' then 1 + end as renlfail /* Renal failure */ + +, CASE + when icd9_code = '07022' then 1 + when icd9_code = '07023' then 1 + when icd9_code = '07032' then 1 + when icd9_code = '07033' then 1 + when icd9_code = '07044' then 1 + when icd9_code = '07054' then 1 + when icd9_code = '4560' then 1 + when icd9_code = '4561' then 1 + when icd9_code = '45620' then 1 + when icd9_code = '45621' then 1 + when icd9_code = '5710' then 1 + when icd9_code = '5712' then 1 + when icd9_code = '5713' then 1 + when icd9_code between '57140' and '57149' then 1 + when icd9_code = '5715' then 1 + when icd9_code = '5716' then 1 + when icd9_code = '5718' then 1 + when icd9_code = '5719' then 1 + when icd9_code = '5723' then 1 + when icd9_code = '5728' then 1 + when icd9_code = '5735' then 1 + when icd9_code = 'V427' then 1 + end as liver /* Liver disease */ + +, CASE + when icd9_code = '53141' then 1 + when icd9_code = '53151' then 1 + when icd9_code = '53161' then 1 + when icd9_code = '53170' then 1 + when icd9_code = '53171' then 1 + when icd9_code = '53191' then 1 + when icd9_code = '53241' then 1 + when icd9_code = '53251' then 1 + when icd9_code = '53261' then 1 + when icd9_code = '53270' then 1 + when icd9_code = '53271' then 1 + when icd9_code = '53291' then 1 + when icd9_code = '53341' then 1 + when icd9_code = '53351' then 1 + when icd9_code = '53361' then 1 + when icd9_code = '53370' then 1 + when icd9_code = '53371' then 1 + when icd9_code = '53391' then 1 + when icd9_code = '53441' then 1 + when icd9_code = '53451' then 1 + when icd9_code = '53461' then 1 + when icd9_code = '53470' then 1 + when icd9_code = '53471' then 1 + when icd9_code = '53491' then 1 + end as ulcer /* Chronic Peptic ulcer disease (includes bleeding only if obstruction is also present) */ + +, CASE + when icd9_code between '042' and '0449' then 1 + end as aids /* HIV and AIDS */ + +, CASE + when icd9_code between '20000' and '20238' then 1 + when icd9_code between '20250' and '20301' then 1 + when icd9_code = '2386' then 1 + when icd9_code = '2733' then 1 + when icd9_code between '20302' and '20382' then 1 + end as lymph /* Lymphoma */ + +, CASE + when icd9_code between '1960' and '1991' then 1 + when icd9_code between '20970' and '20975' then 1 + when icd9_code = '20979' then 1 + when icd9_code = '78951' then 1 + end as mets /* Metastatic cancer */ + +, CASE + when icd9_code between '1400' and '1729' then 1 + when icd9_code between '1740' and '1759' then 1 + when icd9_code between '179' and '1958' then 1 + when icd9_code between '20900' and '20924' then 1 + when icd9_code between '20925' and '2093' then 1 + when icd9_code between '20930' and '20936' then 1 + when icd9_code between '25801' and '25803' then 1 + end as tumor /* Solid tumor without metastasis */ + +, CASE + when icd9_code = '7010' then 1 + when icd9_code between '7100' and '7109' then 1 + when icd9_code between '7140' and '7149' then 1 + when icd9_code between '7200' and '7209' then 1 + when icd9_code = '725' then 1 + end as arth /* Rheumatoid arthritis/collagen vascular diseases */ + +, CASE + when icd9_code between '2860' and '2869' then 1 + when icd9_code = '2871' then 1 + when icd9_code between '2873' and '2875' then 1 + when icd9_code between '64930' and '64934' then 1 + when icd9_code = '28984' then 1 + end as coag /* Coagulation deficiency */ + +, CASE + when icd9_code = '2780' then 1 + when icd9_code = '27800' then 1 + when icd9_code = '27801' then 1 + when icd9_code = '27803' then 1 + when icd9_code between '64910' and '64914' then 1 + when icd9_code between 'V8530' and 'V8539' then 1 + when icd9_code = 'V854' then 1 -- hierarchy used for AHRQ v3.6 and earlier + when icd9_code between 'V8541' and 'V8545' then 1 + when icd9_code = 'V8554' then 1 + when icd9_code = '79391' then 1 + end as obese /* Obesity */ + +, CASE + when icd9_code between '260' and '2639' then 1 + when icd9_code between '78321' and '78322' then 1 + end as wghtloss /* Weight loss */ + +, CASE + when icd9_code between '2760' and '2769' then 1 + end as lytes /* Fluid and electrolyte disorders - note: + this comorbidity should be dropped when + used with the AHRQ Patient Safety Indicators*/ +, CASE + when icd9_code = '2800' then 1 + when icd9_code between '64820' and '64824' then 1 + end as bldloss /* Blood loss anemia */ + +, CASE + when icd9_code between '2801' and '2819' then 1 + when icd9_code between '28521' and '28529' then 1 + when icd9_code = '2859' then 1 + end as anemdef /* Deficiency anemias */ + +, CASE + when icd9_code between '2910' and '2913' then 1 + when icd9_code = '2915' then 1 + when icd9_code = '2918' then 1 + when icd9_code = '29181' then 1 + when icd9_code = '29182' then 1 + when icd9_code = '29189' then 1 + when icd9_code = '2919' then 1 + when icd9_code between '30300' and '30393' then 1 + when icd9_code between '30500' and '30503' then 1 + end as alcohol /* Alcohol abuse */ + +, CASE + when icd9_code = '2920' then 1 + when icd9_code between '29282' and '29289' then 1 + when icd9_code = '2929' then 1 + when icd9_code between '30400' and '30493' then 1 + when icd9_code between '30520' and '30593' then 1 + when icd9_code between '64830' and '64834' then 1 + end as drug /* Drug abuse */ + +, CASE + when icd9_code between '29500' and '2989' then 1 + when icd9_code = '29910' then 1 + when icd9_code = '29911' then 1 + end as psych /* Psychoses */ + +, CASE + when icd9_code = '3004' then 1 + when icd9_code = '30112' then 1 + when icd9_code = '3090' then 1 + when icd9_code = '3091' then 1 + when icd9_code = '311' then 1 + end as depress /* Depression */ +from diagnoses_icd icd +WHERE seq_num = 1 +) +-- collapse the icd9_code specific flags into hadm_id specific flags +-- this groups comorbidities together for a single patient admission +, eligrp as +( + select hadm_id + , max(chf) as chf + , max(arythm) as arythm + , max(valve) as valve + , max(pulmcirc) as pulmcirc + , max(perivasc) as perivasc + , max(htn) as htn + , max(htncx) as htncx + , max(htnpreg) as htnpreg + , max(htnwochf) as htnwochf + , max(htnwchf) as htnwchf + , max(hrenworf) as hrenworf + , max(hrenwrf) as hrenwrf + , max(hhrwohrf) as hhrwohrf + , max(hhrwchf) as hhrwchf + , max(hhrwrf) as hhrwrf + , max(hhrwhrf) as hhrwhrf + , max(ohtnpreg) as ohtnpreg + , max(para) as para + , max(neuro) as neuro + , max(chrnlung) as chrnlung + , max(dm) as dm + , max(dmcx) as dmcx + , max(hypothy) as hypothy + , max(renlfail) as renlfail + , max(liver) as liver + , max(ulcer) as ulcer + , max(aids) as aids + , max(lymph) as lymph + , max(mets) as mets + , max(tumor) as tumor + , max(arth) as arth + , max(coag) as coag + , max(obese) as obese + , max(wghtloss) as wghtloss + , max(lytes) as lytes + , max(bldloss) as bldloss + , max(anemdef) as anemdef + , max(alcohol) as alcohol + , max(drug) as drug + , max(psych) as psych + , max(depress) as depress +from eliflg +group by hadm_id +) + +-- DRG FILTER -- +, msdrg as +( +select + hadm_id +/**** V29 MS-DRG Formats ****/ + +/* Cardiac */ +, case + when d.drg_code between 001 and 002 then 1 + when d.drg_code between 215 and 238 then 1 + when d.drg_code between 242 and 252 then 1 + when d.drg_code between 253 and 254 then 1 + when d.drg_code between 258 and 262 then 1 + when d.drg_code between 265 and 267 then 1 + when d.drg_code between 280 and 293 then 1 + when d.drg_code between 296 and 298 then 1 + when d.drg_code between 302 and 303 then 1 + when d.drg_code between 306 and 313 then 1 +else 0 end as carddrg + +/* Peripheral vascular */ +, case + when d.drg_code between 299 and 301 then 1 +else 0 end as peridrg + +/* Renal */ +, case + when d.drg_code = 652 then 1 + when d.drg_code between 656 and 661 then 1 + when d.drg_code between 673 and 675 then 1 + when d.drg_code between 682 and 700 then 1 +else 0 end as renaldrg + +/* Nervous system */ +, case + when d.drg_code between 020 and 042 then 1 + when d.drg_code between 052 and 103 then 1 +else 0 end as nervdrg + +/* Cerebrovascular */ +, case + when d.drg_code between 020 and 022 then 1 + when d.drg_code between 034 and 039 then 1 + when d.drg_code between 064 and 072 then 1 +else 0 end as ceredrg + +/* COPD asthma */ +, case + when d.drg_code between 190 and 192 then 1 + when d.drg_code between 202 and 203 then 1 +else 0 end as pulmdrg + +/* Diabetes */ +, case + when d.drg_code between 637 and 639 then 1 +else 0 end as DIABDRG + +/* Thyroid endocrine */ +, case + when d.drg_code between 625 and 627 then 1 + when d.drg_code between 643 and 645 then 1 +else 0 end as hypodrg + +/* Kidney transp, renal fail/dialysis */ +, case + when d.drg_code = 652 then 1 + when d.drg_code between 682 and 685 then 1 +else 0 end as renfdrg + +/* Liver */ +, case + when d.drg_code between 420 and 425 then 1 + when d.drg_code between 432 and 434 then 1 + when d.drg_code between 441 and 446 then 1 +else 0 end as liverdrg + +/* GI hemorrhage or ulcer */ +, case + when d.drg_code between 377 and 384 then 1 +else 0 end as ulcedrg + +/* Human immunodeficiency virus */ +, case + when d.drg_code between 969 and 970 then 1 + when d.drg_code between 974 and 977 then 1 +else 0 end as hivdrg + +/* Leukemia/lymphoma */ +, case + when d.drg_code between 820 and 830 then 1 + when d.drg_code between 834 and 849 then 1 +else 0 end as leukdrg + +/* Cancer, lymphoma */ +, case + when d.drg_code = 054 then 1 + when d.drg_code = 055 then 1 + when d.drg_code between 146 and 148 then 1 + when d.drg_code between 180 and 182 then 1 + when d.drg_code between 374 and 376 then 1 + when d.drg_code between 435 and 437 then 1 + when d.drg_code between 542 and 544 then 1 + when d.drg_code between 582 and 585 then 1 + when d.drg_code between 597 and 599 then 1 + when d.drg_code between 656 and 658 then 1 + when d.drg_code between 686 and 688 then 1 + when d.drg_code between 715 and 716 then 1 + when d.drg_code between 722 and 724 then 1 + when d.drg_code between 736 and 741 then 1 + when d.drg_code between 754 and 756 then 1 + when d.drg_code between 826 and 830 then 1 + when d.drg_code between 843 and 849 then 1 +else 0 end as cancdrg + +/* Connective tissue */ +, case + when d.drg_code between 545 and 547 then 1 +else 0 end as arthdrg + +/* Nutrition/metabolic */ +, case + when d.drg_code between 640 and 641 then 1 +else 0 end as nutrdrg + +/* Anemia */ +, case + when d.drg_code between 808 and 812 then 1 +else 0 end as anemdrg + +/* Alcohol drug */ +, case + when d.drg_code between 894 and 897 then 1 +else 0 end as alcdrg + +/*Coagulation disorders*/ +, case + when d.drg_code = 813 then 1 +else 0 end as coagdrg + +/*Hypertensive Complicated */ +, case + when d.drg_code = 077 then 1 + when d.drg_code = 078 then 1 + when d.drg_code = 304 then 1 +else 0 end as htncxdrg + +/*Hypertensive Uncomplicated */ +, case + when d.drg_code = 079 then 1 + when d.drg_code = 305 then 1 +else 0 end as htndrg + +/* Psychoses */ +, case + when d.drg_code = 885 then 1 +else 0 end as psydrg + +/* Obesity */ +, case + when d.drg_code between 619 and 621 then 1 +else 0 end as obesedrg + +/* Depressive Neuroses */ +, case + when d.drg_code = 881 then 1 +else 0 end as deprsdrg + +from +( + select hadm_id, drg_type, cast(drg_code as numeric) as drg_code + from drgcodes + where drg_type = 'MS' +) d + +) +, hcfadrg as +( +select + hadm_id + + /** V24 DRG Formats **/ + + /* Cardiac */ + , case + when d.drg_code between 103 and 112 then 1 + when d.drg_code between 115 and 118 then 1 + when d.drg_code between 121 and 127 then 1 + when d.drg_code = 129 then 1 + when d.drg_code = 132 then 1 + when d.drg_code = 133 then 1 + when d.drg_code between 135 and 143 then 1 + when d.drg_code between 514 and 518 then 1 + when d.drg_code between 525 and 527 then 1 + when d.drg_code between 535 and 536 then 1 + when d.drg_code between 547 and 550 then 1 + when d.drg_code between 551 and 558 then 1 + else 0 end as carddrg + + /* Peripheral vascular */ + , case + when d.drg_code = 130 then 1 + when d.drg_code = 131 then 1 + else 0 end as peridrg + + /* Renal */ + , case + when d.drg_code between 302 and 305 then 1 + when d.drg_code between 315 and 333 then 1 + + else 0 end as renaldrg + + /* Nervous system */ + , case + when d.drg_code between 1 and 35 then 1 + when d.drg_code = 524 then 1 + when d.drg_code between 528 and 534 then 1 + when d.drg_code = 543 then 1 + when d.drg_code between 559 and 564 then 1 + when d.drg_code = 577 then 1 + + else 0 end as nervdrg + + /* Cerebrovascular */ + , case + when d.drg_code = 5 then 1 + when d.drg_code between 14 and 17 then 1 + when d.drg_code = 524 then 1 + when d.drg_code = 528 then 1 + when d.drg_code between 533 and 534 then 1 + when d.drg_code = 577 then 1 + else 0 end as ceredrg + + /* COPD asthma */ + , case + when d.drg_code = 88 then 1 + when d.drg_code between 96 and 98 then 1 + + else 0 end as pulmdrg + + /* Diabetes */ + , case + when d.drg_code = 294 then 1 + when d.drg_code = 295 then 1 + else 0 end as diabdrg + + /* Thyroid endocrine */ + , case + when d.drg_code = 290 then 1 + when d.drg_code = 300 then 1 + when d.drg_code = 301 then 1 + + else 0 end as hypodrg + + /* Kidney transp, renal fail/dialysis */ + , case + when d.drg_code = 302 then 1 + when d.drg_code = 316 then 1 + when d.drg_code = 317 then 1 + else 0 end as renfdrg + + /* Liver */ + , case + when d.drg_code between 199 and 202 then 1 + when d.drg_code between 205 and 208 then 1 + + else 0 end as liverdrg + + /* GI hemorrhage or ulcer */ + , case + when d.drg_code between 174 and 178 then 1 + else 0 end as ulcedrg + + /* Human immunodeficiency virus */ + , case + when d.drg_code = 488 then 1 + when d.drg_code = 489 then 1 + when d.drg_code = 490 then 1 + + else 0 end as hivdrg + + /* Leukemia/lymphoma */ + , case + when d.drg_code between 400 and 414 then 1 + when d.drg_code = 473 then 1 + when d.drg_code = 492 then 1 + when d.drg_code between 539 and 540 then 1 + + else 0 end as leukdrg + + /* Cancer, lymphoma */ + , case + when d.drg_code = 10 then 1 + when d.drg_code = 11 then 1 + when d.drg_code = 64 then 1 + when d.drg_code = 82 then 1 + when d.drg_code = 172 then 1 + when d.drg_code = 173 then 1 + when d.drg_code = 199 then 1 + when d.drg_code = 203 then 1 + when d.drg_code = 239 then 1 + + when d.drg_code between 257 and 260 then 1 + when d.drg_code = 274 then 1 + when d.drg_code = 275 then 1 + when d.drg_code = 303 then 1 + when d.drg_code = 318 then 1 + when d.drg_code = 319 then 1 + + when d.drg_code = 338 then 1 + when d.drg_code = 344 then 1 + when d.drg_code = 346 then 1 + when d.drg_code = 347 then 1 + when d.drg_code = 354 then 1 + when d.drg_code = 355 then 1 + when d.drg_code = 357 then 1 + when d.drg_code = 363 then 1 + when d.drg_code = 366 then 1 + + when d.drg_code = 367 then 1 + when d.drg_code between 406 and 414 then 1 + else 0 end as cancdrg + + /* Connective tissue */ + , case + when d.drg_code = 240 then 1 + when d.drg_code = 241 then 1 + else 0 end as arthdrg + + /* Nutrition/metabolic */ + , case + when d.drg_code between 296 and 298 then 1 + else 0 end as nutrdrg + + /* Anemia */ + , case + when d.drg_code = 395 then 1 + when d.drg_code = 396 then 1 + when d.drg_code = 574 then 1 + else 0 end as anemdrg + + /* Alcohol drug */ + , case + when d.drg_code between 433 and 437 then 1 + when d.drg_code between 521 and 523 then 1 + else 0 end as alcdrg + + /* Coagulation disorders */ + , case + when d.drg_code = 397 then 1 + else 0 end as coagdrg + + /* Hypertensive Complicated */ + , case + when d.drg_code = 22 then 1 + when d.drg_code = 134 then 1 + else 0 end as htncxdrg + + /* Hypertensive Uncomplicated */ + , case + when d.drg_code = 134 then 1 + else 0 end as htndrg + + /* Psychoses */ + , case + when d.drg_code = 430 then 1 + else 0 end as psydrg + + /* Obesity */ + , case + when d.drg_code = 288 then 1 + else 0 end as obesedrg + + /* Depressive Neuroses */ + , case + when d.drg_code = 426 then 1 + else 0 end as deprsdrg + + from + ( + select hadm_id, drg_type, cast(drg_code as numeric) as drg_code + from drgcodes + where drg_type = 'HCFA' + ) d +) +-- merge DRG groups together +, drggrp as +( + select hadm_id +, max(carddrg) as carddrg +, max(peridrg) as peridrg +, max(renaldrg) as renaldrg +, max(nervdrg) as nervdrg +, max(ceredrg) as ceredrg +, max(pulmdrg) as pulmdrg +, max(diabdrg) as diabdrg +, max(hypodrg) as hypodrg +, max(renfdrg) as renfdrg +, max(liverdrg) as liverdrg +, max(ulcedrg) as ulcedrg +, max(hivdrg) as hivdrg +, max(leukdrg) as leukdrg +, max(cancdrg) as cancdrg +, max(arthdrg) as arthdrg +, max(nutrdrg) as nutrdrg +, max(anemdrg) as anemdrg +, max(alcdrg) as alcdrg +, max(coagdrg) as coagdrg +, max(htncxdrg) as htncxdrg +, max(htndrg) as htndrg +, max(psydrg) as psydrg +, max(obesedrg) as obesedrg +, max(deprsdrg) as deprsdrg +from +( + select d1.* from msdrg d1 + UNION DISTINCT + select d1.* from hcfadrg d1 +) d +group by d.hadm_id +) +-- now merge these flags together to define elixhauser +-- most are straightforward.. but hypertension flags are a bit more complicated +select adm.subject_id, adm.hadm_id +, case + when carddrg = 1 then 0 -- DRG filter + + when chf = 1 then 1 + when htnwchf = 1 then 1 + when hhrwchf = 1 then 1 + when hhrwhrf = 1 then 1 + else 0 end as congestive_heart_failure +, case + when carddrg = 1 then 0 -- DRG filter + when arythm = 1 then 1 + else 0 end as cardiac_arrhythmias +, case + when carddrg = 1 then 0 + when valve = 1 then 1 + else 0 end as valvular_disease +, case + when carddrg = 1 or pulmdrg = 1 then 0 + when pulmcirc = 1 then 1 + else 0 end as pulmonary_circulation +, case + when peridrg = 1 then 0 + when perivasc = 1 then 1 + else 0 end as peripheral_vascular + +-- we combine 'htn' and 'htncx' into 'HYPERTENSION' +-- note 'htn' (hypertension) is only 1 if 'htncx' (complicated hypertension) is 0 +-- also if htncxdrg = 1, then htndrg = 1 + +-- In the original Sas code, it appears that: +-- HTN can be 1 +-- HTNCX is set to 0 by DRGs +-- but HTN_C is still 1, because HTN is 1 +-- so we have to do this complex addition. +, +case + when +( +-- first hypertension +case + when htndrg = 0 then 0 + when htn = 1 then 1 +else 0 end +) ++ +( +-- next complicated hypertension +case + when htncx = 1 and htncxdrg = 1 then 0 + + when htnpreg = 1 and htncxdrg = 1 then 0 + when htnwochf = 1 and (htncxdrg = 1 OR carddrg = 1) then 0 + when htnwchf = 1 and htncxdrg = 1 then 0 + when htnwchf = 1 and carddrg = 1 then 0 + when hrenworf = 1 and (htncxdrg = 1 or renaldrg = 1) then 0 + when hrenwrf = 1 and htncxdrg = 1 then 0 + when hrenwrf = 1 and renaldrg = 1 then 0 + when hhrwohrf = 1 and (htncxdrg = 1 or carddrg = 1 or renaldrg = 1) then 0 + when hhrwchf = 1 and (htncxdrg = 1 or carddrg = 1 or renaldrg = 1) then 0 + when hhrwrf = 1 and (htncxdrg = 1 or carddrg = 1 or renaldrg = 1) then 0 + when hhrwhrf = 1 and (htncxdrg = 1 or carddrg = 1 or renaldrg = 1) then 0 + when ohtnpreg = 1 and (htncxdrg = 1 or carddrg = 1 or renaldrg = 1) then 0 + + when htncx = 1 then 1 + when htnpreg = 1 then 1 + when htnwochf = 1 then 1 + when htnwchf = 1 then 1 + when hrenworf = 1 then 1 + when hrenwrf = 1 then 1 + when hhrwohrf = 1 then 1 + when hhrwchf = 1 then 1 + when hhrwrf = 1 then 1 + when hhrwhrf = 1 then 1 + when ohtnpreg = 1 then 1 + else 0 end +) + > 0 then 1 else 0 end as hypertension + +, case when ceredrg = 1 then 0 when para = 1 then 1 else 0 end as paralysis +, case when nervdrg = 1 then 0 when neuro = 1 then 1 else 0 end as other_neurological +, case when pulmdrg = 1 then 0 when chrnlung = 1 then 1 else 0 end as chronic_pulmonary +, case + -- only the more severe comorbidity (complicated diabetes) is kept + when diabdrg = 1 then 0 + when dmcx = 1 then 0 + when dm = 1 then 1 + else 0 end as diabetes_uncomplicated +, case when diabdrg = 1 then 0 when dmcx = 1 then 1 else 0 end as diabetes_complicated +, case when hypodrg = 1 then 0 when hypothy = 1 then 1 else 0 end as hypothyroidism +, case + when renaldrg = 1 then 0 + when renlfail = 1 then 1 + when hrenwrf = 1 then 1 + when hhrwrf = 1 then 1 + when hhrwhrf = 1 then 1 + else 0 end as renal_failure + +, case when liverdrg = 1 then 0 when liver = 1 then 1 else 0 end as liver_disease +, case when ulcedrg = 1 then 0 when ulcer = 1 then 1 else 0 end as peptic_ulcer +, case when hivdrg = 1 then 0 when aids = 1 then 1 else 0 end as aids +, case when leukdrg = 1 then 0 when lymph = 1 then 1 else 0 end as lymphoma +, case when cancdrg = 1 then 0 when mets = 1 then 1 else 0 end as metastatic_cancer +, case + when cancdrg = 1 then 0 + -- only the more severe comorbidity (metastatic cancer) is kept + when mets = 1 then 0 + when tumor = 1 then 1 + else 0 end as solid_tumor +, case when arthdrg = 1 then 0 when arth = 1 then 1 else 0 end as rheumatoid_arthritis +, case when coagdrg = 1 then 0 when coag = 1 then 1 else 0 end as coagulopathy +, case when nutrdrg = 1 + OR obesedrg = 1 then 0 when obese = 1 then 1 else 0 end as obesity +, case when nutrdrg = 1 then 0 when wghtloss = 1 then 1 else 0 end as weight_loss +, case when nutrdrg = 1 then 0 when lytes = 1 then 1 else 0 end as fluid_electrolyte +, case when anemdrg = 1 then 0 when bldloss = 1 then 1 else 0 end as blood_loss_anemia +, case when anemdrg = 1 then 0 when anemdef = 1 then 1 else 0 end as deficiency_anemias +, case when alcdrg = 1 then 0 when alcohol = 1 then 1 else 0 end as alcohol_abuse +, case when alcdrg = 1 then 0 when drug = 1 then 1 else 0 end as drug_abuse +, case when psydrg = 1 then 0 when psych = 1 then 1 else 0 end as psychoses +, case when deprsdrg = 1 then 0 when depress = 1 then 1 else 0 end as depression + + +FROM admissions adm +left join eligrp eli + on adm.hadm_id = eli.hadm_id +left join drggrp d + on adm.hadm_id = d.hadm_id +order by adm.hadm_id; diff --git a/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37_no_drg.sql b/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37_no_drg.sql new file mode 100644 index 000000000..e481b3651 --- /dev/null +++ b/mimic-iii/concepts_postgres/comorbidity/elixhauser_ahrq_v37_no_drg.sql @@ -0,0 +1,523 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS elixhauser_ahrq_v37_no_drg; CREATE TABLE elixhauser_ahrq_v37_no_drg AS +-- This code uses the latest version of Elixhauser provided by AHRQ +-- However, it does *not* filter based on diagnosis related groups (DRGs) +-- As such, "comorbidities" identified are more likely to be associated with the primary reason for their hospital stay + +-- The code: +-- removes "primary" ICD9_CODE (seq_num != 1) +-- uses AHRQ published rules to define comorbidities +with +eliflg as +( +select hadm_id, seq_num, icd9_code +-- note that these codes will seem incomplete at first +-- for example, CHF is missing a lot of codes referenced in the literature (402.11, 402.91, etc) +-- these codes are captured by hypertension flags instead +-- later there are some complicated rules which confirm/reject those codes as chf +, CASE + when icd9_code = '39891' then 1 + when icd9_code between '4280' and '4289' then 1 + end as chf /* Congestive heart failure */ + +-- cardiac arrhythmias is removed in up to date versions +, case + when icd9_code = '42610' then 1 + when icd9_code = '42611' then 1 + when icd9_code = '42613' then 1 + when icd9_code between '4262' and '42653' then 1 + when icd9_code between '4266' and '42689' then 1 + when icd9_code = '4270' then 1 + when icd9_code = '4272' then 1 + when icd9_code = '42731' then 1 + when icd9_code = '42760' then 1 + when icd9_code = '4279' then 1 + when icd9_code = '7850' then 1 + when icd9_code between 'V450' and 'V4509' then 1 + when icd9_code between 'V533' and 'V5339' then 1 + end as arythm /* Cardiac arrhythmias */ + +, CASE + when icd9_code between '09320' and '09324' then 1 + when icd9_code between '3940' and '3971' then 1 + when icd9_code = '3979' then 1 + when icd9_code between '4240' and '42499' then 1 + when icd9_code between '7463' and '7466' then 1 + when icd9_code = 'V422' then 1 + when icd9_code = 'V433' then 1 + end as valve /* Valvular disease */ + +, CASE + when icd9_code between '41511' and '41519' then 1 + when icd9_code between '4160' and '4169' then 1 + when icd9_code = '4179' then 1 + end as pulmcirc /* Pulmonary circulation disorder */ + +, CASE + when icd9_code between '4400' and '4409' then 1 + when icd9_code between '44100' and '4419' then 1 + when icd9_code between '4420' and '4429' then 1 + when icd9_code between '4431' and '4439' then 1 + when icd9_code between '44421' and '44422' then 1 + when icd9_code = '4471' then 1 + when icd9_code = '449' then 1 + when icd9_code = '5571' then 1 + when icd9_code = '5579' then 1 + when icd9_code = 'V434' then 1 + end as perivasc /* Peripheral vascular disorder */ + +, CASE + when icd9_code = '4011' then 1 + when icd9_code = '4019' then 1 + when icd9_code between '64200' and '64204' then 1 + end as htn /* Hypertension, uncomplicated */ + +, CASE + when icd9_code = '4010' then 1 + when icd9_code = '4372' then 1 + end as htncx /* Hypertension, complicated */ + + + /******************************************************************/ + /* The following are special, temporary formats used in the */ + /* creation of the hypertension complicated comorbidity when */ + /* overlapping with congestive heart failure or renal failure */ + /* occurs. These temporary formats are referenced in the program */ + /* called comoanaly2009.txt. */ + /******************************************************************/ +, CASE + when icd9_code between '64220' and '64224' then 1 + end as htnpreg /* Pre-existing hypertension complicating pregnancy */ + +, CASE + when icd9_code = '40200' then 1 + when icd9_code = '40210' then 1 + when icd9_code = '40290' then 1 + when icd9_code = '40509' then 1 + when icd9_code = '40519' then 1 + when icd9_code = '40599' then 1 + end as htnwochf /* Hypertensive heart disease without heart failure */ + +, CASE + when icd9_code = '40201' then 1 + when icd9_code = '40211' then 1 + when icd9_code = '40291' then 1 + end as htnwchf /* Hypertensive heart disease with heart failure */ + +, CASE + when icd9_code = '40300' then 1 + when icd9_code = '40310' then 1 + when icd9_code = '40390' then 1 + when icd9_code = '40501' then 1 + when icd9_code = '40511' then 1 + when icd9_code = '40591' then 1 + when icd9_code between '64210' and '64214' then 1 + end as hrenworf /* Hypertensive renal disease without renal failure */ + +, CASE + when icd9_code = '40301' then 1 + when icd9_code = '40311' then 1 + when icd9_code = '40391' then 1 + end as hrenwrf /* Hypertensive renal disease with renal failure */ + +, CASE + when icd9_code = '40400' then 1 + when icd9_code = '40410' then 1 + when icd9_code = '40490' then 1 + end as hhrwohrf /* Hypertensive heart and renal disease without heart or renal failure */ + +, CASE + when icd9_code = '40401' then 1 + when icd9_code = '40411' then 1 + when icd9_code = '40491' then 1 + end as hhrwchf /* Hypertensive heart and renal disease with heart failure */ + +, CASE + when icd9_code = '40402' then 1 + when icd9_code = '40412' then 1 + when icd9_code = '40492' then 1 + end as hhrwrf /* Hypertensive heart and renal disease with renal failure */ + +, CASE + when icd9_code = '40403' then 1 + when icd9_code = '40413' then 1 + when icd9_code = '40493' then 1 + end as hhrwhrf /* Hypertensive heart and renal disease with heart and renal failure */ + +, CASE + when icd9_code between '64270' and '64274' then 1 + when icd9_code between '64290' and '64294' then 1 + end as ohtnpreg /* Other hypertension in pregnancy */ + + /******************** End Temporary Formats ***********************/ + +, CASE + when icd9_code between '3420' and '3449' then 1 + when icd9_code between '43820' and '43853' then 1 + when icd9_code = '78072' then 1 + end as para /* Paralysis */ + +, CASE + when icd9_code between '3300' and '3319' then 1 + when icd9_code = '3320' then 1 + when icd9_code = '3334' then 1 + when icd9_code = '3335' then 1 + when icd9_code = '3337' then 1 + when icd9_code in ('33371','33372','33379','33385','33394') then 1 + when icd9_code between '3340' and '3359' then 1 + when icd9_code = '3380' then 1 + when icd9_code = '340' then 1 + when icd9_code between '3411' and '3419' then 1 + when icd9_code between '34500' and '34511' then 1 + when icd9_code between '3452' and '3453' then 1 + when icd9_code between '34540' and '34591' then 1 + when icd9_code between '34700' and '34701' then 1 + when icd9_code between '34710' and '34711' then 1 + when icd9_code = '3483' then 1 -- discontinued icd-9 + when icd9_code between '64940' and '64944' then 1 + when icd9_code = '7687' then 1 + when icd9_code between '76870' and '76873' then 1 + when icd9_code = '7803' then 1 + when icd9_code = '78031' then 1 + when icd9_code = '78032' then 1 + when icd9_code = '78033' then 1 + when icd9_code = '78039' then 1 + when icd9_code = '78097' then 1 + when icd9_code = '7843' then 1 + end as neuro /* Other neurological */ + +, CASE + when icd9_code between '490' and '4928' then 1 + when icd9_code between '49300' and '49392' then 1 + when icd9_code between '494' and '4941' then 1 + when icd9_code between '4950' and '505' then 1 + when icd9_code = '5064' then 1 + end as chrnlung /* Chronic pulmonary disease */ + +, CASE + when icd9_code between '25000' and '25033' then 1 + when icd9_code between '64800' and '64804' then 1 + when icd9_code between '24900' and '24931' then 1 + end as dm /* Diabetes w/o chronic complications*/ + +, CASE + when icd9_code between '25040' and '25093' then 1 + when icd9_code = '7751' then 1 + when icd9_code between '24940' and '24991' then 1 + end as dmcx /* Diabetes w/ chronic complications */ + +, CASE + when icd9_code between '243' and '2442' then 1 + when icd9_code = '2448' then 1 + when icd9_code = '2449' then 1 + end as hypothy /* Hypothyroidism */ + +, CASE + when icd9_code = '585' then 1 -- discontinued code + when icd9_code = '5853' then 1 + when icd9_code = '5854' then 1 + when icd9_code = '5855' then 1 + when icd9_code = '5856' then 1 + when icd9_code = '5859' then 1 + when icd9_code = '586' then 1 + when icd9_code = 'V420' then 1 + when icd9_code = 'V451' then 1 + when icd9_code between 'V560' and 'V5632' then 1 + when icd9_code = 'V568' then 1 + when icd9_code between 'V4511' and 'V4512' then 1 + end as renlfail /* Renal failure */ + +, CASE + when icd9_code = '07022' then 1 + when icd9_code = '07023' then 1 + when icd9_code = '07032' then 1 + when icd9_code = '07033' then 1 + when icd9_code = '07044' then 1 + when icd9_code = '07054' then 1 + when icd9_code = '4560' then 1 + when icd9_code = '4561' then 1 + when icd9_code = '45620' then 1 + when icd9_code = '45621' then 1 + when icd9_code = '5710' then 1 + when icd9_code = '5712' then 1 + when icd9_code = '5713' then 1 + when icd9_code between '57140' and '57149' then 1 + when icd9_code = '5715' then 1 + when icd9_code = '5716' then 1 + when icd9_code = '5718' then 1 + when icd9_code = '5719' then 1 + when icd9_code = '5723' then 1 + when icd9_code = '5728' then 1 + when icd9_code = '5735' then 1 + when icd9_code = 'V427' then 1 + end as liver /* Liver disease */ + +, CASE + when icd9_code = '53141' then 1 + when icd9_code = '53151' then 1 + when icd9_code = '53161' then 1 + when icd9_code = '53170' then 1 + when icd9_code = '53171' then 1 + when icd9_code = '53191' then 1 + when icd9_code = '53241' then 1 + when icd9_code = '53251' then 1 + when icd9_code = '53261' then 1 + when icd9_code = '53270' then 1 + when icd9_code = '53271' then 1 + when icd9_code = '53291' then 1 + when icd9_code = '53341' then 1 + when icd9_code = '53351' then 1 + when icd9_code = '53361' then 1 + when icd9_code = '53370' then 1 + when icd9_code = '53371' then 1 + when icd9_code = '53391' then 1 + when icd9_code = '53441' then 1 + when icd9_code = '53451' then 1 + when icd9_code = '53461' then 1 + when icd9_code = '53470' then 1 + when icd9_code = '53471' then 1 + when icd9_code = '53491' then 1 + end as ulcer /* Chronic Peptic ulcer disease (includes bleeding only if obstruction is also present) */ + +, CASE + when icd9_code between '042' and '0449' then 1 + end as aids /* HIV and AIDS */ + +, CASE + when icd9_code between '20000' and '20238' then 1 + when icd9_code between '20250' and '20301' then 1 + when icd9_code = '2386' then 1 + when icd9_code = '2733' then 1 + when icd9_code between '20302' and '20382' then 1 + end as lymph /* Lymphoma */ + +, CASE + when icd9_code between '1960' and '1991' then 1 + when icd9_code between '20970' and '20975' then 1 + when icd9_code = '20979' then 1 + when icd9_code = '78951' then 1 + end as mets /* Metastatic cancer */ + +, CASE + when icd9_code between '1400' and '1729' then 1 + when icd9_code between '1740' and '1759' then 1 + when icd9_code between '179' and '1958' then 1 + when icd9_code between '20900' and '20924' then 1 + when icd9_code between '20925' and '2093' then 1 + when icd9_code between '20930' and '20936' then 1 + when icd9_code between '25801' and '25803' then 1 + end as tumor /* Solid tumor without metastasis */ + +, CASE + when icd9_code = '7010' then 1 + when icd9_code between '7100' and '7109' then 1 + when icd9_code between '7140' and '7149' then 1 + when icd9_code between '7200' and '7209' then 1 + when icd9_code = '725' then 1 + end as arth /* Rheumatoid arthritis/collagen vascular diseases */ + +, CASE + when icd9_code between '2860' and '2869' then 1 + when icd9_code = '2871' then 1 + when icd9_code between '2873' and '2875' then 1 + when icd9_code between '64930' and '64934' then 1 + when icd9_code = '28984' then 1 + end as coag /* Coagulation deficiency */ + +, CASE + when icd9_code = '2780' then 1 + when icd9_code = '27800' then 1 + when icd9_code = '27801' then 1 + when icd9_code = '27803' then 1 + when icd9_code between '64910' and '64914' then 1 + when icd9_code between 'V8530' and 'V8539' then 1 + when icd9_code = 'V854' then 1 -- hierarchy used for AHRQ v3.6 and earlier + when icd9_code between 'V8541' and 'V8545' then 1 + when icd9_code = 'V8554' then 1 + when icd9_code = '79391' then 1 + end as obese /* Obesity */ + +, CASE + when icd9_code between '260' and '2639' then 1 + when icd9_code between '78321' and '78322' then 1 + end as wghtloss /* Weight loss */ + +, CASE + when icd9_code between '2760' and '2769' then 1 + end as lytes /* Fluid and electrolyte disorders - note: + this comorbidity should be dropped when + used with the AHRQ Patient Safety Indicators*/ +, CASE + when icd9_code = '2800' then 1 + when icd9_code between '64820' and '64824' then 1 + end as bldloss /* Blood loss anemia */ + +, CASE + when icd9_code between '2801' and '2819' then 1 + when icd9_code between '28521' and '28529' then 1 + when icd9_code = '2859' then 1 + end as anemdef /* Deficiency anemias */ + +, CASE + when icd9_code between '2910' and '2913' then 1 + when icd9_code = '2915' then 1 + when icd9_code = '2918' then 1 + when icd9_code = '29181' then 1 + when icd9_code = '29182' then 1 + when icd9_code = '29189' then 1 + when icd9_code = '2919' then 1 + when icd9_code between '30300' and '30393' then 1 + when icd9_code between '30500' and '30503' then 1 + end as alcohol /* Alcohol abuse */ + +, CASE + when icd9_code = '2920' then 1 + when icd9_code between '29282' and '29289' then 1 + when icd9_code = '2929' then 1 + when icd9_code between '30400' and '30493' then 1 + when icd9_code between '30520' and '30593' then 1 + when icd9_code between '64830' and '64834' then 1 + end as drug /* Drug abuse */ + +, CASE + when icd9_code between '29500' and '2989' then 1 + when icd9_code = '29910' then 1 + when icd9_code = '29911' then 1 + end as psych /* Psychoses */ + +, CASE + when icd9_code = '3004' then 1 + when icd9_code = '30112' then 1 + when icd9_code = '3090' then 1 + when icd9_code = '3091' then 1 + when icd9_code = '311' then 1 + end as depress /* Depression */ +from diagnoses_icd icd +WHERE seq_num = 1 +) +-- collapse the icd9_code specific flags into hadm_id specific flags +-- this groups comorbidities together for a single patient admission +, eligrp as +( + select hadm_id + , max(chf) as chf + , max(arythm) as arythm + , max(valve) as valve + , max(pulmcirc) as pulmcirc + , max(perivasc) as perivasc + , max(htn) as htn + , max(htncx) as htncx + , max(htnpreg) as htnpreg + , max(htnwochf) as htnwochf + , max(htnwchf) as htnwchf + , max(hrenworf) as hrenworf + , max(hrenwrf) as hrenwrf + , max(hhrwohrf) as hhrwohrf + , max(hhrwchf) as hhrwchf + , max(hhrwrf) as hhrwrf + , max(hhrwhrf) as hhrwhrf + , max(ohtnpreg) as ohtnpreg + , max(para) as para + , max(neuro) as neuro + , max(chrnlung) as chrnlung + , max(dm) as dm + , max(dmcx) as dmcx + , max(hypothy) as hypothy + , max(renlfail) as renlfail + , max(liver) as liver + , max(ulcer) as ulcer + , max(aids) as aids + , max(lymph) as lymph + , max(mets) as mets + , max(tumor) as tumor + , max(arth) as arth + , max(coag) as coag + , max(obese) as obese + , max(wghtloss) as wghtloss + , max(lytes) as lytes + , max(bldloss) as bldloss + , max(anemdef) as anemdef + , max(alcohol) as alcohol + , max(drug) as drug + , max(psych) as psych + , max(depress) as depress +from eliflg +group by hadm_id +) +-- now merge these flags together to define elixhauser +-- most are straightforward.. but hypertension flags are a bit more complicated +select adm.subject_id, adm.hadm_id +, case + when chf = 1 then 1 + when htnwchf = 1 then 1 + when hhrwchf = 1 then 1 + when hhrwhrf = 1 then 1 + else 0 end as congestive_heart_failure +, case + when arythm = 1 then 1 + else 0 end as cardiac_arrhythmias +, case when valve = 1 then 1 else 0 end as valvular_disease +, case when pulmcirc = 1 then 1 else 0 end as pulmonary_circulation +, case when perivasc = 1 then 1 else 0 end as peripheral_vascular + +-- we combine "htn" and "htncx" into "HYPERTENSION" +-- note "htn" (hypertension) is only 1 if "htncx" (complicated hypertension) is 0 +-- this matters if you filter on DRG but for this query we can just merge them immediately +, case + when htn = 1 then 1 + when htncx = 1 then 1 + when htnpreg = 1 then 1 + when htnwochf = 1 then 1 + when htnwchf = 1 then 1 + when hrenworf = 1 then 1 + when hrenwrf = 1 then 1 + when hhrwohrf = 1 then 1 + when hhrwchf = 1 then 1 + when hhrwrf = 1 then 1 + when hhrwhrf = 1 then 1 + when ohtnpreg = 1 then 1 + else 0 end as hypertension + +, case when para = 1 then 1 else 0 end as paralysis +, case when neuro = 1 then 1 else 0 end as other_neurological +, case when chrnlung = 1 then 1 else 0 end as chronic_pulmonary +, case + -- only the more severe comorbidity (complicated diabetes) is kept + when dmcx = 1 then 0 + when dm = 1 then 1 + else 0 end as diabetes_uncomplicated +, case when dmcx = 1 then 1 else 0 end as diabetes_complicated +, case when hypothy = 1 then 1 else 0 end as hypothyroidism +, case + when renlfail = 1 then 1 + when hrenwrf = 1 then 1 + when hhrwrf = 1 then 1 + when hhrwhrf = 1 then 1 + else 0 end as renal_failure + +, case when liver = 1 then 1 else 0 end as liver_disease +, case when ulcer = 1 then 1 else 0 end as peptic_ulcer +, case when aids = 1 then 1 else 0 end as aids +, case when lymph = 1 then 1 else 0 end as lymphoma +, case when mets = 1 then 1 else 0 end as metastatic_cancer +, case + -- only the more severe comorbidity (metastatic cancer) is kept + when mets = 1 then 0 + when tumor = 1 then 1 + else 0 end as solid_tumor +, case when arth = 1 then 1 else 0 end as rheumatoid_arthritis +, case when coag = 1 then 1 else 0 end as coagulopathy +, case when obese = 1 then 1 else 0 end as obesity +, case when wghtloss = 1 then 1 else 0 end as weight_loss +, case when lytes = 1 then 1 else 0 end as fluid_electrolyte +, case when bldloss = 1 then 1 else 0 end as blood_loss_anemia +, case when anemdef = 1 then 1 else 0 end as deficiency_anemias +, case when alcohol = 1 then 1 else 0 end as alcohol_abuse +, case when drug = 1 then 1 else 0 end as drug_abuse +, case when psych = 1 then 1 else 0 end as psychoses +, case when depress = 1 then 1 else 0 end as depression + +FROM admissions adm +left join eligrp eli + on adm.hadm_id = eli.hadm_id +order by adm.hadm_id; diff --git a/mimic-iii/concepts_postgres/comorbidity/elixhauser_quan.sql b/mimic-iii/concepts_postgres/comorbidity/elixhauser_quan.sql new file mode 100644 index 000000000..18aceeb4b --- /dev/null +++ b/mimic-iii/concepts_postgres/comorbidity/elixhauser_quan.sql @@ -0,0 +1,272 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS elixhauser_quan; CREATE TABLE elixhauser_quan AS +-- This code calculates the Elixhauser comorbidities as defined in Quan et. al 2009: +-- Quan, Hude, et al. "Coding algorithms for defining comorbidities in +-- ICD-9-CM and ICD-10 administrative data." Medical care (2005): 1130-1139. +-- https://www.ncbi.nlm.nih.gov/pubmed/16224307 + +-- Quan defined an "Enhanced ICD-9" coding scheme for deriving Elixhauser +-- comorbidities from ICD-9 billing codes. This script implements that calculation. + +-- The logic of the code is roughly that, if the comorbidity lists a length 3 +-- ICD-9 code (e.g. 585), then we only require a match on the first 3 characters. + +-- This code derives each comorbidity as follows: +-- 1) ICD9_CODE is directly compared to 5 character codes +-- 2) The first 4 characters of ICD9_CODE are compared to 4 character codes +-- 3) The first 3 characters of ICD9_CODE are compared to 3 character codes +with eliflg as +( +select hadm_id, seq_num, icd9_code +, CASE + when icd9_code in ('39891','40201','40211','40291','40401','40403','40411','40413','40491','40493') then 1 + when SUBSTR(icd9_code, 1, 4) in ('4254','4255','4257','4258','4259') then 1 + when SUBSTR(icd9_code, 1, 3) in ('428') then 1 + else 0 end as chf /* Congestive heart failure */ + +, CASE + when icd9_code in ('42613','42610','42612','99601','99604') then 1 + when SUBSTR(icd9_code, 1, 4) in ('4260','4267','4269','4270','4271','4272','4273','4274','4276','4278','4279','7850','V450','V533') then 1 + else 0 end as arrhy + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('0932','7463','7464','7465','7466','V422','V433') then 1 + when SUBSTR(icd9_code, 1, 3) in ('394','395','396','397','424') then 1 + else 0 end as valve /* Valvular disease */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('4150','4151','4170','4178','4179') then 1 + when SUBSTR(icd9_code, 1, 3) in ('416') then 1 + else 0 end as pulmcirc /* Pulmonary circulation disorder */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('0930','4373','4431','4432','4438','4439','4471','5571','5579','V434') then 1 + when SUBSTR(icd9_code, 1, 3) in ('440','441') then 1 + else 0 end as perivasc /* Peripheral vascular disorder */ + +, CASE + when SUBSTR(icd9_code, 1, 3) in ('401') then 1 + else 0 end as htn /* Hypertension, uncomplicated */ + +, CASE + when SUBSTR(icd9_code, 1, 3) in ('402','403','404','405') then 1 + else 0 end as htncx /* Hypertension, complicated */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('3341','3440','3441','3442','3443','3444','3445','3446','3449') then 1 + when SUBSTR(icd9_code, 1, 3) in ('342','343') then 1 + else 0 end as para /* Paralysis */ + +, CASE + when icd9_code in ('33392') then 1 + when SUBSTR(icd9_code, 1, 4) in ('3319','3320','3321','3334','3335','3362','3481','3483','7803','7843') then 1 + when SUBSTR(icd9_code, 1, 3) in ('334','335','340','341','345') then 1 + else 0 end as neuro /* Other neurological */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('4168','4169','5064','5081','5088') then 1 + when SUBSTR(icd9_code, 1, 3) in ('490','491','492','493','494','495','496','500','501','502','503','504','505') then 1 + else 0 end as chrnlung /* Chronic pulmonary disease */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2500','2501','2502','2503') then 1 + else 0 end as dm /* Diabetes w/o chronic complications*/ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2504','2505','2506','2507','2508','2509') then 1 + else 0 end as dmcx /* Diabetes w/ chronic complications */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2409','2461','2468') then 1 + when SUBSTR(icd9_code, 1, 3) in ('243','244') then 1 + else 0 end as hypothy /* Hypothyroidism */ + +, CASE + when icd9_code in ('40301','40311','40391','40402','40403','40412','40413','40492','40493') then 1 + when SUBSTR(icd9_code, 1, 4) in ('5880','V420','V451') then 1 + when SUBSTR(icd9_code, 1, 3) in ('585','586','V56') then 1 + else 0 end as renlfail /* Renal failure */ + +, CASE + when icd9_code in ('07022','07023','07032','07033','07044','07054') then 1 + when SUBSTR(icd9_code, 1, 4) in ('0706','0709','4560','4561','4562','5722','5723','5724','5728','5733','5734','5738','5739','V427') then 1 + when SUBSTR(icd9_code, 1, 3) in ('570','571') then 1 + else 0 end as liver /* Liver disease */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('5317','5319','5327','5329','5337','5339','5347','5349') then 1 + else 0 end as ulcer /* Chronic Peptic ulcer disease (includes bleeding only if obstruction is also present) */ + +, CASE + when SUBSTR(icd9_code, 1, 3) in ('042','043','044') then 1 + else 0 end as aids /* HIV and AIDS */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2030','2386') then 1 + when SUBSTR(icd9_code, 1, 3) in ('200','201','202') then 1 + else 0 end as lymph /* Lymphoma */ + +, CASE + when SUBSTR(icd9_code, 1, 3) in ('196','197','198','199') then 1 + else 0 end as mets /* Metastatic cancer */ + +, CASE + when SUBSTR(icd9_code, 1, 3) in + ( + '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','174','175','176','177','178','179' + ,'180','181','182','183','184','185','186','187','188','189','190','191','192' + ,'193','194','195' + ) then 1 + else 0 end as tumor /* Solid tumor without metastasis */ + +, CASE + when icd9_code in ('72889','72930') then 1 + when SUBSTR(icd9_code, 1, 4) in ('7010','7100','7101','7102','7103','7104','7108','7109','7112','7193','7285') then 1 + when SUBSTR(icd9_code, 1, 3) in ('446','714','720','725') then 1 + else 0 end as arth /* Rheumatoid arthritis/collagen vascular diseases */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2871','2873','2874','2875') then 1 + when SUBSTR(icd9_code, 1, 3) in ('286') then 1 + else 0 end as coag /* Coagulation deficiency */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2780') then 1 + else 0 end as obese /* Obesity */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('7832','7994') then 1 + when SUBSTR(icd9_code, 1, 3) in ('260','261','262','263') then 1 + else 0 end as wghtloss /* Weight loss */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2536') then 1 + when SUBSTR(icd9_code, 1, 3) in ('276') then 1 + else 0 end as lytes /* Fluid and electrolyte disorders */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2800') then 1 + else 0 end as bldloss /* Blood loss anemia */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2801','2808','2809') then 1 + when SUBSTR(icd9_code, 1, 3) in ('281') then 1 + else 0 end as anemdef /* Deficiency anemias */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2652','2911','2912','2913','2915','2918','2919','3030','3039','3050','3575','4255','5353','5710','5711','5712','5713','V113') then 1 + when SUBSTR(icd9_code, 1, 3) in ('980') then 1 + else 0 end as alcohol /* Alcohol abuse */ + +, CASE + when icd9_code in ('V6542') then 1 + when SUBSTR(icd9_code, 1, 4) in ('3052','3053','3054','3055','3056','3057','3058','3059') then 1 + when SUBSTR(icd9_code, 1, 3) in ('292','304') then 1 + else 0 end as drug /* Drug abuse */ + +, CASE + when icd9_code in ('29604','29614','29644','29654') then 1 + when SUBSTR(icd9_code, 1, 4) in ('2938') then 1 + when SUBSTR(icd9_code, 1, 3) in ('295','297','298') then 1 + else 0 end as psych /* Psychoses */ + +, CASE + when SUBSTR(icd9_code, 1, 4) in ('2962','2963','2965','3004') then 1 + when SUBSTR(icd9_code, 1, 3) in ('309','311') then 1 + else 0 end as depress /* Depression */ +from diagnoses_icd icd +where seq_num != 1 -- we do not include the primary icd-9 code +) +-- collapse the icd9_code specific flags into hadm_id specific flags +-- this groups comorbidities together for a single patient admission +, eligrp as +( + select hadm_id + , max(chf) as chf + , max(arrhy) as arrhy + , max(valve) as valve + , max(pulmcirc) as pulmcirc + , max(perivasc) as perivasc + , max(htn) as htn + , max(htncx) as htncx + , max(para) as para + , max(neuro) as neuro + , max(chrnlung) as chrnlung + , max(dm) as dm + , max(dmcx) as dmcx + , max(hypothy) as hypothy + , max(renlfail) as renlfail + , max(liver) as liver + , max(ulcer) as ulcer + , max(aids) as aids + , max(lymph) as lymph + , max(mets) as mets + , max(tumor) as tumor + , max(arth) as arth + , max(coag) as coag + , max(obese) as obese + , max(wghtloss) as wghtloss + , max(lytes) as lytes + , max(bldloss) as bldloss + , max(anemdef) as anemdef + , max(alcohol) as alcohol + , max(drug) as drug + , max(psych) as psych + , max(depress) as depress +from eliflg +group by hadm_id +) +-- now merge these flags together to define elixhauser +-- most are straightforward.. but hypertension flags are a bit more complicated + + +select adm.hadm_id +, chf as congestive_heart_failure +, arrhy as cardiac_arrhythmias +, valve as valvular_disease +, pulmcirc as pulmonary_circulation +, perivasc as peripheral_vascular +-- we combine "htn" and "htncx" into "HYPERTENSION" +, case + when htn = 1 then 1 + when htncx = 1 then 1 + else 0 end as hypertension +, para as paralysis +, neuro as other_neurological +, chrnlung as chronic_pulmonary +-- only the more severe comorbidity (complicated diabetes) is kept +, case + when dmcx = 1 then 0 + when dm = 1 then 1 + else 0 end as diabetes_uncomplicated +, dmcx as diabetes_complicated +, hypothy as hypothyroidism +, renlfail as renal_failure +, liver as liver_disease +, ulcer as peptic_ulcer +, aids as aids +, lymph as lymphoma +, mets as metastatic_cancer +-- only the more severe comorbidity (metastatic cancer) is kept +, case + when mets = 1 then 0 + when tumor = 1 then 1 + else 0 end as solid_tumor +, arth as rheumatoid_arthritis +, coag as coagulopathy +, obese as obesity +, wghtloss as weight_loss +, lytes as fluid_electrolyte +, bldloss as blood_loss_anemia +, anemdef as deficiency_anemias +, alcohol as alcohol_abuse +, drug as drug_abuse +, psych as psychoses +, depress as depression + +FROM admissions adm +left join eligrp eli + on adm.hadm_id = eli.hadm_id +order by adm.hadm_id; diff --git a/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_ahrq.sql b/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_ahrq.sql new file mode 100644 index 000000000..c295f96ad --- /dev/null +++ b/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_ahrq.sql @@ -0,0 +1,108 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS elixhauser_score_ahrq; CREATE TABLE elixhauser_score_ahrq AS +-- This query provides various methods of combining the Elixhauser components into a single score +-- The methods are called "vanWalRaven" and "SID30", and "SID29" +select subject_id, hadm_id +, -- Below is the van Walraven score + 0 * aids + + 0 * alcohol_abuse + + -2 * blood_loss_anemia + + 7 * congestive_heart_failure + + -- Cardiac arrhythmias are not included in van Walraven based on Quan 2007 + 3 * chronic_pulmonary + + 3 * coagulopathy + + -2 * deficiency_anemias + + -3 * depression + + 0 * diabetes_complicated + + 0 * diabetes_uncomplicated + + -7 * drug_abuse + + 5 * fluid_electrolyte + + 0 * hypertension + + 0 * hypothyroidism + + 11 * liver_disease + + 9 * lymphoma + + 12 * metastatic_cancer + + 6 * other_neurological + + -4 * obesity + + 7 * paralysis + + 2 * peripheral_vascular + + 0 * peptic_ulcer + + 0 * psychoses + + 4 * pulmonary_circulation + + 0 * rheumatoid_arthritis + + 5 * renal_failure + + 4 * solid_tumor + + -1 * valvular_disease + + 6 * weight_loss +as elixhauser_vanwalraven + + + +, -- Below is the 29 component SID score + 0 * aids + + -2 * alcohol_abuse + + -2 * blood_loss_anemia + + -- Cardiac arrhythmias are not included in SID-29 + 9 * congestive_heart_failure + + 3 * chronic_pulmonary + + 9 * coagulopathy + + 0 * deficiency_anemias + + -4 * depression + + 0 * diabetes_complicated + + -1 * diabetes_uncomplicated + + -8 * drug_abuse + + 9 * fluid_electrolyte + + -1 * hypertension + + 0 * hypothyroidism + + 5 * liver_disease + + 6 * lymphoma + + 13 * metastatic_cancer + + 4 * other_neurological + + -4 * obesity + + 3 * paralysis + + 0 * peptic_ulcer + + 4 * peripheral_vascular + + -4 * psychoses + + 5 * pulmonary_circulation + + 6 * renal_failure + + 0 * rheumatoid_arthritis + + 8 * solid_tumor + + 0 * valvular_disease + + 8 * weight_loss +as elixhauser_SID29 + + +, -- Below is the 30 component SID score + 0 * aids + + 0 * alcohol_abuse + + -3 * blood_loss_anemia + + 8 * cardiac_arrhythmias + + 9 * congestive_heart_failure + + 3 * chronic_pulmonary + + 12 * coagulopathy + + 0 * deficiency_anemias + + -5 * depression + + 1 * diabetes_complicated + + 0 * diabetes_uncomplicated + + -11 * drug_abuse + + 11 * fluid_electrolyte + + -2 * hypertension + + 0 * hypothyroidism + + 7 * liver_disease + + 8 * lymphoma + + 17 * metastatic_cancer + + 5 * other_neurological + + -5 * obesity + + 4 * paralysis + + 0 * peptic_ulcer + + 4 * peripheral_vascular + + -6 * psychoses + + 5 * pulmonary_circulation + + 7 * renal_failure + + 0 * rheumatoid_arthritis + + 10 * solid_tumor + + 0 * valvular_disease + + 10 * weight_loss +as elixhauser_SID30 + +from elixhauser_ahrq_v37; diff --git a/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_quan.sql b/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_quan.sql new file mode 100644 index 000000000..1afad718e --- /dev/null +++ b/mimic-iii/concepts_postgres/comorbidity/elixhauser_score_quan.sql @@ -0,0 +1,109 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS elixhauser_score_quan; CREATE TABLE elixhauser_score_quan AS +-- This query provides various methods of combining the Elixhauser components into a single score +-- The methods are called "vanWalRaven" and "SID30", and "SID29" + +select hadm_id +, -- Below is the van Walraven score + 0 * aids + + 0 * alcohol_abuse + + -2 * blood_loss_anemia + + 7 * congestive_heart_failure + + -- Cardiac arrhythmias are not included in van Walraven based on Quan 2007 + 3 * chronic_pulmonary + + 3 * coagulopathy + + -2 * deficiency_anemias + + -3 * depression + + 0 * diabetes_complicated + + 0 * diabetes_uncomplicated + + -7 * drug_abuse + + 5 * fluid_electrolyte + + 0 * hypertension + + 0 * hypothyroidism + + 11 * liver_disease + + 9 * lymphoma + + 12 * metastatic_cancer + + 6 * other_neurological + + -4 * obesity + + 7 * paralysis + + 2 * peripheral_vascular + + 0 * peptic_ulcer + + 0 * psychoses + + 4 * pulmonary_circulation + + 0 * rheumatoid_arthritis + + 5 * renal_failure + + 4 * solid_tumor + + -1 * valvular_disease + + 6 * weight_loss +as elixhauser_vanwalraven + + + +, -- Below is the 29 component SID score + 0 * aids + + -2 * alcohol_abuse + + -2 * blood_loss_anemia + + -- Cardiac arrhythmias are not included in SID-29 + 9 * congestive_heart_failure + + 3 * chronic_pulmonary + + 9 * coagulopathy + + 0 * deficiency_anemias + + -4 * depression + + 0 * diabetes_complicated + + -1 * diabetes_uncomplicated + + -8 * drug_abuse + + 9 * fluid_electrolyte + + -1 * hypertension + + 0 * hypothyroidism + + 5 * liver_disease + + 6 * lymphoma + + 13 * metastatic_cancer + + 4 * other_neurological + + -4 * obesity + + 3 * paralysis + + 0 * peptic_ulcer + + 4 * peripheral_vascular + + -4 * psychoses + + 5 * pulmonary_circulation + + 6 * renal_failure + + 0 * rheumatoid_arthritis + + 8 * solid_tumor + + 0 * valvular_disease + + 8 * weight_loss +as elixhauser_SID29 + + +, -- Below is the 30 component SID score + 0 * aids + + 0 * alcohol_abuse + + -3 * blood_loss_anemia + + 8 * cardiac_arrhythmias + + 9 * congestive_heart_failure + + 3 * chronic_pulmonary + + 12 * coagulopathy + + 0 * deficiency_anemias + + -5 * depression + + 1 * diabetes_complicated + + 0 * diabetes_uncomplicated + + -11 * drug_abuse + + 11 * fluid_electrolyte + + -2 * hypertension + + 0 * hypothyroidism + + 7 * liver_disease + + 8 * lymphoma + + 17 * metastatic_cancer + + 5 * other_neurological + + -5 * obesity + + 4 * paralysis + + 0 * peptic_ulcer + + 4 * peripheral_vascular + + -6 * psychoses + + 5 * pulmonary_circulation + + 7 * renal_failure + + 0 * rheumatoid_arthritis + + 10 * solid_tumor + + 0 * valvular_disease + + 10 * weight_loss +as elixhauser_SID30 + +from elixhauser_quan; diff --git a/mimic-iii/concepts_postgres/demographics/heightweight.sql b/mimic-iii/concepts_postgres/demographics/heightweight.sql new file mode 100644 index 000000000..65036af88 --- /dev/null +++ b/mimic-iii/concepts_postgres/demographics/heightweight.sql @@ -0,0 +1,107 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS heightweight; CREATE TABLE heightweight AS +-- ------------------------------------------------------------------ +-- Title: Extract height and weight for ICUSTAY_IDs +-- Description: This query gets the first, minimum, and maximum weight and height +-- for a single ICUSTAY_ID. It extracts data from the CHARTEVENTS table. +-- MIMIC version: MIMIC-III v1.4 +-- ------------------------------------------------------------------ + +-- prep height +WITH ht_stg AS +( + SELECT + c.subject_id, c.icustay_id, c.charttime, + -- Ensure that all heights are in centimeters, and fix data as needed + CASE + WHEN c.itemid IN (920, 1394, 4187, 3486, 226707) + THEN + CASE + -- rule for neonates + WHEN c.charttime <= DATETIME_ADD(pt.dob, INTERVAL '1' YEAR) + AND (c.valuenum * 2.54) < 80 + THEN c.valuenum * 2.54 + -- rule for adults + WHEN c.charttime > DATETIME_ADD(pt.dob, INTERVAL '1' YEAR) + AND (c.valuenum * 2.54) > 120 + AND (c.valuenum * 2.54) < 230 + THEN c.valuenum * 2.54 + ELSE NULL END + ELSE + CASE + -- rule for neonates + WHEN c.charttime <= DATETIME_ADD(pt.dob, INTERVAL '1' YEAR) + AND c.valuenum < 80 + THEN c.valuenum + -- rule for adults + WHEN c.charttime > DATETIME_ADD(pt.dob, INTERVAL '1' YEAR) + AND c.valuenum > 120 + AND c.valuenum < 230 + THEN c.valuenum + ELSE NULL END + END AS height + FROM chartevents c + INNER JOIN patients pt + ON c.subject_id = pt.subject_id + WHERE c.valuenum IS NOT NULL + AND c.valuenum != 0 + -- exclude rows marked as error + AND COALESCE(c.error, 0) = 0 + AND c.itemid IN + ( + -- CareVue + 920, 1394, 4187, 3486, -- Height inches + 3485, 4188 -- Height cm + -- Metavision + , 226707 -- Height (measured in inches) + -- note we intentionally ignore the below ITEMID in metavision + -- these are duplicate data in a different unit + -- , 226730 -- Height (cm) + ) +) +SELECT + ie.icustay_id, + ROUND(CAST(wt.weight_first AS NUMERIC), 2) AS weight_first, + ROUND(CAST(wt.weight_min AS NUMERIC), 2) AS weight_min, + ROUND(CAST(wt.weight_max AS NUMERIC), 2) AS weight_max, + ROUND(CAST(ht.height_first AS NUMERIC), 2) AS height_first, + ROUND(CAST(ht.height_min AS NUMERIC), 2) AS height_min, + ROUND(CAST(ht.height_max AS NUMERIC), 2) AS height_max +FROM icustays ie +-- get weight from weight_durations table +LEFT JOIN +( + SELECT icustay_id, + MIN(CASE WHEN rn = 1 THEN weight ELSE NULL END) as weight_first, + MIN(weight) AS weight_min, + MAX(weight) AS weight_max + FROM + ( + SELECT + icustay_id, + weight, + ROW_NUMBER() OVER (PARTITION BY icustay_id ORDER BY starttime) as rn + FROM weight_durations + ) wt_stg + GROUP BY icustay_id +) wt + ON ie.icustay_id = wt.icustay_id +-- get first/min/max height from above, after filtering bad data +LEFT JOIN +( + SELECT icustay_id, + MIN(CASE WHEN rn = 1 THEN height ELSE NULL END) as height_first, + MIN(height) AS height_min, + MAX(height) AS height_max + FROM + ( + SELECT + icustay_id, + height, + ROW_NUMBER() OVER (PARTITION BY icustay_id ORDER BY charttime) as rn + FROM ht_stg + ) ht_stg2 + GROUP BY icustay_id +) ht + ON ie.icustay_id = ht.icustay_id +ORDER BY icustay_id; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/demographics/icustay_detail.sql b/mimic-iii/concepts_postgres/demographics/icustay_detail.sql new file mode 100644 index 000000000..54553302e --- /dev/null +++ b/mimic-iii/concepts_postgres/demographics/icustay_detail.sql @@ -0,0 +1,105 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS icustay_detail; CREATE TABLE icustay_detail AS +-- ------------------------------------------------------------------ +-- Title: Detailed information on ICUSTAY_ID +-- Description: This query provides a useful set of information regarding patient +-- ICU stays. The information is combined from the admissions, patients, and +-- icustays tables. It includes age, length of stay, sequence, and expiry flags. +-- MIMIC version: MIMIC-III v1.3 +-- ------------------------------------------------------------------ + +-- This query extracts useful demographic/administrative information for patient ICU stays + +SELECT ie.subject_id, ie.hadm_id, ie.icustay_id + +-- patient level factors +, pat.gender, pat.dod + +-- hospital level factors +, adm.admittime, adm.dischtime +, DATETIME_DIFF(adm.dischtime, adm.admittime, 'DAY') as los_hospital +, DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') as admission_age +, adm.ethnicity +, case when ethnicity in + ( + 'WHITE' -- 40996 + , 'WHITE - RUSSIAN' -- 164 + , 'WHITE - OTHER EUROPEAN' -- 81 + , 'WHITE - BRAZILIAN' -- 59 + , 'WHITE - EASTERN EUROPEAN' -- 25 + ) then 'white' + when ethnicity in + ( + 'BLACK/AFRICAN AMERICAN' -- 5440 + , 'BLACK/CAPE VERDEAN' -- 200 + , 'BLACK/HAITIAN' -- 101 + , 'BLACK/AFRICAN' -- 44 + , 'CARIBBEAN ISLAND' -- 9 + ) then 'black' + when ethnicity in + ( + 'HISPANIC OR LATINO' -- 1696 + , 'HISPANIC/LATINO - PUERTO RICAN' -- 232 + , 'HISPANIC/LATINO - DOMINICAN' -- 78 + , 'HISPANIC/LATINO - GUATEMALAN' -- 40 + , 'HISPANIC/LATINO - CUBAN' -- 24 + , 'HISPANIC/LATINO - SALVADORAN' -- 19 + , 'HISPANIC/LATINO - CENTRAL AMERICAN (OTHER)' -- 13 + , 'HISPANIC/LATINO - MEXICAN' -- 13 + , 'HISPANIC/LATINO - COLOMBIAN' -- 9 + , 'HISPANIC/LATINO - HONDURAN' -- 4 + ) then 'hispanic' + when ethnicity in + ( + 'ASIAN' -- 1509 + , 'ASIAN - CHINESE' -- 277 + , 'ASIAN - ASIAN INDIAN' -- 85 + , 'ASIAN - VIETNAMESE' -- 53 + , 'ASIAN - FILIPINO' -- 25 + , 'ASIAN - CAMBODIAN' -- 17 + , 'ASIAN - OTHER' -- 17 + , 'ASIAN - KOREAN' -- 13 + , 'ASIAN - JAPANESE' -- 7 + , 'ASIAN - THAI' -- 4 + ) then 'asian' + when ethnicity in + ( + 'AMERICAN INDIAN/ALASKA NATIVE' -- 51 + , 'AMERICAN INDIAN/ALASKA NATIVE FEDERALLY RECOGNIZED TRIBE' -- 3 + ) then 'native' + when ethnicity in + ( + 'UNKNOWN/NOT SPECIFIED' -- 4523 + , 'UNABLE TO OBTAIN' -- 814 + , 'PATIENT DECLINED TO ANSWER' -- 559 + ) then 'unknown' + else 'other' end as ethnicity_grouped + -- , 'OTHER' -- 1512 + -- , 'MULTI RACE ETHNICITY' -- 130 + -- , 'PORTUGUESE' -- 61 + -- , 'MIDDLE EASTERN' -- 43 + -- , 'NATIVE HAWAIIAN OR OTHER PACIFIC ISLANDER' -- 18 + -- , 'SOUTH AMERICAN' -- 8 +, adm.hospital_expire_flag +, DENSE_RANK() OVER (PARTITION BY adm.subject_id ORDER BY adm.admittime) AS hospstay_seq +, CASE + WHEN DENSE_RANK() OVER (PARTITION BY adm.subject_id ORDER BY adm.admittime) = 1 THEN True + ELSE False END AS first_hosp_stay + +-- icu level factors +, ie.intime, ie.outtime +, DATETIME_DIFF(ie.outtime, ie.intime, 'DAY') as los_icu +, DENSE_RANK() OVER (PARTITION BY ie.hadm_id ORDER BY ie.intime) AS icustay_seq + +-- first ICU stay *for the current hospitalization* +, CASE + WHEN DENSE_RANK() OVER (PARTITION BY ie.hadm_id ORDER BY ie.intime) = 1 THEN True + ELSE False END AS first_icu_stay + +FROM icustays ie +INNER JOIN admissions adm + ON ie.hadm_id = adm.hadm_id +INNER JOIN patients pat + ON ie.subject_id = pat.subject_id +WHERE adm.has_chartevents_data = 1 +ORDER BY ie.subject_id, adm.admittime, ie.intime; diff --git a/mimic-iii/concepts_postgres/demographics/icustay_hours.sql b/mimic-iii/concepts_postgres/demographics/icustay_hours.sql new file mode 100644 index 000000000..0e8033db0 --- /dev/null +++ b/mimic-iii/concepts_postgres/demographics/icustay_hours.sql @@ -0,0 +1,36 @@ +DROP TABLE IF EXISTS icustay_hours; CREATE TABLE icustay_hours AS +-- This query generates a row for every hour the patient is in the ICU. +-- The hours are based on clock-hours (i.e. 02:00, 03:00). +-- The hour clock starts 24 hours before the first heart rate measurement. +-- Note that the time of the first heart rate measurement is ceilinged to the hour. + +-- this query extracts the cohort and every possible hour they were in the ICU +-- this table can be to other tables on ICUSTAY_ID and (ENDTIME - 1 hour,ENDTIME] +-- get first/last measurement time +with all_hours as +( + select + it.icustay_id + + -- ceiling the intime to the nearest hour by adding 59 minutes then truncating + , date_trunc('hour', it.intime_hr + interval '59' minute) as endtime + + -- create integers for each charttime in hours from admission + -- so 0 is admission time, 1 is one hour after admission, etc, up to ICU disch + , generate_series + ( + -- allow up to 24 hours before ICU admission (to grab labs before admit) + -24, + ceil(extract(EPOCH from it.outtime_hr-it.intime_hr)/60.0/60.0)::INTEGER + ) as hr + + from icustay_times it +) +SELECT + ah.icustay_id + , ah.hr + -- add the hr series + -- endtime now indexes the end time of every hour for each patient + , ah.endtime + ah.hr*interval '1' hour as endtime +from all_hours ah +order by ah.icustay_id; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/demographics/icustay_times.sql b/mimic-iii/concepts_postgres/demographics/icustay_times.sql new file mode 100644 index 000000000..8b3cd3c49 --- /dev/null +++ b/mimic-iii/concepts_postgres/demographics/icustay_times.sql @@ -0,0 +1,55 @@ +DROP TABLE IF EXISTS icustay_times; CREATE TABLE icustay_times AS +with h as +( + select + subject_id, hadm_id, admittime, dischtime + , lag (dischtime) over (partition by subject_id order by admittime) as dischtime_lag + , lead (admittime) over (partition by subject_id order by admittime) as admittime_lead + from admissions +) +, adm as +( + select + h.subject_id, h.hadm_id + -- this rule is: + -- if there are two hospitalizations within 24 hours, set the start/stop + -- time as half way between the two admissions + , case + when h.dischtime_lag is not null + and h.dischtime_lag > (h.admittime - interval '24' hour) + then h.admittime - ((h.admittime - h.dischtime_lag)/2) + else h.admittime - interval '12' hour + end as data_start + , case + when h.admittime_lead is not null + and h.admittime_lead < (h.dischtime + interval '24' hour) + then h.dischtime + ((h.admittime_lead - h.dischtime)/2) + else (h.dischtime + interval '12' hour) + end as data_end + from h +) +-- get first/last heart rate measurement during hospitalization for each ICUSTAY_ID +, t1 as +( +select ce.icustay_id +, min(charttime) as intime_hr +, max(charttime) as outtime_hr +from chartevents ce +-- very loose join to admissions to ensure charttime is near patient admission +inner join adm + on ce.hadm_id = adm.hadm_id + and ce.charttime >= adm.data_start + and ce.charttime < adm.data_end +-- only look at heart rate +where ce.itemid in (211,220045) +group by ce.icustay_id +) +-- add in subject_id/hadm_id +select + ie.subject_id, ie.hadm_id, ie.icustay_id + , t1.intime_hr + , t1.outtime_hr +from icustays ie +left join t1 + on ie.icustay_id = t1.icustay_id +order by ie.subject_id, ie.hadm_id, ie.icustay_id; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/demographics/weight_durations.sql b/mimic-iii/concepts_postgres/demographics/weight_durations.sql new file mode 100644 index 000000000..6ac078c51 --- /dev/null +++ b/mimic-iii/concepts_postgres/demographics/weight_durations.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS weight_durations; CREATE TABLE weight_durations AS diff --git a/mimic-iii/concepts_postgres/durations/adenosine_durations.sql b/mimic-iii/concepts_postgres/durations/adenosine_durations.sql new file mode 100644 index 000000000..b3aa21e94 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/adenosine_durations.sql @@ -0,0 +1,231 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS adenosine_durations; CREATE TABLE adenosine_durations AS +-- This query extracts durations of adenosine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- *** COULD NOT FIND ADENOSINE IN THE INPUTEVENTS_MV TABLE *** +-- This drug is rarely used - it could just be that it was never used in MetaVision. +-- If using this code, ensure the durations make sense for carevue patients first + +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 4649 then 1 else 0 end) as vaso -- adenosine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , 0 as vaso_stopped + , max(case when itemid = 4649 and valuenum is not null then 1 else 0 end) as vaso_null + , max(case when itemid = 4649 then valuenum else null end) as vaso_rate + , max(case when itemid = 4649 then valuenum else null end) as vaso_amount + + FROM chartevents + where itemid = 4649 -- adenosine + -- exclude rows marked as error + AND (error IS NULL OR error = 0) + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221282 -- adenosine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/arterial_line_durations.sql b/mimic-iii/concepts_postgres/durations/arterial_line_durations.sql new file mode 100644 index 000000000..440be48d0 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/arterial_line_durations.sql @@ -0,0 +1,180 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS arterial_line_durations; CREATE TABLE arterial_line_durations AS +with mv as +( + select + pe.icustay_id + , pe.starttime, pe.endtime + , case + when itemid in (225752, 224272) + then 1 + when pe.locationcategory = 'Invasive Arterial' + then 1 + when itemid = 225789 and pe.locationcategory IS NULL + then 1 + else 0 + end as arterial_line + FROM procedureevents_mv pe + where pe.itemid in + ( + 224263 -- Multi Lumen | None | 12 | Processes + -- , 224264 -- PICC Line | None | 12 | Processes + , 224267 -- Cordis/Introducer | None | 12 | Processes + , 224268 -- Trauma line | None | 12 | Processes + , 225199 -- Triple Introducer | None | 12 | Processes + -- , 225202 -- Indwelling Port (PortaCath) | None | 12 | Processes + -- , 225203 -- Pheresis Catheter | None | 12 | Processes + -- , 225315 -- Tunneled (Hickman) Line | None | 12 | Processes + , 225752 -- Arterial Line | None | 12 | Processes + , 225789 -- Sheath + , 224272 -- IABP Line + -- , 227719 -- AVA Line | None | 12 | Processes + -- , 228286 -- Intraosseous Device | None | 12 | Processes + ) +) +, cv_grp as +( + -- group type+site + select ce.icustay_id, ce.charttime + , max(case when itemid = 229 then value else null end) as INV1_Type + , max(case when itemid = 8392 then value else null end) as INV1_Site + , max(case when itemid = 235 then value else null end) as INV2_Type + , max(case when itemid = 8393 then value else null end) as INV2_Site + , max(case when itemid = 241 then value else null end) as INV3_Type + , max(case when itemid = 8394 then value else null end) as INV3_Site + , max(case when itemid = 247 then value else null end) as INV4_Type + , max(case when itemid = 8395 then value else null end) as INV4_Site + , max(case when itemid = 253 then value else null end) as INV5_Type + , max(case when itemid = 8396 then value else null end) as INV5_Site + , max(case when itemid = 259 then value else null end) as INV6_Type + , max(case when itemid = 8397 then value else null end) as INV6_Site + , max(case when itemid = 265 then value else null end) as INV7_Type + , max(case when itemid = 8398 then value else null end) as INV7_Site + , max(case when itemid = 271 then value else null end) as INV8_Type + , max(case when itemid = 8399 then value else null end) as INV8_Site + FROM chartevents ce + where ce.itemid in + ( + 229 -- INV Line#1 [Type] + , 235 -- INV Line#2 [Type] + , 241 -- INV Line#3 [Type] + , 247 -- INV Line#4 [Type] + , 253 -- INV Line#5 [Type] + , 259 -- INV Line#6 [Type] + , 265 -- INV Line#7 [Type] + , 271 -- INV Line#8 [Type] + , 8392 -- INV Line#1 [Site] + , 8393 -- INV Line#2 [Site] + , 8394 -- INV Line#3 [Site] + , 8395 -- INV Line#4 [Site] + , 8396 -- INV Line#5 [Site] + , 8397 -- INV Line#6 [Site] + , 8398 -- INV Line#7 [Site] + , 8399 -- INV Line#8 [Site] + ) + and ce.value is not null + group by ce.icustay_id, ce.charttime +) +-- types of invasive lines in carevue +-- value | count +-- ------------------+-------- +-- A-Line | 460627 +-- Multi-lumen | 345858 +-- PICC line | 92285 +-- PA line | 65702 +-- Dialysis Line | 57579 +-- Introducer | 36027 +-- CCO PA Line | 24831 +-- | 22369 +-- Trauma Line | 15530 +-- Portacath | 12927 +-- Ventriculostomy | 10295 +-- Pre-Sep Catheter | 9678 +-- IABP | 8819 +-- Other/Remarks | 8725 +-- Midline | 5067 +-- Venous Access | 4278 +-- Hickman | 3783 +-- PacerIntroducer | 2663 +-- TripleIntroducer | 2262 +-- RIC | 1625 +-- PermaCath | 1066 +-- Camino Bolt | 913 +-- Lumbar Drain | 361 +-- (23 rows) +, cv as +( + select distinct icustay_id, charttime + from cv_grp + where (inv1_type in ('A-Line', 'IABP')) + OR (inv2_type in ('A-Line', 'IABP')) + OR (inv3_type in ('A-Line', 'IABP')) + OR (inv4_type in ('A-Line', 'IABP')) + OR (inv5_type in ('A-Line', 'IABP')) + OR (inv6_type in ('A-Line', 'IABP')) + OR (inv7_type in ('A-Line', 'IABP')) + OR (inv8_type in ('A-Line', 'IABP')) +) +-- transform carevue data into durations +, cv0 as +( + select + icustay_id + -- this carries over the previous charttime + , LAG(CHARTTIME, 1) OVER (partition by icustay_id order by charttime) as charttime_lag + , charttime + from cv +) +, cv1 as +( + select + icustay_id + , charttime + , charttime_lag + -- if the current observation indicates a line is present + -- calculate the time since the last charted line + , charttime - charttime_lag as arterial_line_duration + -- now we determine if the current line is "new" + -- new == no documentation for 16 hours + , case + when DATETIME_DIFF(charttime, charttime_lag, 'HOUR') > 16 + then 1 + else 0 + end as arterial_line_new + FROM cv0 +) +, cv2 as +( + select cv1.* + -- create a cumulative sum of the instances of new events + -- this results in a monotonic integer assigned to each new instance of a line + , SUM( arterial_line_new ) + OVER ( partition by icustay_id order by charttime ) + as arterial_line_rownum + from cv1 +) +-- create the durations for each line +, cv_dur as +( + select icustay_id + , arterial_line_rownum + , min(charttime) as starttime + , max(charttime) as endtime + , DATETIME_DIFF(max(charttime), min(charttime), 'HOUR') AS duration_hours + from cv2 + group by icustay_id, arterial_line_rownum + having min(charttime) != max(charttime) +) +select icustay_id + -- , arterial_line_rownum + , starttime, endtime, duration_hours +from cv_dur +UNION ALL +--TODO: collapse metavision durations if they overlap +select icustay_id + -- , ROW_NUMBER() over (PARTITION BY icustay_id ORDER BY starttime) as arterial_line_rownum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours +from mv +where arterial_line = 1 +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/central_line_durations.sql b/mimic-iii/concepts_postgres/durations/central_line_durations.sql new file mode 100644 index 000000000..333359c1a --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/central_line_durations.sql @@ -0,0 +1,175 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS central_line_durations; CREATE TABLE central_line_durations AS +with mv as +( + select + pe.icustay_id + , pe.starttime, pe.endtime + , case + when (locationcategory <> 'Invasive Arterial' or locationcategory is null) + then 1 + else 0 + end as central_line + FROM procedureevents_mv pe + where pe.itemid in + ( + 224263 -- Multi Lumen | None | 12 | Processes + , 224264 -- PICC Line | None | 12 | Processes + , 224267 -- Cordis/Introducer | None | 12 | Processes + , 224268 -- Trauma line | None | 12 | Processes + , 225199 -- Triple Introducer | None | 12 | Processes + , 225202 -- Indwelling Port (PortaCath) | None | 12 | Processes + , 225203 -- Pheresis Catheter | None | 12 | Processes + , 225315 -- Tunneled (Hickman) Line | None | 12 | Processes + , 225752 -- Arterial Line | None | 12 | Processes + , 227719 -- AVA Line | None | 12 | Processes + -- , 228286 -- Intraosseous Device | None | 12 | Processes + , 224270 -- Dialysis Catheter + ) +) +, cv_grp as +( + -- group type+site + select ce.icustay_id, ce.charttime + , max(case when itemid = 229 then value else null end) as INV1_Type + , max(case when itemid = 8392 then value else null end) as INV1_Site + , max(case when itemid = 235 then value else null end) as INV2_Type + , max(case when itemid = 8393 then value else null end) as INV2_Site + , max(case when itemid = 241 then value else null end) as INV3_Type + , max(case when itemid = 8394 then value else null end) as INV3_Site + , max(case when itemid = 247 then value else null end) as INV4_Type + , max(case when itemid = 8395 then value else null end) as INV4_Site + , max(case when itemid = 253 then value else null end) as INV5_Type + , max(case when itemid = 8396 then value else null end) as INV5_Site + , max(case when itemid = 259 then value else null end) as INV6_Type + , max(case when itemid = 8397 then value else null end) as INV6_Site + , max(case when itemid = 265 then value else null end) as INV7_Type + , max(case when itemid = 8398 then value else null end) as INV7_Site + , max(case when itemid = 271 then value else null end) as INV8_Type + , max(case when itemid = 8399 then value else null end) as INV8_Site + FROM chartevents ce + where ce.itemid in + ( + 229 -- INV Line#1 [Type] + , 235 -- INV Line#2 [Type] + , 241 -- INV Line#3 [Type] + , 247 -- INV Line#4 [Type] + , 253 -- INV Line#5 [Type] + , 259 -- INV Line#6 [Type] + , 265 -- INV Line#7 [Type] + , 271 -- INV Line#8 [Type] + , 8392 -- INV Line#1 [Site] + , 8393 -- INV Line#2 [Site] + , 8394 -- INV Line#3 [Site] + , 8395 -- INV Line#4 [Site] + , 8396 -- INV Line#5 [Site] + , 8397 -- INV Line#6 [Site] + , 8398 -- INV Line#7 [Site] + , 8399 -- INV Line#8 [Site] + ) + and ce.value is not null + group by ce.icustay_id, ce.charttime +) +-- types of invasive lines in carevue +-- value | count +-- ------------------+-------- +-- A-Line | 460627 +-- Multi-lumen | 345858 +-- PICC line | 92285 +-- PA line | 65702 +-- Dialysis Line | 57579 +-- Introducer | 36027 +-- CCO PA Line | 24831 +-- | 22369 +-- Trauma Line | 15530 +-- Portacath | 12927 +-- Ventriculostomy | 10295 +-- Pre-Sep Catheter | 9678 +-- IABP | 8819 +-- Other/Remarks | 8725 +-- Midline | 5067 +-- Venous Access | 4278 +-- Hickman | 3783 +-- PacerIntroducer | 2663 +-- TripleIntroducer | 2262 +-- RIC | 1625 +-- PermaCath | 1066 +-- Camino Bolt | 913 +-- Lumbar Drain | 361 +-- (23 rows) +, cv as +( + select distinct icustay_id, charttime + from cv_grp + where (inv1_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv2_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv3_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv4_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv5_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv6_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv7_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) + OR (inv8_type in ('Multi-lumen', 'PICC line', 'Dialysis Line', 'Introducer','Trauma Line', 'Portacath', 'Venous Access', 'Hickman', 'PacerIntroducer', 'TripleIntroducer')) +) +-- transform carevue data into durations +, cv0 as +( + select + icustay_id + -- this carries over the previous charttime + , LAG(CHARTTIME, 1) OVER (partition by icustay_id order by charttime) as charttime_lag + , charttime + from cv +) +, cv1 as +( + select + icustay_id + , charttime + , charttime_lag + -- if the current observation indicates a line is present + -- calculate the time since the last charted line + , charttime - charttime_lag as central_line_duration + -- now we determine if the current line is "new" + -- new == no documentation for 16 hours + , case + when DATETIME_DIFF(charttime, charttime_lag, 'HOUR') > 16 + then 1 + else 0 + end as central_line_new + FROM cv0 +) +, cv2 as +( + select cv1.* + -- create a cumulative sum of the instances of new events + -- this results in a monotonic integer assigned to each new instance of a line + , SUM( central_line_new ) + OVER ( partition by icustay_id order by charttime ) + as central_line_rownum + from cv1 +) +-- create the durations for each line +, cv_dur as +( + select icustay_id + , central_line_rownum + , min(charttime) as starttime + , max(charttime) as endtime + , DATETIME_DIFF(max(charttime), min(charttime), 'HOUR') AS duration_hours + from cv2 + group by icustay_id, central_line_rownum + having min(charttime) != max(charttime) +) +select icustay_id + -- , central_line_rownum + , starttime, endtime, duration_hours +from cv_dur +UNION ALL +--TODO: collapse metavision durations if they overlap +select icustay_id + -- , ROW_NUMBER() over (PARTITION BY icustay_id ORDER BY starttime) as central_line_rownum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours +from mv +where central_line = 1 +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/crrt_durations.sql b/mimic-iii/concepts_postgres/durations/crrt_durations.sql new file mode 100644 index 000000000..11f719594 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/crrt_durations.sql @@ -0,0 +1,197 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS crrt_durations; CREATE TABLE crrt_durations AS +with crrt_settings as +( + select ce.icustay_id, ce.charttime + , max( + case + when ce.itemid in + ( + 224149, -- Access Pressure + 224144, -- Blood Flow (ml/min) + 228004, -- Citrate (ACD-A) + 225183, -- Current Goal + 225977, -- Dialysate Fluid + 224154, -- Dialysate Rate + 224151, -- Effluent Pressure + 224150, -- Filter Pressure + 225958, -- Heparin Concentration (units/mL) + 224145, -- Heparin Dose (per hour) + 224191, -- Hourly Patient Fluid Removal + 228005, -- PBP (Prefilter) Replacement Rate + 228006, -- Post Filter Replacement Rate + 225976, -- Replacement Fluid + 224153, -- Replacement Rate + 224152, -- Return Pressure + 226457 -- Ultrafiltrate Output + ) then 1 + when ce.itemid in + ( + 29, -- Access mmHg + 173, -- Effluent Press mmHg + 192, -- Filter Pressure mmHg + 624, -- Return Pressure mmHg + 79, -- Blood Flow ml/min + 142, -- Current Goal + 146, -- Dialysate Flow ml/hr + 611, -- Replace Rate ml/hr + 5683 -- Hourly PFR + ) then 1 + when ce.itemid = 665 and value in ('Active','Clot Increasing','Clots Present','No Clot Present') + then 1 + when ce.itemid = 147 and value = 'Yes' + then 1 + else 0 end) + as RRT + -- Below indicates that a new instance of CRRT has started + , max( + case + -- System Integrity + when ce.itemid = 224146 and value in ('New Filter','Reinitiated') + then 1 + when ce.itemid = 665 and value in ('Initiated') + then 1 + else 0 + end ) as RRT_start + -- Below indicates that the current instance of CRRT has ended + , max( + case + -- System Integrity + when ce.itemid = 224146 and value in ('Discontinued','Recirculating') + then 1 + -- the only value like DC is "DC'D", use like to avoid apostrophe + when ce.itemid = 665 and (value = 'Clotted' OR value LIKE 'DC%') + then 1 + -- Reason for CRRT filter change + when ce.itemid = 225956 + then 1 + else 0 + end ) as RRT_end + FROM chartevents ce + where ce.itemid in + ( + -- MetaVision ITEMIDs + -- Below require special handling + 224146, -- System Integrity + 225956, -- Reason for CRRT Filter Change + -- Below are settings which indicate CRRT is started/continuing + 224149, -- Access Pressure + 224144, -- Blood Flow (ml/min) + 228004, -- Citrate (ACD-A) + 225183, -- Current Goal + 225977, -- Dialysate Fluid + 224154, -- Dialysate Rate + 224151, -- Effluent Pressure + 224150, -- Filter Pressure + 225958, -- Heparin Concentration (units/mL) + 224145, -- Heparin Dose (per hour) + 224191, -- Hourly Patient Fluid Removal + 228005, -- PBP (Prefilter) Replacement Rate + 228006, -- Post Filter Replacement Rate + 225976, -- Replacement Fluid + 224153, -- Replacement Rate + 224152, -- Return Pressure + 226457, -- Ultrafiltrate Output + -- CareVue ITEMIDs + -- Below require special handling + 665, -- System integrity + 147, -- Dialysate Infusing + 612, -- Replace.Fluid Infuse + -- Below are settings which indicate CRRT is started/continuing + 29, -- Access mmHg + 173, -- Effluent Press mmHg + 192, -- Filter Pressure mmHg + 624, -- Return Pressure mmHg + 142, -- Current Goal + 79, -- Blood Flow ml/min + 146, -- Dialysate Flow ml/hr + 611, -- Replace Rate ml/hr + 5683 -- Hourly PFR + ) + and ce.value is not null + and coalesce(ce.valuenum,1) != 0 -- non-zero rates/values + group by icustay_id, charttime +) +-- create various lagged variables for future query +, vd_lag AS +( + select + icustay_id + -- this carries over the previous charttime + , LAG(CHARTTIME, 1) OVER W AS charttime_prev_row + , charttime + , RRT + , RRT_start + , RRT_end + , LAG(RRT_end, 1) OVER W AS rrt_ended_prev_row + FROM crrt_settings + WINDOW w AS + ( + partition by icustay_id, case when RRT=1 or RRT_end=1 then 1 else 0 end + order by charttime + ) +) +, vd1 as +( + select + icustay_id + , charttime + , RRT + , RRT_start + , RRT_end + + -- now we determine if the current event is a new instantiation + , case + when RRT_start = 1 + then 1 + -- if there is an end flag, we mark any subsequent event as new + when RRT_end = 1 + -- note the end is *not* a new event, the *subsequent* row is + -- so here we output 0 + then 0 + when rrt_ended_prev_row = 1 + then 1 + -- if there is less than 2 hours between CRRT settings, we do not treat this as a new CRRT event + when DATETIME_DIFF(charttime, charttime_prev_row, 'HOUR') <= 2 + then 0 + else 1 + end as NewCRRT + -- use the temp table with only settings FROM chartevents + FROM vd_lag +) +, vd2 as +( + select vd1.* + -- create a cumulative sum of the instances of new CRRT + -- this results in a monotonically increasing integer assigned to each CRRT + , case when RRT_start = 1 or RRT=1 or RRT_end = 1 then + SUM( NewCRRT ) + OVER ( partition by icustay_id order by charttime ) + else null end + as num + --- now we convert CHARTTIME of CRRT settings into durations + from vd1 + -- now we can isolate to just rows with settings + -- (before we had rows with start/end flags) + -- this removes any null values for NewCRRT + where + RRT_start = 1 or RRT = 1 or RRT_end = 1 +) +-- create the durations for each CRRT instance +, fin as +( +select icustay_id + , num + , min(charttime) as starttime + , max(charttime) as endtime + , DATETIME_DIFF(max(charttime), min(charttime), 'HOUR') AS duration_hours + -- add durations +from vd2 +group by icustay_id, num +having min(charttime) != max(charttime) +) +select icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as num + , starttime, endtime, duration_hours +from fin +order by icustay_id, num diff --git a/mimic-iii/concepts_postgres/durations/dobutamine_dose.sql b/mimic-iii/concepts_postgres/durations/dobutamine_dose.sql new file mode 100644 index 000000000..bfd3f703e --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/dobutamine_dose.sql @@ -0,0 +1,259 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS dobutamine_dose; CREATE TABLE dobutamine_dose AS +-- This query extracts dose+durations of dopamine administration + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30042,30306) then 1 else 0 end) as vaso -- dobutamine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30042,30306) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30042,30306) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30042,30306) then rate else null end) as vaso_rate + , max(case when itemid in (30042,30306) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in (30042,30306) -- dobutamine + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , rate as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 221653 -- dobutamine + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/dobutamine_durations.sql b/mimic-iii/concepts_postgres/durations/dobutamine_durations.sql new file mode 100644 index 000000000..3d41c38d6 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/dobutamine_durations.sql @@ -0,0 +1,228 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS dobutamine_durations; CREATE TABLE dobutamine_durations AS +-- This query extracts durations of dobutamine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30042,30306) then 1 else 0 end) as vaso -- dobutamine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30042,30306) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30042,30306) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30042,30306) then rate else null end) as vaso_rate + , max(case when itemid in (30042,30306) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in (30042,30306) -- dobutamine + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221653 -- dobutamine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/dopamine_dose.sql b/mimic-iii/concepts_postgres/durations/dopamine_dose.sql new file mode 100644 index 000000000..5b25e6a13 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/dopamine_dose.sql @@ -0,0 +1,262 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS dopamine_dose; CREATE TABLE dopamine_dose AS +-- This query extracts dose+durations of dopamine administration + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30043,30307) then 1 else 0 end) as vaso -- dopamine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30043,30307) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30043,30307) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30043,30307) then rate else null end) as vaso_rate + , max(case when itemid in (30043,30307) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in + ( + 30043,30307 -- dopamine + ) + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , rate as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 221662 -- dopamine + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/dopamine_durations.sql b/mimic-iii/concepts_postgres/durations/dopamine_durations.sql new file mode 100644 index 000000000..55898a647 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/dopamine_durations.sql @@ -0,0 +1,231 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS dopamine_durations; CREATE TABLE dopamine_durations AS +-- This query extracts durations of dopamine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30043,30307) then 1 else 0 end) as vaso -- dopamine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30043,30307) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30043,30307) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30043,30307) then rate else null end) as vaso_rate + , max(case when itemid in (30043,30307) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in + ( + 30043,30307 -- dopamine + ) + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221662 -- dopamine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/epinephrine_dose.sql b/mimic-iii/concepts_postgres/durations/epinephrine_dose.sql new file mode 100644 index 000000000..29b556d97 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/epinephrine_dose.sql @@ -0,0 +1,273 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS epinephrine_dose; CREATE TABLE epinephrine_dose AS +-- This query extracts dose+durations of epinephrine administration + +-- Requires the weightfirstday table + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + cv.icustay_id, cv.charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30044,30119,30309) then 1 else 0 end) as vaso -- epinephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30044,30119,30309) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30044,30119,30309) and rate is not null then 1 else 0 end) as vaso_null + , max(case + when itemid = 30044 and wd.weight is null then rate / 80.0 -- super rare to be missing weight... affects 2 patients for 14 rows + when itemid = 30044 then rate / wd.weight -- measured in mcgmin + when itemid in (30119,30309) then rate -- measured in mcgkgmin + else null + end) as vaso_rate + , max(case when itemid in (30044,30119,30309) then amount else null end) as vaso_amount + + FROM inputevents_cv cv + left join weight_durations wd + on cv.icustay_id = wd.icustay_id + and cv.charttime between wd.starttime and wd.endtime + where itemid in + ( + 30044,30119,30309 -- epinephrine + ) + and cv.icustay_id is not null + group by cv.icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , rate as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 221289 -- epinephrine + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/epinephrine_durations.sql b/mimic-iii/concepts_postgres/durations/epinephrine_durations.sql new file mode 100644 index 000000000..eab85e24a --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/epinephrine_durations.sql @@ -0,0 +1,231 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS epinephrine_durations; CREATE TABLE epinephrine_durations AS +-- This query extracts durations of epinephrine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30044,30119,30309) then 1 else 0 end) as vaso -- epinephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30044,30119,30309) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30044,30119,30309) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30044,30119,30309) then rate else null end) as vaso_rate + , max(case when itemid in (30044,30119,30309) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in + ( + 30044,30119,30309 -- epinephrine + ) + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221289 -- epinephrine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/isuprel_durations.sql b/mimic-iii/concepts_postgres/durations/isuprel_durations.sql new file mode 100644 index 000000000..c780f4c17 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/isuprel_durations.sql @@ -0,0 +1,228 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS isuprel_durations; CREATE TABLE isuprel_durations AS +-- This query extracts durations of isuprel administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 30046 then 1 else 0 end) as vaso -- Isuprel + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid = 30046 and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid = 30046 and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid = 30046 then rate else null end) as vaso_rate + , max(case when itemid = 30046 then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid = 30046 -- Isuprel + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 227692 -- Isuprel + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/milrinone_durations.sql b/mimic-iii/concepts_postgres/durations/milrinone_durations.sql new file mode 100644 index 000000000..0f125fdd2 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/milrinone_durations.sql @@ -0,0 +1,228 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS milrinone_durations; CREATE TABLE milrinone_durations AS +-- This query extracts durations of milrinone administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 30125 then 1 else 0 end) as vaso -- milrinone + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid = 30125 and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid = 30125 and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid = 30125 then rate else null end) as vaso_rate + , max(case when itemid = 30125 then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid = 30125 -- milrinone + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221986 -- milrinone + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/neuroblock_dose.sql b/mimic-iii/concepts_postgres/durations/neuroblock_dose.sql new file mode 100644 index 000000000..20836f333 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/neuroblock_dose.sql @@ -0,0 +1,318 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS neuroblock_dose; CREATE TABLE neuroblock_dose AS +-- This query extracts dose+durations of neuromuscular blocking agents +-- Note: we assume that injections will be filtered for carevue as they will have starttime = stopttime. + +-- Get drug administration data from CareVue and MetaVision +-- metavision is simple and only requires one temporary table +with drugmv as +( + select + icustay_id, orderid + , rate as drug_rate + , amount as drug_amount + , starttime + , endtime + from inputevents_mv + where itemid in + ( + 222062 -- Vecuronium (664 rows, 154 infusion rows) + , 221555 -- Cisatracurium (9334 rows, 8970 infusion rows) + ) + and statusdescription != 'Rewritten' -- only valid orders + and rate is not null -- only continuous infusions +) +, drugcv1 as +( + select + icustay_id, charttime + -- where clause below ensures all rows are instance of the drug + , 1 as drug + + -- the 'stopped' column indicates if a drug has been disconnected + , max(case when stopped in ('Stopped','D/C''d') then 1 else 0 end) as drug_stopped + + -- we only include continuous infusions, therefore expect a rate + , max(case + -- for "free form" entries (itemid >= 40000) rate is not available + when itemid >= 40000 and amount is not null then 1 + when itemid < 40000 and rate is not null then 1 + else 0 end) as drug_null + , max(case + -- for "free form" entries (itemid >= 40000) rate is not available + when itemid >= 40000 then coalesce(rate, amount) + else rate end) as drug_rate + , max(amount) as drug_amount + from inputevents_cv + where itemid in + ( + 30114 -- Cisatracurium (63994 rows) + , 30138 -- Vecuronium (5160 rows) + , 30113 -- Atracurium (1163 rows) + -- Below rows are less frequent ad-hoc documentation, but worth including! + , 42174 -- nimbex cc/hr (207 rows) + , 42385 -- Cisatracurium gtt (156 rows) + , 41916 -- NIMBEX inputevents_cv (136 rows) + , 42100 -- cistatracurium (132 rows) + , 42045 -- nimbex mcg/kg/min (78 rows) + , 42246 -- CISATRICARIUM CC/HR (70 rows) + , 42291 -- NIMBEX CC/HR (48 rows) + , 42590 -- nimbex inputevents_cv (38 rows) + , 42284 -- CISATRACURIUM DRIP (9 rows) + , 45096 -- Vecuronium drip (2 rows) + ) + group by icustay_id, charttime + UNION + -- add data from chartevents + select + icustay_id, charttime + -- where clause below ensures all rows are instance of the drug + , 1 as drug + + -- the 'stopped' column indicates if a drug has been disconnected + , max(case when stopped in ('Stopped','D/C''d') then 1 else 0 end) as drug_stopped + , max(case when valuenum <= 10 then 0 else 1 end) as drug_null + + -- educated guess! + , max(case when valuenum <= 10 then valuenum else null end) as drug_rate + , max(case when valuenum > 10 then valuenum else null end) as drug_amount + from chartevents + where itemid in + ( + 1856 -- Vecuronium mcg/min (8 rows) + , 2164 -- NIMBEX MG/KG/HR (243 rows) + , 2548 -- nimbex mg/kg/hr (103 rows) + , 2285 -- nimbex mcg/kg/min (85 rows) + , 2290 -- nimbex mcg/kg/m (32 rows) + , 2670 -- nimbex (38 rows) + , 2546 -- CISATRACURIUMMG/KG/H (7 rows) + , 1098 -- cisatracurium mg/kg (36 rows) + , 2390 -- cisatracurium mg/hr (15 rows) + , 2511 -- CISATRACURIUM GTT (4 rows) + , 1028 -- Cisatracurium (208 rows) + , 1858 -- cisatracurium (351 rows) + ) + group by icustay_id, charttime + +) +, drugcv2 as +( + select v.* + , sum(drug_null) over (partition by icustay_id order by charttime) as drug_partition + from + drugcv1 v +) +, drugcv3 as +( + select v.* + , first_value(drug_rate) over (partition by icustay_id, drug_partition order by charttime) as drug_prevrate_ifnull + from + drugcv2 v +) +, drugcv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, drug order by charttime))) AS delta + + , drug + , drug_rate + , drug_amount + , drug_stopped + , drug_prevrate_ifnull + + -- We define start time here + , case + when drug = 0 then null + + -- if this is the first instance of the drug + when drug_rate > 0 and + LAG(drug_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, drug, drug_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes drugnum sequential + when drug_rate = 0 and + LAG(drug_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, drug + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- drug_prevrate_ifnull is equal to the previous value *iff* the current value is null + when drug_prevrate_ifnull = 0 and + LAG(drug_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, drug + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newdrug = 1 + when LAG(drug_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, drug + order by charttime + ) = 0 + then 1 + + -- If the last recorded drug was D/C'd, newdrug = 1 + when + LAG(drug_stopped,1) + OVER + ( + partition by icustay_id, drug + order by charttime + ) + = 1 then 1 + + when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, drug order by charttime))) > (interval '8 hours') then 1 + else null + end as drug_start + +FROM + drugcv3 +) +-- propagate start/stop flags forward in time +, drugcv5 as +( + select v.* + , SUM(drug_start) OVER (partition by icustay_id, drug order by charttime) as drug_first +FROM + drugcv4 v +) +, drugcv6 as +( + select v.* + -- We define end time here + , case + when drug = 0 + then null + + -- If the recorded drug was D/C'd, this is an end time + when drug_stopped = 1 + then drug_first + + -- If the rate is zero, this is the end time + when drug_rate = 0 + then drug_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on drug + -- in principle, this could add an extra end time for the drug + -- however, since we later group on drug_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, drug + order by charttime + ) is null + then drug_first + + else null + end as drug_stop + from drugcv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, drug, drug_rate, drug_amount +-- , drug_stopped +-- , drug_start +-- , drug_first +-- , drug_stop +-- from drugcv6 order by icustay_id, charttime; + +, drugcv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, drug_first order by charttime) as endtime + , drug, drug_rate, drug_amount, drug_stop, drug_start, drug_first +from drugcv6 +where + drug_first is not null -- bogus data +and + drug_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, drugcv8 as +( + select + icustay_id + , starttime, endtime + , drug, drug_rate, drug_amount, drug_stop, drug_start, drug_first + from drugcv7 + where endtime is not null + and drug_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, drugcv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(drug_rate) OVER (partition by icustay_id order by starttime, endtime) = drug_rate + THEN 0 + else 1 + end as drug_groups + , drug, drug_rate, drug_amount, drug_stop, drug_start, drug_first + from drugcv8 + where endtime is not null + and drug_rate > 0 + and starttime != endtime +) +, drugcv10 as +( + select + icustay_id + , starttime, endtime + , drug_groups + , SUM(drug_groups) OVER (partition by icustay_id order by starttime, endtime) as drug_groups_sum + , drug, drug_rate, drug_amount, drug_stop, drug_start, drug_first + from drugcv9 +) +, drugcv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , drug_groups_sum + , drug_rate + , sum(drug_amount) as drug_amount + from drugcv10 + group by icustay_id, drug_groups_sum, drug_rate +) +-- now assign this data to every hour of the patient's stay +-- drug_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , drug_rate, drug_amount +from drugcv +UNION +SELECT icustay_id + , starttime, endtime + , drug_rate, drug_amount +from drugmv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/norepinephrine_dose.sql b/mimic-iii/concepts_postgres/durations/norepinephrine_dose.sql new file mode 100644 index 000000000..9f2f97bf3 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/norepinephrine_dose.sql @@ -0,0 +1,270 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS norepinephrine_dose; CREATE TABLE norepinephrine_dose AS +-- This query extracts dose+durations of norepinephrine administration +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + cv.icustay_id, cv.charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30047,30120) then 1 else 0 end) as vaso -- norepinephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30047,30120) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + -- case statement determining whether the ITEMID is an instance of vasopressor usage + + , max(case when itemid in (30047,30120) and rate is not null then 1 else 0 end) as vaso_null + , max(case + when itemid = 30047 and wd.weight is null then rate / 80.0 -- this is rare, only affects a total of ~400 rows + when itemid = 30047 then rate / wd.weight -- measured in mcgmin + when itemid = 30120 then rate -- measured in mcgkgmin ** there are clear errors, perhaps actually mcgmin + else null end) as vaso_rate + , max(case when itemid in (30047,30120) then amount else null end) as vaso_amount + + FROM inputevents_cv cv + left join weight_durations wd + on cv.icustay_id = wd.icustay_id + and cv.charttime between wd.starttime and wd.endtime + where itemid in (30047,30120) -- norepinephrine + and cv.icustay_id is not null + group by cv.icustay_id, cv.charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , rate as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 221906 -- norepinephrine + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/norepinephrine_durations.sql b/mimic-iii/concepts_postgres/durations/norepinephrine_durations.sql new file mode 100644 index 000000000..776af1904 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/norepinephrine_durations.sql @@ -0,0 +1,227 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS norepinephrine_durations; CREATE TABLE norepinephrine_durations AS +-- This query extracts durations of norepinephrine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30047,30120) then 1 else 0 end) as vaso -- norepinephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30047,30120) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30047,30120) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30047,30120) then rate else null end) as vaso_rate + , max(case when itemid in (30047,30120) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in (30047,30120) -- norepinephrine + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the carevue data before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221906 -- norepinephrine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/phenylephrine_dose.sql b/mimic-iii/concepts_postgres/durations/phenylephrine_dose.sql new file mode 100644 index 000000000..eaea5c740 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/phenylephrine_dose.sql @@ -0,0 +1,259 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS phenylephrine_dose; CREATE TABLE phenylephrine_dose AS +-- This query extracts dose+durations of phenylephrine administration + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30127,30128) then 1 else 0 end) as vaso -- phenylephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30127,30128) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30127,30128) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30127,30128) then rate else null end) as vaso_rate + , max(case when itemid in (30127,30128) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in (30127,30128) -- phenylephrine + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , rate as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 221749 -- phenylephrine + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/phenylephrine_durations.sql b/mimic-iii/concepts_postgres/durations/phenylephrine_durations.sql new file mode 100644 index 000000000..0b1e31f93 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/phenylephrine_durations.sql @@ -0,0 +1,231 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS phenylephrine_durations; CREATE TABLE phenylephrine_durations AS +-- This query extracts durations of phenylephrine administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid in (30127,30128) then 1 else 0 end) as vaso -- phenylephrine + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid in (30127,30128) and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid in (30127,30128) and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid in (30127,30128) then rate else null end) as vaso_rate + , max(case when itemid in (30127,30128) then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid in + ( + 30127,30128 -- phenylephrine + ) + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 221749 -- phenylephrine + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/vasopressin_dose.sql b/mimic-iii/concepts_postgres/durations/vasopressin_dose.sql new file mode 100644 index 000000000..6a0bb5b75 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/vasopressin_dose.sql @@ -0,0 +1,259 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS vasopressin_dose; CREATE TABLE vasopressin_dose AS +-- This query extracts dose+durations of vasopressin administration + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 30051 then 1 else 0 end) as vaso -- vasopressin + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid = 30051 and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid = 30051 and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid = 30051 then rate else null end) as vaso_rate + , max(case when itemid = 30051 then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid = 30051 -- vasopressin + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , vaso_stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by icustay_id, charttime; + +, vasocv7 as +( +select + icustay_id + , charttime as starttime + , lead(charttime) OVER (partition by icustay_id, vaso_first order by charttime) as endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +) +-- table of start/stop times for event +, vasocv8 as +( + select + icustay_id + , starttime, endtime + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv7 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +-- collapse these start/stop times down if the rate doesn't change +, vasocv9 as +( + select + icustay_id + , starttime, endtime + , case + when LAG(endtime) OVER (partition by icustay_id order by starttime, endtime) = starttime + AND LAG(vaso_rate) OVER (partition by icustay_id order by starttime, endtime) = vaso_rate + THEN 0 + else 1 + end as vaso_groups + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv8 + where endtime is not null + and vaso_rate > 0 + and starttime != endtime +) +, vasocv10 as +( + select + icustay_id + , starttime, endtime + , vaso_groups + , SUM(vaso_groups) OVER (partition by icustay_id order by starttime, endtime) as vaso_groups_sum + , vaso, vaso_rate, vaso_amount, vaso_stop, vaso_start, vaso_first + from vasocv9 +) +, vasocv as +( + select icustay_id + , min(starttime) as starttime + , max(endtime) as endtime + , vaso_groups_sum + , vaso_rate + , sum(vaso_amount) as vaso_amount + from vasocv10 + group by icustay_id, vaso_groups_sum, vaso_rate +) +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , CASE WHEN rateuom = 'units/min' THEN rate*60.0 ELSE rate END as vaso_rate + , amount as vaso_amount + , starttime + , endtime + from inputevents_mv + where itemid = 222315 -- vasopressin + and statusdescription != 'Rewritten' -- only valid orders +) +-- now assign this data to every hour of the patient's stay +-- vaso_amount for carevue is not accurate +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasocv +UNION ALL +SELECT icustay_id + , starttime, endtime + , vaso_rate, vaso_amount +from vasomv +order by icustay_id, starttime; diff --git a/mimic-iii/concepts_postgres/durations/vasopressin_durations.sql b/mimic-iii/concepts_postgres/durations/vasopressin_durations.sql new file mode 100644 index 000000000..0d8766024 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/vasopressin_durations.sql @@ -0,0 +1,228 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS vasopressin_durations; CREATE TABLE vasopressin_durations AS +-- This query extracts durations of vasopressin administration +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table by grouping using ICUSTAY_ID + +-- Get drug administration data from CareVue first +with vasocv1 as +( + select + icustay_id, charttime + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 30051 then 1 else 0 end) as vaso -- vasopressin + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when itemid = 30051 and (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when itemid = 30051 and rate is not null then 1 else 0 end) as vaso_null + , max(case when itemid = 30051 then rate else null end) as vaso_rate + , max(case when itemid = 30051 then amount else null end) as vaso_amount + + FROM inputevents_cv + where itemid = 30051 -- vasopressin + group by icustay_id, charttime +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) + +-- now we extract the associated data for metavision patients +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + FROM inputevents_mv + where itemid = 222315 -- vasopressin + and statusdescription != 'Rewritten' -- only valid orders + group by icustay_id, linkorderid +) + +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/vasopressor_durations.sql b/mimic-iii/concepts_postgres/durations/vasopressor_durations.sql new file mode 100644 index 000000000..06dbcba78 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/vasopressor_durations.sql @@ -0,0 +1,316 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS vasopressor_durations; CREATE TABLE vasopressor_durations AS +-- This query extracts durations of vasopressor administration +-- It groups together any administration of the below list of drugs: +-- norepinephrine - 30047,30120,221906 +-- epinephrine - 30044,30119,30309,221289 +-- phenylephrine - 30127,30128,221749 +-- vasopressin - 30051,222315 (42273, 42802 also for 2 patients) +-- dopamine - 30043,30307,221662 +-- dobutamine - 30042,30306,221653 +-- milrinone - 30125,221986 + +-- Consecutive administrations are numbered 1, 2, ... +-- Total time on the drug can be calculated from this table +-- by grouping using ICUSTAY_ID + +-- select only the ITEMIDs from the inputevents_cv table related to vasopressors +with io_cv as +( + select + icustay_id, charttime, itemid, stopped + -- ITEMIDs (42273, 42802) accidentally store rate in amount column + , case + when itemid in (42273, 42802) + then amount + else rate + end as rate + , case + when itemid in (42273, 42802) + then rate + else amount + end as amount + FROM inputevents_cv + where itemid in + ( + 30047,30120,30044,30119,30309,30127 + , 30128,30051,30043,30307,30042,30306,30125 + , 42273, 42802 + ) +) +-- select only the ITEMIDs from the inputevents_mv table related to vasopressors +, io_mv as +( + select + icustay_id, linkorderid, starttime, endtime + FROM inputevents_mv io + -- Subselect the vasopressor ITEMIDs + where itemid in + ( + 221906,221289,221749,222315,221662,221653,221986 + ) + and statusdescription != 'Rewritten' -- only valid orders +) +, vasocv1 as +( + select + icustay_id, charttime, itemid + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , 1 as vaso + + -- the 'stopped' column indicates if a vasopressor has been disconnected + , max(case when (stopped = 'Stopped' OR stopped like 'D/C%') then 1 + else 0 end) as vaso_stopped + + , max(case when rate is not null then 1 else 0 end) as vaso_null + , max(rate) as vaso_rate + , max(amount) as vaso_amount + + from io_cv + group by icustay_id, charttime, itemid +) +, vasocv2 as +( + select v.* + , sum(vaso_null) over (partition by icustay_id, itemid order by charttime) as vaso_partition + from + vasocv1 v +) +, vasocv3 as +( + select v.* + , first_value(vaso_rate) over (partition by icustay_id, itemid, vaso_partition order by charttime) as vaso_prevrate_ifnull + from + vasocv2 v +) +, vasocv4 as +( +select + icustay_id + , charttime + , itemid + -- , (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) AS delta + + , vaso + , vaso_rate + , vaso_amount + , vaso_stopped + , vaso_prevrate_ifnull + + -- We define start time here + , case + when vaso = 0 then null + + -- if this is the first instance of the vasoactive drug + when vaso_rate > 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, itemid, vaso, vaso_null + order by charttime + ) + is null + then 1 + + -- you often get a string of 0s + -- we decide not to set these as 1, just because it makes vasonum sequential + when vaso_rate = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, itemid, vaso + order by charttime + ) + = 0 + then 0 + + -- sometimes you get a string of NULL, associated with 0 volumes + -- same reason as before, we decide not to set these as 1 + -- vaso_prevrate_ifnull is equal to the previous value *iff* the current value is null + when vaso_prevrate_ifnull = 0 and + LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, itemid, vaso + order by charttime + ) + = 0 + then 0 + + -- If the last recorded rate was 0, newvaso = 1 + when LAG(vaso_prevrate_ifnull,1) + OVER + ( + partition by icustay_id, itemid, vaso + order by charttime + ) = 0 + then 1 + + -- If the last recorded vaso was D/C'd, newvaso = 1 + when + LAG(vaso_stopped,1) + OVER + ( + partition by icustay_id, itemid, vaso + order by charttime + ) + = 1 then 1 + + -- ** not sure if the below is needed + --when (CHARTTIME - (LAG(CHARTTIME, 1) OVER (partition by icustay_id, vaso order by charttime))) > (interval '4 hours') then 1 + else null + end as vaso_start + +FROM + vasocv3 +) +-- propagate start/stop flags forward in time +, vasocv5 as +( + select v.* + , SUM(vaso_start) OVER (partition by icustay_id, itemid, vaso order by charttime) as vaso_first +FROM + vasocv4 v +) +, vasocv6 as +( + select v.* + -- We define end time here + , case + when vaso = 0 + then null + + -- If the recorded vaso was D/C'd, this is an end time + when vaso_stopped = 1 + then vaso_first + + -- If the rate is zero, this is the end time + when vaso_rate = 0 + then vaso_first + + -- the last row in the table is always a potential end time + -- this captures patients who die/are discharged while on vasopressors + -- in principle, this could add an extra end time for the vasopressor + -- however, since we later group on vaso_start, any extra end times are ignored + when LEAD(CHARTTIME,1) + OVER + ( + partition by icustay_id, itemid, vaso + order by charttime + ) is null + then vaso_first + + else null + end as vaso_stop + from vasocv5 v +) + +-- -- if you want to look at the results of the table before grouping: +-- select +-- icustay_id, charttime, vaso, vaso_rate, vaso_amount +-- , case when vaso_stopped = 1 then 'Y' else '' end as stopped +-- , vaso_start +-- , vaso_first +-- , vaso_stop +-- from vasocv6 order by charttime; + + +, vasocv as +( +-- below groups together vasopressor administrations into groups +select + icustay_id + , itemid + -- the first non-null rate is considered the starttime + , min(case when vaso_rate is not null then charttime else null end) as starttime + -- the *first* time the first/last flags agree is the stop time for this duration + , min(case when vaso_first = vaso_stop then charttime else null end) as endtime +from vasocv6 +where + vaso_first is not null -- bogus data +and + vaso_first != 0 -- sometimes *only* a rate of 0 appears, i.e. the drug is never actually delivered +and + icustay_id is not null -- there are data for "floating" admissions, we don't worry about these +group by icustay_id, itemid, vaso_first +having -- ensure start time is not the same as end time + min(charttime) != min(case when vaso_first = vaso_stop then charttime else null end) +and + max(vaso_rate) > 0 -- if the rate was always 0 or null, we consider it not a real drug delivery +) +-- we do not group by ITEMID in below query +-- this is because we want to collapse all vasopressors together +, vasocv_grp as +( +SELECT + s1.icustay_id, + s1.starttime, + MIN(t1.endtime) AS endtime +FROM vasocv s1 +INNER JOIN vasocv t1 + ON s1.icustay_id = t1.icustay_id + AND s1.starttime <= t1.endtime + AND NOT EXISTS(SELECT * FROM vasocv t2 + WHERE t1.icustay_id = t2.icustay_id + AND t1.endtime >= t2.starttime + AND t1.endtime < t2.endtime) +WHERE NOT EXISTS(SELECT * FROM vasocv s2 + WHERE s1.icustay_id = s2.icustay_id + AND s1.starttime > s2.starttime + AND s1.starttime <= s2.endtime) +GROUP BY s1.icustay_id, s1.starttime +ORDER BY s1.icustay_id, s1.starttime +) +-- now we extract the associated data for metavision patients +-- do not need to group by itemid because we group by linkorderid +, vasomv as +( + select + icustay_id, linkorderid + , min(starttime) as starttime, max(endtime) as endtime + from io_mv + group by icustay_id, linkorderid +) +, vasomv_grp as +( +SELECT + s1.icustay_id, + s1.starttime, + MIN(t1.endtime) AS endtime +FROM vasomv s1 +INNER JOIN vasomv t1 + ON s1.icustay_id = t1.icustay_id + AND s1.starttime <= t1.endtime + AND NOT EXISTS(SELECT * FROM vasomv t2 + WHERE t1.icustay_id = t2.icustay_id + AND t1.endtime >= t2.starttime + AND t1.endtime < t2.endtime) +WHERE NOT EXISTS(SELECT * FROM vasomv s2 + WHERE s1.icustay_id = s2.icustay_id + AND s1.starttime > s2.starttime + AND s1.starttime <= s2.endtime) +GROUP BY s1.icustay_id, s1.starttime +ORDER BY s1.icustay_id, s1.starttime +) +select + icustay_id + -- generate a sequential integer for convenience + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasocv_grp + +UNION ALL + +select + icustay_id + , ROW_NUMBER() over (partition by icustay_id order by starttime) as vasonum + , starttime, endtime + , DATETIME_DIFF(endtime, starttime, 'HOUR') AS duration_hours + -- add durations +from + vasomv_grp + +order by icustay_id, vasonum; diff --git a/mimic-iii/concepts_postgres/durations/ventilation_classification.sql b/mimic-iii/concepts_postgres/durations/ventilation_classification.sql new file mode 100644 index 000000000..c7ab3cc08 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/ventilation_classification.sql @@ -0,0 +1,142 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS ventilation_classification; CREATE TABLE ventilation_classification AS +-- Identify The presence of a mechanical ventilation using settings +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 chartevents ce +where ce.value is not null +-- exclude rows marked as error +and (ce.error != 1 or ce.error IS NULL) +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 DISTINCT +-- 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 procedureevents_mv +where itemid in +( + 227194 -- "Extubation" +, 225468 -- "Unplanned Extubation (patient-initiated)" +, 225477 -- "Unplanned Extubation (non-patient initiated)" +); diff --git a/mimic-iii/concepts_postgres/durations/ventilation_durations.sql b/mimic-iii/concepts_postgres/durations/ventilation_durations.sql new file mode 100644 index 000000000..8e5aa3bac --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/ventilation_durations.sql @@ -0,0 +1,112 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS ventilation_durations; CREATE TABLE ventilation_durations AS +-- This query extracts the duration of mechanical ventilation +-- The main goal of the query is to aggregate sequential ventilator settings +-- into single mechanical ventilation "events". The start and end time of these +-- events can then be used for various purposes: calculating the total duration +-- of mechanical ventilation, cross-checking values (e.g. PaO2:FiO2 on vent), etc + +-- The query's logic is roughly: +-- 1) The presence of a mechanical ventilation setting starts a new ventilation event +-- 2) Any instance of a setting in the next 8 hours continues the event +-- 3) Certain elements end the current ventilation event +-- a) documented extubation ends the current ventilation +-- b) initiation of non-invasive vent and/or oxygen ends the current vent + +-- See the ventilation_classification.sql query for step 1 of the above. +-- This query has the logic for converting events into durations. +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 ventilation_classification +) +, 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 + DATETIME_DIFF(CHARTTIME, charttime_lag, 'MINUTE')/60 + 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 > DATETIME_ADD(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 + , DATETIME_DIFF(max(charttime), min(charttime), 'MINUTE')/60 AS duration_hours +from vd2 +group by icustay_id, vd2.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 \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/durations/weight_durations.sql b/mimic-iii/concepts_postgres/durations/weight_durations.sql new file mode 100644 index 000000000..9a5a61f61 --- /dev/null +++ b/mimic-iii/concepts_postgres/durations/weight_durations.sql @@ -0,0 +1,207 @@ +-- 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 diff --git a/mimic-iii/concepts_postgres/echo_data.sql b/mimic-iii/concepts_postgres/echo_data.sql new file mode 100644 index 000000000..99e888ae2 --- /dev/null +++ b/mimic-iii/concepts_postgres/echo_data.sql @@ -0,0 +1,48 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS echo_data; CREATE TABLE echo_data AS +-- This code extracts structured data from echocardiographies +-- You can join it to the text notes using ROW_ID +-- Just note that ROW_ID will differ across versions of MIMIC-III. + +select ROW_ID + , subject_id, hadm_id + , chartdate + + -- charttime is always null for echoes.. + -- however, the time is available in the echo text, e.g.: + -- , substring(ne.text, 'Date/Time: [\[\]0-9*-]+ at ([0-9:]+)') as TIMESTAMP + -- we can therefore impute it and re-create charttime + , PARSE_DATETIME + ( + '%Y-%m-%d%H:%M:%S', + FORMAT_DATE('%Y-%m-%d', chartdate) + || REGEXP_EXTRACT(ne.text, 'Date/Time: .+? at ([0-9]+:[0-9]{2})') + || ':00' + ) AS charttime + + -- explanation of below substring: + -- 'Indication: ' - matched verbatim + -- (.*?) - match any character + -- \n - the end of the line + -- substring only returns the item in ()s + -- note: the '?' makes it non-greedy. if you exclude it, it matches until it reaches the *last* \n + + , REGEXP_EXTRACT(ne.text, 'Indication: (.*?)\n') as Indication + + -- sometimes numeric values contain de-id text, e.g. [** Numeric Identifier **] + -- this removes that text + , cast(REGEXP_EXTRACT(ne.text, 'Height: \\x28in\\x29 ([0-9]+)') as numeric) as Height + , cast(REGEXP_EXTRACT(ne.text, 'Weight \\x28lb\\x29: ([0-9]+)\n') as numeric) as Weight + , cast(REGEXP_EXTRACT(ne.text, 'BSA \\x28m2\\x29: ([0-9]+) m2\n') as numeric) as BSA -- ends in 'm2' + , REGEXP_EXTRACT(ne.text, 'BP \\x28mm Hg\\x29: (.+)\n') as BP -- Sys/Dias + , cast(REGEXP_EXTRACT(ne.text, 'BP \\x28mm Hg\\x29: ([0-9]+)/[0-9]+?\n') as numeric) as BPSys -- first part of fraction + , cast(REGEXP_EXTRACT(ne.text, 'BP \\x28mm Hg\\x29: [0-9]+/([0-9]+?)\n') as numeric) as BPDias -- second part of fraction + , cast(REGEXP_EXTRACT(ne.text, 'HR \\x28bpm\\x29: ([0-9]+?)\n') as numeric) as HR + + , REGEXP_EXTRACT(ne.text, 'Status: (.*?)\n') as Status + , REGEXP_EXTRACT(ne.text, 'Test: (.*?)\n') as Test + , REGEXP_EXTRACT(ne.text, 'Doppler: (.*?)\n') as Doppler + , REGEXP_EXTRACT(ne.text, 'Contrast: (.*?)\n') as Contrast + , REGEXP_EXTRACT(ne.text, 'Technical Quality: (.*?)\n') as TechnicalQuality +FROM noteevents ne +where category = 'Echo'; diff --git a/mimic-iii/concepts_postgres/firstday/blood_gas_first_day.sql b/mimic-iii/concepts_postgres/firstday/blood_gas_first_day.sql new file mode 100644 index 000000000..f56046a4d --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/blood_gas_first_day.sql @@ -0,0 +1,108 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS blood_gas_first_day; CREATE TABLE blood_gas_first_day AS +-- The aim of this query is to pivot entries related to blood gases and +-- chemistry values which were found in LABEVENTS + +-- things to check: +-- when a mixed venous/arterial blood sample are taken at the same time, is the store time different? + +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 and itemid != 50802 then null -- allow negative baseexcess + 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 icustays ie + left join labevents le + on le.subject_id = ie.subject_id and le.hadm_id = ie.hadm_id + and le.charttime between (DATETIME_SUB(ie.intime, INTERVAL '6' HOUR)) and (DATETIME_ADD(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; diff --git a/mimic-iii/concepts_postgres/firstday/blood_gas_first_day_arterial.sql b/mimic-iii/concepts_postgres/firstday/blood_gas_first_day_arterial.sql new file mode 100644 index 000000000..d2e750438 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/blood_gas_first_day_arterial.sql @@ -0,0 +1,156 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS blood_gas_first_day_arterial; CREATE TABLE blood_gas_first_day_arterial 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 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 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 NULL OR error = 0) + 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 blood_gas_first_day 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 >= DATETIME_SUB(bg.charttime, INTERVAL '2' HOUR) + and s1.charttime <= 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 DATETIME_SUB(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; diff --git a/mimic-iii/concepts_postgres/firstday/first_day_sofa.sql b/mimic-iii/concepts_postgres/firstday/first_day_sofa.sql new file mode 100644 index 000000000..037c683f4 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/first_day_sofa.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS first_day_sofa; CREATE TABLE first_day_sofa AS diff --git a/mimic-iii/concepts_postgres/firstday/gcs_first_day.sql b/mimic-iii/concepts_postgres/firstday/gcs_first_day.sql new file mode 100644 index 000000000..66546170c --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/gcs_first_day.sql @@ -0,0 +1,143 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS gcs_first_day; CREATE TABLE gcs_first_day AS +-- ITEMIDs used: + +-- CAREVUE +-- 723 as GCSVerbal +-- 454 as GCSMotor +-- 184 as GCSEyes + +-- METAVISION +-- 223900 GCS - Verbal Response +-- 223901 GCS - Motor Response +-- 220739 GCS - Eye Opening + +-- The code combines the ITEMIDs into the carevue itemids, then pivots those +-- So 223900 is changed to 723, then the ITEMID 723 is pivoted to form GCSVerbal + +-- Note: +-- The GCS for sedated patients is defaulted to 15 in this code. +-- This is in line with how the data is meant to be collected. +-- e.g., from the SAPS II publication: +-- For sedated patients, the Glasgow Coma Score before sedation was used. +-- This was ascertained either from interviewing the physician who ordered the sedation, +-- or by reviewing the patient's medical record. + +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 chartevents l + + -- get intime for charttime subselection + inner join 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 DATETIME_ADD(b.intime, INTERVAL '1' DAY) + -- exclude rows marked as error + AND (l.error IS NULL OR l.error = 0) + ) 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 > DATETIME_SUB(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 () + OVER (PARTITION BY gcs.ICUSTAY_ID + ORDER BY gcs.GCS + ) as IsMinGCS + from gcs +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +-- The minimum GCS is determined by the above row partition, we only join if IsMinGCS=1 +, GCS as mingcs +, coalesce(GCSMotor,GCSMotorPrev) as gcsmotor +, coalesce(GCSVerbal,GCSVerbalPrev) as gcsverbal +, coalesce(GCSEyes,GCSEyesPrev) as gcseyes +, EndoTrachFlag as endotrachflag + +-- subselect down to the cohort of eligible patients +FROM icustays ie +left join gcs_final gs + on ie.icustay_id = gs.icustay_id and gs.IsMinGCS = 1 +ORDER BY ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/firstday/height_first_day.sql b/mimic-iii/concepts_postgres/firstday/height_first_day.sql new file mode 100644 index 000000000..d848a3430 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/height_first_day.sql @@ -0,0 +1,76 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS height_first_day; CREATE TABLE height_first_day AS +-- This query extracts heights for adult ICU patients. +-- It uses all information from the patient's first ICU day. +-- This is done for consistency with other queries - it's not necessarily needed. +-- Height is unlikely to change throughout a patient's stay. + +-- ** Requires the echodata view, generated by concepts/echo-data.sql + +-- staging table to ensure all heights are in centimeters +with ce0 as +( + SELECT + c.icustay_id + , case + -- convert inches to centimetres + when itemid in (920, 1394, 4187, 3486) + then valuenum * 2.54 + else valuenum + end as Height + FROM chartevents c + inner join icustays ie + on c.icustay_id = ie.icustay_id + and c.charttime <= DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and c.charttime > DATETIME_SUB(ie.intime, INTERVAL '1' DAY) -- some fuzziness for admit time + WHERE c.valuenum IS NOT NULL + AND c.itemid in (226730,920, 1394, 4187, 3486,3485,4188) -- height + AND c.valuenum != 0 + -- exclude rows marked as error + AND (c.error IS NULL OR c.error = 0) +) +, ce as +( + SELECT + icustay_id + -- extract the median height from the chart to add robustness against outliers + , AVG(height) as Height_chart + from ce0 + where height > 100 + group by icustay_id +) +-- requires the echo-data.sql query to run +-- this adds heights from the free-text echo notes +, echo as +( + select + ec.subject_id + -- all echo heights are in inches + , 2.54*AVG(height) as Height_Echo + from echo_data ec + inner join icustays ie + on ec.subject_id = ie.subject_id + and ec.charttime < DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where height is not null + and height*2.54 > 100 + group by ec.subject_id +) +select + ie.icustay_id + , coalesce(ce.Height_chart, ec.Height_Echo) as height + + -- components + , ce.height_chart + , ec.height_echo +FROM icustays ie + +-- filter to only adults +inner join patients pat + on ie.subject_id = pat.subject_id + and ie.intime > DATETIME_ADD(pat.dob, INTERVAL '1' YEAR) + +left join ce + on ie.icustay_id = ce.icustay_id + +left join echo ec + on ie.subject_id = ec.subject_id; diff --git a/mimic-iii/concepts_postgres/firstday/labs_first_day.sql b/mimic-iii/concepts_postgres/firstday/labs_first_day.sql new file mode 100644 index 000000000..eaa4f0b48 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/labs_first_day.sql @@ -0,0 +1,155 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS labs_first_day; CREATE TABLE labs_first_day AS +-- This query pivots lab values taken in the first 24 hours of a patient's stay + +-- Have already confirmed that the unit of measurement is always the same: null or the correct unit + +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 icustays ie + + LEFT JOIN labevents le + ON le.subject_id = ie.subject_id AND le.hadm_id = ie.hadm_id + AND le.charttime BETWEEN (DATETIME_SUB(ie.intime, INTERVAL '6' HOUR)) AND (DATETIME_ADD(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; diff --git a/mimic-iii/concepts_postgres/firstday/rrt_first_day.sql b/mimic-iii/concepts_postgres/firstday/rrt_first_day.sql new file mode 100644 index 000000000..42ab05ee5 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/rrt_first_day.sql @@ -0,0 +1,195 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS rrt_first_day; CREATE TABLE rrt_first_day AS +-- determines if patients received any dialysis during their stay + +-- Some example aggregate queries which summarize the data here.. +-- This query estimates 6.7% of ICU patients received RRT. + -- select count(rrt.icustay_id) as numobs + -- , sum(rrt) as numrrt + -- , sum(case when rrt=1 then 1 else 0 end)*100.0 / count(rrt.icustay_id) + -- as percent_rrt + -- from rrt + -- inner join icustays ie on rrt.icustay_id = ie.icustay_id + -- inner join patients p + -- on rrt.subject_id = p.subject_id + -- and p.dob < ie.intime - interval '1' year + -- inner join admissions adm + -- on rrt.hadm_id = adm.hadm_id; + +-- This query estimates that 4.6% of first ICU stays received RRT. + -- select + -- count(rrt.icustay_id) as numobs + -- , sum(rrt) as numrrt + -- , sum(case when rrt=1 then 1 else 0 end)*100.0 / count(rrt.icustay_id) + -- as percent_rrt + -- from + -- ( + -- select ie.icustay_id, rrt.rrt + -- , ROW_NUMBER() over (partition by ie.subject_id order by ie.intime) rn + -- from rrt + -- inner join icustays ie + -- on rrt.icustay_id = ie.icustay_id + -- inner join patients p + -- on rrt.subject_id = p.subject_id + -- and p.dob < ie.intime - interval '1' year + -- inner join admissions adm + -- on rrt.hadm_id = adm.hadm_id + -- ) rrt + -- where rn = 1; + +with cv as +( + select ie.icustay_id + , max( + case + when ce.itemid in (152,148,149,146,147,151,150) and value is not null then 1 + when ce.itemid in (229,235,241,247,253,259,265,271) and value = 'Dialysis Line' then 1 + when ce.itemid = 582 and value in ('CAVH Start','CAVH D/C','CVVHD Start','CVVHD D/C','Hemodialysis st','Hemodialysis end') then 1 + else 0 end + ) as RRT + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.itemid in + ( + 152 -- "Dialysis Type";61449 + ,148 -- "Dialysis Access Site";60335 + ,149 -- "Dialysis Access Type";60030 + ,146 -- "Dialysate Flow ml/hr";57445 + ,147 -- "Dialysate Infusing";56605 + ,151 -- "Dialysis Site Appear";37345 + ,150 -- "Dialysis Machine";27472 + ,229 -- INV Line#1 [Type] + ,235 -- INV Line#2 [Type] + ,241 -- INV Line#3 [Type] + ,247 -- INV Line#4 [Type] + ,253 -- INV Line#5 [Type] + ,259 -- INV Line#6 [Type] + ,265 -- INV Line#7 [Type] + ,271 -- INV Line#8 [Type] + ,582 -- Procedures + ) + and ce.value is not null + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where ie.dbsource = 'carevue' + group by ie.icustay_id +) +, mv_ce as +( + select ie.icustay_id + , 1 as RRT + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and itemid in + ( + -- Checkboxes + 226118 -- | Dialysis Catheter placed in outside facility | Access Lines - Invasive | chartevents | Checkbox + , 227357 -- | Dialysis Catheter Dressing Occlusive | Access Lines - Invasive | chartevents | Checkbox + , 225725 -- | Dialysis Catheter Tip Cultured | Access Lines - Invasive | chartevents | Checkbox + -- Numeric values + , 226499 -- | Hemodialysis Output | Dialysis | chartevents | Numeric + , 224154 -- | Dialysate Rate | Dialysis | chartevents | Numeric + , 225810 -- | Dwell Time (Peritoneal Dialysis) | Dialysis | chartevents | Numeric + , 227639 -- | Medication Added Amount #2 (Peritoneal Dialysis) | Dialysis | chartevents | Numeric + , 225183 -- | Current Goal | Dialysis | chartevents | Numeric + , 227438 -- | Volume not removed | Dialysis | chartevents | Numeric + , 224191 -- | Hourly Patient Fluid Removal | Dialysis | chartevents | Numeric + , 225806 -- | Volume In (PD) | Dialysis | chartevents | Numeric + , 225807 -- | Volume Out (PD) | Dialysis | chartevents | Numeric + , 228004 -- | Citrate (ACD-A) | Dialysis | chartevents | Numeric + , 228005 -- | PBP (Prefilter) Replacement Rate | Dialysis | chartevents | Numeric + , 228006 -- | Post Filter Replacement Rate | Dialysis | chartevents | Numeric + , 224144 -- | Blood Flow (ml/min) | Dialysis | chartevents | Numeric + , 224145 -- | Heparin Dose (per hour) | Dialysis | chartevents | Numeric + , 224149 -- | Access Pressure | Dialysis | chartevents | Numeric + , 224150 -- | Filter Pressure | Dialysis | chartevents | Numeric + , 224151 -- | Effluent Pressure | Dialysis | chartevents | Numeric + , 224152 -- | Return Pressure | Dialysis | chartevents | Numeric + , 224153 -- | Replacement Rate | Dialysis | chartevents | Numeric + , 224404 -- | ART Lumen Volume | Dialysis | chartevents | Numeric + , 224406 -- | VEN Lumen Volume | Dialysis | chartevents | Numeric + , 226457 -- | Ultrafiltrate Output | Dialysis | chartevents | Numeric + ) + and valuenum > 0 -- also ensures it's not null + group by ie.icustay_id +) +, mv_ie as +( + select ie.icustay_id + , 1 as RRT + FROM icustays ie + inner join inputevents_mv tt + on ie.icustay_id = tt.icustay_id + and tt.starttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and itemid in + ( + 227536 -- KCl (CRRT) Medications inputevents_mv Solution + , 227525 -- Calcium Gluconate (CRRT) Medications inputevents_mv Solution + ) + and amount > 0 -- also ensures it's not null + group by ie.icustay_id +) +, mv_de as +( + select ie.icustay_id + , 1 as RRT + FROM icustays ie + inner join datetimeevents tt + on ie.icustay_id = tt.icustay_id + and tt.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and itemid in + ( + -- TODO: unsure how to handle "Last dialysis" + -- 225128 -- | Last dialysis | Adm History/FHPA | datetimeevents | Date time + 225318 -- | Dialysis Catheter Cap Change | Access Lines - Invasive | datetimeevents | Date time + , 225319 -- | Dialysis Catheter Change over Wire Date | Access Lines - Invasive | datetimeevents | Date time + , 225321 -- | Dialysis Catheter Dressing Change | Access Lines - Invasive | datetimeevents | Date time + , 225322 -- | Dialysis Catheter Insertion Date | Access Lines - Invasive | datetimeevents | Date time + , 225324 -- | Dialysis CatheterTubing Change | Access Lines - Invasive | datetimeevents | Date time + ) + group by ie.icustay_id +) +, mv_pe as +( + select ie.icustay_id + , 1 as RRT + FROM icustays ie + inner join procedureevents_mv tt + on ie.icustay_id = tt.icustay_id + and tt.starttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and itemid in + ( + 225441 -- | Hemodialysis | 4-Procedures | procedureevents_mv | Process + , 225802 -- | Dialysis - CRRT | Dialysis | procedureevents_mv | Process + , 225803 -- | Dialysis - CVVHD | Dialysis | procedureevents_mv | Process + , 225805 -- | Peritoneal Dialysis | Dialysis | procedureevents_mv | Process + , 224270 -- | Dialysis Catheter | Access Lines - Invasive | procedureevents_mv | Process + , 225809 -- | Dialysis - CVVHDF | Dialysis | procedureevents_mv | Process + , 225955 -- | Dialysis - SCUF | Dialysis | procedureevents_mv | Process + , 225436 -- | CRRT Filter Change | Dialysis | procedureevents_mv | Process + ) + group by ie.icustay_id +) +select ie.subject_id, ie.hadm_id, ie.icustay_id + , case + when cv.RRT = 1 then 1 + when mv_ce.RRT = 1 then 1 + when mv_ie.RRT = 1 then 1 + when mv_de.RRT = 1 then 1 + when mv_pe.RRT = 1 then 1 + else 0 + end as rrt +FROM icustays ie +left join cv + on ie.icustay_id = cv.icustay_id +left join mv_ce + on ie.icustay_id = mv_ce.icustay_id +left join mv_ie + on ie.icustay_id = mv_ie.icustay_id +left join mv_de + on ie.icustay_id = mv_de.icustay_id +left join mv_pe + on ie.icustay_id = mv_pe.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/firstday/urine_output_first_day.sql b/mimic-iii/concepts_postgres/firstday/urine_output_first_day.sql new file mode 100644 index 000000000..c7a94ff1d --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/urine_output_first_day.sql @@ -0,0 +1,58 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS urine_output_first_day; CREATE TABLE urine_output_first_day AS +-- ------------------------------------------------------------------ +-- Purpose: Create a view of the urine output for each ICUSTAY_ID over the first 24 hours. +-- ------------------------------------------------------------------ + +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 and oe.value > 0 then -1*oe.value + else oe.value + end) as urineoutput +FROM icustays ie +-- Join to the outputevents table to get urine output +left join 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 (DATETIME_ADD(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; diff --git a/mimic-iii/concepts_postgres/firstday/ventilation_first_day.sql b/mimic-iii/concepts_postgres/firstday/ventilation_first_day.sql new file mode 100644 index 000000000..84ece5b9a --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/ventilation_first_day.sql @@ -0,0 +1,26 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS ventilation_first_day; CREATE TABLE ventilation_first_day AS +-- Determines if a patient is ventilated on the first day of their ICU stay. +-- Creates a table with the result. +-- Requires the `ventilation_durations` table, generated by ../ventilation-durations.sql + +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 icustays ie +left join ventilation_durations 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 <= DATETIME_ADD(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; diff --git a/mimic-iii/concepts_postgres/firstday/vitals_first_day.sql b/mimic-iii/concepts_postgres/firstday/vitals_first_day.sql new file mode 100644 index 000000000..3a47f3c1b --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/vitals_first_day.sql @@ -0,0 +1,120 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS vitals_first_day; CREATE TABLE vitals_first_day AS +-- This query pivots the vital signs for the first 24 hours of a patient's stay +-- Vital signs include heart rate, blood pressure, respiration rate, and temperature + +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 icustays ie + left join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and DATETIME_DIFF(ce.charttime, ie.intime, 'SECOND') > 0 + and DATETIME_DIFF(ce.charttime, ie.intime, 'HOUR') <= 24 + -- exclude rows marked as error + and (ce.error IS NULL or ce.error = 0) + 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; diff --git a/mimic-iii/concepts_postgres/firstday/weight_first_day.sql b/mimic-iii/concepts_postgres/firstday/weight_first_day.sql new file mode 100644 index 000000000..e30177065 --- /dev/null +++ b/mimic-iii/concepts_postgres/firstday/weight_first_day.sql @@ -0,0 +1,120 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS weight_first_day; CREATE TABLE weight_first_day AS +-- This query extracts weights for adult ICU patients on their first ICU day. +-- It does *not* use any information after the first ICU day, as weight is +-- sometimes used to monitor fluid balance. + +-- ** Requires the echodata view, generated by concepts/echo-data.sql + +with ce as +( + SELECT + c.icustay_id + -- we take the avg value from roughly first day + -- TODO: eliminate obvious outliers if there is a reasonable weight + -- (e.g. weight of 180kg and 90kg would remove 180kg instead of taking the median) + , AVG(VALUENUM) as Weight_Admit + FROM chartevents c + inner join icustays ie + on c.icustay_id = ie.icustay_id + and c.charttime <= DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and c.charttime > DATETIME_SUB(ie.intime, INTERVAL '1' DAY) -- some fuzziness for admit time + WHERE c.valuenum IS NOT NULL + AND c.itemid in (762,226512) -- Admit Wt + AND c.valuenum != 0 + -- exclude rows marked as error + AND (c.error IS NULL OR c.error = 0) + group by c.icustay_id +) +, dwt as +( + SELECT + c.icustay_id + , AVG(VALUENUM) as Weight_Daily + FROM chartevents c + INNER JOIN icustays ie + on c.icustay_id = ie.icustay_id + and c.charttime <= DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + and c.charttime > DATETIME_SUB(ie.intime, INTERVAL '1' DAY) -- some fuzziness for admit time + WHERE c.valuenum IS NOT NULL + AND c.itemid in (763,224639) -- Daily Weight + AND c.valuenum != 0 + -- exclude rows marked as error + AND (c.error IS NULL OR c.error = 0) + group by c.icustay_id +) +-- we split in-hospital/out of hospital echoes as we would like to prioritize in-hospital data +, echo_hadm as +( + select + ie.icustay_id + , 0.453592*AVG(weight) as Weight_EchoInHosp + from echo_data ec + inner join icustays ie + on ec.hadm_id = ie.hadm_id + and ec.charttime < DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where + ec.HADM_ID is not null + and ec.weight is not null + group by ie.icustay_id +) +, echo_nohadm as +( + select + ie.icustay_id + , 0.453592*AVG(weight) as Weight_EchoPreHosp + from echo_data ec + inner join icustays ie + on ie.subject_id = ec.subject_id + and ie.intime < DATETIME_ADD(ec.charttime, INTERVAL '1' MONTH) + and ie.intime > ec.charttime + where + ec.HADM_ID is null + and ec.weight is not null + group by ie.icustay_id +) +select + ie.icustay_id + , round(cast( + case + when ce.icustay_id is not null + then ce.Weight_Admit + when dwt.icustay_id is not null + then dwt.Weight_Daily + when eh.icustay_id is not null + then eh.Weight_EchoInHosp + when enh.icustay_id is not null + then enh.Weight_EchoPreHosp + else null end + as numeric), 2) + as weight + + -- components + , ce.weight_admit + , dwt.weight_daily + , eh.weight_echoinhosp + , enh.weight_echoprehosp + +FROM icustays ie + +-- filter to only adults +inner join patients pat + on ie.subject_id = pat.subject_id + and ie.intime > DATETIME_ADD(pat.dob, INTERVAL '1' YEAR) + +-- admission weight +left join ce + on ie.icustay_id = ce.icustay_id + +-- daily weights +left join dwt + on ie.icustay_id = dwt.icustay_id + +-- in-hospital echo weight +left join echo_hadm eh + on ie.icustay_id = eh.icustay_id + +-- pre-hospitalization echo weights +left join echo_nohadm enh + on ie.icustay_id = enh.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/fluid_balance/colloid_bolus.sql b/mimic-iii/concepts_postgres/fluid_balance/colloid_bolus.sql new file mode 100644 index 000000000..34610a409 --- /dev/null +++ b/mimic-iii/concepts_postgres/fluid_balance/colloid_bolus.sql @@ -0,0 +1,123 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS colloid_bolus; CREATE TABLE colloid_bolus AS +-- received colloid before admission +-- 226365 -- OR Colloid Intake +-- 226376 -- PACU Colloid Intake + +with t1 as +( + select + mv.icustay_id + , mv.starttime as charttime + -- standardize the units to millilitres + -- also metavision has floating point precision.. but we only care down to the mL + , round(case + when mv.amountuom = 'L' + then mv.amount * 1000.0 + when mv.amountuom = 'ml' + then mv.amount + else null end) as amount + from inputevents_mv mv + where mv.itemid in + ( + 220864, -- Albumin 5% 7466 132 7466 + 220862, -- Albumin 25% 9851 174 9851 + 225174, -- Hetastarch (Hespan) 6% 82 1 82 + 225795, -- Dextran 40 38 3 38 + 225796 -- Dextran 70 + -- below ITEMIDs not in use + -- 220861 | Albumin (Human) 20% + -- 220863 | Albumin (Human) 4% + ) + and mv.statusdescription != 'Rewritten' + and + -- in MetaVision, these ITEMIDs never appear with a null rate + -- so it is sufficient to check the rate is > 100 + ( + (mv.rateuom = 'mL/hour' and mv.rate > 100) + OR (mv.rateuom = 'mL/min' and mv.rate > (100/60.0)) + OR (mv.rateuom = 'mL/kg/hour' and (mv.rate*mv.patientweight) > 100) + ) +) +, t2 as +( + select + cv.icustay_id + , cv.charttime + -- carevue always has units in millilitres (or null) + , round(cv.amount) as amount + from inputevents_cv cv + where cv.itemid in + ( + 30008 -- Albumin 5% + ,30009 -- Albumin 25% + ,42832 -- albumin 12.5% + ,40548 -- ALBUMIN + ,45403 -- albumin + ,44203 -- Albumin 12.5% + ,30181 -- Serum Albumin 5% + ,46564 -- Albumin + ,43237 -- 25% Albumin + ,43353 -- Albumin (human) 25% + + ,30012 -- Hespan + ,46313 -- 6% Hespan + + ,30011 -- Dextran 40 + ,30016 -- Dextrose 10% + ,42975 -- DEXTRAN DRIP + ,42944 -- dextran + ,46336 -- 10% Dextran 40/D5W + ,46729 -- Dextran + ,40033 -- DEXTRAN + ,45410 -- 10% Dextran 40 + ,42731 -- Dextran40 10% + ) + and cv.amount > 100 + and cv.amount < 2000 +) +-- some colloids are charted in chartevents +, t3 as +( + select + ce.icustay_id + , ce.charttime + -- carevue always has units in millilitres (or null) + , round(ce.valuenum) as amount + from chartevents ce + where ce.itemid in + ( + 2510 -- DEXTRAN LML 10% + , 3087 -- DEXTRAN 40 10% + , 6937 -- Dextran + , 3087 -- DEXTRAN 40 10% + , 3088 -- DEXTRAN 40% + ) + and ce.valuenum is not null + and ce.valuenum > 100 + and ce.valuenum < 2000 +) +select + icustay_id + , charttime + , sum(amount) as colloid_bolus +from t1 +-- just because the rate was high enough, does *not* mean the final amount was +where amount > 100 +group by t1.icustay_id, t1.charttime +UNION ALL +select + icustay_id + , charttime + , sum(amount) as colloid_bolus +from t2 +group by t2.icustay_id, t2.charttime +UNION ALL +select + icustay_id + , charttime + , sum(amount) as colloid_bolus +from t3 +group by t3.icustay_id, t3.charttime +order by icustay_id, charttime; + diff --git a/mimic-iii/concepts_postgres/fluid_balance/crystalloid_bolus.sql b/mimic-iii/concepts_postgres/fluid_balance/crystalloid_bolus.sql new file mode 100644 index 000000000..2158c60e0 --- /dev/null +++ b/mimic-iii/concepts_postgres/fluid_balance/crystalloid_bolus.sql @@ -0,0 +1,160 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS crystalloid_bolus; CREATE TABLE crystalloid_bolus AS +with t1 as +( + select + mv.icustay_id + , mv.starttime as charttime + -- standardize the units to millilitres + -- also metavision has floating point precision.. but we only care down to the mL + , round(case + when mv.amountuom = 'L' + then mv.amount * 1000.0 + when mv.amountuom = 'ml' + then mv.amount + else null end) as amount + from inputevents_mv mv + where mv.itemid in + ( + -- 225943 Solution + 225158, -- NaCl 0.9% + 225828, -- LR + 225944, -- Sterile Water + 225797, -- Free Water + 225159, -- NaCl 0.45% + -- 225161, -- NaCl 3% (Hypertonic Saline) + 225823, -- D5 1/2NS + 225825, -- D5NS + 225827, -- D5LR + 225941, -- D5 1/4NS + 226089 -- Piggyback + ) + and mv.statusdescription != 'Rewritten' + and + -- in MetaVision, these ITEMIDs appear with a null rate IFF endtime=starttime + 1 minute + -- so it is sufficient to: + -- (1) check the rate is > 240 if it exists or + -- (2) ensure the rate is null and amount > 240 ml + ( + (mv.rate is not null and mv.rateuom = 'mL/hour' and mv.rate > 248) + OR (mv.rate is not null and mv.rateuom = 'mL/min' and mv.rate > (248/60.0)) + OR (mv.rate is null and mv.amountuom = 'L' and mv.amount > 0.248) + OR (mv.rate is null and mv.amountuom = 'ml' and mv.amount > 248) + ) +) +, t2 as +( + select + cv.icustay_id + , cv.charttime + -- carevue always has units in millilitres + , round(cv.amount) as amount + from inputevents_cv cv + where cv.itemid in + ( + 30015 -- "D5/.45NS" -- mixed colloids and crystalloids + , 30018 -- .9% Normal Saline + , 30020 -- .45% Normal Saline + , 30021 -- Lactated Ringers + , 30058 -- Free Water Bolus + , 30060 -- D5NS + , 30061 -- D5RL + , 30063 -- IV Piggyback + , 30065 -- Sterile Water + -- , 30143 -- 3% Normal Saline + , 30159 -- D5 Ringers Lact. + , 30160 -- D5 Normal Saline + , 30169 -- Sterile H20_GU + , 30190 -- NS .9% + , 40850 -- ns bolus + , 41491 -- fluid bolus + , 42639 -- bolus + , 42187 -- free h20 + , 43819 -- 1:1 NS Repletion. + , 41430 -- free water boluses + , 40712 -- free H20 + , 44160 -- BOLUS + , 42383 -- cc for cc replace + , 42297 -- Fluid bolus + , 42453 -- Fluid Bolus + , 40872 -- free water + , 41915 -- FREE WATER + , 41490 -- NS bolus + , 46501 -- H2O Bolus + , 45045 -- WaterBolus + , 41984 -- FREE H20 + , 41371 -- ns fluid bolus + , 41582 -- free h20 bolus + , 41322 -- rl bolus + , 40778 -- Free H2O + , 41896 -- ivf boluses + , 41428 -- ns .9% bolus + , 43936 -- FREE WATER BOLUSES + , 44200 -- FLUID BOLUS + , 41619 -- frfee water boluses + , 40424 -- free H2O + , 41457 -- Free H20 intake + , 41581 -- Water bolus + , 42844 -- NS fluid bolus + , 42429 -- Free water + , 41356 -- IV Bolus + , 40532 -- FREE H2O + , 42548 -- NS Bolus + , 44184 -- LR Bolus + , 44521 -- LR bolus + , 44741 -- NS FLUID BOLUS + , 44126 -- fl bolus + , 44110 -- RL BOLUS + , 44633 -- ns boluses + , 44983 -- Bolus NS + , 44815 -- LR BOLUS + , 43986 -- iv bolus + , 45079 -- 500 cc ns bolus + , 46781 -- lr bolus + , 45155 -- ns cc/cc replacement + , 43909 -- H20 BOlus + , 41467 -- NS IV bolus + , 44367 -- LR + , 41743 -- water bolus + , 40423 -- Bolus + , 44263 -- fluid bolus ns + , 42749 -- fluid bolus NS + , 45480 -- 500cc ns bolus + , 44491 -- .9NS bolus + , 41695 -- NS fluid boluses + , 46169 -- free water bolus. + , 41580 -- free h2o bolus + , 41392 -- ns b + , 45989 -- NS Fluid Bolus + , 45137 -- NS cc/cc + , 45154 -- Free H20 bolus + , 44053 -- normal saline bolus + , 41416 -- free h2o boluses + , 44761 -- Free H20 + , 41237 -- ns fluid boluses + , 44426 -- bolus ns + , 43975 -- FREE H20 BOLUSES + , 44894 -- N/s 500 ml bolus + , 41380 -- nsbolus + , 42671 -- free h2o + ) + and cv.amount > 248 + and cv.amount <= 2000 + and cv.amountuom = 'ml' +) +select + icustay_id + , charttime + , sum(amount) as crystalloid_bolus +from t1 +-- just because the rate was high enough, does *not* mean the final amount was +where amount > 248 +group by t1.icustay_id, t1.charttime +UNION +select + icustay_id + , charttime + , sum(amount) as crystalloid_bolus +from t2 +group by t2.icustay_id, t2.charttime +; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/fluid_balance/ffp_transfusion.sql b/mimic-iii/concepts_postgres/fluid_balance/ffp_transfusion.sql new file mode 100644 index 000000000..bf202d5a6 --- /dev/null +++ b/mimic-iii/concepts_postgres/fluid_balance/ffp_transfusion.sql @@ -0,0 +1,96 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS ffp_transfusion; CREATE TABLE ffp_transfusion AS +-- Retrieves instances of fresh frozen plasma transfusions +WITH raw_ffp AS ( + SELECT + CASE + WHEN amount IS NOT NULL THEN amount + WHEN stopped IS NOT NULL THEN 0 + -- impute 200 mL when unit is not documented + -- this is an approximation which holds ~90% of the time + ELSE 200 + END AS amount + , amountuom + , icustay_id + , charttime + FROM inputevents_cv + WHERE itemid IN + ( + 30005, -- Fresh Frozen Plasma + 30180 -- Fresh Froz Plasma + ) + AND amount > 0 + AND icustay_id IS NOT NULL + UNION ALL + SELECT amount + , amountuom + , icustay_id + , endtime AS charttime + FROM inputevents_mv + WHERE itemid in + ( + 220970 -- Fresh Frozen Plasma + ) + AND amount > 0 + AND icustay_id IS NOT NULL +), +pre_icu_ffp as ( + SELECT + sum(amount) as amount, icustay_id + FROM inputevents_cv + WHERE itemid IN ( + 44172, -- FFP GTT + 44236, -- E.R. FFP + 46410, -- angio FFP + 46418, -- ER ffp + 46684, -- ER FFP + 44819, -- FFP ON FARR 2 + 46530, -- Floor FFP + 44044, -- FFP Drip + 46122, -- ER in FFP + 45669, -- ED FFP + 42323 -- er ffp + ) + AND amount > 0 + AND icustay_id IS NOT NULL + GROUP BY icustay_id + UNION ALL + SELECT + sum(amount) as amount, icustay_id + FROM inputevents_mv + WHERE itemid IN ( + 227072 -- PACU FFP Intake + ) + AND amount > 0 + AND icustay_id IS NOT NULL + GROUP BY icustay_id +), +cumulative AS ( + SELECT + sum(amount) over (PARTITION BY icustay_id ORDER BY charttime DESC) AS amount + , amountuom + , icustay_id + , charttime + , DATETIME_DIFF(lag(charttime) over (PARTITION BY icustay_id ORDER BY charttime ASC), charttime, 'HOUR') AS delta + FROM raw_ffp +) +-- We consider any transfusions started within 1 hr of the last one +-- to be part of the same event +SELECT + cm.icustay_id + , cm.charttime + , ROUND(CAST(cm.amount AS numeric) - CASE + WHEN ROW_NUMBER() OVER w = 1 THEN CAST(0 AS numeric) + ELSE cast(lag(cm.amount) OVER w AS numeric) + END, 2) AS amount + , ROUND(CAST(cm.amount AS numeric) + CASE + WHEN pre.amount IS NULL THEN CAST(0 AS numeric) + ELSE CAST(pre.amount AS numeric) + END, 2) AS totalamount + , cm.amountuom +FROM cumulative AS cm +LEFT JOIN pre_icu_ffp AS pre + USING (icustay_id) +WHERE delta IS NULL OR delta < -1 +WINDOW w AS (PARTITION BY cm.icustay_id ORDER BY cm.charttime DESC) +ORDER BY icustay_id, charttime; diff --git a/mimic-iii/concepts_postgres/fluid_balance/rbc_transfusion.sql b/mimic-iii/concepts_postgres/fluid_balance/rbc_transfusion.sql new file mode 100644 index 000000000..59f040cb9 --- /dev/null +++ b/mimic-iii/concepts_postgres/fluid_balance/rbc_transfusion.sql @@ -0,0 +1,91 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS rbc_transfusion; CREATE TABLE rbc_transfusion AS +-- Retrieves instances of red blood cell transfusions +with raw_rbc as ( + SELECT + CASE + WHEN amount IS NOT NULL THEN amount + WHEN stopped IS NOT NULL THEN 0 + -- impute 375 mL when unit is not documented + ELSE 375 + END AS amount + , amountuom + , icustay_id + , charttime + FROM inputevents_cv + WHERE itemid IN + ( + 30179, -- PRBC's + 30001, -- Packed RBC's + 30004 -- Washed PRBC's + ) + AND icustay_id IS NOT NULL + UNION ALL + SELECT amount + , amountuom + , icustay_id + , endtime AS charttime + FROM inputevents_mv + WHERE itemid in + ( + 225168 -- Packed Red Blood Cells + ) + AND amount > 0 + AND icustay_id IS NOT NULL +), +pre_icu_rbc as ( + SELECT + sum(amount) as amount, icustay_id + FROM inputevents_cv + WHERE itemid IN ( + 42324, -- er prbc + 42588, -- VICU PRBC + 42239, -- CC7 PRBC + 46407, -- ED PRBC + 46612, -- E.R. prbc + 46124, -- er in prbc + 42740 -- prbc in er + ) + AND amount > 0 + AND icustay_id IS NOT NULL + GROUP BY icustay_id + UNION ALL + SELECT + sum(amount) as amount, icustay_id + FROM inputevents_mv + WHERE itemid IN ( + 227070 -- PACU Packed RBC Intake + ) + AND amount > 0 + AND icustay_id IS NOT NULL + GROUP BY icustay_id +), +cumulative AS ( + SELECT + sum(amount) over (PARTITION BY icustay_id ORDER BY charttime DESC) AS amount + , amountuom + , icustay_id + , charttime + , DATETIME_DIFF(lag(charttime) over (PARTITION BY icustay_id ORDER BY charttime ASC), charttime, 'HOUR') AS delta + FROM raw_rbc +) +-- We consider any transfusions started within 1 hr of the last one +-- to be part of the same event +SELECT + cm.icustay_id + , cm.charttime + , ROUND(CAST(cm.amount AS numeric) - CASE + WHEN ROW_NUMBER() OVER w = 1 THEN CAST(0 AS numeric) + ELSE CAST(lag(cm.amount) OVER w AS numeric) + END, 2) AS amount + , ROUND(CAST(cm.amount AS numeric) + CASE + WHEN CAST(pre.amount AS numeric) IS NULL THEN CAST(0 AS numeric) + ELSE CAST(pre.amount AS numeric) + END, 2) AS totalamount + , cm.amountuom +FROM cumulative AS cm +LEFT JOIN pre_icu_rbc AS pre + USING (icustay_id) +WHERE delta IS NULL OR delta < -1 +WINDOW w AS (PARTITION BY cm.icustay_id ORDER BY cm.charttime DESC) +ORDER BY icustay_id, charttime; diff --git a/mimic-iii/concepts_postgres/fluid_balance/urine_output.sql b/mimic-iii/concepts_postgres/fluid_balance/urine_output.sql new file mode 100644 index 000000000..13056209e --- /dev/null +++ b/mimic-iii/concepts_postgres/fluid_balance/urine_output.sql @@ -0,0 +1,45 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS urine_output; CREATE TABLE urine_output AS +-- First we drop the table if it exists +select oe.icustay_id, oe.charttime +, SUM( + -- we consider input of GU irrigant as a negative volume + case when oe.itemid = 227488 then -1*value + else value end + ) as value +from outputevents oe +where oe.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 +) +and oe.value < 5000 -- sanity check on urine value +and oe.icustay_id is not null +group by icustay_id, charttime; diff --git a/mimic-iii/concepts_postgres/measurement/urine_output.sql b/mimic-iii/concepts_postgres/measurement/urine_output.sql new file mode 100644 index 000000000..90fe68999 --- /dev/null +++ b/mimic-iii/concepts_postgres/measurement/urine_output.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS urine_output; CREATE TABLE urine_output AS diff --git a/mimic-iii/concepts_postgres/medication/norepinephrine_equivalent_dose.sql b/mimic-iii/concepts_postgres/medication/norepinephrine_equivalent_dose.sql new file mode 100644 index 000000000..2add3efa3 --- /dev/null +++ b/mimic-iii/concepts_postgres/medication/norepinephrine_equivalent_dose.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS norepinephrine_equivalent_dose; CREATE TABLE norepinephrine_equivalent_dose AS diff --git a/mimic-iii/concepts_postgres/medication/vasoactive_agent.sql b/mimic-iii/concepts_postgres/medication/vasoactive_agent.sql new file mode 100644 index 000000000..9cf09a2b9 --- /dev/null +++ b/mimic-iii/concepts_postgres/medication/vasoactive_agent.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS vasoactive_agent; CREATE TABLE vasoactive_agent AS diff --git a/mimic-iii/concepts_postgres/organfailure/kdigo_creatinine.sql b/mimic-iii/concepts_postgres/organfailure/kdigo_creatinine.sql new file mode 100644 index 000000000..ea6ff3fc1 --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/kdigo_creatinine.sql @@ -0,0 +1,39 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS kdigo_creatinine; CREATE TABLE kdigo_creatinine AS +-- Extract all creatinine values FROM labevents around patient's ICU stay +with cr as +( +select + ie.icustay_id + , ie.intime, ie.outtime + , le.valuenum as creat + , le.charttime + FROM icustays ie + left join labevents le + on ie.subject_id = le.subject_id + and le.ITEMID = 50912 + and le.VALUENUM is not null + and DATETIME_DIFF(le.charttime, ie.intime, 'HOUR') <= (7*24-6) + and le.CHARTTIME >= DATETIME_SUB(ie.intime, INTERVAL '6' HOUR) + and le.CHARTTIME <= DATETIME_ADD(ie.intime, INTERVAL '7' DAY) +) +-- add in the lowest value in the previous 48 hours/7 days +SELECT + cr.icustay_id + , cr.charttime + , cr.creat + , MIN(cr48.creat) AS creat_low_past_48hr + , MIN(cr7.creat) AS creat_low_past_7day +FROM cr +-- add in all creatinine values in the last 48 hours +LEFT JOIN cr cr48 + ON cr.icustay_id = cr48.icustay_id + AND cr48.charttime < cr.charttime + AND DATETIME_DIFF(cr.charttime, cr48.charttime, 'HOUR') <= 48 +-- add in all creatinine values in the last 7 days +LEFT JOIN cr cr7 + ON cr.icustay_id = cr7.icustay_id + AND cr7.charttime < cr.charttime + AND DATETIME_DIFF(cr.charttime, cr7.charttime, 'DAY') <= 7 +GROUP BY cr.icustay_id, cr.charttime, cr.creat +ORDER BY cr.icustay_id, cr.charttime, cr.creat; diff --git a/mimic-iii/concepts_postgres/organfailure/kdigo_stages.sql b/mimic-iii/concepts_postgres/organfailure/kdigo_stages.sql new file mode 100644 index 000000000..0a45b999c --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/kdigo_stages.sql @@ -0,0 +1,92 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS kdigo_stages; CREATE TABLE kdigo_stages AS +-- This query checks if the patient had AKI according to KDIGO. +-- AKI is calculated every time a creatinine or urine output measurement occurs. +-- Baseline creatinine is defined as the lowest creatinine in the past 7 days. + +-- get creatinine stages +with cr_stg AS +( + SELECT + cr.icustay_id + , cr.charttime + , cr.creat + , case + -- 3x baseline + when cr.creat >= (cr.creat_low_past_7day*3.0) then 3 + -- *OR* cr >= 4.0 with associated increase + when cr.creat >= 4 + -- For patients reaching Stage 3 by SCr >4.0 mg/dl + -- require that the patient first achieve ... acute increase >= 0.3 within 48 hr + -- *or* an increase of >= 1.5 times baseline + and (cr.creat_low_past_48hr <= 3.7 OR cr.creat >= (1.5*cr.creat_low_past_7day)) + then 3 + -- TODO: initiation of RRT + when cr.creat >= (cr.creat_low_past_7day*2.0) then 2 + when cr.creat >= (cr.creat_low_past_48hr+0.3) then 1 + when cr.creat >= (cr.creat_low_past_7day*1.5) then 1 + else 0 end as aki_stage_creat + FROM kdigo_creatinine cr +) +-- stages for UO / creat +, uo_stg as +( + select + uo.icustay_id + , uo.charttime + , uo.weight + , uo.uo_rt_6hr + , uo.uo_rt_12hr + , uo.uo_rt_24hr + -- AKI stages according to urine output + , CASE + WHEN uo.uo_rt_6hr IS NULL THEN NULL + -- require patient to be in ICU for at least 6 hours to stage UO + WHEN uo.charttime <= DATETIME_ADD(ie.intime, INTERVAL '6' HOUR) THEN 0 + -- require the UO rate to be calculated over half the period + -- i.e. for uo rate over 24 hours, require documentation at least 12 hr apart + WHEN uo.uo_tm_24hr >= 11 AND uo.uo_rt_24hr < 0.3 THEN 3 + WHEN uo.uo_tm_12hr >= 5 AND uo.uo_rt_12hr = 0 THEN 3 + WHEN uo.uo_tm_12hr >= 5 AND uo.uo_rt_12hr < 0.5 THEN 2 + WHEN uo.uo_tm_6hr >= 2 AND uo.uo_rt_6hr < 0.5 THEN 1 + ELSE 0 END AS aki_stage_uo + from kdigo_uo uo + INNER JOIN icustays ie + ON uo.icustay_id = ie.icustay_id +) +-- get all charttimes documented +, tm_stg AS +( + SELECT + icustay_id, charttime + FROM cr_stg + UNION DISTINCT + SELECT + icustay_id, charttime + FROM uo_stg +) +select + ie.icustay_id + , tm.charttime + , cr.creat + , cr.aki_stage_creat + , uo.uo_rt_6hr + , uo.uo_rt_12hr + , uo.uo_rt_24hr + , uo.aki_stage_uo + -- Classify AKI using both creatinine/urine output criteria + , GREATEST( + COALESCE(cr.aki_stage_creat, 0), + COALESCE(uo.aki_stage_uo, 0) + ) AS aki_stage +FROM icustays ie +-- get all possible charttimes as listed in tm_stg +LEFT JOIN tm_stg tm + ON ie.icustay_id = tm.icustay_id +LEFT JOIN cr_stg cr + ON ie.icustay_id = cr.icustay_id + AND tm.charttime = cr.charttime +LEFT JOIN uo_stg uo + ON ie.icustay_id = uo.icustay_id + AND tm.charttime = uo.charttime +order by ie.icustay_id, tm.charttime; diff --git a/mimic-iii/concepts_postgres/organfailure/kdigo_stages_48hr.sql b/mimic-iii/concepts_postgres/organfailure/kdigo_stages_48hr.sql new file mode 100644 index 000000000..189fef282 --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/kdigo_stages_48hr.sql @@ -0,0 +1,69 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS kdigo_stages_48hr; CREATE TABLE kdigo_stages_48hr AS +-- This query checks if the patient had AKI during the first 48 hours of their ICU +-- stay according to the KDIGO guideline. +-- https://kdigo.org/wp-content/uploads/2016/10/KDIGO-2012-AKI-Guideline-English.pdf + +-- get the worst staging of creatinine in the first 48 hours +WITH cr_aki AS +( + SELECT + k.icustay_id + , k.charttime + , k.creat + , k.aki_stage_creat + , ROW_NUMBER() OVER (PARTITION BY k.icustay_id ORDER BY k.aki_stage_creat DESC, k.creat DESC) AS rn + FROM icustays ie + INNER JOIN kdigo_stages k + ON ie.icustay_id = k.icustay_id + WHERE DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') > -6 + AND DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') <= 48 + AND k.aki_stage_creat IS NOT NULL +) +-- get the worst staging of urine output in the first 48 hours +, uo_aki AS +( + SELECT + k.icustay_id + , k.charttime + , k.uo_rt_6hr, k.uo_rt_12hr, k.uo_rt_24hr + , k.aki_stage_uo + , ROW_NUMBER() OVER + ( + PARTITION BY k.icustay_id + ORDER BY k.aki_stage_uo DESC, k.uo_rt_24hr DESC, k.uo_rt_12hr DESC, k.uo_rt_6hr DESC + ) AS rn + FROM icustays ie + INNER JOIN kdigo_stages k + ON ie.icustay_id = k.icustay_id + WHERE DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') > -6 + AND DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') <= 48 + AND k.aki_stage_uo IS NOT NULL +) +-- final table is aki_stage, include worst cr/uo for convenience +select + ie.icustay_id + , cr.charttime as charttime_creat + , cr.creat + , cr.aki_stage_creat + , uo.charttime as charttime_uo + , uo.uo_rt_6hr + , uo.uo_rt_12hr + , uo.uo_rt_24hr + , uo.aki_stage_uo + + -- Classify AKI using both creatinine/urine output criteria + , GREATEST( + COALESCE(cr.aki_stage_creat, 0), + COALESCE(uo.aki_stage_uo, 0) + ) AS aki_stage_48hr + , CASE WHEN cr.aki_stage_creat > 0 OR uo.aki_stage_uo > 0 THEN 1 ELSE 0 END AS aki_48hr + +FROM icustays ie +LEFT JOIN cr_aki cr + ON ie.icustay_id = cr.icustay_id + AND cr.rn = 1 +LEFT JOIN uo_aki uo + ON ie.icustay_id = uo.icustay_id + AND uo.rn = 1 +order by ie.icustay_id; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/organfailure/kdigo_stages_7day.sql b/mimic-iii/concepts_postgres/organfailure/kdigo_stages_7day.sql new file mode 100644 index 000000000..1a500f5d2 --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/kdigo_stages_7day.sql @@ -0,0 +1,69 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS kdigo_stages_7day; CREATE TABLE kdigo_stages_7day AS +-- This query checks if the patient had AKI during the first 7 days of their ICU +-- stay according to the KDIGO guideline. +-- https://kdigo.org/wp-content/uploads/2016/10/KDIGO-2012-AKI-Guideline-English.pdf + +-- get the worst staging of creatinine in the first 48 hours +WITH cr_aki AS +( + SELECT + k.icustay_id + , k.charttime + , k.creat + , k.aki_stage_creat + , ROW_NUMBER() OVER (PARTITION BY k.icustay_id ORDER BY k.aki_stage_creat DESC, k.creat DESC) AS rn + FROM icustays ie + INNER JOIN kdigo_stages k + ON ie.icustay_id = k.icustay_id + WHERE DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') > -6 + AND DATETIME_DIFF(k.charttime, ie.intime, 'DAY') <= 7 + AND k.aki_stage_creat IS NOT NULL +) +-- get the worst staging of urine output in the first 48 hours +, uo_aki AS +( + SELECT + k.icustay_id + , k.charttime + , k.uo_rt_6hr, k.uo_rt_12hr, k.uo_rt_24hr + , k.aki_stage_uo + , ROW_NUMBER() OVER + ( + PARTITION BY k.icustay_id + ORDER BY k.aki_stage_uo DESC, k.uo_rt_24hr DESC, k.uo_rt_12hr DESC, k.uo_rt_6hr DESC + ) AS rn + FROM icustays ie + INNER JOIN kdigo_stages k + ON ie.icustay_id = k.icustay_id + WHERE DATETIME_DIFF(k.charttime, ie.intime, 'HOUR') > -6 + AND DATETIME_DIFF(k.charttime, ie.intime, 'DAY') <= 7 + AND k.aki_stage_uo IS NOT NULL +) +-- final table is aki_stage, include worst cr/uo for convenience +select + ie.icustay_id + , cr.charttime as charttime_creat + , cr.creat + , cr.aki_stage_creat + , uo.charttime as charttime_uo + , uo.uo_rt_6hr + , uo.uo_rt_12hr + , uo.uo_rt_24hr + , uo.aki_stage_uo + + -- Classify AKI using both creatinine/urine output criteria + , GREATEST( + COALESCE(cr.aki_stage_creat, 0), + COALESCE(uo.aki_stage_uo, 0) + ) AS aki_stage_7day + , CASE WHEN cr.aki_stage_creat > 0 OR uo.aki_stage_uo > 0 THEN 1 ELSE 0 END AS aki_7day + +FROM icustays ie +LEFT JOIN cr_aki cr + ON ie.icustay_id = cr.icustay_id + AND cr.rn = 1 +LEFT JOIN uo_aki uo + ON ie.icustay_id = uo.icustay_id + AND uo.rn = 1 +order by ie.icustay_id; \ No newline at end of file diff --git a/mimic-iii/concepts_postgres/organfailure/kdigo_uo.sql b/mimic-iii/concepts_postgres/organfailure/kdigo_uo.sql new file mode 100644 index 000000000..f5d2a9de4 --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/kdigo_uo.sql @@ -0,0 +1,84 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS kdigo_uo; CREATE TABLE kdigo_uo AS +with ur_stg as +( + select io.icustay_id, io.charttime + -- we have joined each row to all rows preceding within 24 hours + -- we can now sum these rows to get total UO over the last 24 hours + -- we can use case statements to restrict it to only the last 6/12 hours + -- therefore we have three sums: + -- 1) over a 6 hour period + -- 2) over a 12 hour period + -- 3) over a 24 hour period + + -- note that we assume data charted at charttime corresponds to 1 hour of UO + -- therefore we use '5' and '11' to restrict the period, rather than 6/12 + -- this assumption may overestimate UO rate when documentation is done less than hourly + , sum(case when DATETIME_DIFF(io.charttime, iosum.charttime, 'HOUR') <= 5 + then iosum.VALUE + else null end) as urineoutput_6hr + , sum(case when DATETIME_DIFF(io.charttime, iosum.charttime, 'HOUR') <= 11 + then iosum.VALUE + else null end) as urineoutput_12hr + -- 24 hours + , sum(iosum.VALUE) as urineoutput_24hr + + -- retain the earliest time used for each summation + -- this is later used to tabulate rates + , MIN(case when io.charttime <= DATETIME_ADD(iosum.charttime, INTERVAL '5' HOUR) + then iosum.charttime + else null end) + AS starttime_6hr + , MIN(case when io.charttime <= DATETIME_ADD(iosum.charttime, INTERVAL '11' HOUR) + then iosum.charttime + else null end) + AS starttime_12hr + , MIN(iosum.charttime) AS starttime_24hr + from urine_output io + -- this join gives you all UO measurements over a 24 hour period + left join urine_output iosum + on io.icustay_id = iosum.icustay_id + and io.charttime >= iosum.charttime + and io.charttime <= (DATETIME_ADD(iosum.charttime, INTERVAL '23' HOUR)) + group by io.icustay_id, io.charttime +) +-- calculate hours used to sum UO over +, ur_stg2 AS +( + select + icustay_id + , charttime + , urineoutput_6hr + , urineoutput_12hr + , urineoutput_24hr + -- calculate time over which we summed UO + -- note: adding 1 hour as we assume data charted corresponds to previous hour + -- i.e. if documentation is: + -- 10:00, 100 mL + -- 11:00, 50 mL + -- then this is two hours of documentation, even though (11:00 - 10:00) is 1 hour + , ROUND(DATETIME_DIFF(charttime, starttime_6hr, 'HOUR'), 4) + 1 AS uo_tm_6hr + , ROUND(DATETIME_DIFF(charttime, starttime_12hr, 'HOUR'), 4) + 1 AS uo_tm_12hr + , ROUND(DATETIME_DIFF(charttime, starttime_24hr, 'HOUR'), 4) + 1 AS uo_tm_24hr + from ur_stg +) +select + ur.icustay_id +, ur.charttime +, wd.weight +, ur.urineoutput_6hr +, ur.urineoutput_12hr +, ur.urineoutput_24hr +, ROUND(CAST((ur.urineoutput_6hr/wd.weight/uo_tm_6hr) AS NUMERIC), 4) AS uo_rt_6hr +, ROUND(CAST((ur.urineoutput_12hr/wd.weight/uo_tm_12hr) AS NUMERIC), 4) AS uo_rt_12hr +, ROUND(CAST((ur.urineoutput_24hr/wd.weight/uo_tm_24hr) AS NUMERIC), 4) AS uo_rt_24hr +-- time of earliest UO measurement that was used to calculate the rate +, uo_tm_6hr +, uo_tm_12hr +, uo_tm_24hr +from ur_stg2 ur +left join weight_durations wd + on ur.icustay_id = wd.icustay_id + and ur.charttime >= wd.starttime + and ur.charttime < wd.endtime +order by icustay_id, charttime; diff --git a/mimic-iii/concepts_postgres/organfailure/meld.sql b/mimic-iii/concepts_postgres/organfailure/meld.sql new file mode 100644 index 000000000..9a7dad3fe --- /dev/null +++ b/mimic-iii/concepts_postgres/organfailure/meld.sql @@ -0,0 +1,158 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS meld; CREATE TABLE meld AS +-- Model for end-stage liver disease (MELD) +-- This model is used to determine prognosis and receipt of liver transplantation. + +-- Reference: +-- Kamath PS, Wiesner RH, Malinchoc M, Kremers W, Therneau TM, +-- Kosberg CL, D'Amico G, Dickson ER, Kim WR. +-- A model to predict survival in patients with end-stage liver disease. +-- Hepatology. 2001 Feb;33(2):464-70. + + +-- Updated January 2016 to include serum sodium, see: +-- https://optn.transplant.hrsa.gov/news/meld-serum-sodium-policy-changes/ + +-- Here is the relevant portion of the policy note: +-- 9.1.D MELD Score +-- Candidates who are at least 12 years old receive an initial MELD(i) score equal to: +-- 0.957 x ln(creatinine mg/dL) + 0.378 x ln(bilirubin mg/dL) + 1.120 x ln(INR) + 0.643 + +-- Laboratory values less than 1.0 will be set to 1.0 when calculating a candidate’s MELD +-- score. + +-- The following candidates will receive a creatinine value of 4.0 mg/dL: +-- - Candidates with a creatinine value greater than 4.0 mg/dL +-- - Candidates who received two or more dialysis treatments within the prior week +-- - Candidates who received 24 hours of continuous veno-venous hemodialysis (CVVHD) within the prior week + +-- The maximum MELD score is 40. The MELD score derived from this calculation will be rounded to the tenth decimal place and then multiplied by 10. + +-- For candidates with an initial MELD score greater than 11, The MELD score is then recalculated as follows: +-- MELD = MELD(i) + 1.32*(137-Na) – [0.033*MELD(i)*(137-Na)] +-- Sodium values less than 125 mmol/L will be set to 125, and values greater than 137 mmol/L will be set to 137. + + + +-- TODO needed in this code: +-- 1. identify 2x dialysis in the past week, or 24 hours of CVVH +-- at the moment it just checks for any dialysis on the day +-- 2. adjust the serum sodium using the corresponding glucose measurement +-- Measured sodium + 0.024 * (Serum glucose - 100) (Hiller, 1999) + +with cohort as +( +select ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime + , ie.outtime + + , labs.creatinine_max + , labs.bilirubin_max + , labs.inr_max + , labs.sodium_min + + , r.rrt + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to custom tables to get more data.... +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +left join rrt_first_day r + on ie.icustay_id = r.icustay_id +) +, score as +( + select subject_id, hadm_id, icustay_id + , rrt + , creatinine_max + , bilirubin_max + , inr_max + , sodium_min + + -- TODO: Corrected Sodium + , case + when sodium_min is null + then 0.0 + when sodium_min > 137 + then 0.0 + when sodium_min < 125 + then 12.0 -- 137 - 125 = 12 + else 137.0-sodium_min + end as sodium_score + + -- if hemodialysis, value for Creatinine is automatically set to 4.0 + , case + when rrt = 1 or creatinine_max > 4.0 + then (0.957 * ln(4)) + -- if creatinine < 1, score is 1 + when creatinine_max < 1 + then (0.957 * ln(1)) + else 0.957 * coalesce(ln(creatinine_max),ln(1)) + end as creatinine_score + + , case + -- if value < 1, score is 1 + when bilirubin_max < 1 + then 0.378 * ln(1) + else 0.378 * coalesce(ln(bilirubin_max),ln(1)) + end as bilirubin_score + + , case + when inr_max < 1 + then ( 1.120 * ln(1) + 0.643 ) + else ( 1.120 * coalesce(ln(inr_max),ln(1)) + 0.643 ) + end as inr_score + + from cohort +) +, score2 as +( + select + subject_id, hadm_id, icustay_id + , rrt + , creatinine_max + , bilirubin_max + , inr_max + , sodium_min + + , creatinine_score + , sodium_score + , bilirubin_score + , inr_score + + , case + when (creatinine_score + bilirubin_score + inr_score) > 40 + then 40.0 + else + round(cast(creatinine_score + bilirubin_score + inr_score as numeric),1)*10 + end as meld_initial + from score +) +select + subject_id, hadm_id, icustay_id + + -- MELD Score without sodium change + , meld_initial + + -- MELD Score (2016) = MELD*10 + 1.32*(137-Na) – [0.033*MELD*10*(137-Na)] + , case + when meld_initial > 11 + then meld_initial + 1.32*sodium_score - 0.033*meld_initial*sodium_score + else + meld_initial + end as meld + + -- original variables + , rrt + , creatinine_max + , bilirubin_max + , inr_max + , sodium_min + +from score2 +order by icustay_id; diff --git a/mimic-iii/concepts_postgres/rrt.sql b/mimic-iii/concepts_postgres/rrt.sql new file mode 100644 index 000000000..cd4b6fcd6 --- /dev/null +++ b/mimic-iii/concepts_postgres/rrt.sql @@ -0,0 +1,351 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS rrt; CREATE TABLE rrt AS +-- determines if patients received any dialysis during their stay + +-- Some example aggregate queries which summarize the data here.. +-- This query estimates 6.7% of ICU patients received RRT. + -- select count(rrt.icustay_id) as numobs + -- , sum(rrt) as numrrt + -- , sum(case when rrt=1 then 1 else 0 end)*100.0 / count(rrt.icustay_id) + -- as percent_rrt + -- from rrt + -- inner join icustays ie on rrt.icustay_id = ie.icustay_id + -- inner join patients p + -- on rrt.subject_id = p.subject_id + -- and p.dob < ie.intime - interval '1' year + -- inner join admissions adm + -- on rrt.hadm_id = adm.hadm_id; + +-- This query estimates that 4.6% of first ICU stays received RRT. + -- select + -- count(rrt.icustay_id) as numobs + -- , sum(rrt) as numrrt + -- , sum(case when rrt=1 then 1 else 0 end)*100.0 / count(rrt.icustay_id) + -- as percent_rrt + -- from + -- ( + -- select ie.icustay_id, rrt.rrt + -- , ROW_NUMBER() over (partition by ie.subject_id order by ie.intime) rn + -- from rrt + -- inner join icustays ie + -- on rrt.icustay_id = ie.icustay_id + -- inner join patients p + -- on rrt.subject_id = p.subject_id + -- and p.dob < ie.intime - interval '1' year + -- inner join admissions adm + -- on rrt.hadm_id = adm.hadm_id + -- ) rrt + -- where rn = 1; + +with cv_ce as +( + select ie.icustay_id + , max( + case + when ce.itemid in (152,148,149,146,147,151,150) and value is not null then 1 + when ce.itemid in (229,235,241,247,253,259,265,271) and value = 'Dialysis Line' then 1 + when ce.itemid = 466 and value = 'Dialysis RN' then 1 + when ce.itemid = 927 and value = 'Dialysis Solutions' then 1 + when ce.itemid = 6250 and value = 'dialys' then 1 + when ce.itemid = 917 and value in ('+ INITIATE DIALYSIS','BLEEDING FROM DIALYSIS CATHETER','FAILED DIALYSIS CATH.','FEBRILE SYNDROME;DIALYSIS','HYPOTENSION WITH HEMODIALYSIS','HYPOTENSION.GLOGGED DIALYSIS','INFECTED DIALYSIS CATHETER') then 1 + when ce.itemid = 582 and value in ('CAVH Start','CAVH D/C','CVVHD Start','CVVHD D/C','Hemodialysis st','Hemodialysis end') then 1 + else 0 end + ) as RRT + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.itemid in + ( + 152 -- "Dialysis Type";61449 + ,148 -- "Dialysis Access Site";60335 + ,149 -- "Dialysis Access Type";60030 + ,146 -- "Dialysate Flow ml/hr";57445 + ,147 -- "Dialysate Infusing";56605 + ,151 -- "Dialysis Site Appear";37345 + ,150 -- "Dialysis Machine";27472 + ,7949 -- "Calcium for CVVH" + ,229 -- INV Line#1 [Type] + ,235 -- INV Line#2 [Type] + ,241 -- INV Line#3 [Type] + ,247 -- INV Line#4 [Type] + ,253 -- INV Line#5 [Type] + ,259 -- INV Line#6 [Type] + ,265 -- INV Line#7 [Type] + ,271 -- INV Line#8 [Type] + ,582 -- Procedures + ,466 -- Nursing Consultation + ,917 -- Diagnosis/op + ,927 -- Allergy 2 + ,6250 -- lt av fistula + + ) + and ce.value is not null + where ie.dbsource = 'carevue' + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by ie.icustay_id +) +, cv_ie as +( + select icustay_id + , 1 as RRT + FROM inputevents_cv + where itemid in + ( + 40788 -- PD dialysate in | Free Form Intake | inputevents_cv + , 40907 -- dialysate | Free Form Intake | inputevents_cv + , 41063 -- PD Dialysate Intake | Free Form Intake | inputevents_cv + , 41147 -- Dialysate instilled | Free Form Intake | inputevents_cv + , 41307 -- Peritoneal Dialysate | Free Form Intake | inputevents_cv + , 41460 -- capd dialysate | Free Form Intake | inputevents_cv + , 41620 -- dialysate in | Free Form Intake | inputevents_cv + , 41711 -- CAPD dialysate dwell | Free Form Intake | inputevents_cv + , 41791 -- 2.5% dialysate in | Free Form Intake | inputevents_cv + , 41792 -- 1.5% dialysate | Free Form Intake | inputevents_cv + , 42562 -- pos. dialysate intak | Free Form Intake | inputevents_cv + , 43829 -- PERITONEAL DIALYSATE | Free Form Intake | inputevents_cv + , 44037 -- Dialysate Instilled | Free Form Intake | inputevents_cv + , 44188 -- rep.+dialysate | Free Form Intake | inputevents_cv + , 44526 -- dialysate 1.5% dex | Free Form Intake | inputevents_cv + , 44527 -- dialysate 2.5% | Free Form Intake | inputevents_cv + , 44584 -- Dialysate IN | Free Form Intake | inputevents_cv + , 44591 -- dialysate 4.25% | Free Form Intake | inputevents_cv + , 44698 -- peritoneal dialysate | Free Form Intake | inputevents_cv + , 44927 -- CRRT HEPARIN | Free Form Intake | inputevents_cv + , 44954 -- OR CVVHDF | | inputevents_cv + , 45157 -- ca+ gtt for cvvh | Free Form Intake | inputevents_cv + , 45268 -- CALCIUM FOR CVVHD | Free Form Intake | inputevents_cv + , 45352 -- CA GLUC for CVVH | Free Form Intake | inputevents_cv + , 45353 -- KCL for CVVH | Free Form Intake | inputevents_cv + , 46012 -- CA GLUC CVVHDF | Free Form Intake | inputevents_cv + , 46013 -- KCL CVVHDF | Free Form Intake | inputevents_cv + , 46172 -- CVVHDF CA GLUC | Free Form Intake | inputevents_cv + , 46173 -- CVVHDF KCL | Free Form Intake | inputevents_cv + , 46250 -- EBL CVVH | | inputevents_cv + , 46262 -- dialysate 2.5% in | Free Form Intake | inputevents_cv + , 46292 -- CRRT Irrigation | Free Form Intake | inputevents_cv + , 46293 -- CRRT Citrate | Free Form Intake | inputevents_cv + , 46311 -- crrt irrigation | Free Form Intake | inputevents_cv + , 46389 -- CRRT FLUSH | Free Form Intake | inputevents_cv + , 46574 -- CRRT rescue line NS | Free Form Intake | inputevents_cv + , 46681 -- CRRT Rescue Flush | Free Form Intake | inputevents_cv + , 46720 -- PD Dialysate | Free Form Intake | inputevents_cv + , 46769 -- cvvdh rescue line | Free Form Intake | inputevents_cv + , 46773 -- CVVHD NS line flush | Free Form Intake | inputevents_cv + ) + and amount > 0 -- also ensures it's not null + group by icustay_id +) +, cv_oe as +( + select icustay_id + , 1 as RRT + from outputevents + where itemid in + ( + 40386 -- hemodialysis + , 40425 -- dialysis output + , 40426 -- dialysis out + , 40507 -- Dialysis out + , 40613 -- DIALYSIS OUT + , 40624 -- dialysis + , 40690 -- DIALYSIS + , 40745 -- Dialysis + , 40789 -- PD dialysate out + , 40881 -- Hemodialysis + , 40910 -- PERITONEAL DIALYSIS + , 41016 -- hemodialysis out + , 41034 -- dialysis in + , 41069 -- PD Dialysate Output + , 41112 -- Dialysys out + , 41250 -- HEMODIALYSIS OUT + , 41374 -- Dialysis Out + , 41417 -- Hemodialysis Out + , 41500 -- hemodialysis output + , 41527 -- HEMODIALYSIS + , 41623 -- dialysate out + , 41635 -- Hemodialysis removal + , 41713 -- dialyslate out + , 41750 -- dialysis out + , 41829 -- HEMODIALYSIS OUTPUT + , 41842 -- Dialysis Output. + , 41897 -- CVVH OUTPUT FROM OR + , 42289 -- dialysis off + , 42388 -- DIALYSIS OUTPUT + , 42464 -- hemodialysis ultrafe + , 42524 -- HemoDialysis + , 42536 -- Dialysis output + , 42868 -- hemodialysis off + , 42928 -- HEMODIALYSIS. + , 42972 -- HEMODIALYSIS OFF + , 43016 -- DIALYSIS TOTAL OUT + , 43052 -- DIALYSIS REMOVED + , 43098 -- hemodialysis crystal + , 43115 -- dialysis net + , 43687 -- crystalloid/dialysis + , 43941 -- dialysis/intake + , 44027 -- dialysis fluid off + , 44085 -- DIALYSIS OFF + , 44193 -- Dialysis. + , 44199 -- HEMODIALYSIS O/P + , 44216 -- Hemodialysis out + , 44286 -- Dialysis indwelling + , 44567 -- Hemodialysis. + , 44843 -- peritoneal dialysis + , 44845 -- Dialysis fluids + , 44857 -- dialysis- fluid off + , 44901 -- Dialysis Removed + , 44943 -- fluid removed dialys + , 45479 -- Dialysis In + , 45828 -- Hemo dialysis out + , 46230 -- Dialysis 1.5% IN + , 46232 -- dialysis flush + , 46394 -- Peritoneal dialysis + , 46464 -- Hemodialysis OUT + , 46712 -- CALCIUM-DIALYSIS + , 46713 -- KCL-10 MEQ-DIALYSIS + , 46715 -- Citrate - dialysis + , 46741 -- dialysis removed + ) + and value > 0 -- also ensures it's not null + group by icustay_id +) +, mv_ce as +( + select icustay_id + , 1 as RRT + FROM chartevents ce + where itemid in + ( + -- Checkboxes + 226118 -- | Dialysis Catheter placed in outside facility | Access Lines - Invasive | chartevents | Checkbox + , 227357 -- | Dialysis Catheter Dressing Occlusive | Access Lines - Invasive | chartevents | Checkbox + , 225725 -- | Dialysis Catheter Tip Cultured | Access Lines - Invasive | chartevents | Checkbox + -- Numeric values + , 226499 -- | Hemodialysis Output | Dialysis | chartevents | Numeric + , 224154 -- | Dialysate Rate | Dialysis | chartevents | Numeric + , 225810 -- | Dwell Time (Peritoneal Dialysis) | Dialysis | chartevents | Numeric + , 227639 -- | Medication Added Amount #2 (Peritoneal Dialysis) | Dialysis | chartevents | Numeric + , 225183 -- | Current Goal | Dialysis | chartevents | Numeric + , 227438 -- | Volume not removed | Dialysis | chartevents | Numeric + , 224191 -- | Hourly Patient Fluid Removal | Dialysis | chartevents | Numeric + , 225806 -- | Volume In (PD) | Dialysis | chartevents | Numeric + , 225807 -- | Volume Out (PD) | Dialysis | chartevents | Numeric + , 228004 -- | Citrate (ACD-A) | Dialysis | chartevents | Numeric + , 228005 -- | PBP (Prefilter) Replacement Rate | Dialysis | chartevents | Numeric + , 228006 -- | Post Filter Replacement Rate | Dialysis | chartevents | Numeric + , 224144 -- | Blood Flow (ml/min) | Dialysis | chartevents | Numeric + , 224145 -- | Heparin Dose (per hour) | Dialysis | chartevents | Numeric + , 224149 -- | Access Pressure | Dialysis | chartevents | Numeric + , 224150 -- | Filter Pressure | Dialysis | chartevents | Numeric + , 224151 -- | Effluent Pressure | Dialysis | chartevents | Numeric + , 224152 -- | Return Pressure | Dialysis | chartevents | Numeric + , 224153 -- | Replacement Rate | Dialysis | chartevents | Numeric + , 224404 -- | ART Lumen Volume | Dialysis | chartevents | Numeric + , 224406 -- | VEN Lumen Volume | Dialysis | chartevents | Numeric + , 226457 -- | Ultrafiltrate Output | Dialysis | chartevents | Numeric + , 225959 -- | Medication Added Amount #1 (Peritoneal Dialysis) | Dialysis | chartevents | Numeric + -- Text values + , 224135 -- | Dialysis Access Site | Dialysis | chartevents | Text + , 224139 -- | Dialysis Site Appearance | Dialysis | chartevents | Text + , 224146 -- | System Integrity | Dialysis | chartevents | Text + , 225323 -- | Dialysis Catheter Site Appear | Access Lines - Invasive | chartevents | Text + , 225740 -- | Dialysis Catheter Discontinued | Access Lines - Invasive | chartevents | Text + , 225776 -- | Dialysis Catheter Dressing Type | Access Lines - Invasive | chartevents | Text + , 225951 -- | Peritoneal Dialysis Fluid Appearance | Dialysis | chartevents | Text + , 225952 -- | Medication Added #1 (Peritoneal Dialysis) | Dialysis | chartevents | Text + , 225953 -- | Solution (Peritoneal Dialysis) | Dialysis | chartevents | Text + , 225954 -- | Dialysis Access Type | Dialysis | chartevents | Text + , 225956 -- | Reason for CRRT Filter Change | Dialysis | chartevents | Text + , 225958 -- | Heparin Concentration (units/mL) | Dialysis | chartevents | Text + , 225961 -- | Medication Added Units #1 (Peritoneal Dialysis) | Dialysis | chartevents | Text + , 225963 -- | Peritoneal Dialysis Catheter Type | Dialysis | chartevents | Text + , 225965 -- | Peritoneal Dialysis Catheter Status | Dialysis | chartevents | Text + , 225976 -- | Replacement Fluid | Dialysis | chartevents | Text + , 225977 -- | Dialysate Fluid | Dialysis | chartevents | Text + , 227124 -- | Dialysis Catheter Type | Access Lines - Invasive | chartevents | Text + , 227290 -- | CRRT mode | Dialysis | chartevents | Text + , 227638 -- | Medication Added #2 (Peritoneal Dialysis) | Dialysis | chartevents | Text + , 227640 -- | Medication Added Units #2 (Peritoneal Dialysis) | Dialysis | chartevents | Text + , 227753 -- | Dialysis Catheter Placement Confirmed by X-ray | Access Lines - Invasive | chartevents | Text + ) + and ce.valuenum > 0 -- also ensures it's not null + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by icustay_id +) +, mv_ie as +( + select icustay_id + , 1 as RRT + FROM inputevents_mv + where itemid in + ( + 227536 -- KCl (CRRT) Medications inputevents_mv Solution + , 227525 -- Calcium Gluconate (CRRT) Medications inputevents_mv Solution + ) + and amount > 0 -- also ensures it's not null + group by icustay_id +) +, mv_de as +( + select icustay_id + , 1 as RRT + from datetimeevents + where itemid in + ( + -- TODO: unsure how to handle "Last dialysis" + -- 225128 -- | Last dialysis | Adm History/FHPA | datetimeevents | Date time + 225318 -- | Dialysis Catheter Cap Change | Access Lines - Invasive | datetimeevents | Date time + , 225319 -- | Dialysis Catheter Change over Wire Date | Access Lines - Invasive | datetimeevents | Date time + , 225321 -- | Dialysis Catheter Dressing Change | Access Lines - Invasive | datetimeevents | Date time + , 225322 -- | Dialysis Catheter Insertion Date | Access Lines - Invasive | datetimeevents | Date time + , 225324 -- | Dialysis CatheterTubing Change | Access Lines - Invasive | datetimeevents | Date time + ) + group by icustay_id +) +, mv_pe as +( + select icustay_id + , 1 as RRT + FROM procedureevents_mv + where itemid in + ( + 225441 -- | Hemodialysis | 4-Procedures | procedureevents_mv | Process + , 225802 -- | Dialysis - CRRT | Dialysis | procedureevents_mv | Process + , 225803 -- | Dialysis - CVVHD | Dialysis | procedureevents_mv | Process + , 225805 -- | Peritoneal Dialysis | Dialysis | procedureevents_mv | Process + , 224270 -- | Dialysis Catheter | Access Lines - Invasive | procedureevents_mv | Process + , 225809 -- | Dialysis - CVVHDF | Dialysis | procedureevents_mv | Process + , 225955 -- | Dialysis - SCUF | Dialysis | procedureevents_mv | Process + , 225436 -- | CRRT Filter Change | Dialysis | procedureevents_mv | Process + ) + group by icustay_id +) +select ie.subject_id, ie.hadm_id, ie.icustay_id + , case + when cv_ce.RRT = 1 then 1 + when cv_ie.RRT = 1 then 1 + when cv_oe.RRT = 1 then 1 + when mv_ce.RRT = 1 then 1 + when mv_ie.RRT = 1 then 1 + when mv_de.RRT = 1 then 1 + when mv_pe.RRT = 1 then 1 + else 0 + end as RRT +FROM icustays ie +left join cv_ce + on ie.icustay_id = cv_ce.icustay_id +left join cv_ie + on ie.icustay_id = cv_ie.icustay_id +left join cv_oe + on ie.icustay_id = cv_oe.icustay_id +left join mv_ce + on ie.icustay_id = mv_ce.icustay_id +left join mv_ie + on ie.icustay_id = mv_ie.icustay_id +left join mv_de + on ie.icustay_id = mv_de.icustay_id +left join mv_pe + on ie.icustay_id = mv_pe.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/sepsis/angus.sql b/mimic-iii/concepts_postgres/sepsis/angus.sql new file mode 100644 index 000000000..9ae39ef88 --- /dev/null +++ b/mimic-iii/concepts_postgres/sepsis/angus.sql @@ -0,0 +1,104 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS angus; CREATE TABLE angus AS +-- ICD-9 codes for Angus criteria of sepsis + +-- Angus et al, 2001. Epidemiology of severe sepsis in the United States +-- http://www.ncbi.nlm.nih.gov/pubmed/11445675 + +-- Case selection and definitions +-- To identify cases with severe sepsis, we selected all acute care +-- hospitalizations with ICD-9-CM codes for both: +-- (a) a bacterial or fungal infectious process AND +-- (b) a diagnosis of acute organ dysfunction (Appendix 2). + +-- ICD-9 codes for infection - as sourced from Appendix 1 of above paper +WITH infection_group AS +( + SELECT subject_id, hadm_id, + CASE + WHEN SUBSTR(icd9_code,1,3) IN ('001','002','003','004','005','008', + '009','010','011','012','013','014','015','016','017','018', + '020','021','022','023','024','025','026','027','030','031', + '032','033','034','035','036','037','038','039','040','041', + '090','091','092','093','094','095','096','097','098','100', + '101','102','103','104','110','111','112','114','115','116', + '117','118','320','322','324','325','420','421','451','461', + '462','463','464','465','481','482','485','486','494','510', + '513','540','541','542','566','567','590','597','601','614', + '615','616','681','682','683','686','730') THEN 1 + WHEN SUBSTR(icd9_code,1,4) IN ('5695','5720','5721','5750','5990','7110', + '7907','9966','9985','9993') THEN 1 + WHEN SUBSTR(icd9_code,1,5) IN ('49121','56201','56203','56211','56213', + '56983') THEN 1 + ELSE 0 END AS infection + from diagnoses_icd +), +-- ICD-9 codes for organ dysfunction - as sourced from Appendix 2 of above paper +organ_diag_group as +( + SELECT subject_id, hadm_id, + CASE + -- Acute Organ Dysfunction Diagnosis Codes + WHEN SUBSTR(icd9_code,1,3) IN ('458','293','570','584') THEN 1 + WHEN SUBSTR(icd9_code,1,4) IN ('7855','3483','3481', + '2874','2875','2869','2866','5734') THEN 1 + ELSE 0 END AS organ_dysfunction, + -- Explicit diagnosis of severe sepsis or septic shock + CASE + WHEN SUBSTR(icd9_code,1,5) IN ('99592','78552') THEN 1 + ELSE 0 END AS explicit_sepsis + from diagnoses_icd +), +-- Mechanical ventilation +organ_proc_group as +( + SELECT subject_id, hadm_id, + CASE + WHEN icd9_code IN ('9670', '9671', '9672') THEN 1 + ELSE 0 END AS mech_vent + FROM procedures_icd +), +-- Aggregate above views together +aggregate as +( + SELECT subject_id, hadm_id, + CASE + WHEN hadm_id in + (SELECT DISTINCT hadm_id + FROM infection_group + WHERE infection = 1) + THEN 1 + ELSE 0 END AS infection, + CASE + WHEN hadm_id in + (SELECT DISTINCT hadm_id + FROM organ_diag_group + WHERE explicit_sepsis = 1) + THEN 1 + ELSE 0 END AS explicit_sepsis, + CASE + WHEN hadm_id in + (SELECT DISTINCT hadm_id + FROM organ_diag_group + WHERE organ_dysfunction = 1) + THEN 1 + ELSE 0 END AS organ_dysfunction, + CASE + WHEN hadm_id in + (SELECT DISTINCT hadm_id + FROM organ_proc_group + WHERE mech_vent = 1) + THEN 1 + ELSE 0 END AS mech_vent + FROM admissions +) +-- Output component flags (explicit sepsis, organ dysfunction) and final flag (angus) +SELECT subject_id, hadm_id, infection, + explicit_sepsis, organ_dysfunction, mech_vent, +CASE + WHEN explicit_sepsis = 1 THEN 1 + WHEN infection = 1 AND organ_dysfunction = 1 THEN 1 + WHEN infection = 1 AND mech_vent = 1 THEN 1 + ELSE 0 END +AS angus +FROM aggregate; diff --git a/mimic-iii/concepts_postgres/sepsis/explicit.sql b/mimic-iii/concepts_postgres/sepsis/explicit.sql new file mode 100644 index 000000000..e0473a9b8 --- /dev/null +++ b/mimic-iii/concepts_postgres/sepsis/explicit.sql @@ -0,0 +1,36 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS explicit; CREATE TABLE explicit AS +-- This code extracts explicit sepsis using ICD-9 diagnosis codes +-- That is, the two codes 995.92 (severe sepsis) or 785.52 (septic shock) +-- These codes are extremely specific to sepsis, but have very low sensitivity +-- From Iwashyna et al. (vs. chart reviews): 100% PPV, 9.3% sens, 100% specificity + +WITH co_dx AS +( + SELECT hadm_id + -- sepsis codes + , MAX( + CASE + WHEN icd9_code = '99592' THEN 1 + ELSE 0 END + ) AS severe_sepsis + , MAX( + CASE + WHEN icd9_code = '78552' THEN 1 + ELSE 0 END + ) AS septic_shock + from diagnoses_icd + GROUP BY hadm_id +) +select + adm.subject_id + , adm.hadm_id + , co_dx.severe_sepsis + , co_dx.septic_shock + , case when co_dx.severe_sepsis = 1 or co_dx.septic_shock = 1 + then 1 + else 0 end as sepsis +FROM admissions adm +left join co_dx + on adm.hadm_id = co_dx.hadm_id +order by adm.subject_id, adm.hadm_id; diff --git a/mimic-iii/concepts_postgres/sepsis/martin.sql b/mimic-iii/concepts_postgres/sepsis/martin.sql new file mode 100644 index 000000000..8b3710ee4 --- /dev/null +++ b/mimic-iii/concepts_postgres/sepsis/martin.sql @@ -0,0 +1,109 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS martin; CREATE TABLE martin AS +-- ICD-9 codes for sepsis as validated by Martin et al. + +-- Greg S. Martin, David M. Mannino, Stephanie Eaton, and Marc Moss. The epidemiology of +-- sepsis in the united states from 1979 through 2000. N Engl J Med, 348(16):1546–1554, Apr +-- 2003. doi: 10.1056/NEJMoa022139. URL http://dx.doi.org/10.1056/NEJMoa022139. + +WITH co_dx AS +( + SELECT subject_id, hadm_id + , MAX( + CASE + -- septicemia + WHEN SUBSTR(icd9_code,1,3) = '038' THEN 1 + -- septicemic, bacteremia, disseminated fungal infection, disseminated candida infection + -- NOTE: the paper specifies 020.0 ... but this is bubonic plague + -- presumably, they meant 020.2, which is septicemic plague + WHEN SUBSTR(icd9_code,1,4) in ('0202','7907','1179','1125') THEN 1 + -- disseminated fungal endocarditis + WHEN SUBSTR(icd9_code,1,5) = '11281' THEN 1 + ELSE 0 END + ) AS sepsis + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,4) in ('7991') THEN 1 + WHEN SUBSTR(icd9_code,1,5) in ('51881','51882','51885','78609') THEN 1 + ELSE 0 END + ) AS respiratory + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,4) in ('4580','7855','4580','4588','4589','7963') THEN 1 + WHEN SUBSTR(icd9_code,1,5) in ('785.51','785.59') THEN 1 + ELSE 0 END + ) AS cardiovascular + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,3) in ('584','580','585') THEN 1 + ELSE 0 END + ) AS renal + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,3) in ('570') THEN 1 + WHEN SUBSTR(icd9_code,1,4) in ('5722','5733') THEN 1 + ELSE 0 END + ) AS hepatic + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,4) in ('2862','2866','2869','2873','2874','2875') THEN 1 + ELSE 0 END + ) AS hematologic + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,4) in ('2762') THEN 1 + ELSE 0 END + ) AS metabolic + , MAX( + CASE + WHEN SUBSTR(icd9_code,1,3) in ('293') THEN 1 + WHEN SUBSTR(icd9_code,1,4) in ('3481','3483') THEN 1 + WHEN SUBSTR(icd9_code,1,5) in ('78001','78009') THEN 1 + ELSE 0 END + ) AS neurologic + from diagnoses_icd + GROUP BY subject_id, hadm_id +) +-- procedure codes: +-- "96.7 - Ventilator management" +-- translated: +-- 9670 Continuous invasive mechanical ventilation of unspecified duration +-- 9671 Continuous invasive mechanical ventilation for less than 96 consecutive hours +-- 9672 Continuous invasive mechanical ventilation for 96 consecutive hours or more +-- "39.95 - Hemodialysis" +-- 3995 Hemodialysis +-- "89.14 - Electroencephalography" +-- 8914 Electroencephalogram +, co_proc as +( + SELECT subject_id, hadm_id + , MAX(CASE WHEN icd9_code = '967' then 1 ELSE 0 END) as respiratory + , MAX(CASE WHEN icd9_code = '3995' then 1 ELSE 0 END) as renal + , MAX(CASE WHEN icd9_code = '8914' then 1 ELSE 0 END) as neurologic + FROM procedures_icd + GROUP BY subject_id, hadm_id +) +select adm.subject_id, adm.hadm_id +, co_dx.sepsis +, CASE + WHEN co_dx.respiratory = 1 OR co_proc.respiratory = 1 + OR co_dx.cardiovascular = 1 + OR co_dx.renal = 1 OR co_proc.renal = 1 + OR co_dx.hepatic = 1 + OR co_dx.hematologic = 1 + OR co_dx.metabolic = 1 + OR co_dx.neurologic = 1 OR co_proc.neurologic = 1 + THEN 1 + ELSE 0 END as organ_failure +, case when co_dx.respiratory = 1 or co_proc.respiratory = 1 then 1 else 0 end as respiratory +, co_dx.cardiovascular +, case when co_dx.renal = 1 or co_proc.renal = 1 then 1 else 0 end as renal +, co_dx.hepatic +, co_dx.hematologic +, co_dx.metabolic +, case when co_dx.neurologic = 1 or co_proc.neurologic = 1 then 1 else 0 end as neurologic +FROM admissions adm +left join co_dx + on adm.hadm_id = co_dx.hadm_id +left join co_proc + on adm.hadm_id = co_proc.hadm_id; diff --git a/mimic-iii/concepts_postgres/sepsis/sepsis3.sql b/mimic-iii/concepts_postgres/sepsis/sepsis3.sql new file mode 100644 index 000000000..99d6437bf --- /dev/null +++ b/mimic-iii/concepts_postgres/sepsis/sepsis3.sql @@ -0,0 +1,2 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS sepsis3; CREATE TABLE sepsis3 AS diff --git a/mimic-iii/concepts_postgres/severityscores/apsiii.sql b/mimic-iii/concepts_postgres/severityscores/apsiii.sql new file mode 100644 index 000000000..d21e903a4 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/apsiii.sql @@ -0,0 +1,836 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS apsiii; CREATE TABLE apsiii AS +-- ------------------------------------------------------------------ +-- Title: Acute Physiology Score III (APS III) +-- This query extracts the acute physiology score III. +-- This score is a measure of patient severity of illness. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for APS III: +-- Knaus WA, Wagner DP, Draper EA, Zimmerman JE, Bergner M, Bastos PG, Sirio CA, Murphy DJ, Lotring T, Damiano A. +-- The APACHE III prognostic system. Risk prediction of hospital mortality for critically ill hospitalized adults. +-- Chest Journal. 1991 Dec 1;100(6):1619-36. + +-- Reference for the equation for calibrating APS III to hospital mortality: +-- Johnson, A. E. W. (2015). Mortality prediction and acuity assessment in critical care. +-- University of Oxford, Oxford, UK. + +-- Variables used in APS III: +-- GCS +-- VITALS: Heart rate, mean blood pressure, temperature, respiration rate +-- FLAGS: ventilation/cpap, chronic dialysis +-- IO: urine output +-- LABS: pao2, A-aDO2, hematocrit, WBC, creatinine +-- , blood urea nitrogen, sodium, albumin, bilirubin, glucose, pH, pCO2 + +-- The following views are required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) ventilation_first_day - generated by ventilated-first-day.sql +-- 3) vitals_first_day - generated by vitals-first-day.sql +-- 4) gcs_first_day - generated by gcs-first-day.sql +-- 5) labs_first_day - generated by labs-first-day.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate icustay_ids. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +-- List of TODO: +-- The site of temperature is not incorporated. Axillary measurements should be increased by 1 degree. +-- Unfortunately the data for metavision is not available at the moment. +-- 674 | Temp. Site +-- 224642 | Temperature Site + +with pa as +( + select bg.icustay_id, bg.charttime + , po2 as PaO2 + , ROW_NUMBER() over (partition by bg.icustay_id ORDER BY bg.po2 DESC) as rn + from blood_gas_first_day_arterial bg + left join ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + WHERE vd.icustay_id is null -- patient is *not* ventilated + -- and fio2 < 50, or if no fio2, assume room air + AND coalesce(fio2, fio2_chartevents, 21) < 50 + AND bg.po2 IS NOT NULL +) +, aa as +( + -- join blood gas to ventilation durations to determine if patient was vent + -- also join to cpap table for the same purpose + select bg.icustay_id, bg.charttime + , bg.aado2 + , ROW_NUMBER() over (partition by bg.icustay_id ORDER BY bg.aado2 DESC) as rn + -- row number indicating the highest AaDO2 + from blood_gas_first_day_arterial bg + INNER JOIN ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + WHERE vd.icustay_id is not null -- patient is ventilated + AND coalesce(fio2, fio2_chartevents) >= 50 + AND bg.aado2 IS NOT NULL +) +-- because ph/pco2 rules are an interaction *within* a blood gas, we calculate them here +-- the worse score is then taken for the final calculation +, acidbase as +( + select bg.icustay_id + , ph, pco2 as paco2 + , case + when ph is null or pco2 is null then null + when ph < 7.20 then + case + when pco2 < 50 then 12 + else 4 + end + when ph < 7.30 then + case + when pco2 < 30 then 9 + when pco2 < 40 then 6 + when pco2 < 50 then 3 + else 2 + end + when ph < 7.35 then + case + when pco2 < 30 then 9 + when pco2 < 45 then 0 + else 1 + end + when ph < 7.45 then + case + when pco2 < 30 then 5 + when pco2 < 45 then 0 + else 1 + end + when ph < 7.50 then + case + when pco2 < 30 then 5 + when pco2 < 35 then 0 + when pco2 < 45 then 2 + else 12 + end + when ph < 7.60 then + case + when pco2 < 40 then 3 + else 12 + end + else -- ph >= 7.60 + case + when pco2 < 25 then 0 + when pco2 < 40 then 3 + else 12 + end + end as acidbase_score + from blood_gas_first_day_arterial bg + where ph is not null and pco2 is not null +) +, acidbase_max as +( + select icustay_id, acidbase_score, ph, paco2 + -- create integer which indexes maximum value of score with 1 + , ROW_NUMBER() over (partition by icustay_id ORDER BY acidbase_score DESC) as acidbase_rn + from acidbase +) +-- define acute renal failure (ARF) as: +-- creatinine >=1.5 mg/dl +-- and urine output <410 cc/day +-- and no chronic dialysis +, arf as +( + select ie.icustay_id + , case + when labs.creatinine_max >= 1.5 + and uo.urineoutput < 410 + -- acute renal failure is only coded if the patient is not on chronic dialysis + -- we use ICD-9 coding of ESRD as a proxy for chronic dialysis + and icd.ckd = 0 + then 1 + else 0 end as arf + FROM icustays ie + left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id + left join labs_first_day labs + on ie.icustay_id = labs.icustay_id + left join + ( + select hadm_id + , max(case + -- severe kidney failure requiring use of dialysis + when icd9_code in ('5854','5855','5856') then 1 + -- we do not include 5859 as that is sometimes coded for acute-on-chronic ARF + else 0 end) + as ckd + from diagnoses_icd + group by hadm_id + ) icd + on ie.hadm_id = icd.hadm_id +) +, cohort as +( +select ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime + , ie.outtime + + , vital.heartrate_min + , vital.heartrate_max + , vital.meanbp_min + , vital.meanbp_max + , vital.tempc_min + , vital.tempc_max + , vital.resprate_min + , vital.resprate_max + + , pa.pao2 + , aa.aado2 + + , ab.ph + , ab.paco2 + , ab.acidbase_score + + , labs.hematocrit_min + , labs.hematocrit_max + , labs.wbc_min + , labs.wbc_max + , labs.creatinine_min + , labs.creatinine_max + , labs.bun_min + , labs.bun_max + , labs.sodium_min + , labs.sodium_max + , labs.albumin_min + , labs.albumin_max + , labs.bilirubin_min + , labs.bilirubin_max + + , case + when labs.glucose_max is null and vital.glucose_max is null + then null + when labs.glucose_max is null or vital.glucose_max > labs.glucose_max + then vital.glucose_max + when vital.glucose_max is null or labs.glucose_max > vital.glucose_max + then labs.glucose_max + else labs.glucose_max -- if equal, just pick labs + end as glucose_max + + , case + when labs.glucose_min is null and vital.glucose_min is null + then null + when labs.glucose_min is null or vital.glucose_min < labs.glucose_min + then vital.glucose_min + when vital.glucose_min is null or labs.glucose_min < vital.glucose_min + then labs.glucose_min + else labs.glucose_min -- if equal, just pick labs + end as glucose_min + + -- , labs.bicarbonate_min + -- , labs.bicarbonate_max + , vent.vent + , uo.urineoutput + -- gcs and its components + , gcs.mingcs + , gcs.gcsmotor, gcs.gcsverbal, gcs.gcseyes, gcs.endotrachflag + -- acute renal failure + , arf.arf as arf + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to above views - the row number filters to 1 row per icustay_id +left join pa + on ie.icustay_id = pa.icustay_id + and pa.rn = 1 +left join aa + on ie.icustay_id = aa.icustay_id + and aa.rn = 1 +left join acidbase_max ab + on ie.icustay_id = ab.icustay_id + and ab.acidbase_rn = 1 +left join arf + on ie.icustay_id = arf.icustay_id + +-- join to custom tables to get more data.... +left join ventilation_first_day vent + on ie.icustay_id = vent.icustay_id +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +) +-- First, we calculate the score for the minimum values +, score_min as +( + select cohort.subject_id, cohort.hadm_id, cohort.icustay_id + , case + when heartrate_min is null then null + when heartrate_min < 40 then 8 + when heartrate_min < 50 then 5 + when heartrate_min < 100 then 0 + when heartrate_min < 110 then 1 + when heartrate_min < 120 then 5 + when heartrate_min < 140 then 7 + when heartrate_min < 155 then 13 + when heartrate_min >= 155 then 17 + end as hr_score + + , case + when meanbp_min is null then null + when meanbp_min < 40 then 23 + when meanbp_min < 60 then 15 + when meanbp_min < 70 then 7 + when meanbp_min < 80 then 6 + when meanbp_min < 100 then 0 + when meanbp_min < 120 then 4 + when meanbp_min < 130 then 7 + when meanbp_min < 140 then 9 + when meanbp_min >= 140 then 10 + end as meanbp_score + + -- TODO: add 1 degree to axillary measurements + , case + when tempc_min is null then null + when tempc_min < 33.0 then 20 + when tempc_min < 33.5 then 16 + when tempc_min < 34.0 then 13 + when tempc_min < 35.0 then 8 + when tempc_min < 36.0 then 2 + when tempc_min < 40.0 then 0 + when tempc_min >= 40.0 then 4 + end as temp_score + + , case + when resprate_min is null then null + -- special case for ventilated patients + when vent = 1 and resprate_min < 14 then 0 + when resprate_min < 6 then 17 + when resprate_min < 12 then 8 + when resprate_min < 14 then 7 + when resprate_min < 25 then 0 + when resprate_min < 35 then 6 + when resprate_min < 40 then 9 + when resprate_min < 50 then 11 + when resprate_min >= 50 then 18 + end as resprate_score + + , case + when hematocrit_min is null then null + when hematocrit_min < 41.0 then 3 + when hematocrit_min < 50.0 then 0 + when hematocrit_min >= 50.0 then 3 + end as hematocrit_score + + , case + when wbc_min is null then null + when wbc_min < 1.0 then 19 + when wbc_min < 3.0 then 5 + when wbc_min < 20.0 then 0 + when wbc_min < 25.0 then 1 + when wbc_min >= 25.0 then 5 + end as wbc_score + + , case + when creatinine_min is null then null + when arf = 1 and creatinine_min < 1.5 then 0 + when arf = 1 and creatinine_min >= 1.5 then 10 + when creatinine_min < 0.5 then 3 + when creatinine_min < 1.5 then 0 + when creatinine_min < 1.95 then 4 + when creatinine_min >= 1.95 then 7 + end as creatinine_score + + , case + when bun_min is null then null + when bun_min < 17.0 then 0 + when bun_min < 20.0 then 2 + when bun_min < 40.0 then 7 + when bun_min < 80.0 then 11 + when bun_min >= 80.0 then 12 + end as bun_score + + , case + when sodium_min is null then null + when sodium_min < 120 then 3 + when sodium_min < 135 then 2 + when sodium_min < 155 then 0 + when sodium_min >= 155 then 4 + end as sodium_score + + , case + when albumin_min is null then null + when albumin_min < 2.0 then 11 + when albumin_min < 2.5 then 6 + when albumin_min < 4.5 then 0 + when albumin_min >= 4.5 then 4 + end as albumin_score + + , case + when bilirubin_min is null then null + when bilirubin_min < 2.0 then 0 + when bilirubin_min < 3.0 then 5 + when bilirubin_min < 5.0 then 6 + when bilirubin_min < 8.0 then 8 + when bilirubin_min >= 8.0 then 16 + end as bilirubin_score + + , case + when glucose_min is null then null + when glucose_min < 40 then 8 + when glucose_min < 60 then 9 + when glucose_min < 200 then 0 + when glucose_min < 350 then 3 + when glucose_min >= 350 then 5 + end as glucose_score + +from cohort +) +, score_max as +( + select cohort.subject_id, cohort.hadm_id, cohort.icustay_id + , case + when heartrate_max is null then null + when heartrate_max < 40 then 8 + when heartrate_max < 50 then 5 + when heartrate_max < 100 then 0 + when heartrate_max < 110 then 1 + when heartrate_max < 120 then 5 + when heartrate_max < 140 then 7 + when heartrate_max < 155 then 13 + when heartrate_max >= 155 then 17 + end as hr_score + + , case + when meanbp_max is null then null + when meanbp_max < 40 then 23 + when meanbp_max < 60 then 15 + when meanbp_max < 70 then 7 + when meanbp_max < 80 then 6 + when meanbp_max < 100 then 0 + when meanbp_max < 120 then 4 + when meanbp_max < 130 then 7 + when meanbp_max < 140 then 9 + when meanbp_max >= 140 then 10 + end as meanbp_score + + -- TODO: add 1 degree to axillary measurements + , case + when tempc_max is null then null + when tempc_max < 33.0 then 20 + when tempc_max < 33.5 then 16 + when tempc_max < 34.0 then 13 + when tempc_max < 35.0 then 8 + when tempc_max < 36.0 then 2 + when tempc_max < 40.0 then 0 + when tempc_max >= 40.0 then 4 + end as temp_score + + , case + when resprate_max is null then null + -- special case for ventilated patients + when vent = 1 and resprate_max < 14 then 0 + when resprate_max < 6 then 17 + when resprate_max < 12 then 8 + when resprate_max < 14 then 7 + when resprate_max < 25 then 0 + when resprate_max < 35 then 6 + when resprate_max < 40 then 9 + when resprate_max < 50 then 11 + when resprate_max >= 50 then 18 + end as resprate_score + + , case + when hematocrit_max is null then null + when hematocrit_max < 41.0 then 3 + when hematocrit_max < 50.0 then 0 + when hematocrit_max >= 50.0 then 3 + end as hematocrit_score + + , case + when wbc_max is null then null + when wbc_max < 1.0 then 19 + when wbc_max < 3.0 then 5 + when wbc_max < 20.0 then 0 + when wbc_max < 25.0 then 1 + when wbc_max >= 25.0 then 5 + end as wbc_score + + , case + when creatinine_max is null then null + when arf = 1 and creatinine_max < 1.5 then 0 + when arf = 1 and creatinine_max >= 1.5 then 10 + when creatinine_max < 0.5 then 3 + when creatinine_max < 1.5 then 0 + when creatinine_max < 1.95 then 4 + when creatinine_max >= 1.95 then 7 + end as creatinine_score + + , case + when bun_max is null then null + when bun_max < 17.0 then 0 + when bun_max < 20.0 then 2 + when bun_max < 40.0 then 7 + when bun_max < 80.0 then 11 + when bun_max >= 80.0 then 12 + end as bun_score + + , case + when sodium_max is null then null + when sodium_max < 120 then 3 + when sodium_max < 135 then 2 + when sodium_max < 155 then 0 + when sodium_max >= 155 then 4 + end as sodium_score + + , case + when albumin_max is null then null + when albumin_max < 2.0 then 11 + when albumin_max < 2.5 then 6 + when albumin_max < 4.5 then 0 + when albumin_max >= 4.5 then 4 + end as albumin_score + + , case + when bilirubin_max is null then null + when bilirubin_max < 2.0 then 0 + when bilirubin_max < 3.0 then 5 + when bilirubin_max < 5.0 then 6 + when bilirubin_max < 8.0 then 8 + when bilirubin_max >= 8.0 then 16 + end as bilirubin_score + + , case + when glucose_max is null then null + when glucose_max < 40 then 8 + when glucose_max < 60 then 9 + when glucose_max < 200 then 0 + when glucose_max < 350 then 3 + when glucose_max >= 350 then 5 + end as glucose_score + +from cohort +) +-- Combine together the scores for min/max, using the following rules: +-- 1) select the value furthest from a predefined normal value +-- 2) if both equidistant, choose the one which gives a worse score +-- 3) calculate score for acid-base abnormalities as it requires interactions +-- sometimes the code is a bit redundant, i.e. we know the max would always be furthest from 0 +, scorecomp as +( + select co.* + -- The rules for APS III require the definition of a "worst" value + -- This value is defined as whatever value is furthest from a predefined normal + -- e.g., for heart rate, worst is defined as furthest from 75 + , case + when heartrate_max is null then null + when abs(heartrate_max-75) > abs(heartrate_min-75) + then smax.hr_score + when abs(heartrate_max-75) < abs(heartrate_min-75) + then smin.hr_score + when abs(heartrate_max-75) = abs(heartrate_min-75) + and smax.hr_score >= smin.hr_score + then smax.hr_score + when abs(heartrate_max-75) = abs(heartrate_min-75) + and smax.hr_score < smin.hr_score + then smin.hr_score + end as hr_score + + , case + when meanbp_max is null then null + when abs(meanbp_max-90) > abs(meanbp_min-90) + then smax.meanbp_score + when abs(meanbp_max-90) < abs(meanbp_min-90) + then smin.meanbp_score + -- values are equidistant - pick the larger score + when abs(meanbp_max-90) = abs(meanbp_min-90) + and smax.meanbp_score >= smin.meanbp_score + then smax.meanbp_score + when abs(meanbp_max-90) = abs(meanbp_min-90) + and smax.meanbp_score < smin.meanbp_score + then smin.meanbp_score + end as meanbp_score + + , case + when tempc_max is null then null + when abs(tempc_max-38) > abs(tempc_min-38) + then smax.temp_score + when abs(tempc_max-38) < abs(tempc_min-38) + then smin.temp_score + -- values are equidistant - pick the larger score + when abs(tempc_max-38) = abs(tempc_min-38) + and smax.temp_score >= smin.temp_score + then smax.temp_score + when abs(tempc_max-38) = abs(tempc_min-38) + and smax.temp_score < smin.temp_score + then smin.temp_score + end as temp_score + + , case + when resprate_max is null then null + when abs(resprate_max-19) > abs(resprate_min-19) + then smax.resprate_score + when abs(resprate_max-19) < abs(resprate_min-19) + then smin.resprate_score + -- values are equidistant - pick the larger score + when abs(resprate_max-19) = abs(resprate_max-19) + and smax.resprate_score >= smin.resprate_score + then smax.resprate_score + when abs(resprate_max-19) = abs(resprate_max-19) + and smax.resprate_score < smin.resprate_score + then smin.resprate_score + end as resprate_score + + , case + when hematocrit_max is null then null + when abs(hematocrit_max-45.5) > abs(hematocrit_min-45.5) + then smax.hematocrit_score + when abs(hematocrit_max-45.5) < abs(hematocrit_min-45.5) + then smin.hematocrit_score + -- values are equidistant - pick the larger score + when abs(hematocrit_max-45.5) = abs(hematocrit_max-45.5) + and smax.hematocrit_score >= smin.hematocrit_score + then smax.hematocrit_score + when abs(hematocrit_max-45.5) = abs(hematocrit_max-45.5) + and smax.hematocrit_score < smin.hematocrit_score + then smin.hematocrit_score + end as hematocrit_score + + , case + when wbc_max is null then null + when abs(wbc_max-11.5) > abs(wbc_min-11.5) + then smax.wbc_score + when abs(wbc_max-11.5) < abs(wbc_min-11.5) + then smin.wbc_score + -- values are equidistant - pick the larger score + when abs(wbc_max-11.5) = abs(wbc_max-11.5) + and smax.wbc_score >= smin.wbc_score + then smax.wbc_score + when abs(wbc_max-11.5) = abs(wbc_max-11.5) + and smax.wbc_score < smin.wbc_score + then smin.wbc_score + end as wbc_score + + + -- For some labs, "furthest from normal" doesn't make sense + -- e.g. creatinine w/ ARF, the minimum could be 0.3, and the max 1.6 + -- while the minimum of 0.3 is "further from 1", seems like the max should be scored + + , case + when creatinine_max is null then null + -- if they have arf then use the max to score + when arf = 1 then smax.creatinine_score + -- otherwise furthest from 1 + when abs(creatinine_max-1) > abs(creatinine_min-1) + then smax.creatinine_score + when abs(creatinine_max-1) < abs(creatinine_min-1) + then smin.creatinine_score + -- values are equidistant + when smax.creatinine_score >= smin.creatinine_score + then smax.creatinine_score + when smax.creatinine_score < smin.creatinine_score + then smin.creatinine_score + end as creatinine_score + + -- the rule for BUN is the furthest from 0.. equivalent to the max value + , case + when bun_max is null then null + else smax.bun_score + end as bun_score + + , case + when sodium_max is null then null + when abs(sodium_max-145.5) > abs(sodium_min-145.5) + then smax.sodium_score + when abs(sodium_max-145.5) < abs(sodium_min-145.5) + then smin.sodium_score + -- values are equidistant - pick the larger score + when abs(sodium_max-145.5) = abs(sodium_max-145.5) + and smax.sodium_score >= smin.sodium_score + then smax.sodium_score + when abs(sodium_max-145.5) = abs(sodium_max-145.5) + and smax.sodium_score < smin.sodium_score + then smin.sodium_score + end as sodium_score + + , case + when albumin_max is null then null + when abs(albumin_max-3.5) > abs(albumin_min-3.5) + then smax.albumin_score + when abs(albumin_max-3.5) < abs(albumin_min-3.5) + then smin.albumin_score + -- values are equidistant - pick the larger score + when abs(albumin_max-3.5) = abs(albumin_max-3.5) + and smax.albumin_score >= smin.albumin_score + then smax.albumin_score + when abs(albumin_max-3.5) = abs(albumin_max-3.5) + and smax.albumin_score < smin.albumin_score + then smin.albumin_score + end as albumin_score + + , case + when bilirubin_max is null then null + else smax.bilirubin_score + end as bilirubin_score + + , case + when glucose_max is null then null + when abs(glucose_max-130) > abs(glucose_min-130) + then smax.glucose_score + when abs(glucose_max-130) < abs(glucose_min-130) + then smin.glucose_score + -- values are equidistant - pick the larger score + when abs(glucose_max-130) = abs(glucose_max-130) + and smax.glucose_score >= smin.glucose_score + then smax.glucose_score + when abs(glucose_max-130) = abs(glucose_max-130) + and smax.glucose_score < smin.glucose_score + then smin.glucose_score + end as glucose_score + + + -- Below are interactions/special cases where only 1 value is important + , case + when urineoutput is null then null + when urineoutput < 400 then 15 + when urineoutput < 600 then 8 + when urineoutput < 900 then 7 + when urineoutput < 1500 then 5 + when urineoutput < 2000 then 4 + when urineoutput < 4000 then 0 + when urineoutput >= 4000 then 1 + end as uo_score + + , case + when endotrachflag = 1 + -- here they are intubated, so their verbal score is inappropriate + -- normally you are supposed to use "clinical judgement" + -- we don't have that, so we just assume normal (as was done in the original study) + then 0 + when gcseyes = 1 + then case + when gcsverbal = 1 and gcsmotor in (1,2) + then 48 + when gcsverbal = 1 and gcsmotor in (3,4) + then 33 + when gcsverbal = 1 and gcsmotor in (5,6) + then 16 + when gcsverbal in (2,3) and gcsmotor in (1,2) + then 29 + when gcsverbal in (2,3) and gcsmotor in (3,4) + then 24 + when gcsverbal in (2,3) and gcsmotor >= 5 + -- highly unlikely clinical combination + then null + when gcsverbal >= 4 + then null + end + when gcseyes > 1 + then case + when gcsverbal = 1 and gcsmotor in (1,2) + then 29 + when gcsverbal = 1 and gcsmotor in (3,4) + then 24 + when gcsverbal = 1 and gcsmotor in (5,6) + then 15 + when gcsverbal in (2,3) and gcsmotor in (1,2) + then 29 + when gcsverbal in (2,3) and gcsmotor in (3,4) + then 24 + when gcsverbal in (2,3) and gcsmotor = 5 + then 13 + when gcsverbal in (2,3) and gcsmotor = 6 + then 10 + when gcsverbal = 4 and gcsmotor in (1,2,3,4) + then 13 + when gcsverbal = 4 and gcsmotor = 5 + then 8 + when gcsverbal = 4 and gcsmotor = 6 + then 3 + when gcsverbal = 5 and gcsmotor in (1,2,3,4,5) + then 3 + when gcsverbal = 5 and gcsmotor = 6 + then 0 + end + else null + end as gcs_score + + , case + when pao2 is null and aado2 is null + then null + when pao2 is not null then + case + when pao2 < 50 then 15 + when pao2 < 70 then 5 + when pao2 < 80 then 2 + else 0 end + when aado2 is not null then + case + when aado2 < 100 then 0 + when aado2 < 250 then 7 + when aado2 < 350 then 9 + when aado2 < 500 then 11 + when aado2 >= 500 then 14 + else 0 end + end as pao2_aado2_score + +from cohort co +left join score_min smin + on co.icustay_id = smin.icustay_id +left join score_max smax + on co.icustay_id = smax.icustay_id +) +-- tabulate the APS III using the scores from the worst values +, score as +( + select s.* + -- coalesce statements impute normal score of zero if data element is missing + , coalesce(hr_score,0) + + coalesce(meanbp_score,0) + + coalesce(temp_score,0) + + coalesce(resprate_score,0) + + coalesce(pao2_aado2_score,0) + + coalesce(hematocrit_score,0) + + coalesce(wbc_score,0) + + coalesce(creatinine_score,0) + + coalesce(uo_score,0) + + coalesce(bun_score,0) + + coalesce(sodium_score,0) + + coalesce(albumin_score,0) + + coalesce(bilirubin_score,0) + + coalesce(glucose_score,0) + + coalesce(acidbase_score,0) + + coalesce(gcs_score,0) + as apsiii + from scorecomp s +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +, apsiii +-- Calculate probability of hospital mortality using equation from Johnson 2014. +, 1 / (1 + exp(- (-4.4360 + 0.04726*(apsiii) ))) as apsiii_prob +, hr_score +, meanbp_score +, temp_score +, resprate_score +, pao2_aado2_score +, hematocrit_score +, wbc_score +, creatinine_score +, uo_score +, bun_score +, sodium_score +, albumin_score +, bilirubin_score +, glucose_score +, acidbase_score +, gcs_score +FROM icustays ie +left join score s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/lods.sql b/mimic-iii/concepts_postgres/severityscores/lods.sql new file mode 100644 index 000000000..af27a3169 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/lods.sql @@ -0,0 +1,236 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS lods; CREATE TABLE lods AS +-- ------------------------------------------------------------------ +-- Title: Logistic Organ Dysfunction Score (LODS) +-- This query extracts the logistic organ dysfunction system. +-- This score is a measure of organ failure in a patient. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for LODS: +-- Le Gall, J. R., Klar, J., Lemeshow, S., Saulnier, F., Alberti, C., Artigas, A., & Teres, D. +-- The Logistic Organ Dysfunction system: a new way to assess organ dysfunction in the intensive care unit. +-- JAMA 276.10 (1996): 802-810. + +-- Variables used in LODS: +-- GCS +-- VITALS: Heart rate, systolic blood pressure +-- FLAGS: ventilation/cpap +-- IO: urine output +-- LABS: blood urea nitrogen, WBC, bilirubin, creatinine, prothrombin time (PT), platelets +-- ABG: PaO2 with associated FiO2 + +-- The following views are required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) ventilation_durations - generated by ventilation_durations.sql +-- 3) vitals_first_day - generated by vitals-first-day.sql +-- 4) gcs_first_day - generated by gcs-first-day.sql +-- 5) labs_first_day - generated by labs-first-day.sql +-- 5) blood_gas_first_day_arterial - generated by blood-gas-first-day-arterial.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +-- extract CPAP from the "Oxygen Delivery Device" fields +with cpap as +( + select ie.icustay_id + , min(DATETIME_SUB(charttime, INTERVAL '1' HOUR)) as starttime + , max(DATETIME_ADD(charttime, INTERVAL '4' HOUR)) as endtime + , max(CASE + WHEN lower(ce.value) LIKE '%cpap%' THEN 1 + WHEN lower(ce.value) LIKE '%bipap mask%' THEN 1 + else 0 end) as cpap + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where itemid in + ( + -- TODO: when metavision data import fixed, check the values in 226732 match the value clause below + 467, 469, 226732 + ) + and (lower(ce.value) LIKE '%cpap%' or lower(ce.value) LIKE '%bipap mask%') + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by ie.icustay_id +) +, pafi1 as +( + -- join blood gas to ventilation durations to determine if patient was vent + -- also join to cpap table for the same purpose + select bg.icustay_id, bg.charttime + , pao2fio2 + , case when vd.icustay_id is not null then 1 else 0 end as vent + , case when cp.icustay_id is not null then 1 else 0 end as cpap + from blood_gas_first_day_arterial bg + left join ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + left join cpap cp + on bg.icustay_id = cp.icustay_id + and bg.charttime >= cp.starttime + and bg.charttime <= cp.endtime +) +, pafi2 as +( + -- get the minimum PaO2/FiO2 ratio *only for ventilated/cpap patients* + select icustay_id + , min(pao2fio2) as pao2fio2_vent_min + from pafi1 + where vent = 1 or cpap = 1 + group by icustay_id +) +, cohort as +( +select ie.subject_id + , ie.hadm_id + , ie.icustay_id + , ie.intime + , ie.outtime + + , gcs.mingcs + , vital.heartrate_max + , vital.heartrate_min + , vital.sysbp_max + , vital.sysbp_min + + -- this value is non-null iff the patient is on vent/cpap + , pf.pao2fio2_vent_min + + , labs.bun_max + , labs.bun_min + , labs.wbc_max + , labs.wbc_min + , labs.bilirubin_max + , labs.creatinine_max + , labs.pt_min + , labs.pt_max + , labs.platelet_min + + , uo.urineoutput + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to above view to get pao2/fio2 ratio +left join pafi2 pf + on ie.icustay_id = pf.icustay_id + +-- join to custom tables to get more data.... +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +) +, scorecomp as +( +select + cohort.* + -- Below code calculates the component scores needed for SAPS + + -- neurologic + , case + when mingcs is null then null + when mingcs < 3 then null -- erroneous value/on trach + when mingcs <= 5 then 5 + when mingcs <= 8 then 3 + when mingcs <= 13 then 1 + else 0 + end as neurologic + + -- cardiovascular + , case + when heartrate_max is null + and sysbp_min is null then null + when heartrate_min < 30 then 5 + when sysbp_min < 40 then 5 + when sysbp_min < 70 then 3 + when sysbp_max >= 270 then 3 + when heartrate_max >= 140 then 1 + when sysbp_max >= 240 then 1 + when sysbp_min < 90 then 1 + else 0 + end as cardiovascular + + -- renal + , case + when bun_max is null + or urineoutput is null + or creatinine_max is null + then null + when urineoutput < 500.0 then 5 + when bun_max >= 56.0 then 5 + when creatinine_max >= 1.60 then 3 + when urineoutput < 750.0 then 3 + when bun_max >= 28.0 then 3 + when urineoutput >= 10000.0 then 3 + when creatinine_max >= 1.20 then 1 + when bun_max >= 17.0 then 1 + when bun_max >= 7.50 then 1 + else 0 + end as renal + + -- pulmonary + , case + when pao2fio2_vent_min is null then 0 + when pao2fio2_vent_min >= 150 then 1 + when pao2fio2_vent_min < 150 then 3 + else null + end as pulmonary + + -- hematologic + , case + when wbc_max is null + and platelet_min is null + then null + when wbc_min < 1.0 then 3 + when wbc_min < 2.5 then 1 + when platelet_min < 50.0 then 1 + when wbc_max >= 50.0 then 1 + else 0 + end as hematologic + + -- hepatic + -- We have defined the "standard" PT as 12 seconds. + -- This is an assumption and subsequent analyses may be affected by this assumption. + , case + when pt_max is null + and bilirubin_max is null + then null + when bilirubin_max >= 2.0 then 1 + when pt_max > (12+3) then 1 + when pt_min < (12*0.25) then 1 + else 0 + end as hepatic + +from cohort +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +-- coalesce statements impute normal score of zero if data element is missing +, coalesce(neurologic,0) ++ coalesce(cardiovascular,0) ++ coalesce(renal,0) ++ coalesce(pulmonary,0) ++ coalesce(hematologic,0) ++ coalesce(hepatic,0) + as LODS +, neurologic +, cardiovascular +, renal +, pulmonary +, hematologic +, hepatic +FROM icustays ie +left join scorecomp s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/mlods.sql b/mimic-iii/concepts_postgres/severityscores/mlods.sql new file mode 100644 index 000000000..fcc843ba0 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/mlods.sql @@ -0,0 +1,224 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS mlods; CREATE TABLE mlods AS +-- ------------------------------------------------------------------ +-- Title: Modified Logistic organ dysfunction system (mLODS) +-- This query extracts a modified version of the logistic organ dysfunction system. +-- This score was used in the third international definition of sepsis: Sepsis-3. +-- This score is a measure of organ failure in a patient. +-- ------------------------------------------------------------------ + +-- Reference for LODS: +-- Le Gall, J. R., Klar, J., Lemeshow, S., Saulnier, F., Alberti, C., Artigas, A., & Teres, D. +-- The Logistic Organ Dysfunction system: a new way to assess organ dysfunction in the intensive care unit. +-- JAMA 276.10 (1996): 802-810. + +-- Reference for modified LODS: +-- Le Gall, J. R., Klar, J., Lemeshow, S., Saulnier, F., Alberti, C., Artigas, A., & Teres, D. +-- The Logistic Organ Dysfunction system: a new way to assess organ dysfunction in the intensive care unit. +-- JAMA 276.10 (1996): 802-810. + +-- Variables used in mLODS: +-- GCS +-- VITALS: Heart rate, systolic blood pressure +-- FLAGS: ventilation/cpap +-- LABS: WBC, bilirubin, creatinine, platelets +-- ABG: PaO2 with associated FiO2 + +-- Variables *excluded*, that are used in the original LODS: +-- prothrombin time (PT), blood urea nitrogen, urine output + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +-- extract CPAP from the "Oxygen Delivery Device" fields +with cpap as +( + select ie.icustay_id + , min(DATETIME_SUB(charttime, INTERVAL '1' HOUR)) as starttime + , max(DATETIME_ADD(charttime, INTERVAL '4' HOUR)) as endtime + , max(CASE + WHEN lower(ce.value) LIKE '%cpap%' THEN 1 + WHEN lower(ce.value) LIKE '%bipap mask%' THEN 1 + else 0 end) as cpap + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and ie.outtime + where itemid in + ( + -- TODO: when metavision data import fixed, check the values in 226732 match the value clause below + 467, 469, 226732 + ) + and (lower(ce.value) LIKE '%cpap%' or lower(ce.value) LIKE '%bipap mask%') + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by ie.icustay_id +) +, pafi1 as +( + -- join blood gas to ventilation durations to determine if patient was vent + -- also join to cpap table for the same purpose + select bg.icustay_id, bg.charttime + , PaO2FiO2 + , case when vd.icustay_id is not null then 1 else 0 end as vent + , case when cp.icustay_id is not null then 1 else 0 end as cpap + from blood_gas_first_day_arterial bg + left join ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + left join cpap cp + on bg.icustay_id = cp.icustay_id + and bg.charttime >= cp.starttime + and bg.charttime <= cp.endtime +) +, pafi2 as +( + -- get the minimum PaO2/FiO2 ratio *only for ventilated/cpap patients* + select icustay_id + , min(PaO2FiO2) as PaO2FiO2_vent_min + from pafi1 + where vent = 1 or cpap = 1 + group by icustay_id +) +, cohort as +( +select ie.subject_id + , ie.hadm_id + , ie.icustay_id + , ie.intime + , ie.outtime + + , gcs.mingcs + , vital.heartrate_max + , vital.heartrate_min + , vital.sysbp_max + , vital.sysbp_min + + -- this value is non-null iff the patient is on vent/cpap + , pf.PaO2FiO2_vent_min + + , labs.wbc_max + , labs.wbc_min + , labs.bilirubin_max + , labs.creatinine_max + , labs.platelet_min + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to above view to get pao2/fio2 ratio +left join pafi2 pf + on ie.icustay_id = pf.icustay_id + +-- join to custom tables to get more data.... +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +) +, scorecomp as +( +select + cohort.* + + -- neurologic + , case + when mingcs is null then null + when mingcs < 3 then null -- erroneous value/on trach + when mingcs <= 5 then 5 + when mingcs <= 8 then 3 + when mingcs <= 13 then 1 + else 0 + end as neurologic + + -- cardiovascular + , case + when heartrate_max is null + and sysbp_min is null then null + when heartrate_min < 30 then 5 + when sysbp_min < 40 then 5 + when sysbp_min < 70 then 3 + when sysbp_max >= 270 then 3 + when heartrate_max >= 140 then 1 + when sysbp_max >= 240 then 1 + when sysbp_min < 90 then 1 + else 0 + end as cardiovascular + + -- renal + , case + when creatinine_max is null + -- or UrineOutput is null + -- or bun_max is null + then null + -- when UrineOutput < 500.0 then 5 + -- when bun_max >= 56.0 then 5 + when creatinine_max >= 1.60 then 3 + -- when UrineOutput < 750.0 then 3 + -- when bun_max >= 28.0 then 3 + -- when UrineOutput >= 10000.0 then 3 + when creatinine_max >= 1.20 then 1 + -- when bun_max >= 17.0 then 1 + -- when bun_max >= 7.50 then 1 + else 0 + end as renal + + -- pulmonary + , case + when PaO2FiO2_vent_min is null then 0 + when PaO2FiO2_vent_min >= 150 then 1 + when PaO2FiO2_vent_min < 150 then 3 + else null + end as pulmonary + + -- hematologic + , case + when wbc_max is null + and platelet_min is null + then null + when wbc_min < 1.0 then 3 + when wbc_min < 2.5 then 1 + when platelet_min < 50.0 then 1 + when wbc_max >= 50.0 then 1 + else 0 + end as hematologic + + -- hepatic + , case + when bilirubin_max is null + -- and pt_max is null + then null + when bilirubin_max >= 2.0 then 1 + -- when pt_max > (12+3) then 1 + -- when pt_min < (12*0.25) then 1 + else 0 + end as hepatic + +from cohort +) +select ie.icustay_id +-- coalesce statements impute normal score of zero if data element is missing +, coalesce(neurologic,0) ++ coalesce(cardiovascular,0) ++ coalesce(renal,0) ++ coalesce(pulmonary,0) ++ coalesce(hematologic,0) ++ coalesce(hepatic,0) + as mLODS +, neurologic +, cardiovascular +, renal +, pulmonary +, hematologic +, hepatic +FROM icustays ie +left join scorecomp s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/oasis.sql b/mimic-iii/concepts_postgres/severityscores/oasis.sql new file mode 100644 index 000000000..7c618896c --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/oasis.sql @@ -0,0 +1,254 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS oasis; CREATE TABLE oasis AS +-- ------------------------------------------------------------------ +-- Title: Oxford Acute Severity of Illness Score (oasis) +-- This query extracts the Oxford acute severity of illness score. +-- This score is a measure of severity of illness for patients in the ICU. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for OASIS: +-- Johnson, Alistair EW, Andrew A. Kramer, and Gari D. Clifford. +-- "A new severity of illness scale using a subset of acute physiology and chronic health evaluation data elements shows comparable predictive accuracy*." +-- Critical care medicine 41, no. 7 (2013): 1711-1718. + +-- Variables used in OASIS: +-- Heart rate, GCS, MAP, Temperature, Respiratory rate, Ventilation status (sourced FROM chartevents) +-- Urine output (sourced from OUTPUTEVENTS) +-- Elective surgery (sourced FROM admissions and SERVICES) +-- Pre-ICU in-hospital length of stay (sourced FROM admissions and ICUSTAYS) +-- Age (sourced FROM patients) + +-- The following views are required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) vent_first_day - generated by ventilated-first-day.sql +-- 3) vitals_first_day - generated by vitals-first-day.sql +-- 4) gcs_first_day - generated by gcs-first-day.sql + + +-- Regarding missing values: +-- The ventilation flag is always 0/1. It cannot be missing, since VENT=0 if no data is found for vent settings. + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + + +with surgflag as +( + select ie.icustay_id + , max(case + when lower(curr_service) like '%surg%' then 1 + when curr_service = 'ORTHO' then 1 + else 0 end) as surgical + FROM icustays ie + left join services se + on ie.hadm_id = se.hadm_id + and se.transfertime < DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + group by ie.icustay_id +) +, cohort as +( +select ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime + , ie.outtime + , adm.deathtime + , DATETIME_DIFF(ie.intime, adm.admittime, 'MINUTE') as preiculos + , DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') as age + , gcs.mingcs + , vital.heartrate_max + , vital.heartrate_min + , vital.meanbp_max + , vital.meanbp_min + , vital.resprate_max + , vital.resprate_min + , vital.tempc_max + , vital.tempc_min + , vent.vent as mechvent + , uo.urineoutput + + , case + when adm.ADMISSION_TYPE = 'ELECTIVE' and sf.surgical = 1 + then 1 + when adm.ADMISSION_TYPE is null or sf.surgical is null + then null + else 0 + end as electivesurgery + + -- age group + , case + when DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') <= 1 then 'neonate' + when DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') <= 15 then 'middle' + else 'adult' end as icustay_age_group + + -- mortality flags + , case + when adm.deathtime between ie.intime and ie.outtime + then 1 + when adm.deathtime <= ie.intime -- sometimes there are typographical errors in the death date + then 1 + when adm.dischtime <= ie.outtime and adm.discharge_location = 'DEAD/EXPIRED' + then 1 + else 0 end + as icustay_expire_flag + , adm.hospital_expire_flag +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id +left join surgflag sf + on ie.icustay_id = sf.icustay_id +-- join to custom tables to get more data.... +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join ventilation_first_day vent + on ie.icustay_id = vent.icustay_id +) +, scorecomp as +( +select co.subject_id, co.hadm_id, co.icustay_id +, co.icustay_age_group +, co.icustay_expire_flag +, co.hospital_expire_flag + +-- Below code calculates the component scores needed for oasis +, case when preiculos is null then null + when preiculos < 10.2 then 5 + when preiculos < 297 then 3 + when preiculos < 1440 then 0 + when preiculos < 18708 then 1 + else 2 end as preiculos_score +, case when age is null then null + when age < 24 then 0 + when age <= 53 then 3 + when age <= 77 then 6 + when age <= 89 then 9 + when age >= 90 then 7 + else 0 end as age_score +, case when mingcs is null then null + when mingcs <= 7 then 10 + when mingcs < 14 then 4 + when mingcs = 14 then 3 + else 0 end as gcs_score +, case when heartrate_max is null then null + when heartrate_max > 125 then 6 + when heartrate_min < 33 then 4 + when heartrate_max >= 107 and heartrate_max <= 125 then 3 + when heartrate_max >= 89 and heartrate_max <= 106 then 1 + else 0 end as heartrate_score +, case when meanbp_min is null then null + when meanbp_min < 20.65 then 4 + when meanbp_min < 51 then 3 + when meanbp_max > 143.44 then 3 + when meanbp_min >= 51 and meanbp_min < 61.33 then 2 + else 0 end as meanbp_score +, case when resprate_min is null then null + when resprate_min < 6 then 10 + when resprate_max > 44 then 9 + when resprate_max > 30 then 6 + when resprate_max > 22 then 1 + when resprate_min < 13 then 1 else 0 + end as resprate_score +, case when tempc_max is null then null + when tempc_max > 39.88 then 6 + when tempc_min >= 33.22 and tempc_min <= 35.93 then 4 + when tempc_max >= 33.22 and tempc_max <= 35.93 then 4 + when tempc_min < 33.22 then 3 + when tempc_min > 35.93 and tempc_min <= 36.39 then 2 + when tempc_max >= 36.89 and tempc_max <= 39.88 then 2 + else 0 end as temp_score +, case when UrineOutput is null then null + when UrineOutput < 671.09 then 10 + when UrineOutput > 6896.80 then 8 + when UrineOutput >= 671.09 + and UrineOutput <= 1426.99 then 5 + when UrineOutput >= 1427.00 + and UrineOutput <= 2544.14 then 1 + else 0 end as urineoutput_score +, case when mechvent is null then null + when mechvent = 1 then 9 + else 0 end as mechvent_score +, case when electivesurgery is null then null + when electivesurgery = 1 then 0 + else 6 end as electivesurgery_score + + +-- The below code gives the component associated with each score +-- This is not needed to calculate oasis, but provided for user convenience. +-- If both the min/max are in the normal range (score of 0), then the average value is stored. +, preiculos +, age +, mingcs as gcs +, case when heartrate_max is null then null + when heartrate_max > 125 then heartrate_max + when heartrate_min < 33 then heartrate_min + when heartrate_max >= 107 and heartrate_max <= 125 then heartrate_max + when heartrate_max >= 89 and heartrate_max <= 106 then heartrate_max + else (heartrate_min+heartrate_max)/2 end as heartrate +, case when meanbp_min is null then null + when meanbp_min < 20.65 then meanbp_min + when meanbp_min < 51 then meanbp_min + when meanbp_max > 143.44 then meanbp_max + when meanbp_min >= 51 and meanbp_min < 61.33 then meanbp_min + else (meanbp_min+meanbp_max)/2 end as meanbp +, case when resprate_min is null then null + when resprate_min < 6 then resprate_min + when resprate_max > 44 then resprate_max + when resprate_max > 30 then resprate_max + when resprate_max > 22 then resprate_max + when resprate_min < 13 then resprate_min + else (resprate_min+resprate_max)/2 end as resprate +, case when tempc_max is null then null + when tempc_max > 39.88 then tempc_max + when tempc_min >= 33.22 and tempc_min <= 35.93 then tempc_min + when tempc_max >= 33.22 and tempc_max <= 35.93 then tempc_max + when tempc_min < 33.22 then tempc_min + when tempc_min > 35.93 and tempc_min <= 36.39 then tempc_min + when tempc_max >= 36.89 and tempc_max <= 39.88 then tempc_max + else (tempc_min+tempc_max)/2 end as temp +, UrineOutput +, mechvent +, electivesurgery +from cohort co +) +, score as +( +select s.* + , coalesce(age_score,0) + + coalesce(preiculos_score,0) + + coalesce(gcs_score,0) + + coalesce(heartrate_score,0) + + coalesce(meanbp_score,0) + + coalesce(resprate_score,0) + + coalesce(temp_score,0) + + coalesce(urineoutput_score,0) + + coalesce(mechvent_score,0) + + coalesce(electivesurgery_score,0) + as oasis +from scorecomp s +) +select + subject_id, hadm_id, icustay_id + , icustay_age_group + , hospital_expire_flag + , icustay_expire_flag + , oasis + -- Calculate the probability of in-hospital mortality + , 1 / (1 + exp(- (-6.1746 + 0.1275*(oasis) ))) as oasis_PROB + , age, age_score + , preiculos, preiculos_score + , gcs, gcs_score + , heartrate, heartrate_score + , meanbp, meanbp_score + , resprate, resprate_score + , temp, temp_score + , urineoutput, urineoutput_score + , mechvent, mechvent_score + , electivesurgery, electivesurgery_score +from score +order by icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/qsofa.sql b/mimic-iii/concepts_postgres/severityscores/qsofa.sql new file mode 100644 index 000000000..3e46029fb --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/qsofa.sql @@ -0,0 +1,71 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS qsofa; CREATE TABLE qsofa AS +-- ------------------------------------------------------------------ +-- Title: Quick Sequential Organ Failure Assessment (qSOFA) +-- This query extracts the quick sequential organ failure assessment. +-- This score was a recent revision of SOFA, aiming to detect patients at risk of sepsis. +-- The score is calculated on the first day of each ICU patients' stay - though needn't be. +-- ------------------------------------------------------------------ + +-- Reference for qSOFA: +-- Singer M, et al. The Third International Consensus Definitions for Sepsis and Septic Shock (Sepsis-3) +-- Seymour CW, et al. Assessment of Clinical Criteria for Sepsis: For the Third International Consensus Definitions for Sepsis and Septic Shock (Sepsis-3) + +-- Variables used in qSOFA: +-- GCS, respiratory rate, systolic blood pressure + +-- The following views required to run this query: +-- 1) gcsfirstday - generated by gcs-first-day.sql +-- 2) vitalsfirstday - generated by vitals-first-day.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +with scorecomp as +( +select ie.icustay_id + , v.sysbp_min + , v.resprate_max + , gcs.mingcs +FROM icustays ie +left join vitals_first_day v + on ie.icustay_id = v.icustay_id +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +) +, scorecalc as +( + -- Calculate the final score + -- note that if the underlying data is missing, the component is null + -- eventually these are treated as 0 (normal), but knowing when data is missing is useful for debugging + select icustay_id + , case + when sysbp_min is null then null + when sysbp_min <= 100 then 1 + else 0 end + as sysbp_score + , case + when mingcs is null then null + when mingcs <= 13 then 1 + else 0 end + as gcs_score + , case + when resprate_max is null then null + when resprate_max >= 22 then 1 + else 0 end + as resprate_score + from scorecomp +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +, coalesce(sysbp_score,0) + + coalesce(gcs_score,0) + + coalesce(resprate_score,0) + as qsofa +, sysbp_score +, gcs_score +, resprate_score +FROM icustays ie +left join scorecalc s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/saps.sql b/mimic-iii/concepts_postgres/severityscores/saps.sql new file mode 100644 index 000000000..375c2fdb9 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/saps.sql @@ -0,0 +1,336 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS saps; CREATE TABLE saps AS +-- ------------------------------------------------------------------ +-- Title: Simplified Acute Physiology Score (SAPS) +-- This query extracts the simplified acute physiology score. +-- This score is a measure of patient severity of illness. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for SAPS: +-- Jean-Roger Le Gall, Philippe Loirat, Annick Alperovitch, Paul Glaser, Claude Granthil, +-- Daniel Mathieu, Philippe Mercier, Remi Thomas, and Daniel Villers. +-- "A simplified acute physiology score for ICU patients." +-- Critical care medicine 12, no. 11 (1984): 975-977. + +-- Variables used in SAPS: +-- Age, GCS +-- VITALS: Heart rate, systolic blood pressure, temperature, respiration rate +-- FLAGS: ventilation/cpap +-- IO: urine output +-- LABS: blood urea nitrogen, hematocrit, WBC, glucose, potassium, sodium, HCO3 + +-- The following views are required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) vent_first_day - generated by ventilated-first-day.sql +-- 3) vitals_first_day - generated by vitals-first-day.sql +-- 4) gcs_first_day - generated by gcs-first-day.sql +-- 5) labs_first_day - generated by labs-first-day.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +-- extract CPAP from the "Oxygen Delivery Device" fields +with cpap as +( + select ie.icustay_id + , max(CASE + WHEN lower(ce.value) LIKE '%cpap%' THEN 1 + WHEN lower(ce.value) LIKE '%bipap mask%' THEN 1 + else 0 end) as cpap + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where itemid in + ( + -- TODO: when metavision data import fixed, check the values in 226732 match the value clause below + 467, 469, 226732 + ) + and (lower(ce.value) LIKE '%cpap%' or lower(ce.value) LIKE '%bipap mask%') + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by ie.icustay_id +) +, cohort as +( +select ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime + , ie.outtime + + -- the casts ensure the result is numeric.. we could equally extract EPOCH from the interval + -- however this code works in Oracle and Postgres + , DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') as age + , gcs.mingcs + , vital.heartrate_max + , vital.heartrate_min + , vital.sysbp_max + , vital.sysbp_min + , vital.resprate_max + , vital.resprate_min + , vital.tempc_max + , vital.tempc_min + + , coalesce(vital.glucose_max, labs.glucose_max) as glucose_max + , coalesce(vital.glucose_min, labs.glucose_min) as glucose_min + + , labs.bun_max + , labs.bun_min + , labs.hematocrit_max + , labs.hematocrit_min + , labs.wbc_max + , labs.wbc_min + , labs.sodium_max + , labs.sodium_min + , labs.potassium_max + , labs.potassium_min + , labs.bicarbonate_max + , labs.bicarbonate_min + + , vent.vent as mechvent + , uo.urineoutput + + , cp.cpap + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to above view to get CPAP +left join cpap cp + on ie.icustay_id = cp.icustay_id + +-- join to custom tables to get more data.... +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join ventilation_first_day vent + on ie.icustay_id = vent.icustay_id +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +) +, scorecomp as +( +select + cohort.* + -- Below code calculates the component scores needed for SAPS + , case + when age is null then null + when age <= 45 then 0 + when age <= 55 then 1 + when age <= 65 then 2 + when age <= 75 then 3 + when age > 75 then 4 + end as age_score + , case + when heartrate_max is null then null + when heartrate_max >= 180 then 4 + when heartrate_min < 40 then 4 + when heartrate_max >= 140 then 3 + when heartrate_min <= 54 then 3 + when heartrate_max >= 110 then 2 + when heartrate_min <= 69 then 2 + when heartrate_max >= 70 and heartrate_max <= 109 + and heartrate_min >= 70 and heartrate_min <= 109 + then 0 + end as hr_score + , case + when sysbp_min is null then null + when sysbp_max >= 190 then 4 + when sysbp_min < 55 then 4 + when sysbp_max >= 150 then 2 + when sysbp_min <= 79 then 2 + when sysbp_max >= 80 and sysbp_max <= 149 + and sysbp_min >= 80 and sysbp_min <= 149 + then 0 + end as sysbp_score + + , case + when tempc_max is null then null + when tempc_max >= 41.0 then 4 + when tempc_min < 30.0 then 4 + when tempc_max >= 39.0 then 3 + when tempc_min <= 31.9 then 3 + when tempc_min <= 33.9 then 2 + when tempc_max > 38.4 then 1 + when tempc_min < 36.0 then 1 + when tempc_max >= 36.0 and tempc_max <= 38.4 + and tempc_min >= 36.0 and tempc_min <= 38.4 + then 0 + end as temp_score + + , case + when resprate_min is null then null + when resprate_max >= 50 then 4 + when resprate_min < 6 then 4 + when resprate_max >= 35 then 3 + when resprate_min <= 9 then 2 + when resprate_max >= 25 then 1 + when resprate_min <= 11 then 1 + when resprate_max >= 12 and resprate_max <= 24 + and resprate_min >= 12 and resprate_min <= 24 + then 0 + end as resp_score + + , case + when coalesce(mechvent,cpap) is null then null + when cpap = 1 then 3 + when mechvent = 1 then 3 + else 0 + end as vent_score + + , case + when UrineOutput is null then null + when UrineOutput > 5000.0 then 2 + when UrineOutput >= 3500.0 then 1 + when UrineOutput >= 700.0 then 0 + when UrineOutput >= 500.0 then 2 + when UrineOutput >= 200.0 then 3 + when UrineOutput < 200.0 then 4 + end as uo_score + + , case + when bun_max is null then null + when bun_max >= 55.0 then 4 + when bun_max >= 36.0 then 3 + when bun_max >= 29.0 then 2 + when bun_max >= 7.50 then 1 + when bun_min < 3.5 then 1 + when bun_max >= 3.5 and bun_max < 7.5 + and bun_min >= 3.5 and bun_min < 7.5 + then 0 + end as bun_score + + , case + when hematocrit_max is null then null + when hematocrit_max >= 60.0 then 4 + when hematocrit_min < 20.0 then 4 + when hematocrit_max >= 50.0 then 2 + when hematocrit_min < 30.0 then 2 + when hematocrit_max >= 46.0 then 1 + when hematocrit_max >= 30.0 and hematocrit_max < 46.0 + and hematocrit_min >= 30.0 and hematocrit_min < 46.0 + then 0 + end as hematocrit_score + + , case + when wbc_max is null then null + when wbc_max >= 40.0 then 4 + when wbc_min < 1.0 then 4 + when wbc_max >= 20.0 then 2 + when wbc_min < 3.0 then 2 + when wbc_max >= 15.0 then 1 + when wbc_max >= 3.0 and wbc_max < 15.0 + and wbc_min >= 3.0 and wbc_min < 15.0 + then 0 + end as wbc_score + + , case + when glucose_max is null then null + when glucose_max >= 44.5 then 4 + when glucose_min < 1.6 then 4 + when glucose_max >= 27.8 then 3 + when glucose_min < 2.8 then 3 + when glucose_min < 3.9 then 2 + when glucose_max >= 14.0 then 1 + when glucose_max >= 3.9 and glucose_max < 14.0 + and glucose_min >= 3.9 and glucose_min < 14.0 + then 0 + end as glucose_score + + , case + when potassium_max is null then null + when potassium_max >= 7.0 then 4 + when potassium_min < 2.5 then 4 + when potassium_max >= 6.0 then 3 + when potassium_min < 3.0 then 2 + when potassium_max >= 5.5 then 1 + when potassium_min < 3.5 then 1 + when potassium_max >= 3.5 and potassium_max < 5.5 + and potassium_min >= 3.5 and potassium_min < 5.5 + then 0 + end as potassium_score + + , case + when sodium_max is null then null + when sodium_max >= 180 then 4 + when sodium_min < 110 then 4 + when sodium_max >= 161 then 3 + when sodium_min < 120 then 3 + when sodium_max >= 156 then 2 + when sodium_min < 130 then 2 + when sodium_max >= 151 then 1 + when sodium_max >= 130 and sodium_max < 151 + and sodium_min >= 130 and sodium_min < 151 + then 0 + end as sodium_score + + , case + when bicarbonate_max is null then null + when bicarbonate_min < 5.0 then 4 + when bicarbonate_max >= 40.0 then 3 + when bicarbonate_min < 10.0 then 3 + when bicarbonate_max >= 30.0 then 1 + when bicarbonate_min < 20.0 then 1 + when bicarbonate_max >= 20.0 and bicarbonate_max < 30.0 + and bicarbonate_min >= 20.0 and bicarbonate_min < 30.0 + then 0 + end as bicarbonate_score + + , case + when mingcs is null then null + when mingcs < 3 then null -- erroneous value/on trach + when mingcs = 3 then 4 + when mingcs < 7 then 3 + when mingcs < 10 then 2 + when mingcs < 13 then 1 + when mingcs >= 13 + and mingcs <= 15 + then 0 + end as gcs_score +from cohort +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +-- coalesce statements impute normal score of zero if data element is missing +, coalesce(age_score,0) ++ coalesce(hr_score,0) ++ coalesce(sysbp_score,0) ++ coalesce(resp_score,0) ++ coalesce(temp_score,0) ++ coalesce(uo_score,0) ++ coalesce(vent_score,0) ++ coalesce(bun_score,0) ++ coalesce(hematocrit_score,0) ++ coalesce(wbc_score,0) ++ coalesce(glucose_score,0) ++ coalesce(potassium_score,0) ++ coalesce(sodium_score,0) ++ coalesce(bicarbonate_score,0) ++ coalesce(gcs_score,0) + as SAPS +, age_score +, hr_score +, sysbp_score +, resp_score +, temp_score +, uo_score +, vent_score +, bun_score +, hematocrit_score +, wbc_score +, glucose_score +, potassium_score +, sodium_score +, bicarbonate_score +, gcs_score + +FROM icustays ie +left join scorecomp s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/sapsii.sql b/mimic-iii/concepts_postgres/severityscores/sapsii.sql new file mode 100644 index 000000000..3ab3c3447 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/sapsii.sql @@ -0,0 +1,384 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS sapsii; CREATE TABLE sapsii AS +-- ------------------------------------------------------------------ +-- Title: Simplified Acute Physiology Score II (SAPS II) +-- This query extracts the simplified acute physiology score II. +-- This score is a measure of patient severity of illness. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for SAPS II: +-- Le Gall, Jean-Roger, Stanley Lemeshow, and Fabienne Saulnier. +-- "A new simplified acute physiology score (SAPS II) based on a European/North American multicenter study." +-- JAMA 270, no. 24 (1993): 2957-2963. + +-- Variables used in SAPS II: +-- Age, GCS +-- VITALS: Heart rate, systolic blood pressure, temperature +-- FLAGS: ventilation/cpap +-- IO: urine output +-- LABS: PaO2/FiO2 ratio, blood urea nitrogen, WBC, potassium, sodium, HCO3 + +-- The following views are required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) ventilation_durations - generated by ventilation_durations.sql +-- 3) vitals_first_day - generated by vitals-first-day.sql +-- 4) gcs_first_day - generated by gcs-first-day.sql +-- 5) labs_first_day - generated by labs-first-day.sql +-- 6) blood_gas_arterial_first_day - generated by blood-gas-first-day-arterial.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +-- extract CPAP from the "Oxygen Delivery Device" fields +with cpap as +( + select ie.icustay_id + , min(DATETIME_SUB(charttime, INTERVAL '1' HOUR)) as starttime + , max(DATETIME_ADD(charttime, INTERVAL '4' HOUR)) as endtime + , max(CASE + WHEN lower(ce.value) LIKE '%cpap%' THEN 1 + WHEN lower(ce.value) LIKE '%bipap mask%' THEN 1 + else 0 end) as cpap + FROM icustays ie + inner join chartevents ce + on ie.icustay_id = ce.icustay_id + and ce.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where itemid in + ( + -- TODO: when metavision data import fixed, check the values in 226732 match the value clause below + 467, 469, 226732 + ) + and (lower(ce.value) LIKE '%cpap%' or lower(ce.value) LIKE '%bipap mask%') + -- exclude rows marked as error + AND (ce.error IS NULL OR ce.error = 0) + group by ie.icustay_id +) +-- extract a flag for surgical service +-- this combined with "elective" FROM admissions table defines elective/non-elective surgery +, surgflag as +( + select adm.hadm_id + , case when lower(curr_service) like '%surg%' then 1 else 0 end as surgical + , ROW_NUMBER() over + ( + PARTITION BY adm.HADM_ID + ORDER BY TRANSFERTIME + ) as serviceOrder + FROM admissions adm + left join services se + on adm.hadm_id = se.hadm_id +) +-- icd-9 diagnostic codes are our best source for comorbidity information +-- unfortunately, they are technically a-causal +-- however, this shouldn't matter too much for the SAPS II comorbidities +, comorb as +( +select hadm_id +-- these are slightly different than elixhauser comorbidities, but based on them +-- they include some non-comorbid ICD-9 codes (e.g. 20302, relapse of multiple myeloma) + , max(CASE + when SUBSTR(icd9_code,1,3) BETWEEN '042' AND '044' THEN 1 + end) as aids /* HIV and AIDS */ + , max(CASE + when icd9_code between '20000' and '20238' then 1 -- lymphoma + when icd9_code between '20240' and '20248' then 1 -- leukemia + when icd9_code between '20250' and '20302' then 1 -- lymphoma + when icd9_code between '20310' and '20312' then 1 -- leukemia + when icd9_code between '20302' and '20382' then 1 -- lymphoma + when icd9_code between '20400' and '20522' then 1 -- chronic leukemia + when icd9_code between '20580' and '20702' then 1 -- other myeloid leukemia + when icd9_code between '20720' and '20892' then 1 -- other myeloid leukemia + when SUBSTR(icd9_code,1,4) = '2386' then 1 -- lymphoma + when SUBSTR(icd9_code,1,4) = '2733' then 1 -- lymphoma + end) as hem + , max(CASE + when SUBSTR(icd9_code,1,4) BETWEEN '1960' AND '1991' THEN 1 + when icd9_code between '20970' and '20975' then 1 + when icd9_code = '20979' then 1 + when icd9_code = '78951' then 1 + end) as mets /* Metastatic cancer */ + from diagnoses_icd + group by hadm_id +) +, pafi1 as +( + -- join blood gas to ventilation durations to determine if patient was vent + -- also join to cpap table for the same purpose + select bg.icustay_id, bg.charttime + , pao2fio2 + , case when vd.icustay_id is not null then 1 else 0 end as vent + , case when cp.icustay_id is not null then 1 else 0 end as cpap + from blood_gas_first_day_arterial bg + left join ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + left join cpap cp + on bg.icustay_id = cp.icustay_id + and bg.charttime >= cp.starttime + and bg.charttime <= cp.endtime +) +, pafi2 as +( + -- get the minimum PaO2/FiO2 ratio *only for ventilated/cpap patients* + select icustay_id + , min(pao2fio2) as pao2fio2_vent_min + from pafi1 + where vent = 1 or cpap = 1 + group by icustay_id +) +, cohort as +( +select ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime + , ie.outtime + + -- the casts ensure the result is numeric.. we could equally extract EPOCH from the interval + -- however this code works in Oracle and Postgres + , DATETIME_DIFF(ie.intime, pat.dob, 'YEAR') as age + + , vital.heartrate_max + , vital.heartrate_min + , vital.sysbp_max + , vital.sysbp_min + , vital.tempc_max + , vital.tempc_min + + -- this value is non-null iff the patient is on vent/cpap + , pf.pao2fio2_vent_min + + , uo.urineoutput + + , labs.bun_min + , labs.bun_max + , labs.wbc_min + , labs.wbc_max + , labs.potassium_min + , labs.potassium_max + , labs.sodium_min + , labs.sodium_max + , labs.bicarbonate_min + , labs.bicarbonate_max + , labs.bilirubin_min + , labs.bilirubin_max + + , gcs.mingcs + + , comorb.aids + , comorb.hem + , comorb.mets + + , case + when adm.ADMISSION_TYPE = 'ELECTIVE' and sf.surgical = 1 + then 'ScheduledSurgical' + when adm.ADMISSION_TYPE != 'ELECTIVE' and sf.surgical = 1 + then 'UnscheduledSurgical' + else 'Medical' + end as admissiontype + + +FROM icustays ie +inner join admissions adm + on ie.hadm_id = adm.hadm_id +inner join patients pat + on ie.subject_id = pat.subject_id + +-- join to above views +left join pafi2 pf + on ie.icustay_id = pf.icustay_id +left join surgflag sf + on adm.hadm_id = sf.hadm_id and sf.serviceOrder = 1 +left join comorb + on ie.hadm_id = comorb.hadm_id + +-- join to custom tables to get more data.... +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +left join vitals_first_day vital + on ie.icustay_id = vital.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join labs_first_day labs + on ie.icustay_id = labs.icustay_id +) +, scorecomp as +( +select + cohort.* + -- Below code calculates the component scores needed for SAPS + , case + when age is null then null + when age < 40 then 0 + when age < 60 then 7 + when age < 70 then 12 + when age < 75 then 15 + when age < 80 then 16 + when age >= 80 then 18 + end as age_score + + , case + when heartrate_max is null then null + when heartrate_min < 40 then 11 + when heartrate_max >= 160 then 7 + when heartrate_max >= 120 then 4 + when heartrate_min < 70 then 2 + when heartrate_max >= 70 and heartrate_max < 120 + and heartrate_min >= 70 and heartrate_min < 120 + then 0 + end as hr_score + + , case + when sysbp_min is null then null + when sysbp_min < 70 then 13 + when sysbp_min < 100 then 5 + when sysbp_max >= 200 then 2 + when sysbp_max >= 100 and sysbp_max < 200 + and sysbp_min >= 100 and sysbp_min < 200 + then 0 + end as sysbp_score + + , case + when tempc_max is null then null + when tempc_min < 39.0 then 0 + when tempc_max >= 39.0 then 3 + end as temp_score + + , case + when pao2fio2_vent_min is null then null + when pao2fio2_vent_min < 100 then 11 + when pao2fio2_vent_min < 200 then 9 + when pao2fio2_vent_min >= 200 then 6 + end as pao2fio2_score + + , case + when urineoutput is null then null + when urineoutput < 500.0 then 11 + when urineoutput < 1000.0 then 4 + when urineoutput >= 1000.0 then 0 + end as uo_score + + , case + when bun_max is null then null + when bun_max < 28.0 then 0 + when bun_max < 84.0 then 6 + when bun_max >= 84.0 then 10 + end as bun_score + + , case + when wbc_max is null then null + when wbc_min < 1.0 then 12 + when wbc_max >= 20.0 then 3 + when wbc_max >= 1.0 and wbc_max < 20.0 + and wbc_min >= 1.0 and wbc_min < 20.0 + then 0 + end as wbc_score + + , case + when potassium_max is null then null + when potassium_min < 3.0 then 3 + when potassium_max >= 5.0 then 3 + when potassium_max >= 3.0 and potassium_max < 5.0 + and potassium_min >= 3.0 and potassium_min < 5.0 + then 0 + end as potassium_score + + , case + when sodium_max is null then null + when sodium_min < 125 then 5 + when sodium_max >= 145 then 1 + when sodium_max >= 125 and sodium_max < 145 + and sodium_min >= 125 and sodium_min < 145 + then 0 + end as sodium_score + + , case + when bicarbonate_max is null then null + when bicarbonate_min < 15.0 then 5 + when bicarbonate_min < 20.0 then 3 + when bicarbonate_max >= 20.0 + and bicarbonate_min >= 20.0 + then 0 + end as bicarbonate_score + + , case + when bilirubin_max is null then null + when bilirubin_max < 4.0 then 0 + when bilirubin_max < 6.0 then 4 + when bilirubin_max >= 6.0 then 9 + end as bilirubin_score + + , case + when mingcs is null then null + when mingcs < 3 then null -- erroneous value/on trach + when mingcs < 6 then 26 + when mingcs < 9 then 13 + when mingcs < 11 then 7 + when mingcs < 14 then 5 + when mingcs >= 14 + and mingcs <= 15 + then 0 + end as gcs_score + + , case + when aids = 1 then 17 + when hem = 1 then 10 + when mets = 1 then 9 + else 0 + end as comorbidity_score + + , case + when admissiontype = 'ScheduledSurgical' then 0 + when admissiontype = 'Medical' then 6 + when admissiontype = 'UnscheduledSurgical' then 8 + else null + end as admissiontype_score + +from cohort +) +-- Calculate SAPS II here so we can use it in the probability calculation below +, score as +( + select s.* + -- coalesce statements impute normal score of zero if data element is missing + , coalesce(age_score,0) + + coalesce(hr_score,0) + + coalesce(sysbp_score,0) + + coalesce(temp_score,0) + + coalesce(pao2fio2_score,0) + + coalesce(uo_score,0) + + coalesce(bun_score,0) + + coalesce(wbc_score,0) + + coalesce(potassium_score,0) + + coalesce(sodium_score,0) + + coalesce(bicarbonate_score,0) + + coalesce(bilirubin_score,0) + + coalesce(gcs_score,0) + + coalesce(comorbidity_score,0) + + coalesce(admissiontype_score,0) + as sapsii + from scorecomp s +) +select ie.subject_id, ie.hadm_id, ie.icustay_id +, sapsii +, 1 / (1 + exp(- (-7.7631 + 0.0737*(sapsii) + 0.9971*(ln(sapsii + 1))) )) as sapsii_prob +, age_score +, hr_score +, sysbp_score +, temp_score +, pao2fio2_score +, uo_score +, bun_score +, wbc_score +, potassium_score +, sodium_score +, bicarbonate_score +, bilirubin_score +, gcs_score +, comorbidity_score +, admissiontype_score +FROM icustays ie +left join score s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/sirs.sql b/mimic-iii/concepts_postgres/severityscores/sirs.sql new file mode 100644 index 000000000..ce182e571 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/sirs.sql @@ -0,0 +1,113 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS sirs; CREATE TABLE sirs AS +-- ------------------------------------------------------------------ +-- Title: Systemic inflammatory response syndrome (SIRS) criteria +-- This query extracts the Systemic inflammatory response syndrome (SIRS) criteria +-- The criteria quantify the level of inflammatory response of the body +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for SIRS: +-- American College of Chest Physicians/Society of Critical Care Medicine Consensus Conference: +-- definitions for sepsis and organ failure and guidelines for the use of innovative therapies in sepsis" +-- Crit. Care Med. 20 (6): 864–74. 1992. +-- doi:10.1097/00003246-199206000-00025. PMID 1597042. + +-- Variables used in SIRS: +-- Body temperature (min and max) +-- Heart rate (max) +-- Respiratory rate (max) +-- PaCO2 (min) +-- White blood cell count (min and max) +-- the presence of greater than 10% immature neutrophils (band forms) + +-- The following views required to run this query: +-- 1) vitals_first_day - generated by vitals-first-day.sql +-- 2) labs_first_day - generated by labs-first-day.sql +-- 3) blood_gas_first_day_arterial - generated by blood-gas-first-day-arterial.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +with bg as +( + -- join blood gas to ventilation durations to determine if patient was vent + select bg.icustay_id + , min(pco2) as paco2_min + from blood_gas_first_day_arterial bg + where specimen_pred = 'ART' + group by bg.icustay_id +) +-- Aggregate the components for the score +, scorecomp as +( +select ie.icustay_id + , v.tempc_min + , v.tempc_max + , v.heartrate_max + , v.resprate_max + , bg.paco2_min + , l.wbc_min + , l.wbc_max + , l.bands_max +FROM icustays ie +left join bg + on ie.icustay_id = bg.icustay_id +left join vitals_first_day v + on ie.icustay_id = v.icustay_id +left join labs_first_day l + on ie.icustay_id = l.icustay_id +) +, scorecalc as +( + -- Calculate the final score + -- note that if the underlying data is missing, the component is null + -- eventually these are treated as 0 (normal), but knowing when data is missing is useful for debugging + select icustay_id + + , case + when tempc_min < 36.0 then 1 + when tempc_max > 38.0 then 1 + when tempc_min is null then null + else 0 + end as temp_score + + + , case + when heartrate_max > 90.0 then 1 + when heartrate_max is null then null + else 0 + end as heartrate_score + + , case + when resprate_max > 20.0 then 1 + when paco2_min < 32.0 then 1 + when coalesce(resprate_max, paco2_min) is null then null + else 0 + end as resp_score + + , case + when wbc_min < 4.0 then 1 + when wbc_max > 12.0 then 1 + when bands_max > 10 then 1-- > 10% immature neurophils (band forms) + when coalesce(wbc_min, bands_max) is null then null + else 0 + end as wbc_score + + from scorecomp +) +select + ie.subject_id, ie.hadm_id, ie.icustay_id + -- Combine all the scores to get SOFA + -- Impute 0 if the score is missing + , coalesce(temp_score,0) + + coalesce(heartrate_score,0) + + coalesce(resp_score,0) + + coalesce(wbc_score,0) + as sirs + , temp_score, heartrate_score, resp_score, wbc_score +FROM icustays ie +left join scorecalc s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/severityscores/sofa.sql b/mimic-iii/concepts_postgres/severityscores/sofa.sql new file mode 100644 index 000000000..988792995 --- /dev/null +++ b/mimic-iii/concepts_postgres/severityscores/sofa.sql @@ -0,0 +1,272 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS sofa; CREATE TABLE sofa AS +-- ------------------------------------------------------------------ +-- Title: Sequential Organ Failure Assessment (SOFA) +-- This query extracts the sequential organ failure assessment (formally: sepsis-related organ failure assessment). +-- This score is a measure of organ failure for patients in the ICU. +-- The score is calculated on the first day of each ICU patients' stay. +-- ------------------------------------------------------------------ + +-- Reference for SOFA: +-- Jean-Louis Vincent, Rui Moreno, Jukka Takala, Sheila Willatts, Arnaldo De Mendonça, +-- Hajo Bruining, C. K. Reinhart, Peter M Suter, and L. G. Thijs. +-- "The SOFA (Sepsis-related Organ Failure Assessment) score to describe organ dysfunction/failure." +-- Intensive care medicine 22, no. 7 (1996): 707-710. + +-- Variables used in SOFA: +-- GCS, MAP, FiO2, Ventilation status (sourced FROM chartevents) +-- Creatinine, Bilirubin, FiO2, PaO2, Platelets (sourced FROM labevents) +-- Dobutamine, Epinephrine, Norepinephrine (sourced FROM inputevents_mv and INPUTEVENTS_CV) +-- Urine output (sourced from OUTPUTEVENTS) + +-- The following views required to run this query: +-- 1) urine_output_first_day - generated by urine-output-first-day.sql +-- 2) vitals_first_day - generated by vitals-first-day.sql +-- 3) gcs_first_day - generated by gcs-first-day.sql +-- 4) labs_first_day - generated by labs-first-day.sql +-- 5) blood_gas_first_day_arterial - generated by blood-gas-first-day-arterial.sql +-- 6) echodata - generated by echo-data.sql +-- 7) ventilation_durations - generated by ventilation_durations.sql + +-- Note: +-- The score is calculated for *all* ICU patients, with the assumption that the user will subselect appropriate ICUSTAY_IDs. +-- For example, the score is calculated for neonates, but it is likely inappropriate to actually use the score values for these patients. + +with wt AS +( + SELECT ie.icustay_id + -- ensure weight is measured in kg + , avg(CASE + WHEN itemid IN (762, 763, 3723, 3580, 226512) + THEN valuenum + -- convert lbs to kgs + WHEN itemid IN (3581) + THEN valuenum * 0.45359237 + WHEN itemid IN (3582) + THEN valuenum * 0.0283495231 + ELSE null + END) AS weight + + FROM icustays ie + left join chartevents c + on ie.icustay_id = c.icustay_id + WHERE valuenum IS NOT NULL + AND itemid IN + ( + 762, 763, 3723, 3580, -- Weight Kg + 3581, -- Weight lb + 3582, -- Weight oz + 226512 -- Metavision: Admission Weight (Kg) + ) + AND valuenum != 0 + and charttime between DATETIME_SUB(ie.intime, INTERVAL '1' DAY) and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + -- exclude rows marked as error + AND (c.error IS NULL OR c.error = 0) + group by ie.icustay_id +) +-- 5% of patients are missing a weight, but we can impute weight using their echo notes +, echo2 as( + select ie.icustay_id, avg(weight * 0.45359237) as weight + FROM icustays ie + left join echo_data echo + on ie.hadm_id = echo.hadm_id + and echo.charttime > DATETIME_SUB(ie.intime, INTERVAL '7' DAY) + and echo.charttime < DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + group by ie.icustay_id +) +, vaso_cv as +( + select ie.icustay_id + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case + when itemid = 30047 then rate / coalesce(wt.weight,ec.weight) -- measured in mcgmin + when itemid = 30120 then rate -- measured in mcgkgmin ** there are clear errors, perhaps actually mcgmin + else null + end) as rate_norepinephrine + + , max(case + when itemid = 30044 then rate / coalesce(wt.weight,ec.weight) -- measured in mcgmin + when itemid in (30119,30309) then rate -- measured in mcgkgmin + else null + end) as rate_epinephrine + + , max(case when itemid in (30043,30307) then rate end) as rate_dopamine + , max(case when itemid in (30042,30306) then rate end) as rate_dobutamine + + FROM icustays ie + inner join inputevents_cv cv + on ie.icustay_id = cv.icustay_id and cv.charttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + left join wt + on ie.icustay_id = wt.icustay_id + left join echo2 ec + on ie.icustay_id = ec.icustay_id + where itemid in (30047,30120,30044,30119,30309,30043,30307,30042,30306) + and rate is not null + group by ie.icustay_id +) +, vaso_mv as +( + select ie.icustay_id + -- case statement determining whether the ITEMID is an instance of vasopressor usage + , max(case when itemid = 221906 then rate end) as rate_norepinephrine + , max(case when itemid = 221289 then rate end) as rate_epinephrine + , max(case when itemid = 221662 then rate end) as rate_dopamine + , max(case when itemid = 221653 then rate end) as rate_dobutamine + FROM icustays ie + inner join inputevents_mv mv + on ie.icustay_id = mv.icustay_id and mv.starttime between ie.intime and DATETIME_ADD(ie.intime, INTERVAL '1' DAY) + where itemid in (221906,221289,221662,221653) + -- 'Rewritten' orders are not delivered to the patient + and statusdescription != 'Rewritten' + group by ie.icustay_id +) +, pafi1 as +( + -- join blood gas to ventilation durations to determine if patient was vent + select bg.icustay_id, bg.charttime + , pao2fio2 + , case when vd.icustay_id is not null then 1 else 0 end as isvent + from blood_gas_first_day_arterial bg + left join ventilation_durations vd + on bg.icustay_id = vd.icustay_id + and bg.charttime >= vd.starttime + and bg.charttime <= vd.endtime + order by bg.icustay_id, bg.charttime +) +, pafi2 as +( + -- because pafi has an interaction between vent/PaO2:FiO2, we need two columns for the score + -- it can happen that the lowest unventilated PaO2/FiO2 is 68, but the lowest ventilated PaO2/FiO2 is 120 + -- in this case, the SOFA score is 3, *not* 4. + select icustay_id + , min(case when isvent = 0 then pao2fio2 else null end) as pao2fio2_novent_min + , min(case when isvent = 1 then pao2fio2 else null end) as pao2fio2_vent_min + from pafi1 + group by icustay_id +) +-- Aggregate the components for the score +, scorecomp as +( +select ie.icustay_id + , v.meanbp_min + , coalesce(cv.rate_norepinephrine, mv.rate_norepinephrine) as rate_norepinephrine + , coalesce(cv.rate_epinephrine, mv.rate_epinephrine) as rate_epinephrine + , coalesce(cv.rate_dopamine, mv.rate_dopamine) as rate_dopamine + , coalesce(cv.rate_dobutamine, mv.rate_dobutamine) as rate_dobutamine + + , l.creatinine_max + , l.bilirubin_max + , l.platelet_min + + , pf.pao2fio2_novent_min + , pf.pao2fio2_vent_min + + , uo.urineoutput + + , gcs.mingcs +FROM icustays ie +left join vaso_cv cv + on ie.icustay_id = cv.icustay_id +left join vaso_mv mv + on ie.icustay_id = mv.icustay_id +left join pafi2 pf + on ie.icustay_id = pf.icustay_id +left join vitals_first_day v + on ie.icustay_id = v.icustay_id +left join labs_first_day l + on ie.icustay_id = l.icustay_id +left join urine_output_first_day uo + on ie.icustay_id = uo.icustay_id +left join gcs_first_day gcs + on ie.icustay_id = gcs.icustay_id +) +, scorecalc as +( + -- Calculate the final score + -- note that if the underlying data is missing, the component is null + -- eventually these are treated as 0 (normal), but knowing when data is missing is useful for debugging + select icustay_id + -- Respiration + , case + when pao2fio2_vent_min < 100 then 4 + when pao2fio2_vent_min < 200 then 3 + when pao2fio2_novent_min < 300 then 2 + when pao2fio2_novent_min < 400 then 1 + when coalesce(pao2fio2_vent_min, pao2fio2_novent_min) is null then null + else 0 + end as respiration + + -- Coagulation + , case + when platelet_min < 20 then 4 + when platelet_min < 50 then 3 + when platelet_min < 100 then 2 + when platelet_min < 150 then 1 + when platelet_min is null then null + else 0 + end as coagulation + + -- Liver + , case + -- Bilirubin checks in mg/dL + when bilirubin_max >= 12.0 then 4 + when bilirubin_max >= 6.0 then 3 + when bilirubin_max >= 2.0 then 2 + when bilirubin_max >= 1.2 then 1 + when bilirubin_max is null then null + else 0 + end as liver + + -- Cardiovascular + , case + when rate_dopamine > 15 or rate_epinephrine > 0.1 or rate_norepinephrine > 0.1 then 4 + when rate_dopamine > 5 or rate_epinephrine <= 0.1 or rate_norepinephrine <= 0.1 then 3 + when rate_dopamine > 0 or rate_dobutamine > 0 then 2 + when meanbp_min < 70 then 1 + when coalesce(meanbp_min, rate_dopamine, rate_dobutamine, rate_epinephrine, rate_norepinephrine) is null then null + else 0 + end as cardiovascular + + -- Neurological failure (GCS) + , case + when (mingcs >= 13 and mingcs <= 14) then 1 + when (mingcs >= 10 and mingcs <= 12) then 2 + when (mingcs >= 6 and mingcs <= 9) then 3 + when mingcs < 6 then 4 + when mingcs is null then null + else 0 end + as cns + + -- Renal failure - high creatinine or low urine output + , case + when (creatinine_max >= 5.0) then 4 + when urineoutput < 200 then 4 + when (creatinine_max >= 3.5 and creatinine_max < 5.0) then 3 + when urineoutput < 500 then 3 + when (creatinine_max >= 2.0 and creatinine_max < 3.5) then 2 + when (creatinine_max >= 1.2 and creatinine_max < 2.0) then 1 + when coalesce(urineoutput, creatinine_max) is null then null + else 0 end + as renal + from scorecomp +) +select ie.subject_id, ie.hadm_id, ie.icustay_id + -- Combine all the scores to get SOFA + -- Impute 0 if the score is missing + , coalesce(respiration,0) + + coalesce(coagulation,0) + + coalesce(liver,0) + + coalesce(cardiovascular,0) + + coalesce(cns,0) + + coalesce(renal,0) + as SOFA +, respiration +, coagulation +, liver +, cardiovascular +, cns +, renal +FROM icustays ie +left join scorecalc s + on ie.icustay_id = s.icustay_id +order by ie.icustay_id; diff --git a/mimic-iii/concepts_postgres/treatment/abx_prescriptions_list.sql b/mimic-iii/concepts_postgres/treatment/abx_prescriptions_list.sql new file mode 100644 index 000000000..235e2cf06 --- /dev/null +++ b/mimic-iii/concepts_postgres/treatment/abx_prescriptions_list.sql @@ -0,0 +1,175 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS abx_prescriptions_list; CREATE TABLE abx_prescriptions_list AS +with t1 as +( + select + drug, drug_name_generic + , route + , case + when lower(drug) like '%adoxa%' then 1 + when lower(drug) like '%ala-tet%' then 1 + when lower(drug) like '%alodox%' then 1 + when lower(drug) like '%amikacin%' then 1 + when lower(drug) like '%amikin%' then 1 + when lower(drug) like '%amoxicillin%' then 1 + when lower(drug) like '%amoxicillin%clavulanate%' then 1 + when lower(drug) like '%clavulanate%' then 1 + when lower(drug) like '%ampicillin%' then 1 + when lower(drug) like '%augmentin%' then 1 + when lower(drug) like '%avelox%' then 1 + when lower(drug) like '%avidoxy%' then 1 + when lower(drug) like '%azactam%' then 1 + when lower(drug) like '%azithromycin%' then 1 + when lower(drug) like '%aztreonam%' then 1 + when lower(drug) like '%axetil%' then 1 + when lower(drug) like '%bactocill%' then 1 + when lower(drug) like '%bactrim%' then 1 + when lower(drug) like '%bethkis%' then 1 + when lower(drug) like '%biaxin%' then 1 + when lower(drug) like '%bicillin l-a%' then 1 + when lower(drug) like '%cayston%' then 1 + when lower(drug) like '%cefazolin%' then 1 + when lower(drug) like '%cedax%' then 1 + when lower(drug) like '%cefoxitin%' then 1 + when lower(drug) like '%ceftazidime%' then 1 + when lower(drug) like '%cefaclor%' then 1 + when lower(drug) like '%cefadroxil%' then 1 + when lower(drug) like '%cefdinir%' then 1 + when lower(drug) like '%cefditoren%' then 1 + when lower(drug) like '%cefepime%' then 1 + when lower(drug) like '%cefotetan%' then 1 + when lower(drug) like '%cefotaxime%' then 1 + when lower(drug) like '%cefpodoxime%' then 1 + when lower(drug) like '%cefprozil%' then 1 + when lower(drug) like '%ceftibuten%' then 1 + when lower(drug) like '%ceftin%' then 1 + when lower(drug) like '%cefuroxime %' then 1 + when lower(drug) like '%cefuroxime%' then 1 + when lower(drug) like '%cephalexin%' then 1 + when lower(drug) like '%chloramphenicol%' then 1 + when lower(drug) like '%cipro%' then 1 + when lower(drug) like '%ciprofloxacin%' then 1 + when lower(drug) like '%claforan%' then 1 + when lower(drug) like '%clarithromycin%' then 1 + when lower(drug) like '%cleocin%' then 1 + when lower(drug) like '%clindamycin%' then 1 + when lower(drug) like '%cubicin%' then 1 + when lower(drug) like '%dicloxacillin%' then 1 + when lower(drug) like '%doryx%' then 1 + when lower(drug) like '%doxycycline%' then 1 + when lower(drug) like '%duricef%' then 1 + when lower(drug) like '%dynacin%' then 1 + when lower(drug) like '%ery-tab%' then 1 + when lower(drug) like '%eryped%' then 1 + when lower(drug) like '%eryc%' then 1 + when lower(drug) like '%erythrocin%' then 1 + when lower(drug) like '%erythromycin%' then 1 + when lower(drug) like '%factive%' then 1 + when lower(drug) like '%flagyl%' then 1 + when lower(drug) like '%fortaz%' then 1 + when lower(drug) like '%furadantin%' then 1 + when lower(drug) like '%garamycin%' then 1 + when lower(drug) like '%gentamicin%' then 1 + when lower(drug) like '%kanamycin%' then 1 + when lower(drug) like '%keflex%' then 1 + when lower(drug) like '%ketek%' then 1 + when lower(drug) like '%levaquin%' then 1 + when lower(drug) like '%levofloxacin%' then 1 + when lower(drug) like '%lincocin%' then 1 + when lower(drug) like '%macrobid%' then 1 + when lower(drug) like '%macrodantin%' then 1 + when lower(drug) like '%maxipime%' then 1 + when lower(drug) like '%mefoxin%' then 1 + when lower(drug) like '%metronidazole%' then 1 + when lower(drug) like '%minocin%' then 1 + when lower(drug) like '%minocycline%' then 1 + when lower(drug) like '%monodox%' then 1 + when lower(drug) like '%monurol%' then 1 + when lower(drug) like '%morgidox%' then 1 + when lower(drug) like '%moxatag%' then 1 + when lower(drug) like '%moxifloxacin%' then 1 + when lower(drug) like '%myrac%' then 1 + when lower(drug) like '%nafcillin sodium%' then 1 + when lower(drug) like '%nicazel doxy 30%' then 1 + when lower(drug) like '%nitrofurantoin%' then 1 + when lower(drug) like '%noroxin%' then 1 + when lower(drug) like '%ocudox%' then 1 + when lower(drug) like '%ofloxacin%' then 1 + when lower(drug) like '%omnicef%' then 1 + when lower(drug) like '%oracea%' then 1 + when lower(drug) like '%oraxyl%' then 1 + when lower(drug) like '%oxacillin%' then 1 + when lower(drug) like '%pc pen vk%' then 1 + when lower(drug) like '%pce dispertab%' then 1 + when lower(drug) like '%panixine%' then 1 + when lower(drug) like '%pediazole%' then 1 + when lower(drug) like '%penicillin%' then 1 + when lower(drug) like '%periostat%' then 1 + when lower(drug) like '%pfizerpen%' then 1 + when lower(drug) like '%piperacillin%' then 1 + when lower(drug) like '%tazobactam%' then 1 + when lower(drug) like '%primsol%' then 1 + when lower(drug) like '%proquin%' then 1 + when lower(drug) like '%raniclor%' then 1 + when lower(drug) like '%rifadin%' then 1 + when lower(drug) like '%rifampin%' then 1 + when lower(drug) like '%rocephin%' then 1 + when lower(drug) like '%smz-tmp%' then 1 + when lower(drug) like '%septra%' then 1 + when lower(drug) like '%septra ds%' then 1 + when lower(drug) like '%septra%' then 1 + when lower(drug) like '%solodyn%' then 1 + when lower(drug) like '%spectracef%' then 1 + when lower(drug) like '%streptomycin sulfate%' then 1 + when lower(drug) like '%sulfadiazine%' then 1 + when lower(drug) like '%sulfamethoxazole%' then 1 + when lower(drug) like '%trimethoprim%' then 1 + when lower(drug) like '%sulfatrim%' then 1 + when lower(drug) like '%sulfisoxazole%' then 1 + when lower(drug) like '%suprax%' then 1 + when lower(drug) like '%synercid%' then 1 + when lower(drug) like '%tazicef%' then 1 + when lower(drug) like '%tetracycline%' then 1 + when lower(drug) like '%timentin%' then 1 + when lower(drug) like '%tobi%' then 1 + when lower(drug) like '%tobramycin%' then 1 + when lower(drug) like '%trimethoprim%' then 1 + when lower(drug) like '%unasyn%' then 1 + when lower(drug) like '%vancocin%' then 1 + when lower(drug) like '%vancomycin%' then 1 + when lower(drug) like '%vantin%' then 1 + when lower(drug) like '%vibativ%' then 1 + when lower(drug) like '%vibra-tabs%' then 1 + when lower(drug) like '%vibramycin%' then 1 + when lower(drug) like '%zinacef%' then 1 + when lower(drug) like '%zithromax%' then 1 + when lower(drug) like '%zmax%' then 1 + when lower(drug) like '%zosyn%' then 1 + when lower(drug) like '%zyvox%' then 1 + else 0 + end as antibiotic + from mimiciii_clinical.prescriptions + where drug_type in ('MAIN','ADDITIVE') + -- we exclude routes via the eye, ears, or topically + and route not in ('OU','OS','OD','AU','AS','AD', 'TP') + and lower(route) not like '%ear%' + and lower(route) not like '%eye%' + -- we exclude certain types of antibiotics: topical creams, gels, desens, etc + and lower(drug) not like '%cream%' + and lower(drug) not like '%desensitization%' + and lower(drug) not like '%ophth oint%' + and lower(drug) not like '%gel%' + -- other routes not sure about... + -- for sure keep: ('IV','PO','PO/NG','ORAL', 'IV DRIP', 'IV BOLUS') + -- ? VT, PB, PR, PL, NS, NG, NEB, NAS, LOCK, J TUBE, IVT + -- ? IT, IRR, IP, IO, INHALATION, IN, IM + -- ? IJ, IH, G TUBE, DIALYS + -- ?? enemas?? +) +select + drug --, drug_name_generic + , count(*) as numobs +from t1 +where antibiotic = 1 +group by drug --, drug_name_generic +order by numobs desc; diff --git a/mimic-iii/concepts_postgres/treatment/suspicion_of_infection.sql b/mimic-iii/concepts_postgres/treatment/suspicion_of_infection.sql new file mode 100644 index 000000000..e7a62a9ba --- /dev/null +++ b/mimic-iii/concepts_postgres/treatment/suspicion_of_infection.sql @@ -0,0 +1,139 @@ +-- THIS SCRIPT IS AUTOMATICALLY GENERATED. DO NOT EDIT IT DIRECTLY. +DROP TABLE IF EXISTS suspicion_of_infection; CREATE TABLE suspicion_of_infection AS +-- defines suspicion of infection using prescriptions + microbiologyevents +with abx as +( + select pr.hadm_id + , pr.drug as antibiotic_name + , pr.startdate as antibiotic_time + , pr.enddate as antibiotic_endtime + from mimiciii_clinical.prescriptions pr + -- inner join to subselect to only antibiotic prescriptions + inner join mimiciii_derived.abx_prescriptions_list ab + on pr.drug = ab.drug +) +-- get cultures for each icustay +-- note this duplicates prescriptions +-- each ICU stay in the same hospitalization will get a copy of all prescriptions for that hospitalization +, ab_tbl as +( + select + ie.subject_id, ie.hadm_id, ie.icustay_id + , ie.intime, ie.outtime + , abx.antibiotic_name + , abx.antibiotic_time + , abx.antibiotic_endtime + from mimiciii_clinical.icustays ie + left join abx + on ie.hadm_id = abx.hadm_id +) +, me as +( + select hadm_id + , chartdate, charttime + , spec_type_desc + , max(case when org_name is not null and org_name != '' then 1 else 0 end) as PositiveCulture + from mimiciii_clinical.microbiologyevents + group by hadm_id, chartdate, charttime, spec_type_desc +) +, ab_fnl as +( + select + ab_tbl.icustay_id, ab_tbl.intime, ab_tbl.outtime + , ab_tbl.antibiotic_name + , ab_tbl.antibiotic_time + , coalesce(me72.charttime,me72.chartdate) as last72_charttime + , coalesce(me24.charttime,me24.chartdate) as next24_charttime + , me72.positiveculture as last72_positiveculture + , me72.spec_type_desc as last72_specimen + , me24.positiveculture as next24_positiveculture + , me24.spec_type_desc as next24_specimen + from ab_tbl + -- blood culture in last 72 hours + left join me me72 + on ab_tbl.hadm_id = me72.hadm_id + and ab_tbl.antibiotic_time is not null + and + ( + -- if charttime is available, use it + ( + ab_tbl.antibiotic_time >= me72.charttime + and ab_tbl.antibiotic_time <= datetime_add(me72.charttime, INTERVAL '72' HOUR) + ) + OR + ( + -- if charttime is not available, use chartdate + me72.charttime is null + and ab_tbl.antibiotic_time >= me72.chartdate + and ab_tbl.antibiotic_time <= datetime_add(me72.chartdate, INTERVAL '96' HOUR) + ) + ) + -- blood culture in subsequent 24 hours + left join me me24 + on ab_tbl.hadm_id = me24.hadm_id + and ab_tbl.antibiotic_time is not null + and + ( + -- if charttime is available, use it + ( + ab_tbl.antibiotic_time <= me24.charttime + and ab_tbl.antibiotic_time >= datetime_sub(me24.charttime, INTERVAL '24' HOUR) + ) + OR + ( + -- if charttime is not available, use chartdate + me24.charttime is null + and ab_tbl.antibiotic_time <= me24.chartdate + and ab_tbl.antibiotic_time >= datetime_sub(me24.chartdate, INTERVAL '24' HOUR) + ) + ) +) +, ab_laststg as +( +select + icustay_id + , antibiotic_name + , antibiotic_time + , last72_charttime + , next24_charttime + + -- time of suspected infection: either the culture time (if before antibiotic), or the antibiotic time + , case + when coalesce(last72_charttime,next24_charttime) is null + then 0 + else 1 end as suspected_infection + + , coalesce(last72_charttime,next24_charttime) as suspected_infection_time + + -- the specimen that was cultured + , case + when last72_charttime is not null + then last72_specimen + when next24_charttime is not null + then next24_specimen + else null + end as specimen + + -- whether the cultured specimen ended up being positive or not + , case + when last72_charttime is not null + then last72_positiveculture + when next24_charttime is not null + then next24_positiveculture + else null + end as positiveculture +from ab_fnl +) +select + icustay_id + , antibiotic_name + , antibiotic_time + , last72_charttime + , next24_charttime + , suspected_infection_time + -- -- the below two fields are used to extract data - modifying them facilitates sensitivity analyses + -- , suspected_infection_time - interval '48' hour as si_starttime + -- , suspected_infection_time + interval '24' hour as si_endtime + , specimen, positiveculture +from ab_laststg +order by icustay_id, antibiotic_time; \ No newline at end of file From f8ceb8bd575e9657254d290a81357363bed30796 Mon Sep 17 00:00:00 2001 From: Alistair Johnson Date: Fri, 9 Dec 2022 08:51:16 -0500 Subject: [PATCH 12/12] tidy: update readme with new psql generation approach --- mimic-iii/concepts/README.md | 76 ++++++++---------------------------- 1 file changed, 17 insertions(+), 59 deletions(-) diff --git a/mimic-iii/concepts/README.md b/mimic-iii/concepts/README.md index 9f188f7be..47c8564a2 100644 --- a/mimic-iii/concepts/README.md +++ b/mimic-iii/concepts/README.md @@ -10,14 +10,19 @@ You can read about cloud access to MIMIC-III, including via Google BigQuery, on The rest of this README describes: * [Generating the concepts in BigQuery](#generating-the-concepts-in-bigquery) -* [Generating the concepts in PostgreSQL (\*nix/Mac OS X)](#generating-the-concepts-in-postgresql-nix-mac-os-x) -* [Generating the concepts in PostgreSQL (Windows)](#generating-the-concepts-in-postgresql-windows) +* [Generating the concepts in PostgreSQL](#generating-the-concepts-in-postgresql) ## Generating the concepts in BigQuery You do not need to generate the concepts if you are using BigQuery! They have already been generated for you. If you have access to MIMIC-III on BigQuery, look under `physionet-data.mimic_derived`. If you would like to generate the concepts again, for example on your own dataset, you must modify the `TARGET_DATASET` variable within the [make-concepts.sh](/concepts/make-concepts.sh) script. The script assumes you have installed and configured the [Google Cloud SDK](https://cloud.google.com/sdk/docs/install). -## Generating the concepts in PostgreSQL (\*nix/Mac OS X) +## Generating the concepts in PostgreSQL + +### Quickstart + +Go to the [concepts_postgres](../concepts_postgres) folder, run the [postgres-functions.sql](../concepts_postgres/postgres-make-concepts.sql) and [postgres-make-concepts.sql](../concepts_postgres/postgres-make-concepts.sql) scripts, in that order. + +### In more detail While the SQL scripts here are written in BigQuery's Standard SQL syntax, there are many BigQuery specific functions which do not carry over to PostgreSQL. Nevertheless, with only a few changes, the scripts can be made compatible. In order to generate the concepts on a PostgreSQL database, one must: @@ -25,61 +30,14 @@ While the SQL scripts here are written in BigQuery's Standard SQL syntax, there * modify SQL scripts for incompatible syntax * run the modified SQL scripts and direct the output into tables in the PostgreSQL database -This can be done as follows: - -1. Open a terminal in the `concepts` folder. -2. Run [postgres-functions.sql](postgres-functions.sql). - * e.g. `psql -f postgres-functions.sql` - * This script creates functions which emulate BigQuery syntax. -3. Run [postgres_make_concepts.sh](postgres_make_concepts.sh). - * e.g. `bash postgres_make_concepts.sh` - * This file runs the scripts after applying a few regular expressions which convert table references and date calculations appropriately. - * This file generates all concepts on the `public` schema. - * Exporting DBCONNEXTRA before calling this script will add this to the - connection string. For example, running: - `DBCONNEXTRA="user=mimic password=mimic" bash postgres_make_concepts.sh` - will add these settings to all of the psql calls. (Note that "dbname" - and "search_path" do not need to be set.) - -If you do not have access to a PostgreSQL database with MIMIC, you can read more about building the data within one in the [buildmimic/postgres](https://github.com/MIT-LCP/mimic-code/tree/main/mimic-iii/buildmimic/postgres) folder. - -## Generating the concepts in PostgreSQL (Windows) - -On Windows, it is a bit more complex to generate the concepts in the PostgreSQL database. The approach relies on using \*nix command line tools which are not available by default in a Windows installation. Instead, we have adapted the script into a `.bat` file which relies on the Windows Subsystem for Linux in order to run the shell commands. The steps are: - -1. Install the [Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10). - * If you don't have a preference, follow the steps to install a Ubuntu system. The bat file was tested with Ubuntu, though the commands should work with any flavor of \*nix since we rely on the utils rather than the kernel. -2. Verify you can use the wsl.exe utilities in command prompt. - * Go to run and type `cmd`, or type "command prompt" in the search. - * Run `wsl.exe echo "hi"` - this should print out `hi` back to you -3. Change to your local folder where these concepts are stored - * e.g. `cd C:\Tools\mimic-code-master\concepts` -4. Modify the .bat file: update the `CONNSTR` and `PSQL_PATH` variables. - * Replace `INSERT_PASSWORD_HERE` in `CONNSTR` with your password; or remove it if you have a `.pgpass` file or other form of authentication. If you have a different username or database location, be sure to update those as well. - * Change `PSQL_PATH` to point to your `psql.exe` file. It is currently set to the default location for a PostgreSQL 13 installation. -5. Run the .bat file - * In the command prompt, type `postgres_make_concepts_windows.bat` - -The script echos the commands and the outputs as they run. If it is running successfully, you should see a `SELECT` statement after each command, with the number of rows generated in the table. - -### Can I just do the above manually without WSL? - -Of course! And this might be more informative. - -First, generate the necessary functions as above, by running `postgres-functions.sql` in the SQL shell. -Once that's done, you need to do the following text replacements in all the SQL files: - -1. Replace ````physionet-data.mimiciii_clinical.````, ````physionet-data.mimiciii_derived.```` , and ````physionet-data.mimiciii_notes.```` with just ``. - * This is done by the `REGEX_SCHEMA` variable in the `postgres_make_concepts.sh` script. - * Ideally you should set your search path with `set search_path to public,mimiciii;`. This will create the concepts on `public`, and read data from `mimiciii`. This distinction isn't strictly necessary, but many find it useful. -2. Replace `DATETIME_DIFF(date1, date2, DATE_PART)` with `DATETIME_DIFF(date1, date2, 'DATE_PART')`. - * This adds single quotes around any `DATE_PART`, which is required by PostgreSQL. - * This is done by the `REGEX_DATETIME_DIFF ` variable in the `postgres_make_concepts.sh` script. -3. Add a create table statement at the top of the file, e.g. if the file is named `echo_data.sql`, add `CREATE TABLE echo_data AS` at the top of the file. - * This is done by the `echo` calls in the shell script. -4. Run each file individually in the order specified by the make concepts script. - -The above steps replicate what is done in the shell script (postgres_make_concepts.sh). +The bash script [convert_mimiciii_concepts_bq_to_psql.sh](/convert_mimiciii_concepts_bq_to_psql.sh) has done most of this for you. To generate concepts in PostgreSQL, simply go to the [concepts_postgres](../concepts_postgres) folder and run: + +```sh +\i postgres-functions.sql +\i postgres-make-concepts.sql +``` + +You can also read more about building the data within PostgreSQL in the [buildmimic/postgres](https://github.com/MIT-LCP/mimic-code/tree/main/mimic-iii/buildmimic/postgres) folder. ## List of concepts @@ -190,4 +148,4 @@ Useful snippets of SQL implementing common functions. For example, the `auroc.sq ## other-languages -Scripts in flavours of SQL which are not necessarily compatible with PostgreSQL. +Scripts in flavours of SQL which are not compatible with BigQuery/PostgreSQL.